diff --git a/.github/checks/Makefile b/.github/checks/Makefile index 18cc153d4..93eeddd19 100644 --- a/.github/checks/Makefile +++ b/.github/checks/Makefile @@ -1,7 +1,23 @@ -.PHONY: checkstyle tabs lastline spaces noexec +ifneq ($(shell echo),) + CMD_EXE = 1 +endif -checkstyle: tabs lastline spaces noexec +ifdef CMD_EXE + +.PHONY: checkstyle + +checkstyle: + $(info INFO: style checks require bash.) + +else + +.PHONY: checkstyle lineendings tabs lastline spaces noexec + +checkstyle: lineendings tabs lastline spaces noexec + +lineendings: lineendings.sh + @./lineendings.sh tabs: tabs.sh @./tabs.sh @@ -14,3 +30,5 @@ spaces: spaces.sh noexec: noexec.sh @./noexec.sh + +endif diff --git a/.github/checks/lineendings.sh b/.github/checks/lineendings.sh new file mode 100755 index 000000000..5b445522f --- /dev/null +++ b/.github/checks/lineendings.sh @@ -0,0 +1,18 @@ +#! /bin/bash +OLDCWD=`pwd` +SCRIPT_PATH=`dirname $0` +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 -IUl $'\r'` + +cd $OLDCWD + +if [ x"$FILES"x != xx ]; then + echo "error: found CR in the following files:" >&2 + for n in $FILES; do + echo $n >&2 + done + exit -1 +fi diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 0ba0c6a1f..55be5db1e 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@v2 + uses: actions/checkout@v3 - name: Do some simple style checks shell: bash @@ -43,6 +43,11 @@ jobs: - name: Build the document files. shell: bash run: make -j2 doc + - name: Upload a documents snapshot. + uses: actions/upload-artifact@v3 + with: + name: docs + path: ./html - name: Build 64-bit Windows versions of the tools. run: | make -C src clean @@ -57,7 +62,7 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 769d778d5..2aedb0e25 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -18,7 +18,7 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.1 @@ -44,7 +44,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Do some simple style checks shell: bash @@ -86,20 +86,24 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: - name: cc65-snapshot-win32.zip + name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: - name: cc65-snapshot-win64.zip + name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip - name: Get the online documents repo. - uses: actions/checkout@v2 + uses: actions/checkout@v3 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 + # - apparently only a "classic" token works here + # - the token must exist in the cc65/cc65 repo + token: ${{ secrets.DOC_PAT }} # use secret token instead of default path: doc.git - name: Update the online documents. run: | @@ -110,11 +114,19 @@ jobs: git config user.email "cc65.nomail@github.com" git config push.default simple git add -A - git commit -m "Updated from cc65 commit ${GITHUB_SHA}." - #git push -v + # prevent failure when there is nothing to commit + git diff-index --quiet HEAD || git commit -m "Updated from https://github.com/cc65/cc65/commit/${GITHUB_SHA}" + git push + - name: Package offline documents. + run: 7z a cc65-snapshot-docs.zip ./html/*.* + - name: Upload a Documents Snapshot Zip + uses: actions/upload-artifact@v3 + with: + name: cc65-snapshot-docs + path: cc65-snapshot-docs.zip # enter secrets under "repository secrets" - - name: Upload snapshot to sourceforge + - name: Upload 32-bit Windows snapshot to sourceforge uses: nogsantos/scp-deploy@master with: src: cc65-snapshot-win32.zip @@ -123,5 +135,14 @@ jobs: port: ${{ secrets.SSH_PORT }} user: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} + - name: Upload documents snapshot to sourceforge + uses: nogsantos/scp-deploy@master + with: + src: cc65-snapshot-docs.zip + host: ${{ secrets.SSH_HOST }} + remote: ${{ secrets.SSH_DIR }} + port: ${{ secrets.SSH_PORT }} + user: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_KEY }} # TODO: Publish snapshot zip at https://github.com/cc65/cc65.github.io diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml new file mode 100644 index 000000000..451b37f79 --- /dev/null +++ b/.github/workflows/windows-test-scheduled.yml @@ -0,0 +1,78 @@ +name: Windows Test Scheduled +# Scheduled or manually dispatched because it's slower than the Linux test. + +on: + schedule: + - cron: '0 0 */1 * *' + # every 1 days + workflow_dispatch: + # allow manual dispatch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + # don't run more than once at a time + +jobs: + build_windows: + name: Build, Test (Windows MSVC) + runs-on: windows-latest + + steps: + + # This cache is used to remember the last build. + # If there are no changes and the last build was successful, + # the build and test steps will be omitted. + # If the last build failed, the full attempt will be repeated. + # Github Actions will retain the last build cache for up to 7 days. + + - name: Create Cache + shell: bash + run: mkdir ~/.cache-sha + + - name: Cache SHA + uses: actions/cache@v3 + id: check-sha + with: + path: ~/.cache-sha + key: cache-sha-wintest-${{ github.sha }} + + - name: Git Setup + if: steps.check-sha.outputs.cache-hit != 'true' + shell: bash + run: git config --global core.autocrlf input + + - name: Checkout source + if: steps.check-sha.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + + - name: Add msbuild to PATH + if: steps.check-sha.outputs.cache-hit != 'true' + uses: microsoft/setup-msbuild@v1.1 + + - name: Build app (MSVC debug) + if: steps.check-sha.outputs.cache-hit != 'true' + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + + - name: Build app (MSVC release) + if: steps.check-sha.outputs.cache-hit != 'true' + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + + - name: Build utils (MinGW) + if: steps.check-sha.outputs.cache-hit != 'true' + shell: cmd + run: make -j2 util SHELL=cmd + + - name: Build the platform libraries (make lib) + if: steps.check-sha.outputs.cache-hit != 'true' + shell: cmd + run: make -j2 lib QUIET=1 SHELL=cmd + + - name: Run the regression tests (make test) + if: steps.check-sha.outputs.cache-hit != 'true' + shell: cmd + run: make test QUIET=1 SHELL=cmd + + - name: Test that the samples can be built (make samples) + if: steps.check-sha.outputs.cache-hit != 'true' + shell: cmd + run: make -j2 samples SHELL=cmd diff --git a/Contributing.md b/Contributing.md index 5861a9c2e..3b355373c 100644 --- a/Contributing.md +++ b/Contributing.md @@ -8,11 +8,16 @@ This document contains all kinds of information that you should know if you want * 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 +### Line endings + +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 :) @@ -24,6 +29,23 @@ This is an ongoing controversial topic - everyone knows that. However, the follo 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 + +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: + +* 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. + +Also see the discussion in https://github.com/cc65/cc65/issues/1796 + ### misc * 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. @@ -52,10 +74,12 @@ color := $0787 The following is still very incomplete - if in doubt please look at existing sourcefiles and adapt to the existing style -* Your files should obey the C89 standard. +* 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 - * In printf-style functions use the PRIX64 (and similar) macros to deal with 64bit values + * 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. @@ -112,7 +136,22 @@ You can refer to Annex B of the ISO C99 standard ([here](https://www.open-std.or * 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.) +* 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: +
+    lda #RETURN_VALUE
+    ldx #0 ; return value is char
+
+or, if the value is 0, you can use: +
+    lda #RETURN_VALUE
+    .assert RETURN_VALUE = 0
+    tax
+
+sometimes jumping to return0 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). @@ -159,6 +198,98 @@ The only exception to the above are actions that are exclusive to the github act * 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 +## 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 + +### Floating point support + +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 + +## 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 +``` + ## Test suite * specific tests to check the optimizer (rather than the codegenerator) are needed. diff --git a/Makefile b/Makefile index 909de81ec..29fcbbf96 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ mostlyclean clean: avail unavail bin: @$(MAKE) -C src --no-print-directory $@ -lib: +lib libtest: @$(MAKE) -C libsrc --no-print-directory $@ doc html info: @@ -43,7 +43,7 @@ util: checkstyle: @$(MAKE) -C .github/checks --no-print-directory $@ -# simple "test" target, only run regression tests for c64 target +# runs regression tests, requires libtest target libraries test: @$(MAKE) -C test --no-print-directory $@ diff --git a/README.md b/README.md index 536e59243..e0e2c24e7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Core team members: * [Christian Groessler](https://github.com/groessler): Atari, Atari5200, and CreatiVision library Maintainer * [dqh](https://github.com/dqh-au): GHA help * [Greg King](https://github.com/greg-king5): all around hackery -* [groepaz](https://github.com/mrdudz): CBM libary, Project Maintainer +* [groepaz](https://github.com/mrdudz): CBM library, Project Maintainer * [Oliver Schmidt](https://github.com/oliverschmidt): Apple II library Maintainer External contributors: @@ -23,6 +23,7 @@ External contributors: * [karrika](https://github.com/karrika): Atari 7800 target * [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 *(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)* diff --git a/asminc/_heap.inc b/asminc/_heap.inc index a7d6acea2..1bf80ac82 100644 --- a/asminc/_heap.inc +++ b/asminc/_heap.inc @@ -28,8 +28,8 @@ HEAP_MIN_BLOCKSIZE = .sizeof (freeblock) ; Minimum size of an allocated block HEAP_ADMIN_SPACE = .sizeof (usedblock) ; Additional space for used bock ; Variables -.global __heaporg -.global __heapptr -.global __heapend -.global __heapfirst -.global __heaplast +.global ___heaporg +.global ___heapptr +.global ___heapend +.global ___heapfirst +.global ___heaplast diff --git a/asminc/cx16.inc b/asminc/cx16.inc index 268d701d6..d264add38 100644 --- a/asminc/cx16.inc +++ b/asminc/cx16.inc @@ -239,12 +239,12 @@ BASIC_BUF := $0200 ; Location of command-line BASIC_BUF_LEN = 81 ; Maximum length of command-line SCREEN_PTR := $0262 ; Pointer to current row on text screen (16 bits) -STATUS := $0289 ; Status from previous I/O operation -IN_DEV := $028D ; Current input device number -OUT_DEV := $028E ; Current output device number -FNAM_LEN := $0291 ; Length of filename -SECADR := $0293 ; Secondary address -DEVNUM := $0294 ; Device number +STATUS := $0287 ; Status from previous I/O operation +IN_DEV := $028B ; Current input device number +OUT_DEV := $028C ; Current output device number +FNAM_LEN := $028F ; Length of filename +SECADR := $0291 ; Secondary address +DEVNUM := $0292 ; Device number CURS_COLOR := $0373 ; Color under the cursor CHARCOLOR := $0376 ; Cursor's color nybbles (high: background, low: foreground) RVS := $0377 ; Reverse flag @@ -258,8 +258,8 @@ LLEN := $0386 ; Line length NLINES := $0387 ; Number of screen lines ; BASIC -VARTAB := $03E2 ; Pointer to start of BASIC variables -MEMSIZE := $03EA ; Pointer to highest BASIC RAM location (+1) +VARTAB := $03E1 ; Pointer to start of BASIC variables +MEMSIZE := $0259 ; Pointer to highest BASIC RAM location (+1) ; --------------------------------------------------------------------------- ; Vector and other locations @@ -538,11 +538,11 @@ NMIVec := $0318 ; YM2151 audio chip .struct YM2151 .org $9F40 - .union - STATUS .byte ADDR .byte - .endunion + .union DATA .byte + STATUS .byte + .endunion .endstruct ; X16 Emulator device diff --git a/asminc/errno.inc b/asminc/errno.inc index 1efe88cda..2e876c7ac 100644 --- a/asminc/errno.inc +++ b/asminc/errno.inc @@ -4,10 +4,10 @@ ; Variables and functions - .global __errno, __oserror - .global __osmaperrno - .global __seterrno - .global __directerrno, __mappederrno + .global ___errno, ___oserror + .global ___osmaperrno + .global ___seterrno + .global ___directerrno, ___mappederrno ; Error codes, must match the values in the C headers .enum diff --git a/asminc/kim1.inc b/asminc/kim1.inc new file mode 100644 index 000000000..81e83b83c --- /dev/null +++ b/asminc/kim1.inc @@ -0,0 +1,31 @@ +; --------------------------------------------------------------------------- +; +; KIM-1 definitions +; +; --------------------------------------------------------------------------- + + +RAMSTART := $0200 ; Entry point + + +; --------------------------------------------------------------------------- +; Monitor Functions +; --------------------------------------------------------------------------- +OUTCHR := $1EA0 ; Output character +INTCHR := $1E5A ; Input character without case conversion +DUMPT := $1800 ; Dump memory to tape +LOADT := $1873 ; Load memory from tape +START := $1C4F ; Enter KIM-1 monitor +SCANDS := $1F1F ; Scan 7-segment display +KEYIN := $1F40 ; Open up keyboard channel +GETKEY := $1F6A ; Return key from keyboard + + +; --------------------------------------------------------------------------- +; System Memory +; --------------------------------------------------------------------------- +SAL := $17F5 ; Tape load address low +SAH := $17F6 ; Tape load address high +EAL := $17F7 ; Tape address end low +EAH := $17F8 ; Tape address end high +ID := $17F9 ; Tape Identification number diff --git a/asminc/signal.inc b/asminc/signal.inc index ebde07e42..597cad413 100644 --- a/asminc/signal.inc +++ b/asminc/signal.inc @@ -49,8 +49,8 @@ SIGCOUNT = 6 ; Number of signals .global sigtable ; Function declarations -.global __sig_ign -.global __sig_dfl +.global ___sig_ign +.global ___sig_dfl .global _signal .global _raise diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index 682696887..c57bd3de8 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -277,6 +277,8 @@ XRECLK = $3C ; Reset clock XCLCL = $3D ; Close clock XWRCLK = $3E ; Displays clock in the address in A & Y registers +XFSEEK = $3F ; Only in Orix + ; Sound primitives XSONPS = $40 ; Send data to PSG register (14 values) XOUPS = $42 ; Send Oups sound into PSG diff --git a/cfg/kim1-60k.cfg b/cfg/kim1-60k.cfg new file mode 100644 index 000000000..087715560 --- /dev/null +++ b/cfg/kim1-60k.cfg @@ -0,0 +1,41 @@ +# kim1-60k.cfg (4k) +# +# for expanded KIM-1 +# +# ld65 --config kim1-60k.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.cfg b/cfg/kim1.cfg new file mode 100644 index 000000000..f48fed80e --- /dev/null +++ b/cfg/kim1.cfg @@ -0,0 +1,41 @@ +# kim1.cfg (4k) +# +# for unexpanded KIM-1 +# +# ld65 --config kim1.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $0200; + 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 = $1000 - %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/doc/atari.sgml b/doc/atari.sgml index 170ffd5fb..3057cd8a6 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -332,6 +332,7 @@ See the for declaration and u _scroll _setcolor _setcolor_low +_sound waitvsync @@ -442,19 +443,7 @@ package delivers the same feature. You can switch back to the ATASCII mapping by including " -#include <atari_screen_charmap.h> -char pcScreenMappingString[] = "Hello Atari!"; - -#include <atari_atascii_charmap.h> -char pcAtasciiMappingString[] = "Hello Atari!"; - - -delivers correct results, while +Example: #include <atari_screen_charmap.h> @@ -464,8 +453,6 @@ char* pcScreenMappingString = "Hello Atari!"; char* pcAtasciiMappingString = "Hello Atari!"; -does not. - Keyboard codes

For direct keyboard scanning in conjunction with e.g. the OS location "CH" (764/$2FC), diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 19fd3aa2a..b4ef3e188 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -125,6 +125,7 @@ Long options: --target sys Set the target system --verbose Increase verbosity --version Print the assembler version + --warnings-as-errors Treat warnings as errors --------------------------------------------------------------------------- @@ -183,7 +184,7 @@ Here is a description of all the command line options: Enable an emulation feature. This is identical as using @@ -359,6 +360,13 @@ Here is a description of all the command line options: warning level is 1, and it would probably be silly to set it to something lower. + +

@@ -431,6 +439,15 @@ The assembler accepts command was given). +On 6502-derived platforms the + brk ; 1-byte: $00 + brk $34 ; 2-bytes: $00 $34 + brk #$34 ; 2-bytes: $00 $34 + + 65816 mode

@@ -448,6 +465,17 @@ mnemonics: TSA is an alias for TSC +The + mvn #^src, #^dst ; bank of src to bank of dst + mvn src, dst ; bank of src to bank of dst + mvp #$12, #$78 ; bank $12 to $78 + mvp $123456, $789ABC ; bank $12 to $78 + + 6502X mode

@@ -1381,10 +1409,6 @@ either a string or an expression value. .endmacro - This command is new and must be enabled with the - .BANK

@@ -2019,7 +2043,7 @@ Here's a list of all control commands and a description, what they do: .A16

- Valid only in 65816 mode. Switch the accumulator to 16 bit. + Valid only in 65816 mode. Assume the accumulator is 16 bit. Note: This command will not emit any code, it will tell the assembler to create 16 bit operands for immediate accumulator addressing mode. @@ -2029,7 +2053,7 @@ Here's a list of all control commands and a description, what they do: .A8

- Valid only in 65816 mode. Switch the accumulator to 8 bit. + Valid only in 65816 mode. Assume the accumulator is 8 bit. Note: This command will not emit any code, it will tell the assembler to create 8 bit operands for immediate accu addressing mode. @@ -2112,15 +2136,15 @@ Here's a list of all control commands and a description, what they do: the assembler will force a segment alignment to the least common multiple of - 15, 18 and 251 - which is 22590. To protect the user against errors, the - assembler will issue a warning when the combined alignment exceeds 256. The - command line option will disable this warning. + 15, 18 and 251 - which is 22590. To protect the user against errors, when the + combined alignment is larger than the explicitly requested alignments, + the assembler will issue a warning if it also exceeds 256. The command line + option + will disable this warning. - Please note that with alignments that are a power of two (which were the - only alignments possible in older versions of the assembler), the problem is - less severe, because the least common multiple of powers to the same base is - always the larger one. + Please note that with only alignments that are a power of two, a warning will + never occur, because the least common multiple of powers to the same base is + always simply the larger one. @@ -2255,7 +2279,7 @@ See: ,,,

- Marks the end of a macro definition. + Marks the end of a macro definition. Note, .ENDMACRO should be on + its own line to successfully end the macro definition. It is possible to use + to create a symbol that references + .ENDMACRO without ending the macro definition. + + Example: + + + .macro new_mac + .define startmac .macro + .define endmac .endmacro + .endmacro + See: , , @@ -2738,25 +2774,23 @@ See: , - - Enables the .ADDRSIZE pseudo function. This function is experimental and not enabled by default. - - See also: - at_in_identifiers Accept the at character ('@') as a valid character in identifiers. The @@ -2825,6 +2859,23 @@ See: , + + Affects 65816 mode only. + + Allows jsr and jmp to produce long jumps if the target + address has been previously declared in a far segment, + or imported as far. + Otherwise jsl and jml must be used instead. + + Also allows to convert rts + to a long return rtl when the enclosing scope or memory model + indicates returning from a far procedure. + + This permits compatibility with the old behavior of this assembler, or other + assemblers which similarly allowed jsr and jmp to be used + this way. + loose_char_term Accept single quotes as well as double quotes as terminators for char @@ -3036,7 +3087,7 @@ See: ,

- Valid only in 65816 mode. Switch the index registers to 16 bit. + Valid only in 65816 mode. Assume the index registers are 16 bit. Note: This command will not emit any code, it will tell the assembler to create 16 bit operands for immediate operands. @@ -3047,7 +3098,7 @@ See: ,

- Valid only in 65816 mode. Switch the index registers to 8 bit. + Valid only in 65816 mode. Assume the index registers are 8 bit. Note: This command will not emit any code, it will tell the assembler to create 8 bit operands for immediate operands. @@ -3319,7 +3370,7 @@ See: ,,

- Enable output to the listing. The command must be followed by a boolean + Enable output to the listing. The command can be followed by a boolean switch ("on", "off", "+" or "-") and will enable or disable listing output. The option has no effect if the listing is not enabled by the command line @@ -3979,7 +4030,7 @@ See: ,.SMART

- Switch on or off smart mode. The command must be followed by a '+' or '-' + Switch on or off smart mode. The command can be followed by a '+' or '-' character to switch the option on or off respectively. The default is off (that is, the assembler doesn't try to be smart), but this default may be changed by the -s switch on the command line. @@ -3994,7 +4045,9 @@ See: ,In 65816 mode, replace a In 65816 mode, if the feature is enabled, + smart mode will replace a tests the rest of the line and returns true, if there are any tokens on the remainder of the line. Since @@ -4211,15 +4269,15 @@ opposite. Look at this example: -.macro ldaxy a, x, y -.ifnblank a - lda #a +.macro ldaxy i, j, k +.ifnblank i + lda #i .endif -.ifnblank x - ldx #x +.ifnblank j + ldx #j .endif -.ifnblank y - ldy #y +.ifnblank k + ldy #k .endif .endmacro diff --git a/doc/cc65-intern.sgml b/doc/cc65-intern.sgml index 8e36578b5..904cee070 100644 --- a/doc/cc65-intern.sgml +++ b/doc/cc65-intern.sgml @@ -6,7 +6,8 @@ Internal details of cc65 code generation, -such as calling assembly functions from C. +such as the expected linker configuration, +and calling assembly functions from C. @@ -16,6 +17,76 @@ such as calling assembly functions from C. +Linker configuration

+ +The C libraries and code generation depend directly on a suitable linker configuration. +There are premade configuration files in the + +Used by the C library and generated code for efficient internal and temporary state storage, +also called "pseudo-registers". + + + +Used by each platform instance of the C library in + +The default segment for generated code, and most C library code will be located here. + +Use + +Used for uninitialized variables. +Originally an acronym for "Block Started by Symbol", but the meaning of this is now obscure. + +Use + +Used for initialized variables. + +On some platforms, this may be initialized as part of the program loading process, +but on others it may have a separate + +Used for read-only (constant) data. + +Use + +This currently defines table locations for the Calling assembly functions from C

Calling conventions

diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 43039f713..4303edb48 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -741,7 +741,7 @@ Here is a description of all the command line options: Warn about no return statement in function returning non-void. - Warn when passing structs by value. + Warn when passing structs by value. (Disabled by default.) Warn about #pragmas that aren't recognized by cc65. @@ -754,6 +754,8 @@ Here is a description of all the command line options: Warn about unused function parameters. Warn about unused variables. + + Warn if numerical constant conversion implies overflow. (Disabled by default.) The full list of available warning names can be retrieved by using the @@ -806,9 +808,8 @@ and the one defined by the ISO standard: The datatypes "float" and "double" are not available.

- C Functions may not return structs (or unions), and structs may not - be passed as parameters by value. However, struct assignment *is* - possible. + C Functions may pass and return structs (or unions) by value, but only + of 1, 2 or 4 byte sizes.

Most of the C library is available with only the fastcall calling convention (). It means @@ -1202,17 +1203,34 @@ The compiler defines several macros at startup: This macro is defined if the target is the Commodore Plus/4 (-t plus4). - __STDC_HOSTED__ - - This macro is expands to the integer constant 1. - __SIM6502__ This macro is defined if the target is sim65 in 6502 mode (-t sim6502). __SIM65C02__ + This macro is defined if the target is sim65 in 65C02 mode (-t sim65c02). + __STDC_HOSTED__ + + This macro expands to the integer constant 1. + + __STDC_NO_ATOMICS__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_COMPLEX__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_THREADS__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_VLA__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + __SUPERVISION__ This macro is defined if the target is the Supervision (-t supervision). diff --git a/doc/da65.sgml b/doc/da65.sgml index bf074a667..3a01ce4f5 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -115,14 +115,14 @@ Here is a description of all the command line options: 6502dtv 65sc02 65c02 + 65816 huc6280 4510 6502x is for the NMOS 6502 with unofficial opcodes. 6502dtv is for the emulated CPU of the C64DTV device. huc6280 is the CPU of the PC engine. - 4510 is the CPU of the Commodore C65. Support for the 65816 currently - is not available. + 4510 is the CPU of the Commodore C65. 65816 is the CPU of the SNES.

@@ -461,7 +462,8 @@ following attributes are recognized: END This gives the end address of the range. The end address is inclusive, that means, it is part of the range. Of course, it may not be smaller than the - start address. + start address. Optionally, the end may be given as a decimal offset instead + of an absolute address, "+3", to specify it as a size. NAME This is a convenience attribute. It takes a string argument and will cause @@ -520,6 +522,16 @@ following attributes are recognized: + UNIT + Split the table into sections of this size. For example, if you have a + ByteTable of size 48, but it has logical groups of size 16, specifying + 16 for UNIT adds newlines after every 16 bytes. UNIT is always in bytes. + + ADDRMODE + 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 + mean the flag is enabled. + @@ -692,6 +704,20 @@ directives explained above: +Helper scripts

+ +util/parse-bsnes-log.awk is a supplied script for 65816 disassembly, +to parse bsnes-plus Code-Data log files and output the RANGE sections +for your info file. For typical usage, you'd check the S-CPU log and trace +log mask boxes in the bsnes-plus debugger, play through the game, then grep +for the bank you're disassembling, and pass that to this script. + + +grep ^83 my-game-log | parse-bsnes-log.awk + + + + Copyright

da65 (and all cc65 binutils) is (C) Copyright 1998-2011, Ullrich von diff --git a/doc/doc.css b/doc/doc.css index e4c316e16..0f6e90d67 100644 --- a/doc/doc.css +++ b/doc/doc.css @@ -2,12 +2,14 @@ body { font-family: arial, helvetica, sans-serif; font-size: 100%; text-align: justify; - margin-left: 110px; - margin-top: 10px; - margin-right: 30px; - margin-bottom: 10px; + margin: 0px; + padding-left: 110px; + padding-top: 10px; + padding-right: 30px; + padding-bottom: 10px; background-image: url(doc.png); background-repeat: repeat-y; + background-position:left top; } h1, h2, h2 a:link, h2 a:active, h2 a:visited { @@ -25,7 +27,7 @@ h1 { } h2 { - font-size: 160%; + font-size: 150%; text-shadow: 1px 1px 3px #303030; letter-spacing: 1px; margin-top: 2em; diff --git a/doc/funcref.sgml b/doc/funcref.sgml index a8593ebb5..2a6d77adc 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -126,6 +126,7 @@ function. + @@ -975,7 +976,7 @@ previously been allocated by /, / or /. Passing a pointer to a block that was is not the result of one of the -allocation functions, or that has been free'd will give unpredicable results. +allocation functions, or that has been free'd will give unpredictable results. The function is available only as a fastcall function; so, it may be used only in the presence of a prototype. @@ -1108,6 +1109,46 @@ considered random to a certain degree. +_sound

+ + +/ + +The function is available only as a fastcall function; so, it may be used +only in the presence of a prototype. +The function is specific to the Atari 8 bit. + Voice can be any of 0-3 different sound channels. + Pitch goes from 0-255 (about 125 Hz to 32 Khz). + Distortion (0-14) uses poly dividers to reshape wave in order to create a noise effect. Use 10 for a "pure" square-wave sound. +Volume (0-15) is the intensity for the wave. +Extra bits in those parameters will be ignored. +Parameters are the same as for the AtariBASIC SOUND statement. + + + +#include +int main(void) +{ + int i=0; + unsigned char j; + printf("playing sound \n"); + for (j=0; j<144; j++) { + _sound(1,144-j,10,8); //change the pitch for voice 1 + for (i=0; i<50; i++); //pause for sound duration + } + _sound(1,0,0,0); //use zero in other parameters to stop the sound + return 0; +} + + + + _stroserror

@@ -4148,7 +4189,7 @@ be used in presence of a prototype. chdir (getdevicedir (device, buf, sizeof buf)); -cf. @@ -6908,7 +6949,6 @@ switching the CPU into single clock mode. - srand

diff --git a/doc/index.sgml b/doc/index.sgml index bb3ad5357..727364028 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -59,7 +59,7 @@ Contains hints on creating the most effective code with cc65. - Describes internal details of cc65, such as calling conventions. + Describes internal details of cc65: linker configuration, calling conventions, etc. Build programs, using the GNU Make utility. @@ -154,6 +154,9 @@ Topics specific to the Bit Corporation Gamate Console. + + Topics specific to the MOS Technology KIM-1. + Topics specific to the Atari Lynx Game Console. diff --git a/doc/kim1.sgml b/doc/kim1.sgml new file mode 100644 index 000000000..1387d3b19 --- /dev/null +++ b/doc/kim1.sgml @@ -0,0 +1,150 @@ + + +

+MOS Technology KIM-1 specific information for cc65 +<author><url url="mailto:davepl@davepl.com" name="Dave Plummer"> + +<abstract> +An overview of the KIM-1 runtime system as it is implemented for the cc65 C compiler. +</abstract> + +<!-- Table of contents --> +<toc> + +<!-- Begin the document --> + +<sect>Overview<p> + +This file contains an overview of the KIM-1 runtime system as it comes with the cc65 C compiler. +It describes the memory layout, KIM-1 specific header files, available drivers, and any pitfalls +specific to the platform. + +Please note that KIM-1 specific functions are just mentioned here, they are described in detail +in the separate <url url="funcref.html" name="function reference">. Even functions marked as +"platform dependent" may be available on more than one platform. Please see the +function reference for more information. + +<sect>Binary format<p> + +The output format generated by the linker for the KIM-1 target is a raw binary BIN file, which +is essentially a memory image. You can convert this to a papertape format file using +Convert8bithexformat or KIMPaper, which are open-source conversion utility programs. +A papertape format files can be transferred to the KIM-1 using the RS-232 terminal port (TTY), +just as if the machine-code was entered by hand. Enter 'L' in the TTY and start the paper tape file +transfer. + +<p> + +Included with this distribution is a 4k configuration file and a 60k config file. The KIM-1 +on-board memory is limited to 4 kbytes but system memory can be increased to 60 kbytes of +contiguous RAM with aftermarket add-on boards. So choose the config file that matches your +system configuration before compiling and linking user programs. + +<sect>Memory layout<p> + +The ROMs and I/O areas are defined in the configuration files, as are most of the entry points +for useful subroutines in the KIM-1 monitor ROM. cc65 generated programs compiled and linked +using 4k config run in the memory range of $200 - $0FFF. The 60k config expands +this range to $DFFF. When using the 4k config the starting memory location and entry point +for running the program is $200, so when the program is transferred to the KIM-1, it is +executed by typing '200 G'. With the 60k config the default starting memory location and entry +point is $2000. + +Special locations: + +<descrip> + <tag/Text screen/ + Conio support is not currently available for the KIM-1. But stdio console functions are available. + + <tag/Stack/ + The C runtime stack is located at $0FFF on 4kb KIM-1s, or at $DFFF for 60kb systems. + The stack always grows downwards. + + <tag/Heap/ + The C heap is located at the end of the program and grows towards the C runtime stack. + +</descrip><p> + +<sect>Platform specific header files<p> + +Programs containing KIM-1 code may use the <tt/kim1.h/ header file. See the header file for more information. + +<sect>Loadable drivers<p> + +<sect1>Graphics drivers<p> + +No graphics drivers are currently available for the KIM-1. + +<sect1>Joystick drivers<p> + +No joystick driver is currently available for the KIM-1. + +<sect1>Mouse drivers<p> + +No mouse drivers are currently available for the KIM-1. + +<sect1>RS232 device drivers<p> + +No communication port drivers are currently available for the KIM-1. It has only the "master console" +e.g. stdin and stdout. + +<sect>Limitations<p> + +<sect1>Disk I/O<p> + +The existing library for the KIM-1 doesn't implement C file I/O. + +To be more specific, this limitation means that you cannot use any of the following functions (and a few others): + +<itemize> +<item>fopen +<item>fclose +<item>fread +<item>fwrite +<item>... +</itemize> + +<sect>Other hints<p> + +<sect1>kim1.h<p> +This header exposes KIM-1 specific I/O functions that are useful for reading and writing its ports and front panel. +See the <tt/kim1.h/ include file for a list of the functions available. + +<sect1>Limited memory applications<p> + +As stated earlier, there are config files for 4kb and 60kb systems. If you have 60kb RAM, then you will probably +want to use the kim1-60k configuration, but if not - if you are using the kim1-4k configuration - then you may +want to use functions like getchar, putchar, gets and puts rather than functions like scanf and printf. +Printf, for example, requires about 1KB because it needs to know how to process all the format specifiers. + +<sect1>Sample programs<p> + +These sample programs can be found in the samples/kim1 directory: + +<itemize> +<item>kimHello prints "Hello World!" and then inputs characters, which are echoed on the screen. + This program will run on both 4kb and 60kb systems.</item> +<item>kimSieve finds the prime numbers up to 100,000 using the Sieve of Eratosthenes algorithm, and prints how many + prime numbers were found. This program requires a 60kb system to run.</item> +</itemize> + +<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/ld65.sgml b/doc/ld65.sgml index 307caeaa4..1ad04b395 100644 --- a/doc/ld65.sgml +++ b/doc/ld65.sgml @@ -90,6 +90,7 @@ Long options: --start-group Start a library group --target sys Set the target system --version Print the linker version + --warnings-as-errors Treat warnings as errors --------------------------------------------------------------------------- </verb></tscreen> @@ -330,6 +331,13 @@ Here is a description of all of the command-line options: directory, in the list of directories specified using <tt/--obj-path/, in directories given by environment variables, and in a built-in default directory. + + <label id="option--warnings-as-errors"> + <tag><tt>--warnings-as-errors</tt></tag> + + An error will be generated if any warnings were produced. + + </descrip> diff --git a/doc/osi.sgml b/doc/osi.sgml index eeaee4a97..62d466406 100644 --- a/doc/osi.sgml +++ b/doc/osi.sgml @@ -187,8 +187,21 @@ Currently the following extra screen configuration modules are implemented: <itemize> <item><tt>osic1p-screen-s3-32x28.o</tt>: 32 columns by 28 lines mode for Briel Superboard ///</item> +<item><tt>osic1p-screen-c1p-48x12.s</tt>: 48 columns by 12 lines mode +for Challenger 1P</item> </itemize> +On the Briel Superboard /// you enter 32 column mode by holding down +the BREAK key on powerup. + +On the Challenger 1P you can enable 48 column mode by writing a 1 to +bit 0 of address $D800, and writing a 0 to go back to 24 column mode. +You can use code like the following to do this: + +<tscreen><verb> +*(char*)0xd800 = 1; /* Switch to 48 column mode */ +</verb></tscreen> + <sect>Limitations<p> <sect1>stdio implementation<p> diff --git a/doc/sim65.sgml b/doc/sim65.sgml index 310de4667..b1e5afbdc 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -44,6 +44,13 @@ The simulator is called as follows: --version Print the simulator version number </verb></tscreen> +sim65 will exit with the error code of the simulated program, +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/. + <sect1>Command line options in detail<p> @@ -126,9 +133,17 @@ a set of built-in paravirtualization functions (<ref id="paravirt-internal" name <sect>Creating a Test in Assembly<p> Assembly tests may similarly be assembled and linked with -<tt/--target sim6502/ or <tt/--target sim65c02/, -and the sim65 library provides an <tt/exit/ symbol that the program may <tt/JMP/ -to terminate with the current A register value as an exit code. +<tt/--target sim6502/ or <tt/--target sim65c02/. +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/. + +</itemize> The binary file has a 12 byte header: diff --git a/doc/sp65.sgml b/doc/sp65.sgml index 255d7a552..e1bcdd558 100644 --- a/doc/sp65.sgml +++ b/doc/sp65.sgml @@ -44,6 +44,7 @@ Short options: -lc List all possible conversions -r file[,attrlist] Read an input file -v Increase verbosity + -p tgt,file[,attrlist] Write the palette to a file -w file[,attrlist] Write the output to a file Long options: @@ -56,6 +57,7 @@ Long options: --slice x,y,w,h Generate a slice from the loaded bitmap --verbose Increase verbosity --version Print the version number and exit + --palette tgt,file{,attrlist] Write the palette to a file --write file[,attrlist] Write the output to a file --------------------------------------------------------------------------- </verb></tscreen> @@ -124,6 +126,13 @@ attribute lists see <ref id="attr-lists" name="below">. bugfixes, please include the version number. + <label id="option--palette"> + <tag><tt>-p, --palette target,filename[,attrlist]</tt></tag> + + Write the palette of the input bitmap to a file in a format suitable of + the target. + + <label id="option--write"> <tag><tt>-w, --write filename[,attrlist]</tt></tag> @@ -265,6 +274,7 @@ of a sprite is roughly 508 pixels but in reality the Lynx screen is only 160 by 102 pixels which makes very large sprites useless. The number per pixels is taken from the number of colors of the input bitmap. +You can also force the number of pens used in the conversion. There are a few attributes that you can give to the conversion software. @@ -273,7 +283,7 @@ There are a few attributes that you can give to the conversion software. <tag/mode/ The first is what kind of encoding to use for the sprite. The attribute for this is called "mode" and the possible values are "literal", "packed" or - "transparent". The default is "packed" if no mode is specified. + "shaped". The default is "packed" if no mode is specified. The "literal" is a totally literal mode with no packing. In this mode the number of pixels per scanline will be a multiple of 8 both right and left from @@ -290,10 +300,26 @@ There are a few attributes that you can give to the conversion software. using run-length encoding and literal coding mixed for optimisation to produce a small sprite. - The last encoding mode "transparent" is like packed. But here we know that - the index 0 will be transparent so we can clip off all 0 pixels from the left - and right edge of the sprite. This will produce the smallest sprite possible - on the Lynx. The sprite is not rectangular anymore. + The last encoding mode "shaped" is like packed. But we can stop the conversion + to the right abd left edge when we get the first "edge" colour. If no edge + colour is specified we stop at the first index 0 colour. + If your edge index is outside the range 0..15 then your sprite can use all + the colours in the defined palette. + This will also produce the smallest sprite possible on the Lynx. The sprite + is not rectangular anymore. + + <tag/edge/ + This keyword is only meaningful for shaped sprites. By default it is 0. + The shaped sprite outer edge is defined by the colour index "edge". + + <tag/pen/ + This keyword defines the order the colours in the original bitmap is + mapped to the Lynx sprite. The length of the pen also defines the depth + of the generated sprite. + If you want to create a 1 BPP sprite you can define the two indices used + in the sprite like pen=34. Now areas in colour index 3 will be mapped as 0. + Areas in colour index 4 will be mapped as 1. + The default pen=0123456789abcdef. <tag/ax/ The sprite is painted around the Anchor point. The anchor point x can be @@ -301,7 +327,9 @@ There are a few attributes that you can give to the conversion software. painting the sprite in location 10,20 will set the left edge of the sprite 10 pixels from the left of the Lynx screen. When the sprite is scaled by hardware the anchor point stays in place and the sprite grows or shrinks - around the anchor point. The default value is 0 (left). + around the anchor point. You can also define the location using the words + "mid" for the center or "max" for the right edge. + The default value is 0 (left). <tag/ay/ The sprite is painted around the Anchor point. The anchor point y can be @@ -309,7 +337,8 @@ There are a few attributes that you can give to the conversion software. painting the sprite in location 10,20 will set the top of the sprite 20 pixels from the top of the Lynx screen. When the sprite is scaled by hardware the anchor point stays in place and the sprite grows or shrinks - around the anchor point. The default value is 0 (top). + around the anchor point. You can also define the location using the words + "mid" for the center or "max" for the bottom. The default value is 0 (top). </descrip> diff --git a/include/_atari5200os.h b/include/_atari5200os.h index 5bba43016..196b69e56 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -54,7 +54,21 @@ struct __os { unsigned char color2; // = $0E PF color 2 unsigned char color3; // = $0F PF color 3 unsigned char color4; // = $10 PF color 4 - unsigned char _free_1[0xEF]; // = $11-$FF User space + unsigned char paddl0; // = $11 POT0 Shadow + unsigned char paddl1; // = $12 POT1 Shadow + unsigned char paddl2; // = $13 POT2 Shadow + unsigned char paddl3; // = $14 POT3 Shadow + unsigned char paddl4; // = $15 POT4 Shadow + unsigned char paddl5; // = $16 POT5 Shadow + unsigned char paddl6; // = $17 POT6 Shadow + unsigned char paddl7; // = $18 POT7 Shadow + + /*cc65 runtime zero page variables*/ + unsigned char rowcrs_5200; // = $19 Cursor row (conio) + unsigned char colcrs_5200; // = $1A Cursor column (conio) + unsigned char* savmsc; // = $1B/$1C Pointer to screen memory (conio) + + unsigned char _filler_1[0xE3]; // = $1D-$FF Filler /*Stack*/ unsigned char stack[0x100]; // = $100-$1FF Stack diff --git a/include/_atarios.h b/include/_atarios.h index ec33b98c9..cbf33bda6 100644 --- a/include/_atarios.h +++ b/include/_atarios.h @@ -334,17 +334,17 @@ struct __os { void (*vserin)(void); // = $020A/$020B POKEY SERIAL INPUT READY IRQ void (*vseror)(void); // = $020C/$020D POKEY SERIAL OUTPUT READY IRQ void (*vseroc)(void); // = $020E/$020F POKEY SERIAL OUTPUT COMPLETE IRQ - void (*vtimr1)(void); // = $0210/$0201 POKEY TIMER 1 IRQ - void (*vtimr2)(void); // = $0212/$0203 POKEY TIMER 2 IRQ - void (*vtimr4)(void); // = $0214/$0205 POKEY TIMER 4 IRQ - void (*vimirq)(void); // = $0216/$0207 IMMEDIATE IRQ VECTOR - unsigned int cdtmv1; // = $0218/$0210 COUNT DOWN TIMER 1 + void (*vtimr1)(void); // = $0210/$0211 POKEY TIMER 1 IRQ + void (*vtimr2)(void); // = $0212/$0213 POKEY TIMER 2 IRQ + void (*vtimr4)(void); // = $0214/$0215 POKEY TIMER 4 IRQ + void (*vimirq)(void); // = $0216/$0217 IMMEDIATE IRQ VECTOR + unsigned int cdtmv1; // = $0218/$0219 COUNT DOWN TIMER 1 unsigned int cdtmv2; // = $021A/$021B COUNT DOWN TIMER 2 unsigned int cdtmv3; // = $021C/$021D COUNT DOWN TIMER 3 unsigned int cdtmv4; // = $021E/$021F COUNT DOWN TIMER 4 unsigned int cdtmv5; // = $0220/$0221 COUNT DOWN TIMER 5 void (*vvblki)(void); // = $0222/$0223 IMMEDIATE VERTICAL BLANK NMI VECTOR - void (*vvblkd)(void); // = $0224/$0224 DEFERRED VERTICAL BLANK NMI VECTOR + void (*vvblkd)(void); // = $0224/$0225 DEFERRED VERTICAL BLANK NMI VECTOR void (*cdtma1)(void); // = $0226/$0227 COUNT DOWN TIMER 1 JSR ADDRESS void (*cdtma2)(void); // = $0228/$0229 COUNT DOWN TIMER 2 JSR ADDRESS unsigned char cdtmf3; // = $022A COUNT DOWN TIMER 3 FLAG diff --git a/include/_heap.h b/include/_heap.h index c054cfa34..62a7f4bed 100644 --- a/include/_heap.h +++ b/include/_heap.h @@ -37,13 +37,20 @@ struct freeblock { /* Variables that describe the heap */ -extern unsigned* _heaporg; /* Bottom of heap */ -extern unsigned* _heapptr; /* Current top */ -extern unsigned* _heapend; /* Upper limit */ -extern struct freeblock* _heapfirst; /* First free block in list */ -extern struct freeblock* _heaplast; /* Last free block in list */ - +extern unsigned* __heaporg; /* Bottom of heap */ +extern unsigned* __heapptr; /* Current top */ +extern unsigned* __heapend; /* Upper limit */ +extern struct freeblock* __heapfirst; /* First free block in list */ +extern struct freeblock* __heaplast; /* Last free block in list */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _heaporg __heaporg +#define _heapptr __heapptr +#define _heapend __heapend +#define _heapfirst __heapfirst +#define _heaplast __heaplast +#endif /* End of _heap.h */ diff --git a/include/apple2.h b/include/apple2.h index cb15cab97..9f644bc97 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -171,7 +171,7 @@ extern struct { 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[]; +extern void a2_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ extern void a2_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2_lo_tgi[]; #endif diff --git a/include/apple2enh.h b/include/apple2enh.h index 58e0b397f..bfe5cdb18 100644 --- a/include/apple2enh.h +++ b/include/apple2enh.h @@ -99,7 +99,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[]; +extern void a2e_ssc_ser[]; /* Referred to by ser_static_stddrv[] */ extern void a2e_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void a2e_lo_tgi[]; diff --git a/include/assert.h b/include/assert.h index a4dcd5d7f..ca8d4acc1 100644 --- a/include/assert.h +++ b/include/assert.h @@ -42,8 +42,8 @@ #ifdef NDEBUG # define assert(expr) #else -extern void __fastcall__ _afailed (const char*, unsigned); -# define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) +extern void __fastcall__ __afailed (const char*, unsigned); +# define assert(expr) ((expr)? (void)0 : __afailed(__FILE__, __LINE__)) #endif /* TODO: Guard with #if __CC65_STD__ >= __CC65_STD_C11__ if there diff --git a/include/atari.h b/include/atari.h index 781ee7a80..04cacab33 100644 --- a/include/atari.h +++ b/include/atari.h @@ -235,6 +235,12 @@ extern void __fastcall__ _scroll (signed char numlines); /* numlines < 0 scrolls down */ +/*****************************************************************************/ +/* Sound function */ +/*****************************************************************************/ + +extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); + /*****************************************************************************/ /* Misc. functions */ /*****************************************************************************/ @@ -261,7 +267,7 @@ extern void atrst_mou[]; /* referred to by mouse_static_stddrv[] extern void atrami_mou[]; extern void atrtrk_mou[]; extern void atrtt_mou[]; -extern void atrrdev_ser[]; +extern void atrrdev_ser[]; /* referred to by ser_static_stddrv[] */ extern void atr3_tgi[]; extern void atr4_tgi[]; extern void atr5_tgi[]; @@ -286,7 +292,7 @@ extern void atrxst_mou[]; /* referred to by mouse_static_stddrv[] extern void atrxami_mou[]; extern void atrxtrk_mou[]; extern void atrxtt_mou[]; -extern void atrxrdev_ser[]; +extern void atrxrdev_ser[]; /* referred to by ser_static_stddrv[] */ extern void atrx3_tgi[]; extern void atrx4_tgi[]; extern void atrx5_tgi[]; diff --git a/include/atmos.h b/include/atmos.h index 227c387aa..38d423c46 100644 --- a/include/atmos.h +++ b/include/atmos.h @@ -133,7 +133,7 @@ /* The addresses of the static drivers */ extern void atmos_pase_joy[]; /* Referred to by joy_static_stddrv[] */ extern void atmos_ijk_joy[]; -extern void atmos_acia_ser[]; +extern void atmos_acia_ser[]; /* Referred to by ser_static_stddrv[] */ extern void atmos_228_200_3_tgi[]; extern void atmos_240_200_2_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/c128.h b/include/c128.h index ee1dce99e..5a34904e0 100644 --- a/include/c128.h +++ b/include/c128.h @@ -140,7 +140,7 @@ extern void c128_1351_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void c128_joy_mou[]; extern void c128_inkwell_mou[]; extern void c128_pot_mou[]; -extern void c128_swlink_ser[]; +extern void c128_swlink_ser[]; /* Referred to by ser_static_stddrv[] */ extern void c128_hi_tgi[]; extern void c128_vdc_tgi[]; /* Referred to by tgi_static_stddrv[] */ extern void c128_vdc2_tgi[]; diff --git a/include/c64.h b/include/c64.h index f501a4e7f..ffac801ef 100644 --- a/include/c64.h +++ b/include/c64.h @@ -145,6 +145,7 @@ extern void c64_ram_emd[]; extern void c64_ramcart_emd[]; extern void c64_reu_emd[]; extern void c64_vdc_emd[]; +extern void c64_rrr_emd[]; extern void dtv_himem_emd[]; extern void c64_hitjoy_joy[]; extern void c64_numpad_joy[]; @@ -154,7 +155,7 @@ extern void c64_1351_mou[]; /* Referred to by mouse_static_stddrv[] extern void c64_joy_mou[]; extern void c64_inkwell_mou[]; extern void c64_pot_mou[]; -extern void c64_swlink_ser[]; +extern void c64_swlink_ser[]; /* Referred to by ser_static_stddrv[] */ extern void c64_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/cbm.h b/include/cbm.h index cceb76b1b..0679b2d65 100644 --- a/include/cbm.h +++ b/include/cbm.h @@ -225,7 +225,7 @@ void cbm_k_untlk (void); -/* The cbm_* I/O functions below set _oserror (see errno.h), +/* The cbm_* I/O functions below set __oserror (see errno.h), ** in case of an error. ** ** error-code BASIC error @@ -251,7 +251,7 @@ unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void ** address of the file if "data" is the null pointer (like load"name",8,1 ** in BASIC). ** Returns number of bytes that were loaded if loading was successful; -** otherwise 0, "_oserror" contains an error-code, then (see table above). +** otherwise 0, "__oserror" contains an error-code, then (see table above). */ unsigned char __fastcall__ cbm_save (const char* name, unsigned char device, @@ -274,7 +274,7 @@ void __fastcall__ cbm_close (unsigned char lfn); int __fastcall__ cbm_read (unsigned char lfn, void* buffer, unsigned int size); /* Reads up to "size" bytes from a file into "buffer". ** Returns the number of actually-read bytes, 0 if there are no bytes left. -** -1 in case of an error; then, _oserror contains an error-code (see table +** -1 in case of an error; then, __oserror contains an error-code (see table ** above). (Remember: 0 means end-of-file; -1 means error.) */ @@ -282,7 +282,7 @@ int __fastcall__ cbm_write (unsigned char lfn, const void* buffer, unsigned int size); /* Writes up to "size" bytes from "buffer" to a file. ** Returns the number of actually-written bytes, or -1 in case of an error; -** _oserror contains an error-code, then (see above table). +** __oserror contains an error-code, then (see above table). */ unsigned char cbm_opendir (unsigned char lfn, unsigned char device, ...); diff --git a/include/cbm510.h b/include/cbm510.h index 20b334ed9..8ebbdf3c1 100644 --- a/include/cbm510.h +++ b/include/cbm510.h @@ -128,7 +128,7 @@ extern void cbm510_inkwl_mou[]; extern void cbm510_joy_mou[]; /* Referred to by mouse_static_stddrv[] */ extern void cbm510_ram_emd[]; extern void cbm510_std_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void cbm510_std_ser[]; +extern void cbm510_std_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/cbm610.h b/include/cbm610.h index de7aa50f8..64beab9e6 100644 --- a/include/cbm610.h +++ b/include/cbm610.h @@ -105,7 +105,7 @@ /* The addresses of the static drivers */ extern void cbm610_ram_emd[]; -extern void cbm610_std_ser[]; +extern void cbm610_std_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/cx16.h b/include/cx16.h index 42495c862..66f21843e 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -285,11 +285,11 @@ struct __vera { /* Audio chip */ struct __ym2151 { + unsigned char reg; /* Register number for data */ union { - unsigned char reg; /* Register number for data */ + unsigned char data; unsigned char status; /* Busy flag */ }; - unsigned char data; }; #define YM2151 (*(volatile struct __ym2151 *)0x9F40) diff --git a/include/dio.h b/include/dio.h index 4201728cc..2521a90ff 100644 --- a/include/dio.h +++ b/include/dio.h @@ -35,9 +35,9 @@ -/* Please note: All functions in this file will set _oserror *and* return its -** value. The only exception is dio_open, which will return NULL, but _oserror -** will be set. All function will also set _oserror in case of successful +/* Please note: All functions in this file will set __oserror *and* return its +** value. The only exception is dio_open, which will return NULL, but __oserror +** will be set. All function will also set __oserror in case of successful ** execution, effectively clearing it. */ diff --git a/include/errno.h b/include/errno.h index 92d304938..8f329e3cc 100644 --- a/include/errno.h +++ b/include/errno.h @@ -45,12 +45,17 @@ /* Operating system specific error code */ -extern unsigned char _oserror; +extern unsigned char __oserror; -extern int _errno; +#if __CC65_STD__ >= __CC65_STD_CC65__ +/* define the name with just one underscore for backwards compatibility */ +#define _oserror __oserror +#endif + +extern int __errno; /* System errors go here */ -#define errno _errno +#define errno __errno /* errno must be a macro */ @@ -83,27 +88,45 @@ extern int _errno; -int __fastcall__ _osmaperrno (unsigned char oserror); -/* Map an operating system specific error code (for example from _oserror) +int __fastcall__ __osmaperrno (unsigned char oserror); +/* Map an operating system specific error code (for example from __oserror) ** into one of the E... codes above. It is user callable. */ -unsigned char __fastcall__ _seterrno (unsigned char code); +#if __CC65_STD__ >= __CC65_STD_CC65__ +/* define the name with just one underscore for backwards compatibility */ +#define _osmaperrno __osmaperrno +#endif + +unsigned char __fastcall__ __seterrno (unsigned char code); /* Set errno to a specific error code and return zero. Used by the library */ -int __fastcall__ _directerrno (unsigned char code); -/* Set errno to a specific error code, clear _oserror and return -1. Used +#if __CC65_STD__ >= __CC65_STD_CC65__ +/* define the name with just one underscore for backwards compatibility */ +#define _seterrno __seterrno +#endif + +int __fastcall__ __directerrno (unsigned char code); +/* Set errno to a specific error code, clear __oserror and return -1. Used ** by the library. */ -int __fastcall__ _mappederrno (unsigned char code); -/* Set _oserror to the given platform specific error code. If it is a real +#if __CC65_STD__ >= __CC65_STD_CC65__ +/* define the name with just one underscore for backwards compatibility */ +#define _directerrno __directerrno +#endif + +int __fastcall__ __mappederrno (unsigned char code); +/* Set __oserror to the given platform specific error code. If it is a real ** error code (not zero) set errno to the corresponding system error code ** and return -1. Otherwise return zero. ** Used by the library. */ - +#if __CC65_STD__ >= __CC65_STD_CC65__ +/* define the name with just one underscore for backwards compatibility */ +#define _mappederrno __mappederrno +#endif /* End of errno.h */ #endif diff --git a/include/geos/gconst.h b/include/geos/gconst.h index e70eb9304..3e42feed7 100644 --- a/include/geos/gconst.h +++ b/include/geos/gconst.h @@ -4,13 +4,15 @@ reassembled by Maciej 'YTM/Elysium' Witkowiak */ -/* Here are constants which didn't fit into any other cathegory... */ +/* Here are constants which didn't fit into any other category... */ #ifndef _GCONST_H #define _GCONST_H -#define NULL 0 -#define FALSE NULL +#ifndef NULL +#define NULL ((void *) 0) +#endif +#define FALSE 0 #define TRUE 0xff #define MOUSE_SPRNUM 0 #define DISK_DRV_LGH 0x0d80 diff --git a/include/kim1.h b/include/kim1.h new file mode 100644 index 000000000..03c496223 --- /dev/null +++ b/include/kim1.h @@ -0,0 +1,73 @@ +/*****************************************************************************/ +/* */ +/* kim1.h */ +/* */ +/* KIM-1 system-specific definitions */ +/* */ +/* */ +/* */ +/* (C) 2022 Dave Plummer */ +/* Email: davepl@davepl.com */ +/* */ +/* 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 _KIM1_H +#define _KIM1_H + +/* Check for errors */ +#if !defined(__KIM1__) +# error This module may only be used when compiling for the KIM-1! +#endif + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Hardware */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + +/* Read from tape */ +int __fastcall__ loadt (unsigned char); + +/* Write to tape */ +int __fastcall__ dumpt (unsigned char, const void*, const void*); + + +/* Write to 7-segment LED display. Due to hardware limitations it only +** displays briefly, so must be called repeatedly to update the +** display. +**/ +void __fastcall__ scandisplay(unsigned char left, unsigned char middle, unsigned char right); + +/* +** Get a keypress from the keypad. Returns $00-$0F(0-F), $10(AD), $11(DA), $12(+), +** $13(GO), $14(PC) or $15 for no keypress. +**/ +int __fastcall__ getkey(); + +/* End of kim1.h */ +#endif diff --git a/include/locale.h b/include/locale.h index 3f23e01d2..f408e1ef3 100644 --- a/include/locale.h +++ b/include/locale.h @@ -39,9 +39,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* Locale information constants */ @@ -82,6 +81,3 @@ char* __fastcall__ setlocale (int category, const char* locale); /* End of locale.h */ #endif - - - diff --git a/include/lynx.h b/include/lynx.h index 1b4828a72..41dc5acb3 100644 --- a/include/lynx.h +++ b/include/lynx.h @@ -115,7 +115,7 @@ /* The addresses of the static drivers */ extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void lynx_comlynx_ser[]; +extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */ extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */ diff --git a/include/osic1p.h b/include/osic1p.h index d6ab5fee1..479d9fd52 100644 --- a/include/osic1p.h +++ b/include/osic1p.h @@ -36,6 +36,24 @@ # error "This module may only be used when compiling for the Challenger 1P!" #endif +/* Colors are not functional, display is black and white only. */ +#define COLOR_BLACK 0x00 +#define COLOR_WHITE 0x01 + +#define CH_ULCORNER 0xCC +#define CH_URCORNER 0xCD +#define CH_LLCORNER 0xCB +#define CH_LRCORNER 0xCE +#define CH_TTEE 0xD9 +#define CH_BTEE 0xD7 +#define CH_LTEE 0xD8 +#define CH_RTEE 0xDA +#define CH_CROSS 0xDB +#define CH_HLINE 0x94 +#define CH_VLINE 0x95 + +#define CH_ENTER 0x0D + /* The following #defines will cause the matching functions calls in conio.h ** to be overlaid by macros with the same names, saving the function call ** overhead. @@ -43,5 +61,6 @@ #define _textcolor(color) COLOR_WHITE #define _bgcolor(color) COLOR_BLACK #define _bordercolor(color) COLOR_BLACK +#define _cpeekcolor(color) COLOR_WHITE #endif diff --git a/include/plus4.h b/include/plus4.h index 325ba7d89..7730938e8 100644 --- a/include/plus4.h +++ b/include/plus4.h @@ -56,7 +56,7 @@ /* The addresses of the static drivers */ extern void plus4_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void plus4_stdser_ser[]; +extern void plus4_stdser_ser[]; /* Referred to by ser_static_stddrv[] */ diff --git a/include/serial.h b/include/serial.h index 35d7b8f66..0510cd219 100644 --- a/include/serial.h +++ b/include/serial.h @@ -123,6 +123,13 @@ struct ser_params { unsigned char handshake; /* Type of handshake to use */ }; +/* The name of the standard serial driver for a platform */ +extern const char ser_stddrv[]; + +/* The address of the static standard serial driver for a platform */ +extern const void ser_static_stddrv[]; + + /*****************************************************************************/ /* Code */ diff --git a/include/setjmp.h b/include/setjmp.h index 460829e38..5fac25634 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -42,8 +42,8 @@ typedef char jmp_buf [5]; -int __fastcall__ _setjmp (jmp_buf buf); -#define setjmp _setjmp /* ISO insists on a macro */ +int __fastcall__ __setjmp (jmp_buf buf); +#define setjmp __setjmp /* ISO insists on a macro */ void __fastcall__ longjmp (jmp_buf buf, int retval) __attribute__((noreturn)); diff --git a/include/signal.h b/include/signal.h index 0d5f6ad09..d67cebf7c 100644 --- a/include/signal.h +++ b/include/signal.h @@ -45,12 +45,12 @@ typedef unsigned char sig_atomic_t; typedef void __fastcall__ (*__sigfunc) (int); /* Functions that implement SIG_IGN and SIG_DFL */ -void __fastcall__ _sig_ign (int); -void __fastcall__ _sig_dfl (int); +void __fastcall__ __sig_ign (int); +void __fastcall__ __sig_dfl (int); /* Standard signal handling functions */ -#define SIG_DFL _sig_dfl -#define SIG_IGN _sig_ign +#define SIG_DFL __sig_dfl +#define SIG_IGN __sig_ign #define SIG_ERR ((__sigfunc) 0x0000) /* Signal numbers */ diff --git a/include/stddef.h b/include/stddef.h index 3230a32b0..d2bfd6138 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -53,9 +53,8 @@ typedef unsigned size_t; #endif /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* offsetof macro */ @@ -65,6 +64,3 @@ typedef unsigned size_t; /* End of stddef.h */ #endif - - - diff --git a/include/stdio.h b/include/stdio.h index 84a991a98..012b8e2ba 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -38,9 +38,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ @@ -147,7 +146,11 @@ int __fastcall__ vfscanf (FILE* f, const char* format, __va_list ap); FILE* __fastcall__ fdopen (int fd, const char* mode); /* Unix */ int __fastcall__ fileno (FILE* f); /* Unix */ #endif -void __fastcall__ _poserror (const char* msg); /* cc65 */ +void __fastcall__ __poserror (const char* msg); /* cc65 */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _poserror __poserror +#endif /* Masking macros for some functions */ #define getc(f) fgetc (f) /* ANSI */ diff --git a/include/stdlib.h b/include/stdlib.h index 99151317f..e789f732b 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -44,6 +44,11 @@ typedef unsigned size_t; #define _HAVE_size_t #endif +/* NULL pointer */ +#ifndef NULL +#define NULL ((void *) 0) +#endif + /* Standard exit codes */ #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 @@ -92,24 +97,44 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size); */ #endif -void __fastcall__ _heapadd (void* mem, size_t size); +void __fastcall__ __heapadd (void* mem, size_t size); /* Add a block to the heap */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _heapadd __heapadd +#endif -size_t __fastcall__ _heapblocksize (const void* block); +size_t __fastcall__ __heapblocksize (const void* block); /* Return the size of an allocated block */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _heapblocksize __heapblocksize +#endif -size_t _heapmemavail (void); +size_t __heapmemavail (void); /* Return the total free heap space */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _heapmemavail __heapmemavail +#endif -size_t _heapmaxavail (void); +size_t __heapmaxavail (void); /* Return the size of the largest free block on the heap */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _heapmaxavail __heapmaxavail +#endif /* Random numbers */ #define RAND_MAX 0x7FFF int rand (void); void __fastcall__ srand (unsigned seed); -void _randomize (void); /* Non-standard */ +void __randomize (void); /* Non-standard */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _randomize __randomize +#endif /* Other standard stuff */ void abort (void) __attribute__ ((noreturn)); @@ -130,7 +155,11 @@ unsigned long __fastcall__ strtoul (const char* nptr, char** endptr, int base); int __fastcall__ system (const char* s); /* Non-ANSI functions */ -void __fastcall__ _swap (void* p, void* q, size_t size); +void __fastcall__ __swap (void* p, void* q, size_t size); +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _swap __swap +#endif #if __CC65_STD__ == __CC65_STD_CC65__ char* __fastcall__ itoa (int val, char* buf, int radix); char* __fastcall__ utoa (unsigned val, char* buf, int radix); @@ -143,6 +172,3 @@ int __fastcall__ putenv (char* s); /* End of stdlib.h */ #endif - - - diff --git a/include/string.h b/include/string.h index 1bd83b385..abaf80e7d 100644 --- a/include/string.h +++ b/include/string.h @@ -37,9 +37,8 @@ #define _STRING_H /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ @@ -74,7 +73,7 @@ void* __fastcall__ memset (void* s, int c, size_t count); /* The following is an internal function, the compiler will replace memset ** with it if the fill value is zero. Never use this one directly! */ -void* __fastcall__ _bzero (void* ptr, size_t n); +void* __fastcall__ __bzero (void* ptr, size_t n); /* Non standard: */ #if __CC65_STD__ == __CC65_STD_CC65__ @@ -92,8 +91,12 @@ char* __fastcall__ strupper (char* s); char* __fastcall__ strqtok (char* s1, const char* s2); #endif -const char* __fastcall__ _stroserror (unsigned char errcode); +const char* __fastcall__ __stroserror (unsigned char errcode); /* Map an operating system error number to an error message. */ +#if __CC65_STD__ == __CC65_STD_CC65__ +/* define old name with one underscore for backwards compatibility */ +#define _stroserror __stroserror +#endif /* End of string.h */ diff --git a/include/time.h b/include/time.h index 99bb1c8e3..bfc2ac435 100644 --- a/include/time.h +++ b/include/time.h @@ -39,9 +39,8 @@ /* NULL pointer */ -#ifndef _HAVE_NULL -#define NULL 0 -#define _HAVE_NULL +#ifndef NULL +#define NULL ((void *) 0) #endif /* size_t is needed */ @@ -89,8 +88,8 @@ struct tm { # define CLOCKS_PER_SEC 10 #elif defined(__ATARI__) || defined (__LYNX__) /* Read the clock rate at runtime */ -clock_t _clocks_per_sec (void); -# define CLOCKS_PER_SEC _clocks_per_sec() +clock_t __clocks_per_sec (void); +# define CLOCKS_PER_SEC __clocks_per_sec() #endif #define CLOCK_REALTIME 0 diff --git a/libsrc/Makefile b/libsrc/Makefile index 2018de801..0873d019f 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -27,6 +27,7 @@ TARGETS = apple2 \ $(CBMS) \ $(GEOS) \ gamate \ + kim1 \ lynx \ nes \ none \ @@ -38,6 +39,10 @@ TARGETS = apple2 \ sym1 \ telestrat +TARGETTEST = none \ + sim6502 \ + sim65c02 + DRVTYPES = emd \ joy \ mou \ @@ -52,7 +57,7 @@ OUTPUTDIRS := lib $(subst ../,,$(wildcard ../target/*/drv/*)) \ $(subst ../,,$(wildcard ../target/*/util)) -.PHONY: all mostlyclean clean install zip lib $(TARGETS) +.PHONY: all mostlyclean clean install zip lib libtest $(TARGETS) .SUFFIXES: @@ -80,6 +85,8 @@ datadir = $(PREFIX)/share/cc65 all lib: $(TARGETS) +libtest: $(TARGETTEST) + mostlyclean: $(call RMDIR,../libwrk) @@ -115,9 +122,13 @@ endef # ZIP_recipe zip: $(foreach dir,$(OUTPUTDIRS),$(ZIP_recipe)) -$(TARGETS): +$(TARGETS): | ../lib @$(MAKE) --no-print-directory $@ +# ../lib must be created globally before doing lib targets in parallel +../lib: + @$(call MKDIR,$@) + else # TARGET CA65FLAGS = @@ -286,10 +297,12 @@ $(EXTRA_OBJPAT): $(EXTRA_SRCPAT) | ../libwrk/$(TARGET) ../lib @echo $(TARGET) - $(<F) @$(CA65) -t $(TARGET) $(CA65FLAGS) --create-dep $(@:../lib/%.o=../libwrk/$(TARGET)/%.d) -o $@ $< +$(EXTRA_OBJS): | ../lib + ../lib/$(TARGET).lib: $(OBJS) | ../lib $(AR65) a $@ $? -../libwrk/$(TARGET) ../lib ../target/$(TARGET)/util: +../libwrk/$(TARGET) ../target/$(TARGET)/util: @$(call MKDIR,$@) $(TARGET): $(EXTRA_OBJS) ../lib/$(TARGET).lib diff --git a/libsrc/apple2/close.s b/libsrc/apple2/close.s index cef42b6f8..0002d081d 100644 --- a/libsrc/apple2/close.s +++ b/libsrc/apple2/close.s @@ -33,8 +33,8 @@ zerofd: lda #$00 ; Return success lda #$00 - ; Set __oserror -oserr: jmp __mappederrno + ; Set ___oserror +oserr: jmp ___mappederrno ; Set __errno -errno: jmp __directerrno +errno: jmp ___directerrno diff --git a/libsrc/apple2/devicedir.s b/libsrc/apple2/devicedir.s index 79f4c60de..21cabf59e 100644 --- a/libsrc/apple2/devicedir.s +++ b/libsrc/apple2/devicedir.s @@ -45,9 +45,9 @@ _getdevicedir: ; Handle errors erange: lda #<ERANGE - jsr __directerrno + jsr ___directerrno bne :+ ; Branch always -oserr: jsr __mappederrno +oserr: jsr ___mappederrno : lda #$00 ; Return NULL tax rts @@ -73,7 +73,7 @@ oserr: jsr __mappederrno iny lda #$00 sta (ptr1),y - sta __oserror ; Clear _oserror + sta ___oserror ; Clear __oserror ; Success, return buf lda ptr1 diff --git a/libsrc/apple2/diocommon.s b/libsrc/apple2/diocommon.s index 92d274ea8..b18f0e6ef 100644 --- a/libsrc/apple2/diocommon.s +++ b/libsrc/apple2/diocommon.s @@ -30,6 +30,6 @@ diocommon: dioepilog: ; Return success or error - sta __oserror + sta ___oserror ldx #$00 rts diff --git a/libsrc/apple2/dioopen.s b/libsrc/apple2/dioopen.s index 62b2f06a3..d45677945 100644 --- a/libsrc/apple2/dioopen.s +++ b/libsrc/apple2/dioopen.s @@ -24,7 +24,7 @@ _dio_open: lda #$28 ; "No device connected" ; Return oserror -oserr: sta __oserror +oserr: sta ___oserror jmp return0 ; Return success @@ -34,5 +34,5 @@ oserr: sta __oserror asl asl ldx #$00 - stx __oserror + stx ___oserror rts diff --git a/libsrc/apple2/diosectcount.s b/libsrc/apple2/diosectcount.s index 55f7e376c..117e40e75 100644 --- a/libsrc/apple2/diosectcount.s +++ b/libsrc/apple2/diosectcount.s @@ -16,7 +16,7 @@ _dio_query_sectcount: ; Set handle sta mliparam + MLI::ON_LINE::UNIT_NUM - ; Get ProDOS 8 block size (clears __oserror) + ; Get ProDOS 8 block size (clears ___oserror) jsr _dio_query_sectsize ; Alloc buffer @@ -74,7 +74,7 @@ done: lda ptr4 rts nomem: lda #$FF ; Error code for sure not used by MLI -oserr: sta __oserror +oserr: sta ___oserror ; Save total blocks for failure lda #$00 @@ -85,7 +85,7 @@ oserr: sta __oserror ; Check for non-ProDOS disk check: cmp #$52 ; "Not a ProDOS volume" bne oserr - sta __oserror + sta ___oserror ; Save total blocks for a 16-sector disk lda #<280 diff --git a/libsrc/apple2/diosectsize.s b/libsrc/apple2/diosectsize.s index d3ece3779..7d2e27a09 100644 --- a/libsrc/apple2/diosectsize.s +++ b/libsrc/apple2/diosectsize.s @@ -10,7 +10,7 @@ _dio_query_sectsize: ; Clear error - stx __oserror ; X = 0 + stx ___oserror ; X = 0 ; Return ProDOS 8 block size txa ; X = 0 diff --git a/libsrc/apple2/emd/a2.auxmem.s b/libsrc/apple2/emd/a2.auxmem.s index 5ef2564b1..c582e9eec 100644 --- a/libsrc/apple2/emd/a2.auxmem.s +++ b/libsrc/apple2/emd/a2.auxmem.s @@ -73,7 +73,8 @@ INSTALL: and #$f0 cmp #$80 bne @L1 - lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + txa rts @L1: lda #EM_ERR_NO_DEVICE ; rts diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s index 0ff4bc6f0..d5cbf8788 100644 --- a/libsrc/apple2/exec.s +++ b/libsrc/apple2/exec.s @@ -19,8 +19,8 @@ typerr: lda #$4A ; "Incompatible file format" ; Cleanup name oserr: jsr popname ; Preserves A - ; Set __oserror - jmp __mappederrno + ; Set ___oserror + jmp ___mappederrno _exec: ; Save cmdline diff --git a/libsrc/apple2/getres.s b/libsrc/apple2/getres.s index 4364bc967..75d83cef0 100644 --- a/libsrc/apple2/getres.s +++ b/libsrc/apple2/getres.s @@ -52,7 +52,7 @@ _clock_getres: enosys: lda #ENOSYS ; Set __errno - jmp __directerrno + jmp ___directerrno .rodata diff --git a/libsrc/apple2/gettime.s b/libsrc/apple2/gettime.s index dc00e3b49..7467b0189 100644 --- a/libsrc/apple2/gettime.s +++ b/libsrc/apple2/gettime.s @@ -81,13 +81,13 @@ erange: lda #ERANGE jsr incsp3 ; Preserves A ; Set __errno - jmp __directerrno + jmp ___directerrno ; Cleanup stack oserr: jsr incsp3 ; Preserves A - ; Set __oserror - jmp __mappederrno + ; Set ___oserror + jmp ___mappederrno .bss diff --git a/libsrc/apple2/joy/a2.stdjoy.s b/libsrc/apple2/joy/a2.stdjoy.s index 558013910..11be52eb4 100644 --- a/libsrc/apple2/joy/a2.stdjoy.s +++ b/libsrc/apple2/joy/a2.stdjoy.s @@ -71,8 +71,9 @@ INSTALL: stx gettype+2 gettype:jsr $0000 sta ostype - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; Fall through ; UNINSTALL routine. Is called before the driver is removed from memory. diff --git a/libsrc/apple2/lseek.s b/libsrc/apple2/lseek.s index b6c31515e..be0078c1b 100644 --- a/libsrc/apple2/lseek.s +++ b/libsrc/apple2/lseek.s @@ -107,13 +107,13 @@ seek_common: einval: lda #EINVAL ; Set __errno -errno: jsr __directerrno ; leaves -1 in AX +errno: jsr ___directerrno ; leaves -1 in AX stx sreg ; extend return value to 32 bits stx sreg+1 rts - ; Set __oserror -oserr: jsr __mappederrno ; leaves -1 in AX + ; Set ___oserror +oserr: jsr ___mappederrno ; leaves -1 in AX stx sreg ; extend return value to 32 bits stx sreg+1 rts diff --git a/libsrc/apple2/mou/a2.stdmou.s b/libsrc/apple2/mou/a2.stdmou.s index dfc69a942..c54c09d34 100644 --- a/libsrc/apple2/mou/a2.stdmou.s +++ b/libsrc/apple2/mou/a2.stdmou.s @@ -133,8 +133,8 @@ next: inc ptr1+1 bcc :+ ; Mouse firmware not found - lda #<MOUSE_ERR_NO_DEVICE - ldx #>MOUSE_ERR_NO_DEVICE + lda #MOUSE_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; Check Pascal 1.1 Firmware Protocol ID bytes diff --git a/libsrc/apple2/open.s b/libsrc/apple2/open.s index e47722973..68c203cd6 100644 --- a/libsrc/apple2/open.s +++ b/libsrc/apple2/open.s @@ -64,7 +64,7 @@ _open: errno: jsr incsp4 ; Preserves A ; Set __errno - jmp __directerrno + jmp ___directerrno ; Save fdtab slot found: tya @@ -147,8 +147,8 @@ oserr1: ldy tmp2 ; Restore fdtab slot jsr freebuffer pla ; Restore oserror code - ; Set __oserror - jmp __mappederrno + ; Set ___oserror + jmp ___mappederrno open: ldy tmp2 ; Restore fdtab slot @@ -209,7 +209,7 @@ done: lda tmp1 ; Restore fd ; Return success ldx #$00 - stx __oserror + stx ___oserror rts freebuffer: diff --git a/libsrc/apple2/oserror.s b/libsrc/apple2/oserror.s index ae3efcacc..5f523340f 100644 --- a/libsrc/apple2/oserror.s +++ b/libsrc/apple2/oserror.s @@ -1,14 +1,14 @@ ; ; Ullrich von Bassewitz, 17.05.2000 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; - .export __osmaperrno + .export ___osmaperrno .include "errno.inc" -__osmaperrno: +___osmaperrno: ldx #ErrTabSize : cmp ErrTab-2,x ; Search for the error code beq :+ ; Jump if found diff --git a/libsrc/apple2/randomize.s b/libsrc/apple2/randomize.s index 1558d85a7..e9cd063b8 100644 --- a/libsrc/apple2/randomize.s +++ b/libsrc/apple2/randomize.s @@ -1,16 +1,16 @@ ; ; Ullrich von Bassewitz, 07.11.2002 ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "apple2.inc" -__randomize: +___randomize: ldx RNDH ; Use random value supplied by ROM lda RNDL jmp _srand ; Initialize generator diff --git a/libsrc/apple2/read.s b/libsrc/apple2/read.s index 99c189cbd..d29d6cdfb 100644 --- a/libsrc/apple2/read.s +++ b/libsrc/apple2/read.s @@ -52,7 +52,7 @@ _read: ; Device succeeds always device: lda #$00 - sta __oserror + sta ___oserror ; Set counter to zero sta ptr3 @@ -107,4 +107,4 @@ check: lda ptr3 einval: lda #EINVAL ; Set __errno -errno: jmp __directerrno +errno: jmp ___directerrno diff --git a/libsrc/apple2/rwcommon.s b/libsrc/apple2/rwcommon.s index e5fd9ae90..769247e63 100644 --- a/libsrc/apple2/rwcommon.s +++ b/libsrc/apple2/rwcommon.s @@ -49,10 +49,10 @@ rwcommon: rwepilog: ; Return success - sta __oserror ; A = 0 + sta ___oserror ; A = 0 lda mliparam + MLI::RW::TRANS_COUNT ldx mliparam + MLI::RW::TRANS_COUNT+1 rts - ; Set __oserror -oserr: jmp __mappederrno + ; Set ___oserror +oserr: jmp ___mappederrno diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s index a32110ef2..6ad9c3825 100644 --- a/libsrc/apple2/ser/a2.ssc.s +++ b/libsrc/apple2/ser/a2.ssc.s @@ -57,7 +57,9 @@ ;---------------------------------------------------------------------------- ; I/O definitions -ACIA = $C088 +Offset = $8F ; Move 6502 false read out of I/O to page $BF + +ACIA = $C088-Offset ACIA_DATA = ACIA+0 ; Data register ACIA_STATUS = ACIA+1 ; Status register ACIA_CMD = ACIA+2 ; Command register @@ -166,8 +168,9 @@ SER_CLOSE: sta ACIA_CMD,x ; Done, return an error code -: lda #<SER_ERR_OK - tax ; A is zero +: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax stx Index ; Mark port as closed rts @@ -197,6 +200,7 @@ SER_OPEN: asl asl asl + adc #Offset ; Assume carry to be clear tax ; Check if the handshake setting is valid @@ -253,23 +257,24 @@ SER_OPEN: ; Done stx Index ; Mark port as open - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Device (hardware) not found -NoDevice:lda #<SER_ERR_NO_DEVICE - ldx #>SER_ERR_NO_DEVICE +NoDevice:lda #SER_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; Invalid parameter -InvParam:lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED +InvParam:lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available -InvBaud:lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL +InvBaud:lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -289,8 +294,8 @@ SER_GET: : lda RecvFreeCnt ; (25) cmp #$FF bne :+ - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -333,8 +338,8 @@ SER_PUT: ; Put byte into send buffer & send : ldy SendFreeCnt bne :+ - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts : ldy SendTail @@ -343,7 +348,8 @@ SER_PUT: dec SendFreeCnt lda #$FF ; TryHard = true jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -356,7 +362,8 @@ SER_STATUS: lda ACIA_STATUS,x ldx #$00 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -376,11 +383,12 @@ SER_IOCTL: bcs :+ stx Slot - tax ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts -: lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL +: lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/apple2/ser_stat_stddrv.s b/libsrc/apple2/ser_stat_stddrv.s new file mode 100644 index 000000000..690fe4853 --- /dev/null +++ b/libsrc/apple2/ser_stat_stddrv.s @@ -0,0 +1,22 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .ifdef __APPLE2ENH__ + .import _a2e_ssc_ser + .else + .import _a2_ssc_ser + .endif + +.rodata + + .ifdef __APPLE2ENH__ +_ser_static_stddrv := _a2e_ssc_ser + .else +_ser_static_stddrv := _a2_ssc_ser + .endif diff --git a/libsrc/apple2/ser_stddrv.s b/libsrc/apple2/ser_stddrv.s new file mode 100644 index 000000000..2e8361865 --- /dev/null +++ b/libsrc/apple2/ser_stddrv.s @@ -0,0 +1,18 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: + .ifdef __APPLE2ENH__ + .asciiz "A2E.SSC.SER" + .else + .asciiz "A2.SSC.SER" + .endif diff --git a/libsrc/apple2/settime.s b/libsrc/apple2/settime.s index f722f6f40..32ec8ee43 100644 --- a/libsrc/apple2/settime.s +++ b/libsrc/apple2/settime.s @@ -68,4 +68,4 @@ enosys: lda #ENOSYS erange: lda #ERANGE ; Set __errno -errno: jmp __directerrno +errno: jmp ___directerrno diff --git a/libsrc/apple2/write.s b/libsrc/apple2/write.s index d9dd73ca9..7b50d0705 100644 --- a/libsrc/apple2/write.s +++ b/libsrc/apple2/write.s @@ -107,8 +107,8 @@ done: lda #$00 einval: lda #EINVAL ; Set __errno -errno: jmp __directerrno +errno: jmp ___directerrno - ; Set __oserror -oserr: jmp __mappederrno + ; Set ___oserror +oserr: jmp ___mappederrno diff --git a/libsrc/atari/clock.s b/libsrc/atari/clock.s index 853870520..42809c4a4 100644 --- a/libsrc/atari/clock.s +++ b/libsrc/atari/clock.s @@ -3,10 +3,10 @@ ; originally by Ullrich von Bassewitz and Sidney Cadot ; ; clock_t clock (void); -; clock_t _clocks_per_sec (void); +; clock_t __clocks_per_sec (void); ; - .export _clock, __clocks_per_sec + .export _clock, ___clocks_per_sec .importzp sreg .include "atari.inc" @@ -28,7 +28,7 @@ .endproc -.proc __clocks_per_sec +.proc ___clocks_per_sec ldx #$00 ; Clear byte 1 of return value stx sreg ; Clear byte 2 of return value diff --git a/libsrc/atari/close.s b/libsrc/atari/close.s index d18fbec34..b5bff4173 100644 --- a/libsrc/atari/close.s +++ b/libsrc/atari/close.s @@ -6,7 +6,7 @@ .include "atari.inc" .export _close - .import __do_oserror,popax,__oserror + .import __do_oserror,popax,___oserror .import fdtoiocb_down,__inviocb .proc _close @@ -18,7 +18,7 @@ jsr CIOV bmi closerr ok: ldx #0 - stx __oserror ; clear system specific error code + stx ___oserror ; clear system specific error code txa rts diff --git a/libsrc/atari/dio_cts.s b/libsrc/atari/dio_cts.s index 551b9c0f6..22dc8f9fe 100644 --- a/libsrc/atari/dio_cts.s +++ b/libsrc/atari/dio_cts.s @@ -16,7 +16,7 @@ ; .export _dio_phys_to_log - .import popax,__oserror + .import popax,___oserror .importzp ptr1,ptr2,ptr3 .include "atari.inc" @@ -54,7 +54,7 @@ ldx #0 txa ret: - sta __oserror + sta ___oserror rts ; return success ; invalid handle diff --git a/libsrc/atari/dio_stc.s b/libsrc/atari/dio_stc.s index 768aca085..4edb78aa6 100644 --- a/libsrc/atari/dio_stc.s +++ b/libsrc/atari/dio_stc.s @@ -17,7 +17,7 @@ .export _dio_log_to_phys .include "atari.inc" .importzp ptr1,ptr2,ptr3 - .import popax,popptr1,__oserror + .import popax,popptr1,___oserror .proc _dio_log_to_phys @@ -56,7 +56,7 @@ _l1: lda (ptr1,x) txa ret: - sta __oserror + sta ___oserror rts ; return success ; invalid handle diff --git a/libsrc/atari/diopncls.s b/libsrc/atari/diopncls.s index 528c0fad8..f40d6bba4 100644 --- a/libsrc/atari/diopncls.s +++ b/libsrc/atari/diopncls.s @@ -14,7 +14,7 @@ .export _dio_open, _dio_close .export sectsizetab - .import __oserror, __sio_call, _dio_read + .import ___oserror, __sio_call, _dio_read .import pushax, addysp, subysp .importzp ptr2, sp .include "atari.inc" @@ -31,7 +31,7 @@ sectsizetab: _inv_drive: lda #NONDEV ; non-existent device - sta __oserror + sta ___oserror lda #0 tax rts ; return NULL @@ -49,7 +49,7 @@ _dio_open: sta sectsizetab+sst_flag,x ; set flag that drive is "open" lda #0 sta sectsizetab+sst_sectsize+1,x - sta __oserror ; success + sta ___oserror ; success tya sta sectsizetab+sst_driveno,x stx ptr2 @@ -156,7 +156,7 @@ s128: lda #128 lda #0 ldy #sst_flag sta (ptr2),y - sta __oserror ; success + sta ___oserror ; success tax rts ; return no error diff --git a/libsrc/atari/dioqsize.s b/libsrc/atari/dioqsize.s index caef0588f..3816c249c 100644 --- a/libsrc/atari/dioqsize.s +++ b/libsrc/atari/dioqsize.s @@ -7,7 +7,7 @@ .include "atari.inc" .export _dio_query_sectsize .importzp ptr1,tmp1 - .import popax, __oserror + .import popax, ___oserror .proc _dio_query_sectsize @@ -15,7 +15,7 @@ stx ptr1+1 lda #0 - sta __oserror + sta ___oserror ldy #sst_sectsize+1 lda (ptr1),y diff --git a/libsrc/atari/do_oserr.s b/libsrc/atari/do_oserr.s index eb19dd9d1..9793ff462 100644 --- a/libsrc/atari/do_oserr.s +++ b/libsrc/atari/do_oserr.s @@ -1,5 +1,5 @@ ; -; __do_oserror updates __oserror and errno. Do a JMP here right after +; __do_oserror updates ___oserror and errno. Do a JMP here right after ; calling CIOV. It will return with AX set to -1 ($FFFF). It expects the CIO ; status in Y. ; @@ -9,4 +9,4 @@ __do_oserror: tya - jmp __mappederrno + jmp ___mappederrno diff --git a/libsrc/atari/exec.s b/libsrc/atari/exec.s index 16062a294..145b28e0d 100644 --- a/libsrc/atari/exec.s +++ b/libsrc/atari/exec.s @@ -34,7 +34,7 @@ notsupp:lda #ENOSYS ; "unsupported system call" .byte $2C ; bit opcode, eats the next 2 bytes noiocb: lda #EMFILE ; "too many open files" jsr incsp2 ; clean up stack -seterr: jmp __directerrno +seterr: jmp ___directerrno ; entry point @@ -148,7 +148,7 @@ copycd: lda #ATEOL pha ; remember error code jsr close ; close the IOCB (required even if open failed) pla ; put error code back into A -setmerr:jmp __mappederrno ; update errno from OS specific error code in A +setmerr:jmp ___mappederrno ; update errno from OS specific error code in A openok: lda #>buf sta ICBAH,x ; set buffer address diff --git a/libsrc/atari/getres.s b/libsrc/atari/getres.s index f2e4874f9..9716040d8 100644 --- a/libsrc/atari/getres.s +++ b/libsrc/atari/getres.s @@ -41,7 +41,7 @@ _clock_getres: enosys: lda #ENOSYS ; Set __errno - jmp __directerrno + jmp ___directerrno ;---------------------------------------------------------------------------- ; timespec struct with tv_sec set to 1 second diff --git a/libsrc/atari/gettime.s b/libsrc/atari/gettime.s index 093d34843..df6e24021 100644 --- a/libsrc/atari/gettime.s +++ b/libsrc/atari/gettime.s @@ -105,7 +105,7 @@ errexit:jsr incsp3 ; Preserves A ; set __errno - jmp __directerrno + jmp ___directerrno ; ------- diff --git a/libsrc/atari/graphics.s b/libsrc/atari/graphics.s index ab26ed0da..1f7844c39 100644 --- a/libsrc/atari/graphics.s +++ b/libsrc/atari/graphics.s @@ -10,7 +10,7 @@ .export __graphics .import findfreeiocb - .import __oserror + .import ___oserror .import fddecusage .import clriocb .import fdtoiocb @@ -45,7 +45,7 @@ parmok: jsr findfreeiocb beq iocbok ; we found one lda #<EMFILE ; "too many open files" -seterr: jsr __mappederrno ; @@@ probably not correct to set errno here @@@ +seterr: jsr ___mappederrno ; @@@ probably not correct to set errno here @@@ rts ; return -1 ;invmode:ldx #>EINVAL @@ -94,7 +94,7 @@ doopen: txa lda tmp2 ; get fd ldx #0 - stx __oserror + stx ___oserror rts cioerr: sty tmp3 ; remember error code @@ -103,6 +103,6 @@ cioerr: sty tmp3 ; remember error code jsr CIOV ; close IOCB again since open failed jsr fddecusage ; and decrement usage counter of fd lda tmp3 ; put error code into A - jmp __mappederrno + jmp ___mappederrno .endproc ; __graphics diff --git a/libsrc/atari/inviocb.s b/libsrc/atari/inviocb.s index c1c27ee03..2ceb385b1 100644 --- a/libsrc/atari/inviocb.s +++ b/libsrc/atari/inviocb.s @@ -7,4 +7,4 @@ __inviocb: lda #<EINVAL - jmp __directerrno + jmp ___directerrno diff --git a/libsrc/atari/joy/atrmj8.s b/libsrc/atari/joy/atrmj8.s index 3a26c381d..daf11651d 100644 --- a/libsrc/atari/joy/atrmj8.s +++ b/libsrc/atari/joy/atrmj8.s @@ -69,7 +69,8 @@ INSTALL: lda #$34 sta PACTL lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari/joy/atrstd.s b/libsrc/atari/joy/atrstd.s index fd1d99d31..1a124192f 100644 --- a/libsrc/atari/joy/atrstd.s +++ b/libsrc/atari/joy/atrstd.s @@ -62,7 +62,8 @@ JOY_COUNT = 4 ; Number of joysticks we support INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari/lseek.s b/libsrc/atari/lseek.s index b6a766361..991a86569 100644 --- a/libsrc/atari/lseek.s +++ b/libsrc/atari/lseek.s @@ -8,7 +8,7 @@ .export _lseek - .import incsp6,__oserror + .import incsp6,___oserror .import __inviocb,ldax0sp,ldaxysp,fdtoiocb .import __dos_type .import fd_table @@ -21,7 +21,7 @@ ; seeking not supported, return -1 and ENOSYS errno value no_supp:jsr incsp6 lda #<ENOSYS - jsr __directerrno ; returns with $FFFF in AX + jsr ___directerrno ; returns with $FFFF in AX sta sreg sta sreg+1 rts @@ -94,7 +94,7 @@ xxerr: tya pha jsr incsp6 pla - jsr __mappederrno ; returns with $FFFF in AX + jsr ___mappederrno ; returns with $FFFF in AX sta sreg sta sreg+1 rts diff --git a/libsrc/atari/mou/atrjoy.s b/libsrc/atari/mou/atrjoy.s index 3ea428576..a93c7de13 100644 --- a/libsrc/atari/mou/atrjoy.s +++ b/libsrc/atari/mou/atrjoy.s @@ -137,9 +137,10 @@ INSTALL: ldx YPos+1 jsr CMOVEY -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/mou/atrst.s b/libsrc/atari/mou/atrst.s index 5d8a5cd12..626b7a8f7 100644 --- a/libsrc/atari/mou/atrst.s +++ b/libsrc/atari/mou/atrst.s @@ -268,9 +268,10 @@ INSTALL: and #$0f sta old_porta_vbi -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s index 61963aa61..b1e53935e 100644 --- a/libsrc/atari/mou/atrtt.s +++ b/libsrc/atari/mou/atrtt.s @@ -132,9 +132,10 @@ INSTALL: ldx YPos+1 jsr CMOVEY -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s index 721519525..ed3e40b2f 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 .ifdef UCASE_FILENAME .import ucase_fn .endif @@ -37,7 +37,7 @@ parmok: jsr findfreeiocb beq iocbok ; we found one lda #<EMFILE ; "too many open files" -seterr: jsr __directerrno +seterr: jsr ___directerrno jsr incsp4 ; clean up stack lda #$FF tax @@ -150,11 +150,11 @@ finish: php jsr CIOV ; close IOCB again since open failed jsr fddecusage ; and decrement usage counter of fd lda tmp3 ; put error code into A - jmp __mappederrno + jmp ___mappederrno ok: lda tmp2 ; get fd ldx #0 - stx __oserror + stx ___oserror rts .endproc diff --git a/libsrc/atari/oserror.s b/libsrc/atari/oserror.s index 1d95dbc36..c3bb214e5 100644 --- a/libsrc/atari/oserror.s +++ b/libsrc/atari/oserror.s @@ -2,13 +2,13 @@ ; Christian Groessler, May-2000 ; ; os specific error code mapping -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; .include "errno.inc" - .export __osmaperrno + .export ___osmaperrno -.proc __osmaperrno +.proc ___osmaperrno cmp #$80 ; error or success bcs errcode ; error, jump diff --git a/libsrc/atari/posixdirent.s b/libsrc/atari/posixdirent.s index a722b3b06..56a780d58 100644 --- a/libsrc/atari/posixdirent.s +++ b/libsrc/atari/posixdirent.s @@ -4,7 +4,7 @@ .include "atari.inc" .export _opendir, _readdir, _closedir .import findfreeiocb, clriocb - .import __oserror, return0, __do_oserror + .import ___oserror, return0, __do_oserror .importzp ptr1, tmp1 .ifdef DEFAULT_DEVICE .import __defdev @@ -56,13 +56,13 @@ jsr CIOV bmi cioerr lda #0 - sta __oserror + sta ___oserror tax lda diriocb rts .endproc -cioerr: sty __oserror +cioerr: sty ___oserror lda #CLOSE sta ICCOM,x jsr CIOV ; close IOCB again since open failed @@ -147,7 +147,7 @@ copychar: lda (ptr1),y ; src=y dest=tmp1 jsr CIOV bmi @cioerr ldx #0 - stx __oserror ; clear system specific error code + stx ___oserror ; clear system specific error code txa rts @cioerr: jmp __do_oserror diff --git a/libsrc/atari/randomize.s b/libsrc/atari/randomize.s index 915fee3bd..d004a7c19 100644 --- a/libsrc/atari/randomize.s +++ b/libsrc/atari/randomize.s @@ -1,16 +1,16 @@ ; ; Christian Groessler, 06.11.2002 ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "atari.inc" -__randomize: +___randomize: ldx VCOUNT ; Use vertical line counter as high byte lda RTCLOK+2 ; Use clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/atari/read.s b/libsrc/atari/read.s index f8d10fd15..5078b321d 100644 --- a/libsrc/atari/read.s +++ b/libsrc/atari/read.s @@ -5,7 +5,7 @@ ; .include "atari.inc" - .import __rwsetup,__do_oserror,__inviocb,__oserror + .import __rwsetup,__do_oserror,__inviocb,___oserror .export _read _read: jsr __rwsetup ; do common setup for read and write @@ -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 dependend error code pla ; get buf len lo rts diff --git a/libsrc/atari/ser/atrrdev.s b/libsrc/atari/ser/atrrdev.s index 3a7bc21c2..1d2b98e8d 100644 --- a/libsrc/atari/ser/atrrdev.s +++ b/libsrc/atari/ser/atrrdev.s @@ -135,8 +135,8 @@ my_CIOV: .code invbaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char openerr: rts @@ -229,8 +229,9 @@ SER_OPEN: jsr my_CIOV bmi cioerr - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts inverr: jmp my___inviocb @@ -240,8 +241,8 @@ cioerr: jsr my_fddecusage ; decrement usage counter of fd as open failed init_err: - ldx #0 lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ;---- open the device @@ -313,8 +314,9 @@ SER_CLOSE: stx rshand+1 inx stx cm_run -@done: lda #<SER_ERR_OK - ldx #>SER_ERR_OK +@done: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -365,16 +367,16 @@ SER_GET: rts @nix_da:lda #SER_ERR_NO_DATA - ldx #0 + ldx #0 ; return value is char rts ser_error: lda #SER_ERR_OVERFLOW ; there is no large selection of serial error codes... :-/ - ldx #0 + ldx #0 ; return value is char rts ni_err: lda #SER_ERR_NOT_OPEN - ldx #0 + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -427,8 +429,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -456,8 +458,8 @@ search: lda HATABS,y ; get device name ; R: device not found, return error - lda #<SER_ERR_NO_DEVICE - ldx #0 + lda #SER_ERR_NO_DEVICE + ldx #0 ; return value is char rts ; R: device found, initialize jump table into main program @@ -554,8 +556,9 @@ found: lda ptr3 pla sta ptr3 - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts diff --git a/libsrc/atari/ser_stat_stddrv.s b/libsrc/atari/ser_stat_stddrv.s new file mode 100644 index 000000000..b054f7f32 --- /dev/null +++ b/libsrc/atari/ser_stat_stddrv.s @@ -0,0 +1,22 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .ifdef __ATARIXL__ + .import _atrxrdev_ser + .else + .import _atrrdev_ser + .endif + +.rodata + + .ifdef __ATARIXL__ +_ser_static_stddrv := _atrxrdev_ser + .else +_ser_static_stddrv := _atrrdev_ser + .endif diff --git a/libsrc/atari/ser_stddrv.s b/libsrc/atari/ser_stddrv.s new file mode 100644 index 000000000..e6f4ccd48 --- /dev/null +++ b/libsrc/atari/ser_stddrv.s @@ -0,0 +1,18 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: + .ifdef __ATARIXL__ + .asciiz "atrxrdev.ser" + .else + .asciiz "atrrdev.ser" + .endif diff --git a/libsrc/atari/settime.s b/libsrc/atari/settime.s index 7d6fff2c0..2f4228b62 100644 --- a/libsrc/atari/settime.s +++ b/libsrc/atari/settime.s @@ -96,4 +96,4 @@ done: jmp return0 ; load errno code enosys: lda #ENOSYS -drcter: jmp __directerrno +drcter: jmp ___directerrno diff --git a/libsrc/atari/siocall.s b/libsrc/atari/siocall.s index e4fc505e3..38cbb35d5 100644 --- a/libsrc/atari/siocall.s +++ b/libsrc/atari/siocall.s @@ -17,7 +17,7 @@ .export __sio_call .include "atari.inc" .import popa,popax,popptr1 - .import sectsizetab,__oserror + .import sectsizetab,___oserror .importzp ptr1 .proc __sio_call @@ -76,7 +76,7 @@ _cont: lda #DISKID ; SIO bus ID of diskette drive bmi _req_err ; error occurred txa ; no error occurred _req_err: - sta __oserror + sta ___oserror rts _inv_hand: diff --git a/libsrc/atari/sound.s b/libsrc/atari/sound.s new file mode 100644 index 000000000..a8d11fd82 --- /dev/null +++ b/libsrc/atari/sound.s @@ -0,0 +1,39 @@ +; +; Mariano Domínguez +; 2022-12-4 +; +; this file provides an equivalent to the BASIC SOUND function +; +; void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume); +; + .include "atari.inc" + .export __sound + .import popa + .importzp tmp1,tmp2 + +; play sound, arguments: voice, pitch, distortion, volume +.proc __sound + sta tmp2 ;save volume + jsr popa ;get distortion + sta tmp1 ;save distortion + jsr popa ;get pitch + pha ;save in stack + jsr popa ;get voice + asl a ;adjust voice *2 for offset in x + tax + pla ;get pitch from stack + sta AUDF1,x ;store pitch + lda #0 + sta AUDCTL + lda #3 + sta SKCTL ;init sound + lda tmp1 ;get distortion + asl a ;ignore the high nibble + asl a + asl a + asl a + clc ;setup for adding volume + adc tmp2 ;add volume + sta AUDC1,x ;volume + distortion in control channel + rts +.endproc diff --git a/libsrc/atari/write.s b/libsrc/atari/write.s index 1452b2eee..2ddb88ee3 100644 --- a/libsrc/atari/write.s +++ b/libsrc/atari/write.s @@ -2,7 +2,7 @@ ; int __fastcall__ write (int fd, const void* buf, unsigned count); ; .include "atari.inc" - .import __rwsetup,__do_oserror,__inviocb,__oserror + .import __rwsetup,__do_oserror,__inviocb,___oserror .export _write _write: jsr __rwsetup ; do common setup @@ -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 dependend error code pla rts diff --git a/libsrc/atari5200/joy/atr5200std.s b/libsrc/atari5200/joy/atr5200std.s index fb7946bea..980428d13 100644 --- a/libsrc/atari5200/joy/atr5200std.s +++ b/libsrc/atari5200/joy/atr5200std.s @@ -47,7 +47,8 @@ INSTALL: lda #$04 ; enable POT input from the joystick ports, see section "GTIA" in sta CONSOL ; http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari5200/randomize.s b/libsrc/atari5200/randomize.s index 978ccf3f3..5c554b5f8 100644 --- a/libsrc/atari5200/randomize.s +++ b/libsrc/atari5200/randomize.s @@ -1,16 +1,16 @@ ; ; Christian Groessler, 01-Mar-2014 ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "atari5200.inc" -__randomize: +___randomize: ldx VCOUNT ; Use vertical line counter as high byte lda RTCLOK+1 ; Use clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/atari7800/clocks_per_sec.s b/libsrc/atari7800/clocks_per_sec.s index e2c7d9d8d..d9179448f 100644 --- a/libsrc/atari7800/clocks_per_sec.s +++ b/libsrc/atari7800/clocks_per_sec.s @@ -1,10 +1,10 @@ ; ; 2022-03-15, Karri Kaksonen ; -; clock_t _clocks_per_sec (void); +; clock_t __clocks_per_sec (void); ; - .export __clocks_per_sec + .export ___clocks_per_sec .import sreg: zp .import _paldetected @@ -17,7 +17,7 @@ ;----------------------------------------------------------------------------- ; Return the number of clock ticks in one second. ; - .proc __clocks_per_sec + .proc ___clocks_per_sec lda #0 tax diff --git a/libsrc/atari7800/clrscr.s b/libsrc/atari7800/clrscr.s index f9d4938b0..a1f663ba7 100644 --- a/libsrc/atari7800/clrscr.s +++ b/libsrc/atari7800/clrscr.s @@ -4,7 +4,7 @@ .export _clrscr .import _screen - .import pushax, __bzero + .import pushax, ___bzero .include "extzp.inc" .code @@ -16,7 +16,7 @@ jsr pushax ldx #>(charsperline * screenrows) lda #<(charsperline * screenrows) - jmp __bzero + jmp ___bzero .endproc diff --git a/libsrc/atari7800/joy/atari7800-stdjoy.s b/libsrc/atari7800/joy/atari7800-stdjoy.s index d76e1d105..59f656ada 100644 --- a/libsrc/atari7800/joy/atari7800-stdjoy.s +++ b/libsrc/atari7800/joy/atari7800-stdjoy.s @@ -62,8 +62,9 @@ INSTALL: sty SWCHB ; enable 2-button 7800 controller 2: pull pin 6 (INPT4) high reset: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atari7800/mono_clrscr.s b/libsrc/atari7800/mono_clrscr.s index 19f1fdfd6..f6f6735cb 100644 --- a/libsrc/atari7800/mono_clrscr.s +++ b/libsrc/atari7800/mono_clrscr.s @@ -4,7 +4,7 @@ .export _mono_clrscr .import _mono_screen - .import pushax, __bzero + .import pushax, ___bzero .include "extzp.inc" .code @@ -16,7 +16,7 @@ jsr pushax ldx #>(mono_charsperline * screenrows) lda #<(mono_charsperline * screenrows) - jmp __bzero + jmp ___bzero .endproc diff --git a/libsrc/atmos/joy/atmos-ijk.s b/libsrc/atmos/joy/atmos-ijk.s index 6e75a3e0b..c2bdd67ab 100644 --- a/libsrc/atmos/joy/atmos-ijk.s +++ b/libsrc/atmos/joy/atmos-ijk.s @@ -55,11 +55,12 @@ INSTALL: lda VIA::PRA and #%00100000 bne ijkPresent - lda #<JOY_ERR_NO_DEVICE + lda #JOY_ERR_NO_DEVICE .byte $2C ; Skip next opcode ijkPresent: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atmos/joy/atmos-pase.s b/libsrc/atmos/joy/atmos-pase.s index fd64901c9..d9982cafe 100644 --- a/libsrc/atmos/joy/atmos-pase.s +++ b/libsrc/atmos/joy/atmos-pase.s @@ -58,7 +58,8 @@ temp2: .byte $00 INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/atmos/oserror.s b/libsrc/atmos/oserror.s index 37c9bd7fc..2e902afdf 100644 --- a/libsrc/atmos/oserror.s +++ b/libsrc/atmos/oserror.s @@ -1,14 +1,14 @@ ; ; Stefan Haubenthal, 2011-04-18 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system specific error into a system independent code */ ; .include "errno.inc" - .export __osmaperrno + .export ___osmaperrno -.proc __osmaperrno +.proc ___osmaperrno lda #<EUNKNOWN ldx #>EUNKNOWN diff --git a/libsrc/atmos/ser/atmos-acia.s b/libsrc/atmos/ser/atmos-acia.s index f84b66a0a..f679125d1 100644 --- a/libsrc/atmos/ser/atmos-acia.s +++ b/libsrc/atmos/ser/atmos-acia.s @@ -141,8 +141,9 @@ SER_CLOSE: sta ACIA::CMD,x ; Done, return an error code -: lda #<SER_ERR_OK - tax ; A is zero +: lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax stx Index ; Mark port as closed rts @@ -205,8 +206,9 @@ SER_OPEN: ; Done stx Index ; Mark port as open - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter @@ -235,8 +237,8 @@ SER_GET: : lda RecvFreeCnt ; (25) cmp #$FF bne :+ - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -277,8 +279,8 @@ SER_PUT: ; Put byte into send buffer & send : ldy SendFreeCnt bne :+ - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts : ldy SendTail @@ -287,7 +289,8 @@ SER_PUT: dec SendFreeCnt lda #$FF ; TryHard = true jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -299,7 +302,8 @@ SER_STATUS: lda ACIA::STATUS ldx #$00 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -308,8 +312,8 @@ SER_STATUS: ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/atmos/ser_stat_stddrv.s b/libsrc/atmos/ser_stat_stddrv.s new file mode 100644 index 000000000..2b4373695 --- /dev/null +++ b/libsrc/atmos/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _atmos_acia_ser + +.rodata + +_ser_static_stddrv := _atmos_acia_ser diff --git a/libsrc/atmos/ser_stddrv.s b/libsrc/atmos/ser_stddrv.s new file mode 100644 index 000000000..71e33115a --- /dev/null +++ b/libsrc/atmos/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "atmos-acia.ser" diff --git a/libsrc/atmos/tgi/atmos-228-200-3.s b/libsrc/atmos/tgi/atmos-228-200-3.s index 98d2cef96..466f6922a 100644 --- a/libsrc/atmos/tgi/atmos-228-200-3.s +++ b/libsrc/atmos/tgi/atmos-228-200-3.s @@ -215,7 +215,8 @@ SETPALETTE: jsr PAPER ldy #1 jsr flipcolor - dey ; TGI_ERR_OK + .assert TGI_ERR_OK = 0, error + dey sty ERROR sty PARAM1+1 jmp INK diff --git a/libsrc/c128/emd/c128-efnram.s b/libsrc/c128/emd/c128-efnram.s index 788c73e0f..909c90048 100644 --- a/libsrc/c128/emd/c128-efnram.s +++ b/libsrc/c128/emd/c128-efnram.s @@ -87,16 +87,17 @@ INSTALL: cli cmp tmp1 beq @ram_present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @ram_present: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-georam.s b/libsrc/c128/emd/c128-georam.s index 7511c6841..ecf12f6cd 100644 --- a/libsrc/c128/emd/c128-georam.s +++ b/libsrc/c128/emd/c128-georam.s @@ -120,16 +120,16 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/c128/emd/c128-ifnram.s b/libsrc/c128/emd/c128-ifnram.s index 01a4fdf8e..c51b775b2 100644 --- a/libsrc/c128/emd/c128-ifnram.s +++ b/libsrc/c128/emd/c128-ifnram.s @@ -87,16 +87,17 @@ INSTALL: cli cmp tmp1 beq @ram_present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @ram_present: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ram.s b/libsrc/c128/emd/c128-ram.s index 3fc52c9cc..0ae504b84 100644 --- a/libsrc/c128/emd/c128-ram.s +++ b/libsrc/c128/emd/c128-ram.s @@ -68,8 +68,9 @@ INSTALL: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ram2.s b/libsrc/c128/emd/c128-ram2.s index 7d2703fa5..92e72700a 100644 --- a/libsrc/c128/emd/c128-ram2.s +++ b/libsrc/c128/emd/c128-ram2.s @@ -107,8 +107,9 @@ INSTALL: ldx #$FF stx curpage stx curpage+1 ; Invalidate the current page + .assert EM_ERR_OK = 0, error inx - txa ; A = X = EM_ERR_OK + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-ramcart.s b/libsrc/c128/emd/c128-ramcart.s index e72d053ac..c58c1cd1b 100644 --- a/libsrc/c128/emd/c128-ramcart.s +++ b/libsrc/c128/emd/c128-ramcart.s @@ -97,13 +97,14 @@ INSTALL: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: @readonly: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/emd/c128-reu.s b/libsrc/c128/emd/c128-reu.s index 84e7cb695..8228a0517 100644 --- a/libsrc/c128/emd/c128-reu.s +++ b/libsrc/c128/emd/c128-reu.s @@ -126,8 +126,9 @@ size_found: pagecount_ok: stx pagecount sty pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts ; common REU setup for size check diff --git a/libsrc/c128/emd/c128-vdc.s b/libsrc/c128/emd/c128-vdc.s index accb82154..8d0b77fd2 100644 --- a/libsrc/c128/emd/c128-vdc.s +++ b/libsrc/c128/emd/c128-vdc.s @@ -121,8 +121,9 @@ INSTALL: lda vdc_cset_save jsr vdcputreg @keep64kBit: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/c128/joy/c128-ptvjoy.s b/libsrc/c128/joy/c128-ptvjoy.s index 180f7667d..0a1c53587 100644 --- a/libsrc/c128/joy/c128-ptvjoy.s +++ b/libsrc/c128/joy/c128-ptvjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 4 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/joy/c128-stdjoy.s b/libsrc/c128/joy/c128-stdjoy.s index bf2e2fea7..ee04374ee 100644 --- a/libsrc/c128/joy/c128-stdjoy.s +++ b/libsrc/c128/joy/c128-stdjoy.s @@ -57,8 +57,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c128/mou/c128-1351.s b/libsrc/c128/mou/c128-1351.s index 79ccbe0de..76e28d9f7 100644 --- a/libsrc/c128/mou/c128-1351.s +++ b/libsrc/c128/mou/c128-1351.s @@ -194,9 +194,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/mou/c128-inkwell.s b/libsrc/c128/mou/c128-inkwell.s index b8e71bbb1..2aac7d32d 100644 --- a/libsrc/c128/mou/c128-inkwell.s +++ b/libsrc/c128/mou/c128-inkwell.s @@ -228,9 +228,10 @@ INSTALL: jsr MoveX cli -; Done, return zero. +; Done lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts diff --git a/libsrc/c128/mou/c128-joy.s b/libsrc/c128/mou/c128-joy.s index 065674dc0..d809db526 100644 --- a/libsrc/c128/mou/c128-joy.s +++ b/libsrc/c128/mou/c128-joy.s @@ -195,9 +195,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/mou/c128-pot.s b/libsrc/c128/mou/c128-pot.s index e582d64fb..1cbe4aa18 100644 --- a/libsrc/c128/mou/c128-pot.s +++ b/libsrc/c128/mou/c128-pot.s @@ -195,9 +195,10 @@ INSTALL: sta (ptr3),y cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts diff --git a/libsrc/c128/randomize.s b/libsrc/c128/randomize.s index ae63184a4..2b7754e86 100644 --- a/libsrc/c128/randomize.s +++ b/libsrc/c128/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "c128.inc" -__randomize: +___randomize: ldx VIC_HLINE ; Use VIC rasterline as high byte lda TIME+2 ; Use 60HZ clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s index 3337e2668..7d36eb5bc 100644 --- a/libsrc/c128/ser/c128-swlink.s +++ b/libsrc/c128/ser/c128-swlink.s @@ -187,8 +187,9 @@ SetNMI: sta NMIVec ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -264,22 +265,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -300,8 +302,9 @@ SER_CLOSE: ; Return OK - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -322,8 +325,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -370,7 +373,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -379,7 +382,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -392,7 +396,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -402,8 +407,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c128/ser_stat_stddrv.s b/libsrc/c128/ser_stat_stddrv.s new file mode 100644 index 000000000..8b0732703 --- /dev/null +++ b/libsrc/c128/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _c128_swlink_ser + +.rodata + +_ser_static_stddrv := _c128_swlink_ser diff --git a/libsrc/c128/ser_stddrv.s b/libsrc/c128/ser_stddrv.s new file mode 100644 index 000000000..63f73cadd --- /dev/null +++ b/libsrc/c128/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "c128_swlink.ser" diff --git a/libsrc/c16/emd/c16-ram.s b/libsrc/c16/emd/c16-ram.s index a8083aca4..937019974 100644 --- a/libsrc/c16/emd/c16-ram.s +++ b/libsrc/c16/emd/c16-ram.s @@ -77,12 +77,13 @@ INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa rts -nomem: ldx #>EM_ERR_NO_DEVICE - lda #<EM_ERR_NO_DEVICE +nomem: ldx #EM_ERR_NO_DEVICE + lda #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c16/randomize.s b/libsrc/c16/randomize.s index 796ad118b..0d1ccaf03 100644 --- a/libsrc/c16/randomize.s +++ b/libsrc/c16/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "plus4.inc" -__randomize: +___randomize: ldx TED_VLINELO ; Use TED rasterline as high byte lda TIME+2 ; Use 60HZ clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/c64/emd/c64-65816.s b/libsrc/c64/emd/c64-65816.s index 39f323d28..ce9491fbe 100644 --- a/libsrc/c64/emd/c64-65816.s +++ b/libsrc/c64/emd/c64-65816.s @@ -120,13 +120,14 @@ INSTALL: dex @noextradex: stx bankcount - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @not_present: cli - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/emd/c64-c256k.s b/libsrc/c64/emd/c64-c256k.s index 79706e8fb..5a4bc54c3 100644 --- a/libsrc/c64/emd/c64-c256k.s +++ b/libsrc/c64/emd/c64-c256k.s @@ -158,13 +158,14 @@ INSTALL: jsr restore_data cpy #$01 beq @present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-dqbb.s b/libsrc/c64/emd/c64-dqbb.s index 986c5939d..6a63b3baa 100644 --- a/libsrc/c64/emd/c64-dqbb.s +++ b/libsrc/c64/emd/c64-dqbb.s @@ -147,13 +147,14 @@ INSTALL: jsr restore_data cpy #$01 beq @present - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-georam.s b/libsrc/c64/emd/c64-georam.s index 97f1a7cc4..0116fe8ea 100644 --- a/libsrc/c64/emd/c64-georam.s +++ b/libsrc/c64/emd/c64-georam.s @@ -121,16 +121,17 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + .assert EM_ERR_OK = 0, error + tax rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/c64/emd/c64-isepic.s b/libsrc/c64/emd/c64-isepic.s index 3764443e2..2b7949757 100644 --- a/libsrc/c64/emd/c64-isepic.s +++ b/libsrc/c64/emd/c64-isepic.s @@ -76,13 +76,14 @@ INSTALL: beq @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-kerberos.s b/libsrc/c64/emd/c64-kerberos.s index 30183362f..20be4e409 100644 --- a/libsrc/c64/emd/c64-kerberos.s +++ b/libsrc/c64/emd/c64-kerberos.s @@ -82,13 +82,14 @@ INSTALL: cmp #$AA bne @notpresent - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; use rts from UNINSTALL below ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-ram.s b/libsrc/c64/emd/c64-ram.s index 5355b552d..cdd7b8965 100644 --- a/libsrc/c64/emd/c64-ram.s +++ b/libsrc/c64/emd/c64-ram.s @@ -65,8 +65,9 @@ window: .res 256 ; Memory "window" INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-ramcart.s b/libsrc/c64/emd/c64-ramcart.s index 8998bb6d6..a99f25b4f 100644 --- a/libsrc/c64/emd/c64-ramcart.s +++ b/libsrc/c64/emd/c64-ramcart.s @@ -98,13 +98,13 @@ INSTALL: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts @notpresent: @readonly: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-reu.s b/libsrc/c64/emd/c64-reu.s index 07ac1fbed..832e66f51 100644 --- a/libsrc/c64/emd/c64-reu.s +++ b/libsrc/c64/emd/c64-reu.s @@ -127,8 +127,9 @@ size_found: pagecount_ok: stx pagecount sty pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts ; common REU setup for size check @@ -152,6 +153,7 @@ reu_size_check_common: nodevice: lda #EM_ERR_NO_DEVICE + .assert EM_ERR_OK = 0, error ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/emd/c64-rrr.s b/libsrc/c64/emd/c64-rrr.s new file mode 100644 index 000000000..4d175cd32 --- /dev/null +++ b/libsrc/c64/emd/c64-rrr.s @@ -0,0 +1,363 @@ +; +; Extended Memory Driver for the Action Replay/Retro Replay RAM +; +; original Version 1.0 by Johannes Braun 2006-08-22 <hannenz@freenet.de> +; ------------------------------------------------------------------------ + + .include "zeropage.inc" + + .include "em-kernel.inc" + .include "em-error.inc" + + .macpack generic + .macpack module + +; ------------------------------------------------------------------------ + +c64_ram = ptr1 ; use some more expressive identifiers... +rr_ram = ptr2 +len = ptr3 +aux = ptr4 +temp = tmp1 + +Lo_Mem = $0100 ; location of Lo_Code (must be below $1000 or above $e000) + +RRMODE_OFF = $02 +RRMODE_CART_RAM = $23 + +; ------------------------------------------------------------------------ +; Header. Includes jump table + + module_header _c64_rrr_emd + +; Driver signature + + .byte $65, $6d, $64 ; "emd" + .byte EMD_API_VERSION ; EM API version number + +; Library reference + + .addr $0000 + +; Jump table + + .addr INSTALL + .addr UNINSTALL + .addr PAGECOUNT + .addr MAP + .addr USE + .addr COMMIT + .addr COPYFROM + .addr COPYTO + +; ------------------------------------------------------------------------ +; Data. + +.bss +window: .res 256 ; the memory window (256 bytes) +pagecount: .res 1 ; Number of available pages + +.rodata +dummy: + .word window ; a "pseudo"-em_copy_struct, used by em_map/ em_commit + .byte 0 ; to pass over to COPYTO/COPYFROM +curpage: + .byte $ff ; just this byte is changed according to the desired page + .byte 0 + .word 256 + +.code + +;---------------------------------------------------------------------------------------- +;unsigned char __fastcall__ em_install(void *driver); +;returns an error code +;---------------------------------------------------------------------------------------- +INSTALL: + ldx #c2-c1 +: lda c1,x + sta Lo_Mem,x + dex + bpl :- + ;ldx #$ff + stx curpage ; invalidate current page + + ldy #RRMODE_OFF + sei + jmp Lo_Mem+8 ; jump to the code below + + ; copied to Lo_Mem +c1: + +;detectmodes: + .byte RRMODE_CART_RAM | $00 + .byte RRMODE_CART_RAM | $08 + .byte RRMODE_CART_RAM | $10 + .byte RRMODE_CART_RAM | $18 + .byte RRMODE_CART_RAM | $80 + .byte RRMODE_CART_RAM | $88 + .byte RRMODE_CART_RAM | $90 + .byte RRMODE_CART_RAM | $98 + + ; first save c64 memory + lda $8888 + pha + + ; tag c64 memory + lda #$00 + sta $8888 + + ldx #$07 +: + ; try accessing rr-ram + ;lda detectmodes, x + lda Lo_Mem, x + sta $de00 + + ; tag (hopefully) rr memory + txa + ora #$80 + sta $8888 + + dex + bpl :- + + ;ldy #RRMODE_OFF + sty $de00 + + ; now if C64 memory is $80, there is no AR/RR + ; if C64 memory is $00, there is a AR/RR. + + lda $8888 + beq detectpages + + lda #0 + beq hasnopages + +detectpages: + ; we can now read the highest available bank nr from the highest bank :) + + lda #RRMODE_CART_RAM | $98 + sta $de00 + + ldx $8888 + inx + txa + + ; 8k = 32 pages + asl + asl + asl + asl + asl + +hasnopages: + + ;ldy #RRMODE_OFF + sty $de00 ; c64 ram again + + sta pagecount + + ; restore c64 memory + pla + sta $8888 + + cli + + ldx pagecount + beq no + + ; no error + lda #0 + tax + rts + +no: + lda #4 ; return #4: error code for "device not present" + rts +c2: + +;---------------------------------------------------------------------------------------- +;void em_uninstall(void); +;---------------------------------------------------------------------------------------- +UNINSTALL: +return_null: + lda #$00 + tax + rts + +;---------------------------------------------------------------------------------------- +;unsigned __fastcall__ em_pagecount(void); +;---------------------------------------------------------------------------------------- +PAGECOUNT: + lda pagecount ; always return 32kb (128 pages) + ldx #$00 + rts + +;---------------------------------------------------------------------------------------- +;void* __fastcall__ em_use(unsigned page); +;---------------------------------------------------------------------------------------- +USE: + cmp #$80 ; valid page? + bcs return_null ; no, return NULL pointer + sta curpage ; set to current page +return_win: + lda #<window ; return pointer to window + ldx #>window +return: rts + +;---------------------------------------------------------------------------------------- +;void* __fastcall__ em_map(unsigned page); +;---------------------------------------------------------------------------------------- +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) + jsr COPYFROM + bcs return_win ; function returns pointer to window (returns always with carry set!) + +;---------------------------------------------------------------------------------------- +;void __fastcall__ em_commit(void); +;---------------------------------------------------------------------------------------- +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) + +;---------------------------------------------------------------------------------------- +;void __fastcall__ em_copyto (struct em_copy *copy_data); +;---------------------------------------------------------------------------------------- +COPYTO: + jsr get_struct_data ;read the parameters passed in the em_struct pointed to by .A/.X upon call + + ;copy the main copyto routine into Lo_Mem + + ldy #Lo_Code1_End - Lo_Code1 +: lda Lo_Code1-1,y + sta Lo_Mem-1,y + dey + bne :- +COMMON: + sei + jmp Lo_Mem + + ;this part will be executed in Lo_Mem (!) by COPYFROM + +Lo_Code2: + ; copy byte rr -> c64 + stx $de00 ;map in rr-ram + lda (rr_ram),y ;get byte from rr-ram + sty $de00 ;RR-ROM will be mapped to $8000-$a000 but write access will go to c64-ram anyway!! + sta (c64_ram),y ;and write to c64-ram + nop ;pad to same size as Lo_Code1 + nop +Lo_Code2_End: + + + ;this part will be executed in Lo_Mem (!) by COPYTO + +Lo_Code1: + ; copy byte c64 -> rr + lda (c64_ram),y ;read 1 byte from c64-ram + stx $de00 ;map in rr-ram + sta (rr_ram),y ;write byte to rr-ram + lda #$02 ;map in c64-ram again + sta $de00 + ;12 bytes + + ;this part is common for both COPYFROM/COPYTO and executed in Lo_Mem, too + +Lo_Code_Common: + inc c64_ram ;increase pointers + bne :+ + inc c64_ram+1 +: inc rr_ram + bne @skip + inc rr_ram+1 + lda rr_ram+1 + cmp #$a0 ;wrap around 16k boundary in rr-ram window ($8000-$a000) + bne @skip + + lda #$80 ;reset pointer to $8000 + sta rr_ram+1 + txa ;adjust value in .X to map in next 16k-bank in rr-ram + adc #7 ;carry is set because of former CMP, so it adds 8 + tax + ;27 bytes +@skip: lda c64_ram + cmp len + lda c64_ram+1 + sbc len+1 + bcc Lo_Code1 + lda #2 ;CHANGE to LDA #0 if driver is called from ROM + sta $de00 + cli + rts ;17 bytes = 56 bytes Lo_Code ($38) +Lo_Code1_End: +;---------------------------------------------------------------------------------------- +;void __fastcall__ em_copyfrom(struct em_copy *copy_data); +;copy from extended memory into linear memory +;---------------------------------------------------------------------------------------- +COPYFROM: + jsr get_struct_data + + ldy #Lo_Code2_End - Lo_Code2 ;copy routine into Lo_Mem +: lda Lo_Code2-1,y + sta Lo_Mem-1,y + dey + bne :- + ldy #Lo_Code1_End-Lo_Code_Common +: lda Lo_Code_Common-1,y + sta Lo_Mem+11,y + dey + bne :- + beq COMMON ;and execute... +;---------------------------------------------------------------------------------------- +;read the struct data located at (.A/.X) +;and setup parameters for stash/ fetch operation +;---------------------------------------------------------------------------------------- +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 + stx aux+1 + ldy #0 ;index 0 + + lda (aux),y ;read c64-adress lo + sta c64_ram + iny + lda (aux),y ;read c64-adress hi + sta c64_ram+1 ;(c64_ram) --> points to c64-adress space + iny + lda (aux),y ;read rr-adress lo + sta rr_ram + iny + lda (aux),y ;rr-adress hi + pha ;remember + and #$1f + ora #$80 ;adjust into 16k-window ($8000-$a000) + sta rr_ram+1 + pla ;re-get hi byte of rr-adress + and #$60 ;isolate bits 5 and 6 + 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 + iny + iny ;skip unused byte + lda (aux),y ;read length lo-byte + clc + adc c64_ram ;add to c64-addres + sta len + 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. + rts + ;55 bytes + diff --git a/libsrc/c64/emd/c64-vdc.s b/libsrc/c64/emd/c64-vdc.s index 2448f09d4..60fbccbbf 100644 --- a/libsrc/c64/emd/c64-vdc.s +++ b/libsrc/c64/emd/c64-vdc.s @@ -87,8 +87,8 @@ INSTALL: bne @L0 iny bne @L0 - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE +; ldx #0 ; return value is char rts @present: @@ -131,8 +131,9 @@ INSTALL: sta pagecount stx pagecount+1 @endok: - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/c64/emd/dtv-himem.s b/libsrc/c64/emd/dtv-himem.s index 6dde874f7..4d19b19d5 100644 --- a/libsrc/c64/emd/dtv-himem.s +++ b/libsrc/c64/emd/dtv-himem.s @@ -93,15 +93,16 @@ INSTALL: ; DTV not found - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @present: ldx #$FF stx curpage+1 ; Invalidate curpage - inx ; X = 0 - txa ; A/X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-hitjoy.s b/libsrc/c64/joy/c64-hitjoy.s index 3b4a0b909..a9d454fd0 100644 --- a/libsrc/c64/joy/c64-hitjoy.s +++ b/libsrc/c64/joy/c64-hitjoy.s @@ -59,8 +59,9 @@ temp4: .byte 0 ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-numpad.s b/libsrc/c64/joy/c64-numpad.s index 0ccdc4fcd..f6839d6cf 100644 --- a/libsrc/c64/joy/c64-numpad.s +++ b/libsrc/c64/joy/c64-numpad.s @@ -100,12 +100,14 @@ masktable: ; INSTALL: - lda #JOY_ERR_OK ; Assume we have a joystick - ldx VIC_CLK_128 ; Test for a C128 - cpx #$FF + lda #JOY_ERR_OK ; Assume we have a "joystick" + .assert JOY_ERR_OK = 0, error + tax ; Set high byte + ldy VIC_CLK_128 ; Test for a C128 + cpy #$FF bne @C128 ; Jump if we have one lda #JOY_ERR_NO_DEVICE ; No C128 -> no numpad -@C128: ldx #0 ; Set high byte +@C128: ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/joy/c64-ptvjoy.s b/libsrc/c64/joy/c64-ptvjoy.s index a772fb5f6..30466b2c2 100644 --- a/libsrc/c64/joy/c64-ptvjoy.s +++ b/libsrc/c64/joy/c64-ptvjoy.s @@ -52,8 +52,9 @@ JOY_COUNT = 4 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/joy/c64-stdjoy.s b/libsrc/c64/joy/c64-stdjoy.s index d11093fba..511032507 100644 --- a/libsrc/c64/joy/c64-stdjoy.s +++ b/libsrc/c64/joy/c64-stdjoy.s @@ -56,8 +56,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/c64/mou/c64-1351.s b/libsrc/c64/mou/c64-1351.s index a7d042c7b..ce0f18803 100644 --- a/libsrc/c64/mou/c64-1351.s +++ b/libsrc/c64/mou/c64-1351.s @@ -152,9 +152,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -307,8 +308,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-inkwell.s b/libsrc/c64/mou/c64-inkwell.s index 9c876a7c8..d2f14a6f0 100644 --- a/libsrc/c64/mou/c64-inkwell.s +++ b/libsrc/c64/mou/c64-inkwell.s @@ -168,6 +168,7 @@ INSTALL: ; Done, return zero. lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts @@ -319,8 +320,8 @@ INFO: jsr POS ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-joy.s b/libsrc/c64/mou/c64-joy.s index f2a501000..5ee1b4f84 100644 --- a/libsrc/c64/mou/c64-joy.s +++ b/libsrc/c64/mou/c64-joy.s @@ -156,9 +156,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -312,8 +313,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/mou/c64-pot.s b/libsrc/c64/mou/c64-pot.s index 102ca351c..9bdf24f62 100644 --- a/libsrc/c64/mou/c64-pot.s +++ b/libsrc/c64/mou/c64-pot.s @@ -139,9 +139,10 @@ INSTALL: jsr CMOVEY cli -; Done, return zero (= MOUSE_ERR_OK) +; Done - ldx #$00 + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -297,8 +298,8 @@ INFO: jsr POS ; Must return an error code in a/x. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/randomize.s b/libsrc/c64/randomize.s index a875203af..da32dfc28 100644 --- a/libsrc/c64/randomize.s +++ b/libsrc/c64/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "c64.inc" -__randomize: +___randomize: ldx VIC_HLINE ; Use VIC rasterline as high byte lda TIME+2 ; Use 60HZ clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s index 597cf1dd6..81c9916a6 100644 --- a/libsrc/c64/ser/c64-swlink.s +++ b/libsrc/c64/ser/c64-swlink.s @@ -161,8 +161,9 @@ SetNMI: sta NMIVec ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -238,22 +239,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -274,8 +276,9 @@ SER_CLOSE: ; Return OK - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -296,8 +299,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -344,7 +347,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -353,7 +356,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -366,7 +370,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -376,8 +381,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/c64/ser_stat_stddrv.s b/libsrc/c64/ser_stat_stddrv.s new file mode 100644 index 000000000..327abbe5f --- /dev/null +++ b/libsrc/c64/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _c64_swlink_ser + +.rodata + +_ser_static_stddrv := _c64_swlink_ser diff --git a/libsrc/c64/ser_stddrv.s b/libsrc/c64/ser_stddrv.s new file mode 100644 index 000000000..5b00b7642 --- /dev/null +++ b/libsrc/c64/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "c64_swlink.ser" diff --git a/libsrc/cbm/c_load.s b/libsrc/cbm/c_load.s index d81430a03..7f2a054bd 100644 --- a/libsrc/cbm/c_load.s +++ b/libsrc/cbm/c_load.s @@ -7,7 +7,7 @@ .include "cbm.inc" .export _cbm_k_load - .import __oserror + .import ___oserror .import popa .importzp ptr1 @@ -19,7 +19,7 @@ _cbm_k_load: ldy ptr1+1 jsr LOAD bcc @Ok - sta __oserror + sta ___oserror ldx ptr1 ldy ptr1+1 @Ok: txa diff --git a/libsrc/cbm/cbm_open.s b/libsrc/cbm/cbm_open.s index db4179db8..c7590af2a 100644 --- a/libsrc/cbm/cbm_open.s +++ b/libsrc/cbm/cbm_open.s @@ -15,7 +15,7 @@ ; { ; cbm_k_setlfs(lfn, device, sec_addr); ; cbm_k_setnam(name); -; return _oserror = cbm_k_open(); +; return __oserror = cbm_k_open(); ; } ; @@ -23,7 +23,7 @@ .import popa .import _cbm_k_setlfs, _cbm_k_setnam, _cbm_k_open - .import __oserror + .import ___oserror _cbm_open: jsr _cbm_k_setnam @@ -32,5 +32,5 @@ _cbm_open: jsr _cbm_k_setlfs ; Call SETLFS, pop all args jsr _cbm_k_open - sta __oserror + sta ___oserror rts diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index 29e0e1f19..8a9939eca 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -6,7 +6,7 @@ ; int __fastcall__ cbm_read (unsigned char lfn, void* buffer, unsigned int size) ; /* Reads up to "size" bytes from a file to "buffer". ; ** Returns the number of actually read bytes, 0 if there are no bytes left -; ** (EOF) or -1 in case of an error. _oserror contains an errorcode then (see +; ** (EOF) or -1 in case of an error. __oserror contains an errorcode then (see ; ** table below). ; */ ; { @@ -14,7 +14,7 @@ ; static unsigned char tmp; ; ; /* if we can't change to the inputchannel #lfn then return an error */ -; if (_oserror = cbm_k_chkin(lfn)) return -1; +; if (__oserror = cbm_k_chkin(lfn)) return -1; ; ; bytesread = 0; ; @@ -41,7 +41,7 @@ .export _cbm_read .importzp ptr1, ptr2, ptr3, tmp1 .import popax, popa - .import __oserror + .import ___oserror _cbm_read: @@ -106,7 +106,7 @@ _cbm_read: ; CHKIN failed -@E1: sta __oserror +@E1: sta ___oserror lda #$FF tax rts ; return -1 diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index 5ac07209c..18c6f4684 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -9,7 +9,7 @@ ; static unsigned int byteswritten; ; ; /* if we can't change to the outputchannel #lfn then return an error */ -; if (_oserror = cbm_k_ckout(lfn)) return -1; +; if (__oserror = cbm_k_ckout(lfn)) return -1; ; ; byteswritten = 0; ; @@ -18,7 +18,7 @@ ; } ; ; if (cbm_k_readst()) { -; _oserror = 5; /* device not present */ +; __oserror = 5; /* device not present */ ; byteswritten = -1; ; } ; @@ -33,7 +33,7 @@ .export _cbm_write .importzp ptr1, ptr2, ptr3 .import popax, popa - .import __oserror + .import ___oserror _cbm_write: @@ -87,7 +87,7 @@ _cbm_write: ; Error entry, error code is in A -@E2: sta __oserror +@E2: sta ___oserror lda #$FF tax rts ; return -1 diff --git a/libsrc/cbm/close.s b/libsrc/cbm/close.s index 7fc600e87..d805e37cc 100644 --- a/libsrc/cbm/close.s +++ b/libsrc/cbm/close.s @@ -54,12 +54,12 @@ ldx unittab,y jsr closecmdchannel ; Close the disk command channel pla ; Get the error code from the disk - jmp __mappederrno ; Set _oserror and _errno, return 0/-1 + jmp ___mappederrno ; Set __oserror and _errno, return 0/-1 ; Error entry: The given file descriptor is not valid or not open invalidfd: lda #EBADF - jmp __directerrno ; Set _errno, clear _oserror, return -1 + jmp ___directerrno ; Set _errno, clear __oserror, return -1 .endproc diff --git a/libsrc/cbm/devicedir.s b/libsrc/cbm/devicedir.s index 3a4e6d774..1f2b2166c 100644 --- a/libsrc/cbm/devicedir.s +++ b/libsrc/cbm/devicedir.s @@ -32,7 +32,7 @@ jsr popa jsr diskinit beq size - jsr __mappederrno + jsr ___mappederrno bne fail ; Branch always ; Check for sufficient buf size @@ -43,7 +43,7 @@ size: lda ptr3+1 cmp #3 bcs okay ; Buf >= 3 lda #<ERANGE - jsr __directerrno + jsr ___directerrno fail: lda #0 ; Return NULL tax rts @@ -52,7 +52,7 @@ fail: lda #0 ; Return NULL okay: lda fnunit ; Set by diskinit jsr devicestr ; Returns 0 in A - sta __oserror ; Clear _oserror + sta ___oserror ; Clear __oserror ; Success, return buf diff --git a/libsrc/cbm/dir.s b/libsrc/cbm/dir.s index 808fcf982..734485aaf 100644 --- a/libsrc/cbm/dir.s +++ b/libsrc/cbm/dir.s @@ -92,8 +92,8 @@ L3: sta tmp1 ; Save returned count ; Didn't read enough bytes. This is an error for us, but errno is not set lda #<EIO - sta __errno - stx __errno+1 ; X is zero + sta ___errno + stx ___errno+1 ; X is zero bne L1 ; Branch always diff --git a/libsrc/cbm/initcwd.s b/libsrc/cbm/initcwd.s index d5e5b9296..7064c16e1 100644 --- a/libsrc/cbm/initcwd.s +++ b/libsrc/cbm/initcwd.s @@ -9,8 +9,6 @@ .import pusha0, tosudiva0 .importzp sreg, ptr1, ptr2 - .macpack generic - initcwd: lda #<__cwd ldx #>__cwd @@ -27,15 +25,20 @@ devicestr: lda #10 jsr tosudiva0 ldy #0 - lda sreg - beq @L0 ; >=10 - add #'0' + tax ; result of the division (lsb) + beq @L0 ; < 10 + + clc + adc #'0' sta (ptr2),y iny -@L0: lda ptr1 ; rem - add #'0' +@L0: + lda sreg ; reminder of the division + clc + adc #'0' sta (ptr2),y iny - lda #0 + + lda #0 ; terminating 0 sta (ptr2),y rts diff --git a/libsrc/cbm/open.s b/libsrc/cbm/open.s index e9f0237cc..47224925d 100644 --- a/libsrc/cbm/open.s +++ b/libsrc/cbm/open.s @@ -94,10 +94,10 @@ parmok: jsr popax ; Get flags lda #EINVAL -; Error entry. Sets _errno, clears _oserror, returns -1 +; Error entry. Sets _errno, clears __oserror, returns -1 seterrno: - jmp __directerrno + jmp ___directerrno ; Error entry: Close the file and exit. OS error code is in A on entry @@ -113,7 +113,7 @@ closeandexit: ; Error entry: Set oserror and errno using error code in A and return -1 -oserror:jmp __mappederrno +oserror:jmp ___mappederrno ; Read bit is set. Add an 'r' to the name @@ -189,7 +189,7 @@ nofile: ; ... else use SA=0 (read) txa ; Handle ldx #0 - stx __oserror ; Clear _oserror + stx ___oserror ; Clear __oserror rts .endproc diff --git a/libsrc/cbm/oserror.s b/libsrc/cbm/oserror.s index 872c75912..92da03810 100644 --- a/libsrc/cbm/oserror.s +++ b/libsrc/cbm/oserror.s @@ -2,7 +2,7 @@ ; 2000-05-17, Ullrich von Bassewitz ; 2014-05-28, Greg King ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system-specific error into a system-independent code. */ ; @@ -10,7 +10,7 @@ .code -__osmaperrno: +___osmaperrno: ldx #ErrTabSize @L1: cmp ErrTab-2,x ; Search for the error code beq @L2 ; Jump if found diff --git a/libsrc/cbm/read.s b/libsrc/cbm/read.s index 1c2c670e7..5990ed2d0 100644 --- a/libsrc/cbm/read.s +++ b/libsrc/cbm/read.s @@ -66,7 +66,7 @@ jsr CHKIN bcc @L3 ; Branch if ok - jmp __mappederrno ; Store into __oserror, map to errno, return -1 + jmp ___mappederrno ; Store into ___oserror, map to errno, return -1 ; Read the next byte @@ -123,10 +123,10 @@ done: jsr CLRCH -; Clear _oserror and return the number of chars read +; Clear __oserror and return the number of chars read eof: lda #0 - sta __oserror + sta ___oserror lda ptr3 ldx ptr3+1 rts @@ -141,7 +141,7 @@ devnotpresent: invalidfd: lda #EBADF - jmp __directerrno ; Sets _errno, clears _oserror, returns -1 + jmp ___directerrno ; Sets _errno, clears __oserror, returns -1 .endproc diff --git a/libsrc/cbm/readdir.c b/libsrc/cbm/readdir.c index 1512edc4e..25627a9cb 100644 --- a/libsrc/cbm/readdir.c +++ b/libsrc/cbm/readdir.c @@ -126,7 +126,7 @@ struct dirent* __fastcall__ readdir (register DIR* dir) } /* Something went wrong when parsing the directory entry */ - _errno = EIO; + __errno = EIO; exitpoint: return 0; } diff --git a/libsrc/cbm/write.s b/libsrc/cbm/write.s index 93bdda31d..172e45382 100644 --- a/libsrc/cbm/write.s +++ b/libsrc/cbm/write.s @@ -55,7 +55,7 @@ jsr CKOUT bcc @L2 -@error: jmp __mappederrno ; Store into __oserror, map to errno, return -1 +@error: jmp ___mappederrno ; Store into ___oserror, map to errno, return -1 ; Output the next character from the buffer @@ -92,10 +92,10 @@ @L3: jsr CLRCH -; Clear _oserror and return the number of chars written +; Clear __oserror and return the number of chars written lda #0 - sta __oserror + sta ___oserror lda ptr3 ldx ptr3+1 rts @@ -112,6 +112,6 @@ devnotpresent: invalidfd: lda #EBADF - jmp __directerrno ; Sets _errno, clears _oserror, returns -1 + jmp ___directerrno ; Sets _errno, clears __oserror, returns -1 .endproc diff --git a/libsrc/cbm510/emd/cbm510-ram.s b/libsrc/cbm510/emd/cbm510-ram.s index f724c7360..6cc319b7f 100644 --- a/libsrc/cbm510/emd/cbm510-ram.s +++ b/libsrc/cbm510/emd/cbm510-ram.s @@ -81,8 +81,9 @@ INSTALL: sbc #$00 sta pagecount -@L1: lda #<EM_ERR_OK - ldx #>EM_ERR_OK +@L1: lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm510/joy/cbm510-std.s b/libsrc/cbm510/joy/cbm510-std.s index 4e47fc1a0..f7cbb2cdc 100644 --- a/libsrc/cbm510/joy/cbm510-std.s +++ b/libsrc/cbm510/joy/cbm510-std.s @@ -57,8 +57,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm510/mou/cbm510-inkwl.s b/libsrc/cbm510/mou/cbm510-inkwl.s index 91bc52fcd..ea6d95934 100644 --- a/libsrc/cbm510/mou/cbm510-inkwl.s +++ b/libsrc/cbm510/mou/cbm510-inkwl.s @@ -175,6 +175,7 @@ INSTALL: ; Done, return zero. lda #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error tax rts @@ -331,8 +332,8 @@ INFO: jsr POS ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/mou/cbm510-joy.s b/libsrc/cbm510/mou/cbm510-joy.s index 8aa3a778e..4daa49272 100644 --- a/libsrc/cbm510/mou/cbm510-joy.s +++ b/libsrc/cbm510/mou/cbm510-joy.s @@ -140,7 +140,8 @@ INSTALL: ; Done, return zero. - ldx #>MOUSE_ERR_OK + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -315,8 +316,8 @@ POS: ldy #MOUSE_POS::XCOORD ; Structure offset ; Must return an error code in .XA. ; -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/randomize.s b/libsrc/cbm510/randomize.s index 75c419ccb..8d21cde15 100644 --- a/libsrc/cbm510/randomize.s +++ b/libsrc/cbm510/randomize.s @@ -2,15 +2,15 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .importzp time -__randomize: +___randomize: ldx time+2 ; Use 50/60HZ clock lda time+1 jmp _srand ; Initialize generator diff --git a/libsrc/cbm510/ser/cbm510-std.s b/libsrc/cbm510/ser/cbm510-std.s index 64f613cd5..cc58c1233 100644 --- a/libsrc/cbm510/ser/cbm510-std.s +++ b/libsrc/cbm510/ser/cbm510-std.s @@ -148,8 +148,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -217,22 +218,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -253,8 +255,8 @@ SER_GET: @L1: lda RecvFreeCnt cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -301,7 +303,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -310,7 +312,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -328,7 +331,8 @@ SER_STATUS: sta (ptr1,x) lda IndReg sta ExecReg - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -338,8 +342,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm510/ser_stat_stddrv.s b/libsrc/cbm510/ser_stat_stddrv.s new file mode 100644 index 000000000..a872f19b9 --- /dev/null +++ b/libsrc/cbm510/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _cbm510_std_ser + +.rodata + +_ser_static_stddrv := _cbm510_std_ser diff --git a/libsrc/cbm510/ser_stddrv.s b/libsrc/cbm510/ser_stddrv.s new file mode 100644 index 000000000..ed785f914 --- /dev/null +++ b/libsrc/cbm510/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "cbm510-std.ser" diff --git a/libsrc/cbm610/emd/cbm610-ram.s b/libsrc/cbm610/emd/cbm610-ram.s index 5c67df7a4..5aa43b0c2 100644 --- a/libsrc/cbm610/emd/cbm610-ram.s +++ b/libsrc/cbm610/emd/cbm610-ram.s @@ -81,8 +81,9 @@ INSTALL: sbc #$00 sta pagecount -@L1: lda #<EM_ERR_OK - ldx #>EM_ERR_OK +@L1: lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cbm610/randomize.s b/libsrc/cbm610/randomize.s index 75c419ccb..8d21cde15 100644 --- a/libsrc/cbm610/randomize.s +++ b/libsrc/cbm610/randomize.s @@ -2,15 +2,15 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .importzp time -__randomize: +___randomize: ldx time+2 ; Use 50/60HZ clock lda time+1 jmp _srand ; Initialize generator diff --git a/libsrc/cbm610/ser/cbm610-std.s b/libsrc/cbm610/ser/cbm610-std.s index 7cdb285bd..f7ddde935 100644 --- a/libsrc/cbm610/ser/cbm610-std.s +++ b/libsrc/cbm610/ser/cbm610-std.s @@ -149,8 +149,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -218,22 +219,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -254,8 +256,8 @@ SER_GET: @L1: lda RecvFreeCnt cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -302,7 +304,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -311,7 +313,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -339,8 +342,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/cbm610/ser_stat_stddrv.s b/libsrc/cbm610/ser_stat_stddrv.s new file mode 100644 index 000000000..643a74c7d --- /dev/null +++ b/libsrc/cbm610/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _cbm610_std_ser + +.rodata + +_ser_static_stddrv := _cbm610_std_ser diff --git a/libsrc/cbm610/ser_stddrv.s b/libsrc/cbm610/ser_stddrv.s new file mode 100644 index 000000000..83702c1ef --- /dev/null +++ b/libsrc/cbm610/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "cbm610-std.ser" diff --git a/libsrc/common/_afailed.c b/libsrc/common/_afailed.c index ab8b94ca6..01e8d5397 100644 --- a/libsrc/common/_afailed.c +++ b/libsrc/common/_afailed.c @@ -12,7 +12,7 @@ #include <stdlib.h> -void __fastcall__ _afailed (char* file, unsigned line) +void __fastcall__ __afailed (char* file, unsigned line) { raise (SIGABRT); fprintf (stderr, "ASSERTION FAILED IN %s:%u\n", file, line); diff --git a/libsrc/common/_directerrno.s b/libsrc/common/_directerrno.s index 794247148..20060bdd7 100644 --- a/libsrc/common/_directerrno.s +++ b/libsrc/common/_directerrno.s @@ -10,14 +10,14 @@ .macpack cpu ; ---------------------------------------------------------------------------- -; int __fastcall__ _directerrno (unsigned char code); -; /* Set errno to a specific error code, clear _oserror, and return -1. Used +; int __fastcall__ __directerrno (unsigned char code); +; /* Set errno to a specific error code, clear __oserror, and return -1. Used ; ** by the library. ; */ -__directerrno: - jsr __seterrno ; Set errno (returns with .A = 0) - sta __oserror ; Clear __oserror +___directerrno: + jsr ___seterrno ; Set errno (returns with .A = 0) + sta ___oserror ; Clear ___oserror .if (.cpu .bitand CPU_ISET_65SC02) dec a .else diff --git a/libsrc/common/_fopen.s b/libsrc/common/_fopen.s index 29f1c4cd9..17a1ec19b 100644 --- a/libsrc/common/_fopen.s +++ b/libsrc/common/_fopen.s @@ -69,7 +69,7 @@ invmode: lda #EINVAL - jsr __seterrno ; Set __errno, returns zero in A + jsr ___seterrno ; Set __errno, returns zero in A tax ; a/x = 0 jmp incsp4 @@ -91,7 +91,7 @@ modeok: ldy #$00 bne openok cmp #$FF bne openok - jmp return0 ; Failure, errno/_oserror already set + jmp return0 ; Failure, errno/__oserror already set ; Open call succeeded diff --git a/libsrc/common/_heap.s b/libsrc/common/_heap.s index 4ec8c80cd..eb680fa20 100644 --- a/libsrc/common/_heap.s +++ b/libsrc/common/_heap.s @@ -13,15 +13,15 @@ .data -__heaporg: +___heaporg: .word __BSS_RUN__+__BSS_SIZE__ ; Linker calculates this symbol -__heapptr: +___heapptr: .word __BSS_RUN__+__BSS_SIZE__ ; Dito -__heapend: +___heapend: .word __BSS_RUN__+__BSS_SIZE__ -__heapfirst: +___heapfirst: .word 0 -__heaplast: +___heaplast: .word 0 @@ -33,10 +33,10 @@ initheap: sec lda sp sbc #<__STACKSIZE__ - sta __heapend + sta ___heapend lda sp+1 sbc #>__STACKSIZE__ - sta __heapend+1 + sta ___heapend+1 rts diff --git a/libsrc/common/_heapadd.s b/libsrc/common/_heapadd.s index 14080cb5e..7695a732d 100644 --- a/libsrc/common/_heapadd.s +++ b/libsrc/common/_heapadd.s @@ -10,7 +10,7 @@ .importzp ptr1, ptr2 .import popax .import heapadd - .export __heapadd + .export ___heapadd .include "_heap.inc" @@ -19,7 +19,7 @@ ;----------------------------------------------------------------------------- ; Code -__heapadd: +___heapadd: sta ptr1 ; Store size in ptr1 stx ptr1+1 jsr popax ; Get the block pointer diff --git a/libsrc/common/_heapblocksize.s b/libsrc/common/_heapblocksize.s index db33f594c..e9b0cdad9 100644 --- a/libsrc/common/_heapblocksize.s +++ b/libsrc/common/_heapblocksize.s @@ -7,7 +7,7 @@ ; .importzp ptr1, ptr2 - .export __heapblocksize + .export ___heapblocksize .include "_heap.inc" @@ -17,7 +17,7 @@ ;----------------------------------------------------------------------------- ; Code -__heapblocksize: +___heapblocksize: ; Below the user data is a pointer that points to the start of the real ; (raw) memory block. The first word of this block is the size. To access diff --git a/libsrc/common/_heapmaxavail.s b/libsrc/common/_heapmaxavail.s index 19ae18b8d..b4e72f2d4 100644 --- a/libsrc/common/_heapmaxavail.s +++ b/libsrc/common/_heapmaxavail.s @@ -8,7 +8,7 @@ ; .importzp ptr1, ptr2 - .export __heapmaxavail + .export ___heapmaxavail .include "_heap.inc" @@ -17,22 +17,22 @@ ;----------------------------------------------------------------------------- ; Code -__heapmaxavail: +___heapmaxavail: ; size_t Size = (_heapend - _heapptr) * sizeof (*_heapend); - lda __heapend - sub __heapptr + lda ___heapend + sub ___heapptr sta ptr2 - lda __heapend+1 - sbc __heapptr+1 + lda ___heapend+1 + sbc ___heapptr+1 sta ptr2+1 ; struct freeblock* F = _heapfirst; - lda __heapfirst + lda ___heapfirst sta ptr1 - lda __heapfirst+1 + lda ___heapfirst+1 @L1: sta ptr1+1 ; while (F) { diff --git a/libsrc/common/_heapmemavail.s b/libsrc/common/_heapmemavail.s index 69aa75f8a..14ecc853a 100644 --- a/libsrc/common/_heapmemavail.s +++ b/libsrc/common/_heapmemavail.s @@ -8,7 +8,7 @@ ; .importzp ptr1, ptr2 - .export __heapmemavail + .export ___heapmemavail .include "_heap.inc" @@ -17,7 +17,7 @@ ;----------------------------------------------------------------------------- ; Code -__heapmemavail: +___heapmemavail: ; size_t Size = 0; @@ -27,9 +27,9 @@ __heapmemavail: ; struct freeblock* F = _heapfirst; - lda __heapfirst + lda ___heapfirst sta ptr1 - lda __heapfirst+1 + lda ___heapfirst+1 @L1: sta ptr1+1 ; while (F) { @@ -61,17 +61,17 @@ __heapmemavail: ; return Size + (_heapend - _heapptr) * sizeof (*_heapend); @L2: lda ptr2 - add __heapend + add ___heapend sta ptr2 lda ptr2+1 - adc __heapend+1 + adc ___heapend+1 tax lda ptr2 - sub __heapptr + sub ___heapptr sta ptr2 txa - sbc __heapptr+1 + sbc ___heapptr+1 tax lda ptr2 diff --git a/libsrc/common/_mappederrno.s b/libsrc/common/_mappederrno.s index 33f654c29..83565b723 100644 --- a/libsrc/common/_mappederrno.s +++ b/libsrc/common/_mappederrno.s @@ -11,19 +11,19 @@ .macpack cpu ; ---------------------------------------------------------------------------- -; int __fastcall__ _mappederrno (unsigned char code); -; /* Set _oserror to the given platform-specific error code. If it is a real +; int __fastcall__ __mappederrno (unsigned char code); +; /* Set __oserror to the given platform-specific error code. If it is a real ; ** error code (not zero), set errno to the corresponding system error code, ; ** and return -1. Otherwise, return zero. ; ** Used by the library. ; */ -__mappederrno: - sta __oserror ; Store the error code +___mappederrno: + sta ___oserror ; Store the error code tax ; Did we have an error? bze ok ; Branch if no - jsr __osmaperrno ; Map OS error into errno code - jsr __seterrno ; Save in errno (returns with .A = 0) + jsr ___osmaperrno ; Map OS error into errno code + jsr ___seterrno ; Save in errno (returns with .A = 0) .if (.cpu .bitand CPU_ISET_65SC02) dec a .else diff --git a/libsrc/common/_oserror.s b/libsrc/common/_oserror.s index 256d89658..bd8e40711 100644 --- a/libsrc/common/_oserror.s +++ b/libsrc/common/_oserror.s @@ -1,14 +1,14 @@ ; ; Ullrich von Bassewitz, 16.05.2000 ; -; extern unsigned char _oserror; +; extern unsigned char __oserror; ; /* Operating system specific errors from the low level functions */ - .export __oserror + .export ___oserror .bss -__oserror: +___oserror: .res 1 diff --git a/libsrc/common/_poserror.c b/libsrc/common/_poserror.c index 2777fee98..a2a67dd3e 100644 --- a/libsrc/common/_poserror.c +++ b/libsrc/common/_poserror.c @@ -39,10 +39,10 @@ -void __fastcall__ _poserror (const char* msg) +void __fastcall__ __poserror (const char* msg) { /* Fetch the message that corresponds to _oserror */ - const char* errormsg = _stroserror (_oserror); + const char* errormsg = __stroserror (_oserror); /* Different output depending on msg */ if (msg) { diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index 840d42127..a0074583e 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -502,10 +502,10 @@ DoFormat: ; It is a character jsr GetIntArg ; Get the argument (promoted to int) - sta Buf ; Place it as zero terminated string... - lda #0 - sta Buf+1 ; ...into the buffer - jmp HaveArg ; Done + sta Buf ; Place it into the buffer + ldx #0 + lda #1 ; Buffer length is 1 + jmp HaveArg1 ; Is it an integer? @@ -671,6 +671,7 @@ HaveArg: lda Str ldx Str+1 jsr _strlen ; Get length of argument +HaveArg1: ; Jumped into here from %c handling sta ArgLen stx ArgLen+1 diff --git a/libsrc/common/_seterrno.s b/libsrc/common/_seterrno.s index 79021143a..4344a3302 100644 --- a/libsrc/common/_seterrno.s +++ b/libsrc/common/_seterrno.s @@ -1,7 +1,7 @@ ; ; Ullrich von Bassewitz, 2004-05-13 ; -; __seterrno: Will set __errno to the value in A and return zero in A. Other +; ___seterrno: Will set ___errno to the value in A and return zero in A. Other ; registers aren't changed. The function is C callable, but ; currently only called from asm code. ; @@ -10,11 +10,11 @@ .code -.proc __seterrno +.proc ___seterrno - sta __errno + sta ___errno lda #0 - sta __errno+1 + sta ___errno+1 rts .endproc diff --git a/libsrc/common/_swap.s b/libsrc/common/_swap.s index 9ad771de1..ba3e5922d 100644 --- a/libsrc/common/_swap.s +++ b/libsrc/common/_swap.s @@ -1,15 +1,15 @@ ; ; Ullrich von Bassewitz, 1998-12-09, 2004-11-30 ; -; void __fastcall__ _swap (void* p, void* q, size_t size); +; void __fastcall__ __swap (void* p, void* q, size_t size); ; - .export __swap + .export ___swap .import popax, popptr1 .importzp ptr1, ptr2, ptr3 -__swap: eor #$FF +___swap: eor #$FF sta ptr3 txa eor #$FF diff --git a/libsrc/common/atexit.s b/libsrc/common/atexit.s index 6edfd4a5d..47a1dfd4d 100644 --- a/libsrc/common/atexit.s +++ b/libsrc/common/atexit.s @@ -38,7 +38,7 @@ ; Error, no space left @Error: lda #ENOSPC ; No space left - jsr __seterrno + jsr ___seterrno ldx #$FF ; Return -1 txa rts diff --git a/libsrc/common/calloc.s b/libsrc/common/calloc.s index 6e3e1bd3f..4cb701146 100644 --- a/libsrc/common/calloc.s +++ b/libsrc/common/calloc.s @@ -7,7 +7,7 @@ ; .export _calloc - .import _malloc, __bzero + .import _malloc, ___bzero .import tosumulax, pushax @@ -48,7 +48,7 @@ ClearBlock: jsr pushax ; ptr lda Size ldx Size+1 ; Size - jmp __bzero + jmp ___bzero .endproc diff --git a/libsrc/common/chdir.s b/libsrc/common/chdir.s index 3e638cb04..1f10a6804 100644 --- a/libsrc/common/chdir.s +++ b/libsrc/common/chdir.s @@ -7,7 +7,7 @@ .export _chdir .import __syschdir - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -17,7 +17,7 @@ .proc _chdir jsr __syschdir ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/errno.s b/libsrc/common/errno.s index f448c3c14..43bbe3fb0 100644 --- a/libsrc/common/errno.s +++ b/libsrc/common/errno.s @@ -2,7 +2,7 @@ ; 2003-08-12, Ullrich von Bassewitz ; 2015-09-24, Greg King ; -; extern int _errno; +; extern int __errno; ; /* Library errors go here. */ ; @@ -10,5 +10,5 @@ .bss -__errno: +___errno: .word 0 diff --git a/libsrc/common/fclose.s b/libsrc/common/fclose.s index 5365d70da..2368bf9f6 100644 --- a/libsrc/common/fclose.s +++ b/libsrc/common/fclose.s @@ -31,7 +31,7 @@ ; File is not open lda #EINVAL - jsr __seterrno + jsr ___seterrno lda #$FF ; Return -1 tax rts diff --git a/libsrc/common/fmisc.s b/libsrc/common/fmisc.s index 156a80dbc..90b48e8a6 100644 --- a/libsrc/common/fmisc.s +++ b/libsrc/common/fmisc.s @@ -78,7 +78,7 @@ err: rts ; If the file is not valid, fileno must set errno and return -1 error: lda #<EBADF - jsr __seterrno + jsr ___seterrno lda #$FF tax rts diff --git a/libsrc/common/fopen.s b/libsrc/common/fopen.s index d4d94acee..0f493706b 100644 --- a/libsrc/common/fopen.s +++ b/libsrc/common/fopen.s @@ -36,7 +36,7 @@ ; Failed to allocate a file stream lda #EMFILE - jsr __seterrno ; Set __errno, will return 0 in A + jsr ___seterrno ; Set __errno, will return 0 in A tax rts ; Return zero diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s index c0733994e..647a7f2c8 100644 --- a/libsrc/common/fread.s +++ b/libsrc/common/fread.s @@ -59,7 +59,7 @@ ; File not open or in error state @L1: lda #EINVAL - jsr __seterrno ; Set __errno, return zero in A + jsr ___seterrno ; Set __errno, return zero in A tax ; a/x = 0 jmp @L99 ; Bail out diff --git a/libsrc/common/free.s b/libsrc/common/free.s index 53796303c..709460ff9 100644 --- a/libsrc/common/free.s +++ b/libsrc/common/free.s @@ -105,27 +105,27 @@ _free: sta ptr2 tay lda ptr2+1 adc ptr1+1 - cpy __heapptr + cpy ___heapptr bne heapadd ; Add to free list - cmp __heapptr+1 + cmp ___heapptr+1 bne heapadd ; The pointer is located at the heap top. Lower the heap top pointer to ; release the block. @L3: lda ptr2 - sta __heapptr + sta ___heapptr lda ptr2+1 - sta __heapptr+1 + sta ___heapptr+1 ; Check if the last block in the freelist is now at heap top. If so, remove ; this block from the freelist. - lda __heaplast + lda ___heaplast sta ptr1 - ora __heaplast+1 + ora ___heaplast+1 beq @L9 ; Jump if free list empty - lda __heaplast+1 + lda ___heaplast+1 sta ptr1+1 ; Pointer to last block now in ptr1 ldy #freeblock::size @@ -136,35 +136,35 @@ _free: sta ptr2 lda (ptr1),y adc ptr1+1 - cmp __heapptr+1 + cmp ___heapptr+1 bne @L9 ; Jump if last block not on top of heap - cpx __heapptr + cpx ___heapptr bne @L9 ; Jump if last block not on top of heap ; Remove the last block lda ptr1 - sta __heapptr + sta ___heapptr lda ptr1+1 - sta __heapptr+1 + sta ___heapptr+1 ; Correct the next pointer of the now last block ldy #freeblock::prev+1 ; Offset of ->prev field lda (ptr1),y sta ptr2+1 ; Remember f->prev in ptr2 - sta __heaplast+1 + sta ___heaplast+1 dey lda (ptr1),y sta ptr2 ; Remember f->prev in ptr2 - sta __heaplast - ora __heaplast+1 ; -> prev == 0? + sta ___heaplast + ora ___heaplast+1 ; -> prev == 0? bne @L8 ; Jump if free list not empty ; Free list is now empty (A = 0) - sta __heapfirst - sta __heapfirst+1 + sta ___heapfirst + sta ___heapfirst+1 ; Done @@ -283,9 +283,9 @@ _free: sta ptr2 ; Check if the free list is empty, storing _hfirst into ptr3 for later heapadd: - lda __heapfirst + lda ___heapfirst sta ptr3 - lda __heapfirst+1 + lda ___heapfirst+1 sta ptr3+1 ora ptr3 bne SearchFreeList @@ -301,10 +301,10 @@ heapadd: lda ptr2 ldx ptr2+1 - sta __heapfirst - stx __heapfirst+1 ; _heapfirst = f; - sta __heaplast - stx __heaplast+1 ; _heaplast = f; + sta ___heapfirst + stx ___heapfirst+1 ; _heapfirst = f; + sta ___heaplast + stx ___heaplast+1 ; _heaplast = f; rts ; Done @@ -351,9 +351,9 @@ SearchFreeList: sta (ptr2),y ; Clear low byte of f->next lda ptr2 ; _heaplast = f; - sta __heaplast + sta ___heaplast lda ptr2+1 - sta __heaplast+1 + sta ___heaplast+1 ; Since we have checked the case that the freelist is empty before, if the ; right pointer is NULL, the left *cannot* be NULL here. So skip the @@ -414,9 +414,9 @@ CheckRightMerge: ; f->next is zero, this is now the last block @L1: lda ptr2 ; _heaplast = f; - sta __heaplast + sta ___heaplast lda ptr2+1 - sta __heaplast+1 + sta ___heaplast+1 jmp CheckLeftMerge ; No right merge, just set the link. @@ -451,9 +451,9 @@ CheckLeftMerge: sta (ptr2),y lda ptr2 ; _heapfirst = f; - sta __heapfirst + sta ___heapfirst lda ptr2+1 - sta __heapfirst+1 + sta ___heapfirst+1 rts ; Done @@ -510,9 +510,9 @@ CheckLeftMerge2: ; This is now the last block, do _heaplast = left @L1: lda ptr4 - sta __heaplast + sta ___heaplast lda ptr4+1 - sta __heaplast+1 + sta ___heaplast+1 rts ; Done ; No merge of the left block, just set the link. Y points to size+1 if diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s index b0fa7ec42..861feb120 100644 --- a/libsrc/common/fwrite.s +++ b/libsrc/common/fwrite.s @@ -39,7 +39,7 @@ ; File not open @L1: lda #EBADF - jsr __seterrno ; Returns with A = 0 + jsr ___seterrno ; Returns with A = 0 tax ; A = X = 0 jmp incsp6 diff --git a/libsrc/common/getcwd.s b/libsrc/common/getcwd.s index 9b856ea7c..5fc0bbf0a 100644 --- a/libsrc/common/getcwd.s +++ b/libsrc/common/getcwd.s @@ -51,7 +51,7 @@ loop: dec ptr2 overflow: lda #<ERANGE - jsr __seterrno ; Returns 0 in A + jsr ___seterrno ; Returns 0 in A tax ; Return zero rts diff --git a/libsrc/common/isascii.s b/libsrc/common/isascii.s index 70d2f72a3..9a905d1db 100644 --- a/libsrc/common/isascii.s +++ b/libsrc/common/isascii.s @@ -11,6 +11,7 @@ ; .export _isascii + .import return0 _isascii: asl a ; high-bit to carry @@ -19,6 +20,4 @@ _isascii: adc #$FF ; calculate return value based on carry rts -@L1: lda #$00 ; return false - tax - rts +@L1: jmp return0 ; return false diff --git a/libsrc/common/malloc.s b/libsrc/common/malloc.s index 3118e2e56..6872f1f2e 100644 --- a/libsrc/common/malloc.s +++ b/libsrc/common/malloc.s @@ -140,9 +140,9 @@ _malloc: ; Load a pointer to the freelist into ptr2 -@L2: lda __heapfirst +@L2: lda ___heapfirst sta ptr2 - lda __heapfirst+1 + lda ___heapfirst+1 sta ptr2+1 ; Search the freelist for a block that is big enough. We will calculate @@ -173,16 +173,16 @@ _malloc: ; We did not find a block big enough. Try to use new space from the heap top. - lda __heapptr + lda ___heapptr add ptr1 ; _heapptr + size tay - lda __heapptr+1 + lda ___heapptr+1 adc ptr1+1 bcs OutOfHeapSpace ; On overflow, we're surely out of space - cmp __heapend+1 + cmp ___heapend+1 bne @L5 - cpy __heapend + cpy ___heapend @L5: bcc TakeFromTop beq TakeFromTop @@ -196,13 +196,13 @@ Done: rts ; There is enough space left, take it from the heap top TakeFromTop: - ldx __heapptr ; p = _heapptr; + ldx ___heapptr ; p = _heapptr; stx ptr2 - ldx __heapptr+1 + ldx ___heapptr+1 stx ptr2+1 - sty __heapptr ; _heapptr += size; - sta __heapptr+1 + sty ___heapptr ; _heapptr += size; + sta ___heapptr+1 jmp FillSizeAndRet ; Done ; We found a block big enough. If the block can hold just the @@ -245,10 +245,10 @@ BlockFound: ; Do _hfirst = f->next @L1: lda (ptr2),y ; Load high byte of f->next - sta __heapfirst+1 + sta ___heapfirst+1 dey ; Points to next lda (ptr2),y ; Load low byte of f->next - sta __heapfirst + sta ___heapfirst ; Check f->next. Y points always to next if we come here @@ -275,10 +275,10 @@ BlockFound: ; Do _hlast = f->prev @L3: lda (ptr2),y ; Load low byte of f->prev - sta __heaplast + sta ___heaplast iny ; Points to prev+1 lda (ptr2),y ; Load high byte of f->prev - sta __heaplast+1 + sta ___heaplast+1 jmp RetUserPtr ; Done ; We must slice the block found. Cut off space from the upper end, so we diff --git a/libsrc/common/memset.s b/libsrc/common/memset.s index a57a90e5c..be78fc30d 100644 --- a/libsrc/common/memset.s +++ b/libsrc/common/memset.s @@ -1,6 +1,6 @@ ; ; void* __fastcall__ memset (void* ptr, int c, size_t n); -; void* __fastcall__ _bzero (void* ptr, size_t n); +; void* __fastcall__ __bzero (void* ptr, size_t n); ; void __fastcall__ bzero (void* ptr, size_t n); ; ; Ullrich von Bassewitz, 29.05.1998 @@ -8,19 +8,19 @@ ; Christian Krueger, 12.09.2009, slightly improved 12.01.2011 ; ; NOTE: bzero will return it's first argument as memset does. It is no problem -; to declare the return value as void, since it may be ignored. _bzero -; (note the leading underscore) is declared with the proper return type, -; because the compiler will replace memset by _bzero if the fill value +; to declare the return value as void, since it may be ignored. __bzero +; (note the leading underscores) is declared with the proper return type, +; because the compiler will replace memset by __bzero if the fill value ; is zero, and the optimizer looks at the return type to see if the value ; in a/x is of any use. ; - .export _memset, _bzero, __bzero + .export _memset, _bzero, ___bzero .import popax .importzp sp, ptr1, ptr2, ptr3 _bzero: -__bzero: +___bzero: sta ptr3 stx ptr3+1 ; Save n ldx #0 ; Fill with zeros diff --git a/libsrc/common/mkdir.s b/libsrc/common/mkdir.s index 1144983aa..1a5c7551f 100644 --- a/libsrc/common/mkdir.s +++ b/libsrc/common/mkdir.s @@ -7,7 +7,7 @@ .export _mkdir .import __sysmkdir - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -15,6 +15,6 @@ .proc _mkdir jsr __sysmkdir ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/perror.c b/libsrc/common/perror.c index ddc015c1e..9b0dac7de 100644 --- a/libsrc/common/perror.c +++ b/libsrc/common/perror.c @@ -42,7 +42,7 @@ void __fastcall__ perror (const char* msg) { /* Fetch the message that corresponds to errno */ - const char* errormsg = strerror (_errno); + const char* errormsg = strerror (__errno); /* Different output depending on msg */ if (msg) { diff --git a/libsrc/common/putenv.s b/libsrc/common/putenv.s index c68d20a32..5febcc71e 100644 --- a/libsrc/common/putenv.s +++ b/libsrc/common/putenv.s @@ -169,7 +169,7 @@ addentry: ; Error entries nomem: lda #ENOMEM -error: jsr __seterrno +error: jsr ___seterrno lda #$FF ; Return -1 tax rts diff --git a/libsrc/common/qsort.c b/libsrc/common/qsort.c index 991db3ba1..913165814 100644 --- a/libsrc/common/qsort.c +++ b/libsrc/common/qsort.c @@ -32,13 +32,13 @@ static void QuickSort (register unsigned char* Base, int Lo, int Hi, J -= Size; } if (I <= J) { - _swap (Base + I, Base + J, Size); + __swap (Base + I, Base + J, Size); I += Size; J -= Size; } } if (J != Lo) { - _swap (Base + J, Base + Lo, Size); + __swap (Base + J, Base + Lo, Size); } if (((unsigned) J) * 2 > (Hi + Lo)) { QuickSort (Base, J + Size, Hi, Size, Compare); diff --git a/libsrc/common/raise.s b/libsrc/common/raise.s index 07898ef90..205bbd471 100644 --- a/libsrc/common/raise.s +++ b/libsrc/common/raise.s @@ -33,9 +33,9 @@ _raise: ; introduce race conditions, but it's the simplest way to satisfy the ; standard). - lda #<__sig_dfl + lda #<___sig_dfl sta sigtable,x - lda #>__sig_dfl + lda #>___sig_dfl sta sigtable+1,x ; Restore the signal number and call the function diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c index c47dbbb98..eeb1eeea5 100644 --- a/libsrc/common/realloc.c +++ b/libsrc/common/realloc.c @@ -74,12 +74,12 @@ void* __fastcall__ realloc (void* block, register size_t size) oldsize = b->size; /* Is the block at the current heap top? */ - if (((unsigned) b) + oldsize == ((unsigned) _heapptr)) { + 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)) { + newhptr = ((unsigned) __heapptr) - oldsize + size; + if (newhptr <= ((unsigned) __heapend)) { /* Ok, there's space enough */ - _heapptr = (unsigned*) newhptr; + __heapptr = (unsigned*) newhptr; b->size = size; b->start = b; return block; diff --git a/libsrc/common/remove.s b/libsrc/common/remove.s index e66f047ef..1b8515870 100644 --- a/libsrc/common/remove.s +++ b/libsrc/common/remove.s @@ -7,7 +7,7 @@ .export _remove .import __sysremove - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -15,7 +15,7 @@ .proc _remove jsr __sysremove ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/rename.s b/libsrc/common/rename.s index 0fbffa426..122ae1501 100644 --- a/libsrc/common/rename.s +++ b/libsrc/common/rename.s @@ -7,7 +7,7 @@ .export _rename .import __sysrename - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -15,7 +15,7 @@ .proc _rename jsr __sysrename ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/rmdir.s b/libsrc/common/rmdir.s index 84ae9a54c..ea0780387 100644 --- a/libsrc/common/rmdir.s +++ b/libsrc/common/rmdir.s @@ -7,7 +7,7 @@ .export _rmdir .import __sysrmdir - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -15,6 +15,6 @@ .proc _rmdir jsr __sysrmdir ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/setjmp.s b/libsrc/common/setjmp.s index a763ac3ec..886853368 100644 --- a/libsrc/common/setjmp.s +++ b/libsrc/common/setjmp.s @@ -2,15 +2,15 @@ ; 1998-06-06, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; int __fastcall__ setjmp (jmp_buf buf); +; int __fastcall__ __setjmp (jmp_buf buf); ; - .export __setjmp + .export ___setjmp .import return0 .importzp sp, ptr1 -__setjmp: +___setjmp: sta ptr1 ; Save buf stx ptr1+1 ldy #0 diff --git a/libsrc/common/signal.s b/libsrc/common/signal.s index 333072801..65d0316b4 100644 --- a/libsrc/common/signal.s +++ b/libsrc/common/signal.s @@ -54,15 +54,15 @@ _signal: pla tax pla -__sig_ign: +___sig_ign: rts ; Error entry: We use our knowledge that SIG_ERR is zero here to save a byte invalidsig: lda #<EINVAL - jsr __seterrno ; Returns 0 in A + jsr ___seterrno ; Returns 0 in A tax ; A/X = 0 -__sig_dfl: +___sig_dfl: rts diff --git a/libsrc/common/sigtable.s b/libsrc/common/sigtable.s index 7780ef634..5b2d7f80f 100644 --- a/libsrc/common/sigtable.s +++ b/libsrc/common/sigtable.s @@ -14,11 +14,11 @@ .data sigtable: - .word __sig_dfl ; SIGABRT - .word __sig_dfl ; SIGFPE - .word __sig_dfl ; SIGILL - .word __sig_dfl ; SIGINT - .word __sig_dfl ; SIGSEGV - .word __sig_dfl ; SIGTERM + .word ___sig_dfl ; SIGABRT + .word ___sig_dfl ; SIGFPE + .word ___sig_dfl ; SIGILL + .word ___sig_dfl ; SIGINT + .word ___sig_dfl ; SIGSEGV + .word ___sig_dfl ; SIGTERM diff --git a/libsrc/common/strerror.s b/libsrc/common/strerror.s index 0f41331ea..8cc7c7441 100644 --- a/libsrc/common/strerror.s +++ b/libsrc/common/strerror.s @@ -19,9 +19,9 @@ _strerror: ; The given error code is invalid @L1: lda #<EINVAL - sta __errno + sta ___errno lda #>EINVAL ; = 0 - sta __errno+1 + sta ___errno+1 ; lda #$00 ; A contains zero: "Unknown error" ; Load the pointer to the error message and return diff --git a/libsrc/common/stroserr.s b/libsrc/common/stroserr.s index 25ca30daf..ae8e117f1 100644 --- a/libsrc/common/stroserr.s +++ b/libsrc/common/stroserr.s @@ -1,11 +1,11 @@ ; ; Ullrich von Bassewitz, 17.07.2002 ; -; const char* __fastcall__ _stroserror (unsigned char errcode); +; const char* __fastcall__ __stroserror (unsigned char errcode); ; /* Map an operating system error number to an error message. */ ; - .export __stroserror + .export ___stroserror .import __sys_oserrlist .importzp ptr1, tmp1 @@ -21,7 +21,7 @@ ; and terminated by an entry with length zero that is returned if the ; error code could not be found. -__stroserror: +___stroserror: sta tmp1 ; Save the error code ldy #<__sys_oserrlist diff --git a/libsrc/common/uname.s b/libsrc/common/uname.s index 3c1b0840f..3f2a8ff14 100644 --- a/libsrc/common/uname.s +++ b/libsrc/common/uname.s @@ -7,7 +7,7 @@ .export _uname .import __sysuname - .import __mappederrno + .import ___mappederrno ;-------------------------------------------------------------------------- @@ -15,7 +15,7 @@ .proc _uname jsr __sysuname ; Call the machine specific function - jmp __mappederrno ; Store into _oserror, set errno, return 0/-1 + jmp ___mappederrno ; Store into __oserror, set errno, return 0/-1 .endproc diff --git a/libsrc/common/ungetc.s b/libsrc/common/ungetc.s index 88595068c..7e8c1f94f 100644 --- a/libsrc/common/ungetc.s +++ b/libsrc/common/ungetc.s @@ -62,7 +62,7 @@ ; File is not open or the character is invalid error: lda #EINVAL - jsr __seterrno + jsr ___seterrno lda #$FF ; Return -1 tax rts diff --git a/libsrc/common/vsnprintf.s b/libsrc/common/vsnprintf.s index 228e531d0..048a756c3 100644 --- a/libsrc/common/vsnprintf.s +++ b/libsrc/common/vsnprintf.s @@ -140,7 +140,7 @@ L0: ldy #EINVAL pla ; Drop ap pla tya - jsr __directerrno ; Return -1 + jsr ___directerrno ; Return -1 jmp incsp6 ; Drop parameters diff --git a/libsrc/creativision/joy/creativision-stdjoy.s b/libsrc/creativision/joy/creativision-stdjoy.s index 73b0c249f..43f9a2b40 100644 --- a/libsrc/creativision/joy/creativision-stdjoy.s +++ b/libsrc/creativision/joy/creativision-stdjoy.s @@ -59,7 +59,8 @@ JOY_RIGHT = $08 ; INSTALL: lda #JOY_ERR_OK - ldx #>$0000 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Fall through ; ------------------------------------------------------------------------ diff --git a/libsrc/cx16/joy/cx16-std.s b/libsrc/cx16/joy/cx16-std.s index a40fcb061..5def55511 100644 --- a/libsrc/cx16/joy/cx16-std.s +++ b/libsrc/cx16/joy/cx16-std.s @@ -55,8 +55,9 @@ JOY_COUNT = $05 ; Number of joysticks we support ; Must return a JOY_ERR_xx code in .XA . INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/cx16/mou/cx16-std.s b/libsrc/cx16/mou/cx16-std.s index 3af7d2eb3..f211815de 100644 --- a/libsrc/cx16/mou/cx16-std.s +++ b/libsrc/cx16/mou/cx16-std.s @@ -139,7 +139,8 @@ INSTALL: ; Done, return zero - ldx #>MOUSE_ERR_OK + ldx #MOUSE_ERR_OK + .assert MOUSE_ERR_OK = 0, error txa rts @@ -300,8 +301,8 @@ INFO: jsr BUTTONS ; Will not touch ptr1 ; specific data in ptr1, and the ioctl code in A. ; Must return an error code in .XA . -IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now - ldx #>MOUSE_ERR_INV_IOCTL +IOCTL: lda #MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now + ldx #0 ; return value is char ; rts ; Fall through ;---------------------------------------------------------------------------- diff --git a/libsrc/cx16/randomize.s b/libsrc/cx16/randomize.s index 3d965c82a..49051e012 100644 --- a/libsrc/cx16/randomize.s +++ b/libsrc/cx16/randomize.s @@ -1,14 +1,14 @@ ; ; 2020-05-02, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import ENTROPY_GET, _srand -__randomize: +___randomize: jsr ENTROPY_GET jmp _srand ; Initialize generator diff --git a/libsrc/cx16/waitvsync.s b/libsrc/cx16/waitvsync.s index dc0509223..d75a7a735 100644 --- a/libsrc/cx16/waitvsync.s +++ b/libsrc/cx16/waitvsync.s @@ -5,17 +5,20 @@ ; /* Wait for the start of the next video field. */ ; ; VERA's vertical sync causes IRQs which increment the jiffy timer. +; +; Updated by ZeroByteOrg to use Kernal API RDTIM to retreive the TIMER variable ; .export _waitvsync + .importzp tmp1 + .import RDTIM - .include "cx16.inc" - -_waitvsync: - ldx RAM_BANK ; (TIMER is in RAM bank 0) - stz RAM_BANK - lda TIMER + 2 -: cmp TIMER + 2 - beq :- ; Wait for next jiffy - stx RAM_BANK - rts +.proc _waitvsync: near + jsr RDTIM + sta tmp1 +keep_waiting: + jsr RDTIM + cmp tmp1 + beq keep_waiting + rts +.endproc diff --git a/libsrc/dbg/dbg.c b/libsrc/dbg/dbg.c index 832ed0a20..dd47eae0c 100644 --- a/libsrc/dbg/dbg.c +++ b/libsrc/dbg/dbg.c @@ -1063,7 +1063,7 @@ static char StackHandler (void) break; case 'a': -#ifdef CH_CURS_UP: +#ifdef CH_CURS_UP case CH_CURS_UP: #endif --StackAddr; diff --git a/libsrc/gamate/joy/gamate-stdjoy.s b/libsrc/gamate/joy/gamate-stdjoy.s index 8f927cdf5..514f92db4 100644 --- a/libsrc/gamate/joy/gamate-stdjoy.s +++ b/libsrc/gamate/joy/gamate-stdjoy.s @@ -47,8 +47,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/geos-cbm/disk/dio_cts.s b/libsrc/geos-cbm/disk/dio_cts.s index 478181b69..81b189f65 100644 --- a/libsrc/geos-cbm/disk/dio_cts.s +++ b/libsrc/geos-cbm/disk/dio_cts.s @@ -10,7 +10,7 @@ .export _dio_phys_to_log .export sectab_1541_l, sectab_1541_h ; for log_to_phys - .import popax,__oserror + .import popax,___oserror .importzp ptr1,ptr2,ptr3,tmp1,tmp2,tmp3,tmp4 .include "dio.inc" @@ -82,7 +82,7 @@ dio_ctsend: ldx #0 txa ret: - sta __oserror + sta ___oserror rts ; return success ; errors diff --git a/libsrc/geos-cbm/disk/dio_openclose.s b/libsrc/geos-cbm/disk/dio_openclose.s index 327503017..953aed318 100644 --- a/libsrc/geos-cbm/disk/dio_openclose.s +++ b/libsrc/geos-cbm/disk/dio_openclose.s @@ -11,7 +11,7 @@ ; dio_close does nothing special .export _dio_open, _dio_close - .import __oserror, _OpenDisk + .import ___oserror, _OpenDisk .importzp ptr1, tmp1 .include "dio.inc" @@ -69,7 +69,7 @@ _dio_open: _inv_drive: lda #DEV_NOT_FOUND - sta __oserror + sta ___oserror lda #0 tax rts @@ -80,6 +80,6 @@ _dio_close: lda #0 ldy #sst_flag sta (ptr1),y - sta __oserror ; success + sta ___oserror ; success tax rts ; return no error diff --git a/libsrc/geos-cbm/disk/dio_qcount.s b/libsrc/geos-cbm/disk/dio_qcount.s index 5f10ac247..b5d380825 100644 --- a/libsrc/geos-cbm/disk/dio_qcount.s +++ b/libsrc/geos-cbm/disk/dio_qcount.s @@ -5,11 +5,11 @@ ; .export _dio_query_sectcount - .import __oserror + .import ___oserror _dio_query_sectcount: lda #0 - sta __oserror + sta ___oserror lda #<683 ldx #>683 rts diff --git a/libsrc/geos-cbm/disk/dio_qsize.s b/libsrc/geos-cbm/disk/dio_qsize.s index bf9178035..14c8a460a 100644 --- a/libsrc/geos-cbm/disk/dio_qsize.s +++ b/libsrc/geos-cbm/disk/dio_qsize.s @@ -5,10 +5,10 @@ ; .export _dio_query_sectsize - .import __oserror + .import ___oserror _dio_query_sectsize: lda #<256 ldx #>256 - sta __oserror + sta ___oserror rts diff --git a/libsrc/geos-cbm/disk/dio_read.s b/libsrc/geos-cbm/disk/dio_read.s index ac19f9afa..4c9297e65 100644 --- a/libsrc/geos-cbm/disk/dio_read.s +++ b/libsrc/geos-cbm/disk/dio_read.s @@ -8,7 +8,7 @@ ; .export _dio_read - .import dio_params, __oserror + .import dio_params, ___oserror .include "geossym.inc" .include "jumptab.inc" @@ -18,6 +18,6 @@ _dio_read: tay bne err jsr ReadBlock - stx __oserror + stx ___oserror txa err: rts diff --git a/libsrc/geos-cbm/disk/dio_stc.s b/libsrc/geos-cbm/disk/dio_stc.s index 469df93ca..3c3c9108f 100644 --- a/libsrc/geos-cbm/disk/dio_stc.s +++ b/libsrc/geos-cbm/disk/dio_stc.s @@ -9,7 +9,7 @@ .export _dio_log_to_phys .importzp ptr1,ptr2,ptr3,tmp1,tmp2 - .import popax,__oserror + .import popax,___oserror .import sectab_1541_l, sectab_1541_h .include "dio.inc" @@ -78,7 +78,7 @@ dio_stcend: ldx #0 txa _ret: - sta __oserror + sta ___oserror rts ; return success ; errors diff --git a/libsrc/geos-cbm/disk/dio_writev.s b/libsrc/geos-cbm/disk/dio_writev.s index 9b36ed096..962d7e77f 100644 --- a/libsrc/geos-cbm/disk/dio_writev.s +++ b/libsrc/geos-cbm/disk/dio_writev.s @@ -8,7 +8,7 @@ ; .export _dio_write_verify - .import dio_params, __oserror + .import dio_params, ___oserror .include "geossym.inc" .include "jumptab.inc" @@ -18,6 +18,6 @@ _dio_write_verify: tay bne err jsr VerWriteBlock - stx __oserror + stx ___oserror txa err: rts diff --git a/libsrc/geos-cbm/emd/geos-vdc.s b/libsrc/geos-cbm/emd/geos-vdc.s index 27316e1a0..2e7d19c03 100644 --- a/libsrc/geos-cbm/emd/geos-vdc.s +++ b/libsrc/geos-cbm/emd/geos-vdc.s @@ -125,8 +125,9 @@ INSTALL: pla sta $01 plp - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + lda #EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts test64k: diff --git a/libsrc/geos-cbm/joy/geos-stdjoy.s b/libsrc/geos-cbm/joy/geos-stdjoy.s index 2787cb594..a3fd4ffc8 100644 --- a/libsrc/geos-cbm/joy/geos-stdjoy.s +++ b/libsrc/geos-cbm/joy/geos-stdjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/geos-common/common/_afailed.c b/libsrc/geos-common/common/_afailed.c index 97727d605..acc437fb0 100644 --- a/libsrc/geos-common/common/_afailed.c +++ b/libsrc/geos-common/common/_afailed.c @@ -8,7 +8,7 @@ #include <stdlib.h> #include <geos.h> -void _afailed (char* file, unsigned line) +void __afailed (char* file, unsigned line) { ExitTurbo(); diff --git a/libsrc/geos-common/common/_poserror.c b/libsrc/geos-common/common/_poserror.c index eeb3f368e..b47f0a7d9 100644 --- a/libsrc/geos-common/common/_poserror.c +++ b/libsrc/geos-common/common/_poserror.c @@ -9,9 +9,9 @@ #include <errno.h> #include <geos.h> -void __fastcall__ _poserror (const char* msg) +void __fastcall__ __poserror (const char* msg) { - const char *errmsg = _stroserror(_oserror); + const char *errmsg = __stroserror(_oserror); ExitTurbo(); if (msg && *msg) { diff --git a/libsrc/geos-common/common/memset.s b/libsrc/geos-common/common/memset.s index 2cb6732d1..82b7f4a87 100644 --- a/libsrc/geos-common/common/memset.s +++ b/libsrc/geos-common/common/memset.s @@ -1,14 +1,14 @@ ; ; void* memset (void* ptr, int c, size_t n); -; void* _bzero (void* ptr, size_t n); +; void* __bzero (void* ptr, size_t n); ; void bzero (void* ptr, size_t n); ; ; Maciej 'YTM/Elysium' Witkowiak, 20.08.2003 ; - .export _memset, _bzero, __bzero + .export _memset, _bzero, ___bzero .import _ClearRam, _FillRam _bzero = _ClearRam -__bzero = _ClearRam +___bzero = _ClearRam _memset = _FillRam diff --git a/libsrc/geos-common/disk/calcblksfree.s b/libsrc/geos-common/disk/calcblksfree.s index 7e1bb4f52..fec6d6690 100644 --- a/libsrc/geos-common/disk/calcblksfree.s +++ b/libsrc/geos-common/disk/calcblksfree.s @@ -5,7 +5,7 @@ ; int CalcBlksFree (void); - .import __oserror + .import ___oserror .export _CalcBlksFree .include "jumptab.inc" @@ -18,7 +18,7 @@ _CalcBlksFree: sta r5L stx r5H jsr CalcBlksFree - stx __oserror + stx ___oserror lda r4L ldx r4H rts diff --git a/libsrc/geos-common/disk/getptrcurdknm.s b/libsrc/geos-common/disk/getptrcurdknm.s index d92e5d91e..9112c9f99 100644 --- a/libsrc/geos-common/disk/getptrcurdknm.s +++ b/libsrc/geos-common/disk/getptrcurdknm.s @@ -7,7 +7,7 @@ ; (fills curName[17] with current disk's name) .importzp ptr4, ptr3 - .import __oserror + .import ___oserror .export _GetPtrCurDkNm .include "jumptab.inc" @@ -34,5 +34,5 @@ namelp: lda (ptr4),y bne namelp fin: lda #0 sta (ptr3),y - stx __oserror + stx ___oserror rts diff --git a/libsrc/geos-common/disk/setnextfree.s b/libsrc/geos-common/disk/setnextfree.s index b24f16f6f..2d43c1e36 100644 --- a/libsrc/geos-common/disk/setnextfree.s +++ b/libsrc/geos-common/disk/setnextfree.s @@ -5,7 +5,7 @@ ; struct tr_se SetNextFree (struct tr_se *startTS); - .import __oserror + .import ___oserror .import gettrse .export _SetNextFree @@ -18,7 +18,7 @@ _SetNextFree: sta r3L stx r3H jsr SetNextFree - stx __oserror + stx ___oserror lda r3L ldx r3H rts diff --git a/libsrc/geos-common/drivers/fio_module.s b/libsrc/geos-common/drivers/fio_module.s index 1314fc4c5..affbf6ecc 100644 --- a/libsrc/geos-common/drivers/fio_module.s +++ b/libsrc/geos-common/drivers/fio_module.s @@ -14,7 +14,7 @@ FILEDES = 3 ; first free to use file descriptor .importzp ptr1, ptr2, ptr3, tmp1 .import addysp, popax, popptr1 - .import __oserror + .import ___oserror .import _FindFile, _ReadByte .export _open, _close, _read @@ -65,8 +65,8 @@ _open: stx f_offset stx f_offset+1 lda #0 ; clear errors - sta __oserror - jsr __seterrno + sta ___oserror + jsr ___seterrno lda #FILEDES ; return fd sta filedesc rts @@ -75,14 +75,14 @@ _open: .byte $2c ; skip @alreadyopen: lda #EMFILE ; too many opened files (there can be only one) - jmp __directerrno ; set errno, clear oserror, return -1 + jmp ___directerrno ; set errno, clear oserror, return -1 @oserror: - jmp __mappederrno ; set platform error code, return -1 + jmp ___mappederrno ; set platform error code, return -1 _close: lda #0 - sta __oserror - jsr __seterrno ; clear errors + sta ___oserror + jsr ___seterrno ; clear errors lda #0 ; clear fd sta filedesc tax @@ -92,7 +92,7 @@ _read: ; a/x - number of bytes ; popax - buffer ptr ; popax - fd, must be == to the above one - ; return -1+__oserror or number of bytes read + ; return -1+___oserror or number of bytes read inx stx ptr1+1 @@ -111,14 +111,14 @@ _read: @filenotopen: lda #EBADF - jmp __directerrno ; Sets _errno, clears _oserror, returns -1 + jmp ___directerrno ; Sets _errno, clears __oserror, returns -1 @fileok: lda #0 sta ptr3 sta ptr3+1 ; put 0 into ptr3 (number of bytes read) - sta __oserror ; clear error flags - jsr __seterrno + sta ___oserror ; clear error flags + jsr ___seterrno lda f_track ; restore stuff for ReadByte ldx f_sector @@ -147,11 +147,11 @@ _read: bne @L2 inc ptr3+1 -@L2: lda __oserror ; was there error ? +@L2: lda ___oserror ; was there error ? beq @L3 cmp #BFR_OVERFLOW ; EOF? beq @done ; yes, we're done - jmp __mappederrno ; no, we're screwed + jmp ___mappederrno ; no, we're screwed @L3: dec ptr1 ; decrement the count bne @L0 diff --git a/libsrc/geos-common/file/get1stdirentry.s b/libsrc/geos-common/file/get1stdirentry.s index f0ad59388..2217718ea 100644 --- a/libsrc/geos-common/file/get1stdirentry.s +++ b/libsrc/geos-common/file/get1stdirentry.s @@ -5,7 +5,7 @@ ; struct filehandle* Get1stDirEntry (void); - .import __oserror, return0 + .import ___oserror, return0 .export _Get1stDirEntry .include "diskdrv.inc" @@ -13,7 +13,7 @@ _Get1stDirEntry: jsr Get1stDirEntry - stx __oserror + stx ___oserror txa bne L1 ; jump if disk error lda r5L diff --git a/libsrc/geos-common/file/getnxtdirentry.s b/libsrc/geos-common/file/getnxtdirentry.s index e8ccbf3a2..0f74a0ed8 100644 --- a/libsrc/geos-common/file/getnxtdirentry.s +++ b/libsrc/geos-common/file/getnxtdirentry.s @@ -5,7 +5,7 @@ ; struct filehandle* GetNxtDirEntry (void); - .import __oserror, return0 + .import ___oserror, return0 .export _GetNxtDirEntry .include "diskdrv.inc" @@ -13,7 +13,7 @@ _GetNxtDirEntry: jsr GetNxtDirEntry - stx __oserror + stx ___oserror txa bne L1 ; jump if disk error tya diff --git a/libsrc/geos-common/file/readbyte.s b/libsrc/geos-common/file/readbyte.s index 9a24e68df..90616a426 100644 --- a/libsrc/geos-common/file/readbyte.s +++ b/libsrc/geos-common/file/readbyte.s @@ -5,13 +5,13 @@ ; char ReadByte (void); - .import __oserror + .import ___oserror .export _ReadByte .include "jumptab.inc" _ReadByte: jsr ReadByte - stx __oserror + stx ___oserror ldx #0 rts diff --git a/libsrc/geos-common/system/oserror.s b/libsrc/geos-common/system/oserror.s index f0915fd5b..fa45f3e14 100644 --- a/libsrc/geos-common/system/oserror.s +++ b/libsrc/geos-common/system/oserror.s @@ -3,16 +3,16 @@ ; GEOS port: Maciej 'YTM/Elysium' Witkowiak ; 2.7.2001 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system specific error into a system independent code */ ; - .export __osmaperrno + .export ___osmaperrno .include "errno.inc" .include "const.inc" -__osmaperrno: +___osmaperrno: ldx #ErrTabSize @L1: cmp ErrTab-2,x ; Search for the error code beq @L2 ; Jump if found diff --git a/libsrc/geos-common/system/randomize.s b/libsrc/geos-common/system/randomize.s index 67c217d80..d8f821356 100644 --- a/libsrc/geos-common/system/randomize.s +++ b/libsrc/geos-common/system/randomize.s @@ -1,16 +1,16 @@ ; ; Ullrich von Bassewitz, 05.11.2002 ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "geossym.inc" -__randomize: +___randomize: lda random ; get random value from internal generator ldx random+1 jmp _srand ; and use it as seed diff --git a/libsrc/geos-common/system/setoserror.s b/libsrc/geos-common/system/setoserror.s index adc6ad017..6fb89e96e 100644 --- a/libsrc/geos-common/system/setoserror.s +++ b/libsrc/geos-common/system/setoserror.s @@ -5,10 +5,10 @@ ; .export setoserror - .import __oserror + .import ___oserror setoserror: - stx __oserror + stx ___oserror txa ldx #0 ; X is cleared (high byte for promoting char to int) tay ; Y register is used just to save flags state diff --git a/libsrc/joystick/joy_unload.s b/libsrc/joystick/joy_unload.s index 25d54ff02..f52b7a2c1 100644 --- a/libsrc/joystick/joy_unload.s +++ b/libsrc/joystick/joy_unload.s @@ -10,7 +10,7 @@ .include "modload.inc" .import joy_clear_ptr - .import return0 + .import return0, return1 @@ -31,7 +31,6 @@ _joy_unload: jmp return0 ; Return JOY_ERR_OK no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #JOY_ERR_NO_DRIVER - rts + .assert JOY_ERR_NO_DRIVER = 1, error + jmp return1 ; Return JOY_ERR_NO_DRIVER diff --git a/libsrc/kim1/crt0.s b/libsrc/kim1/crt0.s new file mode 100644 index 000000000..906b3b980 --- /dev/null +++ b/libsrc/kim1/crt0.s @@ -0,0 +1,45 @@ +; +; Startup code for cc65 (KIM-1 version) +; + + .export _init, _exit + .export __STARTUP__ : absolute = 1 ; Mark as startup + + .import _main + .import initlib, donelib, copydata, zerobss + .import __RAM_START__, __RAM_SIZE__ ; Linker generated + .import __STACKSIZE__ ; Linker generated + + .include "zeropage.inc" + .include "kim1.inc" + + +; Place the startup code in a special segment + +.segment "STARTUP" + + +; A little light housekeeping + +_init: cld ; Clear decimal mode + +; 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 (this is also the _exit entry). Jumps to the KIM-1 monitor. + +_exit: jmp START diff --git a/libsrc/kim1/ctype.s b/libsrc/kim1/ctype.s new file mode 100644 index 000000000..1301965eb --- /dev/null +++ b/libsrc/kim1/ctype.s @@ -0,0 +1,5 @@ +; Character specification table. +; +; uses the "common" definition + + .include "ctype_common.inc" diff --git a/libsrc/kim1/getkey.s b/libsrc/kim1/getkey.s new file mode 100644 index 000000000..b36cd4b4c --- /dev/null +++ b/libsrc/kim1/getkey.s @@ -0,0 +1,18 @@ +; +; int __fastcall__ getkey(); +; + +.include "kim1.inc" + +.import popa + +.export _getkey + +.proc _getkey + + jsr KEYIN ; Open up keyboard channel + jsr GETKEY ; Get key code + ldx #0 ; MSB of return value is zero + rts + +.endproc diff --git a/libsrc/kim1/read.s b/libsrc/kim1/read.s new file mode 100644 index 000000000..dd178ee98 --- /dev/null +++ b/libsrc/kim1/read.s @@ -0,0 +1,46 @@ +; +; int __fastcall__ read (int fd, void* buf, unsigned count); +; + +.include "kim1.inc" + +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3 + +.export _read + +.proc _read + + sta ptr3 + stx ptr3+1 ; Count in ptr3 + inx + stx ptr2+1 ; Increment and store in ptr2 + tax + inx + stx ptr2 + jsr popptr1 ; Buffer address in ptr1 + jsr popax + +begin: dec ptr2 + bne getch + dec ptr2+1 + beq done ; If buffer full, return + +getch: jsr INTCHR ; Get character using Monitor ROM call + and #$7F ; Clear top bit + cmp #$0D ; Check for '\r' + bne putch ; ...if CR character + lda #$0A ; Replace with '\n' + +putch: ldy #$00 ; Put char into return buffer + sta (ptr1),y + inc ptr1 ; Increment pointer + bne begin + inc ptr1+1 + bne begin + +done: lda ptr3 + ldx ptr3+1 + rts ; Return count + +.endproc diff --git a/libsrc/kim1/scandisplay.s b/libsrc/kim1/scandisplay.s new file mode 100644 index 000000000..0f46a5de4 --- /dev/null +++ b/libsrc/kim1/scandisplay.s @@ -0,0 +1,21 @@ +; +; void __fastcall__ scandisplay(unsigned char left, unsigned char middle, unsigned char right); +; + +.include "kim1.inc" + +.import popa + +.export _scandisplay + +.proc _scandisplay + + sta $F9 ; Rightmost display data + jsr popa + sta $FA ; Middle display data + jsr popa + sta $FB ; Leftmost display data + jsr SCANDS + rts + +.endproc diff --git a/libsrc/kim1/tapeio.s b/libsrc/kim1/tapeio.s new file mode 100644 index 000000000..4a16d6b9c --- /dev/null +++ b/libsrc/kim1/tapeio.s @@ -0,0 +1,39 @@ +; +; int __fastcall__ loadt (unsigned char id); +; int __fastcall__ dumpt (unsigned char id, void* start_addr, void* end_addr); +; + +.include "kim1.inc" + +.import popa, popax, return0, return1 + +.export _loadt, _dumpt + +.segment "CODE" + +.proc _loadt: near + + sta ID ; Tape record ID to P1L + jsr LOADT ; Read data from tape + bcs error + jmp return0 ; Return 0 if sucessful +error: jmp return1 ; or 1 if not + +.endproc + +.proc _dumpt: near + + sta EAL ; End address + stx EAH + jsr popax + sta SAL ; Start address + stx SAH + jsr popa + sta ID ; Tape Record ID + ldx #$00 + jsr DUMPT ; Write data to tape + bcs error + jmp return0 ; Return 0 if sucessful +error: jmp return1 ; or 1 if not + +.endproc diff --git a/libsrc/kim1/write.s b/libsrc/kim1/write.s new file mode 100644 index 000000000..96bcc91d1 --- /dev/null +++ b/libsrc/kim1/write.s @@ -0,0 +1,48 @@ +; +; int __fastcall__ write (int fd, const void* buf, int count); +; + +.include "kim1.inc" + +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3 + +.export _write + +.proc _write + + sta ptr3 + stx ptr3+1 ; Count in ptr3 + inx + stx ptr2+1 ; Increment and store in ptr2 + tax + inx + stx ptr2 + jsr popptr1 ; Buffer address in ptr1 + jsr popax + +begin: dec ptr2 + bne outch + dec ptr2+1 + beq done + +outch: ldy #0 + lda (ptr1),y + pha ; Save A (changed by OUTCHR) + jsr OUTCHR ; Send character using Monitor call + pla ; Restore A + cmp #$0A ; Check for '\n' + bne next ; ...if LF character + lda #$0D ; Add a carriage return + jsr OUTCHR + +next: inc ptr1 + bne begin + inc ptr1+1 + jmp begin + +done: lda ptr3 + ldx ptr3+1 + rts ; Return count + +.endproc diff --git a/libsrc/lynx/clock.s b/libsrc/lynx/clock.s index e29799df6..d881e5a67 100644 --- a/libsrc/lynx/clock.s +++ b/libsrc/lynx/clock.s @@ -3,13 +3,13 @@ ; 2012-02-06, Greg King ; ; clock_t clock (void); -; clock_t _clocks_per_sec (void); +; clock_t __clocks_per_sec (void); ; ; clocks_per_sec()'s test-values are based on the numbers in "set_tv.s". ; If you change the numbers there, then change them here, too. ; - .export _clock, __clocks_per_sec, clock_count + .export _clock, ___clocks_per_sec, clock_count .interruptor update_clock, 2 ; (low priority) .constructor init_clock @@ -38,7 +38,7 @@ ;----------------------------------------------------------------------------- ; Return the number of clock ticks in one second. ; -__clocks_per_sec: +___clocks_per_sec: ldx #$00 ; >50, >60, >75 ldy PBKUP lda #<75 diff --git a/libsrc/lynx/joy/lynx-stdjoy.s b/libsrc/lynx/joy/lynx-stdjoy.s index c81a97dbf..45eb8ab4e 100644 --- a/libsrc/lynx/joy/lynx-stdjoy.s +++ b/libsrc/lynx/joy/lynx-stdjoy.s @@ -58,8 +58,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/lynx/open.s b/libsrc/lynx/open.s index c48b8eb95..032f55a99 100644 --- a/libsrc/lynx/open.s +++ b/libsrc/lynx/open.s @@ -66,7 +66,7 @@ parmok: jsr popax beq flagsok jsr popax lda #EINVAL - jmp __directerrno + jmp ___directerrno flagsok: jsr popax @@ -74,7 +74,7 @@ flagsok: jsr _openn ldx #$00 lda #$01 - stx __oserror + stx ___oserror rts .endproc diff --git a/libsrc/lynx/oserror.s b/libsrc/lynx/oserror.s index 5570a3bb7..b2ccbf4f0 100644 --- a/libsrc/lynx/oserror.s +++ b/libsrc/lynx/oserror.s @@ -1,7 +1,7 @@ ; ; Karri Kaksonen, 2010 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system specific error into a system independent code */ ; @@ -9,6 +9,6 @@ .code -__osmaperrno: +___osmaperrno: rts diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index ded862eaa..8aa3c838e 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -75,8 +75,9 @@ SER_UNINSTALL: SER_CLOSE: ; Disable interrupts ; Done, return an error code - lda #<SER_ERR_OK - ldx #>SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -190,8 +191,8 @@ SER_OPEN: cmp #SER_BAUD_134_5 beq setbaudrate - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts setprescaler: stx TIM4CTLA @@ -238,12 +239,13 @@ checkhs: lda contrl ora #RxIntEnable|ResetErr sta SERCTL - lda #<SER_ERR_OK - ldx #>SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts invparameter: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -255,8 +257,8 @@ SER_GET: lda RxPtrIn cmp RxPtrOut bne GetByte - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts GetByte: ldy RxPtrOut @@ -277,8 +279,8 @@ SER_PUT: ina cmp TxPtrOut bne PutByte - lda #<SER_ERR_OVERFLOW - ldx #>SER_ERR_OVERFLOW + lda #SER_ERR_OVERFLOW + ldx #0 ; return value is char rts PutByte: ldy TxPtrIn @@ -296,7 +298,8 @@ PutByte: sta TxDone plp @L1: - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -317,8 +320,8 @@ SER_STATUS: ; Must return an SER_ERR_xx code in a/x. SER_IOCTL: - lda #<SER_ERR_INV_IOCTL - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- diff --git a/libsrc/lynx/ser_stat_stddrv.s b/libsrc/lynx/ser_stat_stddrv.s new file mode 100644 index 000000000..37f481c47 --- /dev/null +++ b/libsrc/lynx/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _lynx_comlynx_ser + +.rodata + +_ser_static_stddrv := _lynx_comlynx_ser diff --git a/libsrc/mouse/mouse_unload.s b/libsrc/mouse/mouse_unload.s index 8c9018484..ecd7846ab 100644 --- a/libsrc/mouse/mouse_unload.s +++ b/libsrc/mouse/mouse_unload.s @@ -8,7 +8,7 @@ .include "mouse-kernel.inc" .include "modload.inc" - .import return0 + .import return0, return1 @@ -29,7 +29,6 @@ _mouse_unload: jmp return0 ; Return MOUSE_ERR_OK no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #<MOUSE_ERR_NO_DRIVER - rts + .assert MOUSE_ERR_NO_DRIVER = 1, error + jmp return1 ; Return MOUSE_ERR_NO_DRIVER diff --git a/libsrc/nes/joy/nes-stdjoy.s b/libsrc/nes/joy/nes-stdjoy.s index 63caf364b..0d0e7d9ac 100644 --- a/libsrc/nes/joy/nes-stdjoy.s +++ b/libsrc/nes/joy/nes-stdjoy.s @@ -53,7 +53,8 @@ JOY_COUNT = 2 ; Number of joysticks we support INSTALL: lda #JOY_ERR_OK - ldx #0 + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/nes/randomize.s b/libsrc/nes/randomize.s index 8df4fb88a..a146fef1b 100644 --- a/libsrc/nes/randomize.s +++ b/libsrc/nes/randomize.s @@ -1,16 +1,16 @@ ; ; Ullrich von Bassewitz, 2003-05-02 ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "nes.inc" -__randomize: +___randomize: ldx tickcount ; Use tick clock lda tickcount+1 jmp _srand ; Initialize generator diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index e88e257fd..0d8a74eb7 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -34,8 +34,7 @@ ram_top := __MAIN_START__ + __MAIN_SIZE__ .ifdef ASM - .include "osic1p.inc" - .macpack generic + .include "screen-c1p-24x24.s" load := $08 ; private variables count := $0A @@ -45,7 +44,7 @@ GETCHAR := $FFBF ; gets one character from ACIA FIRSTVISC = $85 ; Offset of first visible character in video RAM LINEDIST = $20 ; Offset in video RAM between two lines - ldy #<$0000 + ldy #$00 lda #<load_addr ldx #>load_addr sta load @@ -57,9 +56,9 @@ LINEDIST = $20 ; Offset in video RAM between two lines stx count+1 ; save size with each byte incremented separately L1: dec count - bnz L2 + bne L2 dec count+1 - bze L3 + beq L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y @@ -70,12 +69,12 @@ L2: jsr GETCHAR ; (doesn't change .Y) lsr a and #8 - 1 ora #$10 ; eight arrow characters - sta SCRNBASE + FIRSTVISC + 2 * LINEDIST + 11 + sta C1P_SCR_BASE + FIRSTVISC + 2 * LINEDIST + 11 iny - bnz L1 + bne L1 inc load+1 - bnz L1 ; branch always + bne L1 ; branch always L3: jmp load_addr @@ -112,18 +111,15 @@ CR = $0D hex2 >load_addr .byte CR, "85", CR, "08", CR .byte "86", CR, "09", CR - .byte "A9", CR - hex2 <load_size - .byte CR, "49", CR, "FF", CR - .byte "85", CR, "0A", CR - .byte "A9", CR - hex2 >load_size - .byte CR, "49", CR, "FF", CR - .byte "85", CR, "0B", CR - - .byte "E6", CR, "0A", CR + .byte "A2", CR + hex2 (<load_size) + 1 + .byte CR, "86", CR, "0A", CR + .byte "A2", CR + hex2 (>load_size) + 1 + .byte CR, "86", CR, "0B", CR + .byte "C6", CR, "0A", CR .byte "D0", CR, "04", CR - .byte "E6", CR, "0B", CR + .byte "C6", CR, "0B", CR .byte "F0", CR, "16", CR .byte "20", CR, "BF", CR, "FF", CR .byte "91", CR, "08", CR diff --git a/libsrc/osic1p/extra/screen-c1p-48x12.s b/libsrc/osic1p/extra/screen-c1p-48x12.s new file mode 100644 index 000000000..91a61338b --- /dev/null +++ b/libsrc/osic1p/extra/screen-c1p-48x12.s @@ -0,0 +1,16 @@ +; +; Implementation of screen-layout related functions for Challenger 1P in 48x12 mode. +; + + .include "../osiscreen.inc" + +C1P_SCR_BASE := $D000 ; Base of C1P video RAM +C1P_VRAM_SIZE = $0400 ; Size of C1P video RAM (1 kB) +C1P_SCR_WIDTH = $30 ; Screen width +C1P_SCR_HEIGHT = $0C ; Screen height +C1P_SCR_FIRSTCHAR = $8B ; Offset of cursor position (0, 0) from base + ; of video RAM +C1P_SCROLL_DIST = $40 ; Memory distance for scrolling by one line + +osi_screen_funcs C1P_SCR_BASE, C1P_VRAM_SIZE, C1P_SCR_FIRSTCHAR, \ + C1P_SCR_WIDTH, C1P_SCR_HEIGHT, C1P_SCROLL_DIST diff --git a/libsrc/osic1p/oserror.s b/libsrc/osic1p/oserror.s index 073691a06..380c5ebb9 100644 --- a/libsrc/osic1p/oserror.s +++ b/libsrc/osic1p/oserror.s @@ -4,14 +4,14 @@ ; original by ; Stefan Haubenthal, 2011-04-18 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system specific error into a system independent code */ ; .include "errno.inc" - .export __osmaperrno + .export ___osmaperrno -.proc __osmaperrno +.proc ___osmaperrno lda #<EUNKNOWN ldx #>EUNKNOWN diff --git a/libsrc/pce/joy/pce-stdjoy.s b/libsrc/pce/joy/pce-stdjoy.s index 2de3d0c4c..dc8576c87 100644 --- a/libsrc/pce/joy/pce-stdjoy.s +++ b/libsrc/pce/joy/pce-stdjoy.s @@ -50,8 +50,9 @@ padbuffer: .res JOY_COUNT ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/pce/memset.s b/libsrc/pce/memset.s index 45a78d533..1148e00c8 100644 --- a/libsrc/pce/memset.s +++ b/libsrc/pce/memset.s @@ -6,21 +6,21 @@ ; 1998-05-29, Ullrich von Bassewitz ; 2015-11-06, Greg King ; -; void* __fastcall__ _bzero (void* ptr, size_t n); +; void* __fastcall__ __bzero (void* ptr, size_t n); ; void __fastcall__ bzero (void* ptr, size_t n); ; void* __fastcall__ memset (void* ptr, int c, size_t n); ; ; NOTE: bzero() will return its first argument, as memset() does. It is no ; problem to declare the return value as void, because it can be ignored. -; _bzero() (note the leading underscore) is declared with the proper -; return type because the compiler will replace memset() by _bzero() if +; __bzero() (note the leading underscores) is declared with the proper +; return type because the compiler will replace memset() by __bzero() if ; the fill value is zero; and, the optimizer looks at the return type ; to see if the value in .XA is of any use. ; ; NOTE: This function uses entry points from "pce/memcpy.s"! ; - .export __bzero, _bzero, _memset + .export ___bzero, _bzero, _memset .import memcpy_getparams, memcpy_increment .import pushax, popax @@ -30,7 +30,7 @@ ; ---------------------------------------------------------------------- -__bzero: +___bzero: _bzero: pha cla ; fill with zeros jsr pushax ; (high byte isn't important) diff --git a/libsrc/pet/joy/pet-ptvjoy.s b/libsrc/pet/joy/pet-ptvjoy.s index c098072fb..ee14c95c8 100644 --- a/libsrc/pet/joy/pet-ptvjoy.s +++ b/libsrc/pet/joy/pet-ptvjoy.s @@ -51,8 +51,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/pet/randomize.s b/libsrc/pet/randomize.s index 44bf4c38e..a173cf076 100644 --- a/libsrc/pet/randomize.s +++ b/libsrc/pet/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "pet.inc" -__randomize: +___randomize: ldx TIME+2 lda TIME+1 ; Use 60HZ clock jmp _srand ; Initialize generator diff --git a/libsrc/plus4/joy/plus4-stdjoy.s b/libsrc/plus4/joy/plus4-stdjoy.s index e8e85fedc..86f080dae 100644 --- a/libsrc/plus4/joy/plus4-stdjoy.s +++ b/libsrc/plus4/joy/plus4-stdjoy.s @@ -58,8 +58,9 @@ JOY_COUNT = 2 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/plus4/randomize.s b/libsrc/plus4/randomize.s index 796ad118b..0d1ccaf03 100644 --- a/libsrc/plus4/randomize.s +++ b/libsrc/plus4/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "plus4.inc" -__randomize: +___randomize: ldx TED_VLINELO ; Use TED rasterline as high byte lda TIME+2 ; Use 60HZ clock as low byte jmp _srand ; Initialize generator diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index bb44a4cf9..fbdc61b2e 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -157,8 +157,9 @@ SER_CLOSE: ; Done, return an error code - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ;---------------------------------------------------------------------------- @@ -225,22 +226,23 @@ SER_OPEN: ; Done - lda #<SER_ERR_OK - tax ; A is zero + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error + tax rts ; Invalid parameter InvParam: - lda #<SER_ERR_INIT_FAILED - ldx #>SER_ERR_INIT_FAILED + lda #SER_ERR_INIT_FAILED + ldx #0 ; return value is char rts ; Baud rate not available InvBaud: - lda #<SER_ERR_BAUD_UNAVAIL - ldx #>SER_ERR_BAUD_UNAVAIL + lda #SER_ERR_BAUD_UNAVAIL + ldx #0 ; return value is char rts ;---------------------------------------------------------------------------- @@ -261,8 +263,8 @@ SER_GET: @L1: lda RecvFreeCnt ; (25) cmp #$ff bne @L2 - lda #<SER_ERR_NO_DATA - ldx #>SER_ERR_NO_DATA + lda #SER_ERR_NO_DATA + ldx #0 ; return value is char rts ; Check for flow stopped & enough free: release flow control @@ -309,7 +311,7 @@ SER_PUT: @L2: ldx SendFreeCnt bne @L3 - lda #<SER_ERR_OVERFLOW ; X is already zero + lda #SER_ERR_OVERFLOW ; X is already zero rts @L3: ldx SendTail @@ -318,7 +320,8 @@ SER_PUT: dec SendFreeCnt lda #$ff jsr TryToSend - lda #<SER_ERR_OK + lda #SER_ERR_OK + .assert SER_ERR_OK = 0, error tax rts @@ -331,7 +334,8 @@ SER_STATUS: lda ACIA_STATUS ldx #0 sta (ptr1,x) - txa ; SER_ERR_OK + .assert SER_ERR_OK = 0, error + txa rts ;---------------------------------------------------------------------------- @@ -341,8 +345,8 @@ SER_STATUS: ; SER_IOCTL: - lda #<SER_ERR_INV_IOCTL ; We don't support ioclts for now - ldx #>SER_ERR_INV_IOCTL + lda #SER_ERR_INV_IOCTL ; We don't support ioclts for now + ldx #0 ; return value is char rts ; Run into IRQ instead ;---------------------------------------------------------------------------- diff --git a/libsrc/plus4/ser_stat_stddrv.s b/libsrc/plus4/ser_stat_stddrv.s new file mode 100644 index 000000000..f35b09232 --- /dev/null +++ b/libsrc/plus4/ser_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const void ser_static_stddrv[]; +; + + .export _ser_static_stddrv + .import _plus4_stdser_ser + +.rodata + +_ser_static_stddrv := _plus4_stdser_ser diff --git a/libsrc/plus4/ser_stddrv.s b/libsrc/plus4/ser_stddrv.s new file mode 100644 index 000000000..f308d5f40 --- /dev/null +++ b/libsrc/plus4/ser_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard serial driver +; +; Oliver Schmidt, 2022-12-22 +; +; const char ser_stddrv[]; +; + + .export _ser_stddrv + +.rodata + +_ser_stddrv: .asciiz "plus4-stdser.ser" diff --git a/libsrc/serial/ser_unload.s b/libsrc/serial/ser_unload.s index bf7201255..7cd0f7125 100644 --- a/libsrc/serial/ser_unload.s +++ b/libsrc/serial/ser_unload.s @@ -10,7 +10,7 @@ .include "modload.inc" .import ser_clear_ptr - .import return0 + .import return0, return1 @@ -28,10 +28,10 @@ _ser_unload: tax pla ; Get pointer to driver jsr _mod_free ; Free the driver - jmp return0 ; Return SER_ERR_OK + .assert SER_ERR_OK = 0, error + jmp return0 no_driver: - tax ; X = 0 pla ; Remove pushed junk - lda #<SER_ERR_NO_DRIVER - rts + .assert SER_ERR_NO_DRIVER = 1, error + jmp return1 diff --git a/libsrc/supervision/joy/supervision-stdjoy.s b/libsrc/supervision/joy/supervision-stdjoy.s index ef790ec0b..2469abf31 100644 --- a/libsrc/supervision/joy/supervision-stdjoy.s +++ b/libsrc/supervision/joy/supervision-stdjoy.s @@ -46,8 +46,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead diff --git a/libsrc/telestrat/joy/telestrat.s b/libsrc/telestrat/joy/telestrat.s index 0f5d28651..7472ab187 100644 --- a/libsrc/telestrat/joy/telestrat.s +++ b/libsrc/telestrat/joy/telestrat.s @@ -54,8 +54,9 @@ INSTALL: sta VIA2::PRB ; We could detect joysticks because with previous command bit0,1,2,3,4 should be set to 1 after ; But if some one press fire or press direction, we could reach others values which could break joystick detection. - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/telestrat/lseek.s b/libsrc/telestrat/lseek.s new file mode 100644 index 000000000..11d1fad33 --- /dev/null +++ b/libsrc/telestrat/lseek.s @@ -0,0 +1,39 @@ +; +; Jede (jede@oric.org), 2023-03-13 +; + +; off_t __fastcall__ lseek(int fd, off_t offset, int whence); + + .export _lseek + + .include "telestrat.inc" + .include "zeropage.inc" + + .import popax + +.proc _lseek + ; Save whence + sta tmp1 + ; Skip X + + ; Get offset and store + + jsr popax + sta tmp2 + stx tmp3 + + jsr popax + sta RESB + stx RESB+1 + + ; Get FD + jsr popax + ; Does not need X + sta RES ; Save FD + + lda tmp2 + ldy tmp3 + ldx tmp1 ; Get whence + BRK_TELEMON XFSEEK + rts +.endproc diff --git a/libsrc/telestrat/oserror.s b/libsrc/telestrat/oserror.s index e3b9e619a..8fe255656 100644 --- a/libsrc/telestrat/oserror.s +++ b/libsrc/telestrat/oserror.s @@ -1,14 +1,14 @@ ; ; Jede, 2017-10-27 ; -; int __fastcall__ _osmaperrno (unsigned char oserror); +; int __fastcall__ __osmaperrno (unsigned char oserror); ; /* Map a system specific error into a system independent code */ ; .include "errno.inc" - .export __osmaperrno + .export ___osmaperrno -.proc __osmaperrno +.proc ___osmaperrno lda #<EUNKNOWN ldx #>EUNKNOWN diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index f31909f45..736546363 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -30,13 +30,14 @@ ldy ptr1+1 BRK_TELEMON XFREAD ; compute nb of bytes read - lda PTR_READ_DEST+1 sec + lda PTR_READ_DEST + sbc ptr2 + pha + lda PTR_READ_DEST+1 sbc ptr2+1 tax - lda PTR_READ_DEST - sec - sbc ptr2 - ; here A and X contains number of bytes read + pla + rts .endproc diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 2ce2657ac..37a896696 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -42,16 +42,16 @@ next: ldy ptr3+1 ldx tmp1 ; send fd in X BRK_TELEMON XFWRITE + ; compute nb of bytes written - - - lda PTR_READ_DEST+1 sec + lda PTR_READ_DEST + sbc ptr1 + pha + lda PTR_READ_DEST+1 sbc ptr1+1 tax - lda PTR_READ_DEST - sec - sbc ptr1 + pla rts diff --git a/libsrc/tgi/tgi_unload.s b/libsrc/tgi/tgi_unload.s index 1012969d2..c01228080 100644 --- a/libsrc/tgi/tgi_unload.s +++ b/libsrc/tgi/tgi_unload.s @@ -29,6 +29,6 @@ _tgi_unload: jmp _mod_free ; Free the driver no_driver: - lda #<TGI_ERR_NO_DRIVER + lda #TGI_ERR_NO_DRIVER sta _tgi_error rts diff --git a/libsrc/vic20/emd/vic20-georam.s b/libsrc/vic20/emd/vic20-georam.s index a960e9a1a..4e3121c05 100644 --- a/libsrc/vic20/emd/vic20-georam.s +++ b/libsrc/vic20/emd/vic20-georam.s @@ -119,16 +119,16 @@ INSTALL: bne @setok @notpresent: - lda #<EM_ERR_NO_DEVICE - ldx #>EM_ERR_NO_DEVICE + lda #EM_ERR_NO_DEVICE + ldx #0 ; return value is char rts @setok: lda #0 sta pagecount stx pagecount+1 - lda #<EM_ERR_OK - ldx #>EM_ERR_OK + .assert EM_ERR_OK = 0, error + tax rts check: diff --git a/libsrc/vic20/emd/vic20-rama.s b/libsrc/vic20/emd/vic20-rama.s index 133c3974b..4264e2caf 100644 --- a/libsrc/vic20/emd/vic20-rama.s +++ b/libsrc/vic20/emd/vic20-rama.s @@ -71,12 +71,13 @@ INSTALL: ldx #$FF stx curpage ; Invalidate the current page - inx ; X = 0 - txa ; A = X = EM_ERR_OK + .assert EM_ERR_OK = 0, error + inx + txa rts -nomem: ldx #>EM_ERR_NO_DEVICE - lda #<EM_ERR_NO_DEVICE +nomem: ldx #0 ; return value is char + lda #EM_ERR_NO_DEVICE ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/joy/vic20-ptvjoy.s b/libsrc/vic20/joy/vic20-ptvjoy.s index 496653e9d..c29d1d5ed 100644 --- a/libsrc/vic20/joy/vic20-ptvjoy.s +++ b/libsrc/vic20/joy/vic20-ptvjoy.s @@ -53,8 +53,9 @@ JOY_COUNT = 3 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/joy/vic20-stdjoy.s b/libsrc/vic20/joy/vic20-stdjoy.s index ee8dc93d7..b3de8766c 100644 --- a/libsrc/vic20/joy/vic20-stdjoy.s +++ b/libsrc/vic20/joy/vic20-stdjoy.s @@ -57,8 +57,9 @@ JOY_COUNT = 1 ; Number of joysticks we support ; INSTALL: - lda #<JOY_ERR_OK - ldx #>JOY_ERR_OK + lda #JOY_ERR_OK + .assert JOY_ERR_OK = 0, error + tax ; rts ; Run into UNINSTALL instead ; ------------------------------------------------------------------------ diff --git a/libsrc/vic20/randomize.s b/libsrc/vic20/randomize.s index 69cf07bb3..ac2c09a7d 100644 --- a/libsrc/vic20/randomize.s +++ b/libsrc/vic20/randomize.s @@ -2,16 +2,16 @@ ; 2002-11-05, Ullrich von Bassewitz ; 2015-09-11, Greg King ; -; void _randomize (void); +; void __randomize (void); ; /* Initialize the random number generator */ ; - .export __randomize + .export ___randomize .import _srand .include "vic20.inc" -__randomize: +___randomize: lda VIC_LINES ; Get overflow bit asl a ; Shift bit 7 into carry lda VIC_HLINE ; Get bit 1-8 of rasterline diff --git a/samples/Makefile b/samples/Makefile index 4007e3522..0e5292306 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -154,7 +154,7 @@ endif # Lists of subdirectories # disasm depends on cpp -DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate lynx supervision sym1 cbm +DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate lynx supervision sym1 kim1 cbm # -------------------------------------------------------------------------- # Lists of executables @@ -171,6 +171,7 @@ EXELIST_apple2 = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -186,6 +187,7 @@ EXELIST_atari = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -203,6 +205,7 @@ EXELIST_atmos = \ hello \ mandelbrot \ sieve \ + terminal \ tgidemo EXELIST_bbc = \ @@ -219,6 +222,7 @@ EXELIST_c64 = \ multdemo \ ovrldemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -231,6 +235,7 @@ EXELIST_c128 = \ mandelbrot \ mousedemo \ sieve \ + terminal \ tinyshell \ tgidemo @@ -247,6 +252,7 @@ EXELIST_cbm510 = \ gunzip65 \ hello \ mousedemo \ + terminal \ tinyshell \ sieve @@ -255,6 +261,7 @@ EXELIST_cbm610 = \ checkversion \ gunzip65 \ hello \ + terminal \ tinyshell \ sieve @@ -314,6 +321,7 @@ EXELIST_plus4 = \ enumdevdir \ gunzip65 \ hello \ + terminal \ tinyshell \ sieve @@ -329,6 +337,9 @@ EXELIST_supervision = \ EXELIST_sym1 = \ notavailable +EXELIST_kim1 = \ + notavailable + EXELIST_telestrat = \ ascii \ checkversion \ @@ -395,6 +406,7 @@ TARGETS := \ creativision \ cx16 \ gamate \ + kim1 \ lunix \ lynx \ nes \ @@ -414,6 +426,7 @@ TARGETS := \ define TARGET_recipe +@echo making samples for: $(T) @$(MAKE) -j2 SYS:=$(T) @$(MAKE) --no-print-directory clean SYS:=$(T) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 1fc49a873..03f6b8cdc 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -80,6 +80,14 @@ EXELIST_geos-apple = \ ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) + +define samples-geos +c1541 -attach $(0).d64 -geoswrite $(1); +endef + +samples-geos: $(EXELIST_$(SYS)) + c1541 -format "$@,01" d64 $@.d64 + $(foreach tool,$(EXELIST_$(SYS)),$(call samples-geos,$(tool))) else samples: ifeq ($(MAKELEVEL),0) @@ -94,37 +102,37 @@ bitmap.c: logo.pcx $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap bitmap-demo.cvt: bitmap.c bitmap-demores.grc bitmap-demo.c - $(CL) -t $(SYS) -O -o bitmap-demo.cvt -m bitmap-demo.map 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 filesel.cvt -m filesel.map fileselres.grc filesel.c + $(CL) -t $(SYS) -O -o $@ -m filesel.map fileselres.grc filesel.c geosconio.cvt: geosconiores.grc geosconio.c - $(CL) -t $(SYS) -O -o geosconio.cvt -m geosconio.map geosconiores.grc geosconio.c + $(CL) -t $(SYS) -O -o $@ -m geosconio.map geosconiores.grc geosconio.c geosver.cvt: geosverres.grc geosver.c - $(CL) -t $(SYS) -O -o geover.cvt -m geosver.map geosverres.grc geosver.c + $(CL) -t $(SYS) -O -o $@ -m geosver.map geosverres.grc geosver.c getid.cvt: getidres.grc getid.c - $(CL) -t $(SYS) -O -o getid.cvt -m getid.map getidres.grc getid.c + $(CL) -t $(SYS) -O -o $@ -m getid.map getidres.grc getid.c hello1.cvt: hello1res.grc hello1.c - $(CL) -t $(SYS) -O -o hello1.cvt -m hello1.map hello1res.grc hello1.c + $(CL) -t $(SYS) -O -o $@ -m hello1.map hello1res.grc hello1.c hello2.cvt: hello2res.grc hello2.c - $(CL) -t $(SYS) -O -o hello2.cvt -m hello2.map hello2res.grc hello2.c + $(CL) -t $(SYS) -O -o $@ -m hello2.map hello2res.grc hello2.c overlay-demo.cvt: overlay-demores.grc overlay-demo.c - $(CL) -t $(SYS) -O -o overlay-demo.cvt -m overlay-demo.map overlay-demores.grc overlay-demo.c + $(CL) -t $(SYS) -O -o $@ -m overlay-demo.map overlay-demores.grc overlay-demo.c rmvprot.cvt: rmvprotres.grc rmvprot.c - $(CL) -t $(SYS) -O -o rmvprot.cvt -m rmvprot.map rmvprotres.grc rmvprot.c + $(CL) -t $(SYS) -O -o $@ -m rmvprot.map rmvprotres.grc rmvprot.c vector-demo.cvt: vector-demores.grc vector-demo.c - $(CL) -t $(SYS) -O -o vector-demo.cvt -m vector-demo.map vector-demores.grc vector-demo.c + $(CL) -t $(SYS) -O -o $@ -m vector-demo.map vector-demores.grc vector-demo.c yesno.cvt: yesnores.grc yesno.c - $(CL) -t $(SYS) -O -o yesno.cvt -m yesno.map yesnores.grc yesno.c + $(CL) -t $(SYS) -O -o $@ -m yesno.map yesnores.grc yesno.c clean: diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile new file mode 100644 index 000000000..74c415fdc --- /dev/null +++ b/samples/kim1/Makefile @@ -0,0 +1,62 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= kim1 + +# 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_kim1 = \ + kimHello.bin \ + kimSieve.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: kim1 tests not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +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 + +clean: + @$(DEL) kimSieve.bin 2>$(NULLDEV) + @$(DEL) kimHello.bin 2>$(NULLDEV) + diff --git a/samples/kim1/kimHello.c b/samples/kim1/kimHello.c new file mode 100644 index 000000000..9e5ca8ab6 --- /dev/null +++ b/samples/kim1/kimHello.c @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------- +// Hello World for KIM-1 +// +// Dave Plummer based on Sym-1 sample by Wayne Parham +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include <stdio.h> +#include <kim1.h> + +int main (void) +{ + char str[100]; + char c = 0x00; + + printf ("\nHello World!\n\n"); + printf ("Type a line and press ENTER, please.\n\n"); + + gets( str ); + + printf ("\n\nThanks: %s\n\n", str); + return 0; +} diff --git a/samples/kim1/kimKeyDisp.c b/samples/kim1/kimKeyDisp.c new file mode 100644 index 000000000..63839d19e --- /dev/null +++ b/samples/kim1/kimKeyDisp.c @@ -0,0 +1,52 @@ +/* Example illustrating scandisplay() and getkey() functions. */ + +#include <stdio.h> +#include <kim1.h> + +int main (void) +{ + int i, j, k, l; + int last = 15; + + printf("\nKIM-1 Demo\n"); + + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + for (k = 0; k < 16; k++) { + scandisplay(i, j, k); + + l = getkey(); + + if (l != last) { + switch (l) { + case 0x0: case 0x1: case 0x2: case 0x3: + case 0x4: case 0x5: case 0x6: case 0x7: + case 0x8: case 0x9: case 0xa: case 0xb: + case 0xc: case 0xd: case 0xe: case 0xf: + printf("Key pressed: %X\n", l); + break; + case 0x10: + printf("Key pressed: AD\n"); + break; + case 0x11: + printf("Key pressed: DA\n"); + break; + case 0x12: + printf("Key pressed: +\n"); + break; + case 0x13: + printf("Key pressed: GO\n"); + break; + case 0x14: + printf("Key pressed: PC\n"); + break; + } + + last = l; + } + } + } + } + + return 0; +} diff --git a/samples/kim1/kimSieve.c b/samples/kim1/kimSieve.c new file mode 100644 index 000000000..d13f776ea --- /dev/null +++ b/samples/kim1/kimSieve.c @@ -0,0 +1,125 @@ +#include <stdio.h> +#include <stdlib.h> + +typedef unsigned char byte; +typedef unsigned short int ushort; +typedef unsigned long int ulong; + +#define LIMIT 100000L + +// BITARRAY +// +// My bit-access macros pre-divide by two on the presumption that you'll never +// try try access both odd and even bits! + +#define GETBIT(array, bit) (array[bit >> 4] & (1 << ((bit >> 1) & 7))) +#define SETBIT(array, bit) (array[bit >> 4] |= (1 << ((bit >> 1) & 7))) +#define CLRBIT(array, bit) (array[bit >> 4] &= ~(1 << ((bit >> 1) & 7))) + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +// sqrti +// +// Binary search integer square root + +ushort sqrti(ulong num) +{ + long i; + ulong ret = 0; + + for(i = 15; i >= 0; i--) + { + ulong temp = ret | (1L << (ulong)i); + if(temp * temp <= num) + { + ret = temp; + } + } + return ret; +} + +// main() +// +// CC65 main function receives no parameters + +int main(void) +{ + // CC65 cannot mix code and data so we have to declare all variables here in the function prolog + + ulong iNumber; + ushort currentFactor; + ulong numBytesAllocated, rootOfLimit; + byte *array; + ulong countOfPrimes; + + rootOfLimit = sqrti(LIMIT); + puts("\r\n\r\n"); + RepeatChar('*', 70); + puts("\r\n** Prime Number Sieve - Dave Plummer 2022 **"); + RepeatChar('*', 70); + + printf("\r\n\r\nCalculating primes to %ld using a sqrt of %ld...\r\n", LIMIT, rootOfLimit); + + // Calculate how much memory should be allocated + + numBytesAllocated = (LIMIT + 15) / 16; + array = malloc(numBytesAllocated); + if (!array) + { + printf("Unable to allocate %ld bytes for %ld bits\r\n", numBytesAllocated, LIMIT); + return 0; + } + else + { + printf("Allocated %ld bytes for %ld slots\r\n", numBytesAllocated, LIMIT); + + // Preset all the bits to true + + for (iNumber = 0; iNumber < numBytesAllocated; iNumber++) + array[iNumber] = 0xFF; + } + + // Search for next unmarked factor + + currentFactor = 3; + while (currentFactor <= rootOfLimit) + { + ulong num, n; + + for (num = currentFactor; num <= LIMIT; num += 2) + { + if (GETBIT(array, num)) + { + currentFactor = num; + break; + } + } + + for (n = (ulong) currentFactor * currentFactor; n <= LIMIT; n += currentFactor * 2) + CLRBIT(array, n); + + currentFactor += 2; + } + + // Display results + // + // printf("The following primes were found at or below %ld:\r\n2, ", LIMIT); + + countOfPrimes = 1; + for (iNumber = 3; iNumber <= LIMIT; iNumber += 2) + if (GETBIT(array, iNumber)) + countOfPrimes++; + + printf("[END: Count = %ld]\r\n", countOfPrimes); + + free(array); + return 1; +} diff --git a/samples/terminal.c b/samples/terminal.c new file mode 100644 index 000000000..51973f7a3 --- /dev/null +++ b/samples/terminal.c @@ -0,0 +1,76 @@ +/* +** Minimalistic terminal program. +** +** Makes use of the serial drivers. +** +** 2022-12-23, Oliver Schmidt (ol.sc@web.de) +** +*/ + + + +#include <cc65.h> +#include <conio.h> +#include <stdio.h> +#include <stdlib.h> +#include <serial.h> + + +static void check (const char* msg, unsigned char err) +{ + if (err == SER_ERR_OK) { + return; + } + + printf ("%s:0x%02x\n", msg, err); + if (doesclrscrafterexit ()) { + cgetc (); + } + exit (1); +} + + +void main (void) +{ + const struct ser_params par = { + SER_BAUD_9600, + SER_BITS_8, + SER_STOP_1, + SER_PAR_NONE, + SER_HS_HW + }; + + check ("ser_install", ser_install (ser_static_stddrv)); + + check ("ser_open", ser_open (&par)); + + atexit ((void (*)) ser_close); + + printf ("Serial Port: 9600-8-1-N RTS/CTS\n" + "Simple Term: Press ESC for exit\n"); + + while (1) + { + char chr; + + if (kbhit ()) + { + chr = cgetc (); + + if (chr == CH_ESC) { + putchar ('\n'); + return; + } + + if (ser_put (chr) == SER_ERR_OK) { + putchar (chr); + } else { + putchar ('\a'); + } + } + + if (ser_get (&chr) == SER_ERR_OK) { + putchar (chr); + } + } +} diff --git a/src/Makefile b/src/Makefile index 8356d0001..034a2230f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -115,7 +115,11 @@ install: $(INSTALL) ../bin/* $(DESTDIR)$(bindir) avail: +ifneq ($(patsubst %,../bin/%,$(PROGS)),$(wildcard $(patsubst %,../bin/%,$(PROGS)))) + $(error executables are missing, please run make first) +else $(foreach prog,$(PROGS),$(AVAIL_recipe)) +endif unavail: $(foreach prog,$(PROGS),$(UNAVAIL_recipe)) diff --git a/src/ca65/ea.h b/src/ca65/ea.h index d861e9a6c..487027c02 100644 --- a/src/ca65/ea.h +++ b/src/ca65/ea.h @@ -43,6 +43,10 @@ /*****************************************************************************/ +/* EffAddr Flags */ +#define EFFADDR_OVERRIDE_ZP 0x00000001UL + + /* GetEA result struct */ typedef struct EffAddr EffAddr; @@ -51,6 +55,7 @@ struct EffAddr { unsigned long AddrModeSet; /* Possible addressing modes */ struct ExprNode* Expr; /* Expression if any (NULL otherwise) */ unsigned Reg; /* Register number in sweet16 mode */ + unsigned long Flags; /* Other properties */ /* The following fields are used inside instr.c */ unsigned AddrMode; /* Actual addressing mode used */ diff --git a/src/ca65/ea65.c b/src/ca65/ea65.c index 275d90b56..5bd2ba82b 100644 --- a/src/ca65/ea65.c +++ b/src/ca65/ea65.c @@ -72,11 +72,13 @@ void GetEA (EffAddr* A) /* Clear the output struct */ A->AddrModeSet = 0; A->Expr = 0; + A->Flags = 0; /* Handle an addressing size override */ switch (CurTok.Tok) { case TOK_OVERRIDE_ZP: Restrictions = AM65_DIR | AM65_DIR_X | AM65_DIR_Y; + A->Flags |= EFFADDR_OVERRIDE_ZP; NextTok (); break; diff --git a/src/ca65/expr.c b/src/ca65/expr.c index 812b6e90c..5dcf5ca71 100644 --- a/src/ca65/expr.c +++ b/src/ca65/expr.c @@ -496,7 +496,7 @@ static ExprNode* FuncIsMnemonic (void) /* Skip the name */ NextTok (); - return GenLiteralExpr (Instr > 0); + return GenLiteralExpr (Instr >= 0); } diff --git a/src/ca65/feature.c b/src/ca65/feature.c index b11345338..9f5ca5876 100644 --- a/src/ca65/feature.c +++ b/src/ca65/feature.c @@ -66,6 +66,7 @@ static const char* const FeatureKeys[FEAT_COUNT] = { "addrsize", "bracket_as_indirect", "string_escapes", + "long_jsr_jmp_rts", }; @@ -97,37 +98,29 @@ feature_t FindFeature (const StrBuf* Key) -feature_t SetFeature (const StrBuf* Key) -/* Find the feature and set the corresponding flag if the feature is known. -** In any case, return the feature found. An invalid Key will return -** FEAT_UNKNOWN. +void SetFeature (feature_t Feature, unsigned char On) +/* Set the corresponding feature flag if Feature is valid. */ { - /* Map the string to an enum value */ - feature_t Feature = FindFeature (Key); - /* Set the flags */ switch (Feature) { - case FEAT_DOLLAR_IS_PC: DollarIsPC = 1; break; - case FEAT_LABELS_WITHOUT_COLONS: NoColonLabels = 1; break; - case FEAT_LOOSE_STRING_TERM: LooseStringTerm = 1; break; - case FEAT_LOOSE_CHAR_TERM: LooseCharTerm = 1; break; - case FEAT_AT_IN_IDENTIFIERS: AtInIdents = 1; break; - case FEAT_DOLLAR_IN_IDENTIFIERS: DollarInIdents = 1; break; - case FEAT_LEADING_DOT_IN_IDENTIFIERS: LeadingDotInIdents= 1; break; - case FEAT_ORG_PER_SEG: OrgPerSeg = 1; break; - case FEAT_PC_ASSIGNMENT: PCAssignment = 1; break; - case FEAT_MISSING_CHAR_TERM: MissingCharTerm = 1; break; - case FEAT_UBIQUITOUS_IDENTS: UbiquitousIdents = 1; break; - case FEAT_C_COMMENTS: CComments = 1; break; - case FEAT_FORCE_RANGE: ForceRange = 1; break; - case FEAT_UNDERLINE_IN_NUMBERS: UnderlineInNumbers= 1; break; - case FEAT_ADDRSIZE: AddrSize = 1; break; - case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = 1; break; - case FEAT_STRING_ESCAPES: StringEscapes = 1; break; - default: /* Keep gcc silent */ break; + case FEAT_DOLLAR_IS_PC: DollarIsPC = On; break; + case FEAT_LABELS_WITHOUT_COLONS: NoColonLabels = On; break; + case FEAT_LOOSE_STRING_TERM: LooseStringTerm = On; break; + case FEAT_LOOSE_CHAR_TERM: LooseCharTerm = On; break; + case FEAT_AT_IN_IDENTIFIERS: AtInIdents = On; break; + case FEAT_DOLLAR_IN_IDENTIFIERS: DollarInIdents = On; break; + case FEAT_LEADING_DOT_IN_IDENTIFIERS: LeadingDotInIdents= On; break; + case FEAT_ORG_PER_SEG: OrgPerSeg = On; break; + case FEAT_PC_ASSIGNMENT: PCAssignment = On; break; + case FEAT_MISSING_CHAR_TERM: MissingCharTerm = On; break; + case FEAT_UBIQUITOUS_IDENTS: UbiquitousIdents = On; break; + case FEAT_C_COMMENTS: CComments = On; break; + case FEAT_FORCE_RANGE: ForceRange = On; break; + case FEAT_UNDERLINE_IN_NUMBERS: UnderlineInNumbers= On; break; + case FEAT_BRACKET_AS_INDIRECT: BracketAsIndirect = On; break; + case FEAT_STRING_ESCAPES: StringEscapes = On; break; + case FEAT_LONG_JSR_JMP_RTS: LongJsrJmpRts = On; break; + default: break; } - - /* Return the value found */ - return Feature; } diff --git a/src/ca65/feature.h b/src/ca65/feature.h index 876f3c4a8..8eeb62e6f 100644 --- a/src/ca65/feature.h +++ b/src/ca65/feature.h @@ -68,6 +68,7 @@ typedef enum { FEAT_ADDRSIZE, FEAT_BRACKET_AS_INDIRECT, FEAT_STRING_ESCAPES, + FEAT_LONG_JSR_JMP_RTS, /* Special value: Number of features available */ FEAT_COUNT @@ -86,10 +87,8 @@ feature_t FindFeature (const StrBuf* Key); ** feature is invalid, return FEAT_UNKNOWN. */ -feature_t SetFeature (const StrBuf* Key); -/* Find the feature and set the corresponding flag if the feature is known. -** In any case, return the feature found. An invalid Key will return -** FEAT_UNKNOWN. +void SetFeature (feature_t Feature, unsigned char On); +/* Set the corresponding feature flag if Feature is valid. */ diff --git a/src/ca65/global.c b/src/ca65/global.c index a216fc406..050d19e09 100644 --- a/src/ca65/global.c +++ b/src/ca65/global.c @@ -67,6 +67,8 @@ unsigned char LineCont = 0; /* Allow line continuation */ unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ unsigned char RelaxChecks = 0; /* Relax a few assembler checks */ unsigned char StringEscapes = 0; /* Allow C-style escapes in strings */ +unsigned char LongJsrJmpRts = 0; /* Allow JSR/JMP/RTS as alias for JSL/JML/RTL */ +unsigned char WarningsAsErrors = 0; /* Error if any warnings */ /* Emulation features */ unsigned char DollarIsPC = 0; /* Allow the $ symbol as current PC */ @@ -83,5 +85,4 @@ unsigned char OrgPerSeg = 0; /* Make .org local to current seg */ unsigned char CComments = 0; /* Allow C like comments */ unsigned char ForceRange = 0; /* Force values into expected range */ unsigned char UnderlineInNumbers = 0; /* Allow underlines in numbers */ -unsigned char AddrSize = 0; /* Allow .ADDRSIZE function */ unsigned char BracketAsIndirect = 0; /* Use '[]' not '()' for indirection */ diff --git a/src/ca65/global.h b/src/ca65/global.h index 77d90beca..b3de99df5 100644 --- a/src/ca65/global.h +++ b/src/ca65/global.h @@ -69,6 +69,8 @@ extern unsigned char LineCont; /* Allow line continuation */ extern unsigned char LargeAlignment; /* Don't warn about large alignments */ extern unsigned char RelaxChecks; /* Relax a few assembler checks */ extern unsigned char StringEscapes; /* Allow C-style escapes in strings */ +extern unsigned char LongJsrJmpRts; /* Allow JSR/JMP/RTS as alias for JSL/JML/RTL */ +extern unsigned char WarningsAsErrors; /* Error if any warnings */ /* Emulation features */ extern unsigned char DollarIsPC; /* Allow the $ symbol as current PC */ @@ -85,7 +87,6 @@ extern unsigned char OrgPerSeg; /* Make .org local to current seg */ extern unsigned char CComments; /* Allow C like comments */ extern unsigned char ForceRange; /* Force values into expected range */ extern unsigned char UnderlineInNumbers; /* Allow underlines in numbers */ -extern unsigned char AddrSize; /* Allow .ADDRSIZE function */ extern unsigned char BracketAsIndirect; /* Use '[]' not '()' for indirection */ diff --git a/src/ca65/incpath.c b/src/ca65/incpath.c index 8ea67df6f..461f1ad79 100644 --- a/src/ca65/incpath.c +++ b/src/ca65/incpath.c @@ -75,7 +75,7 @@ void FinishIncludePaths (void) AddSubSearchPathFromEnv (IncSearchPath, "CC65_HOME", "asminc"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(CA65_INC) && !defined(_WIN32) +#if defined(CA65_INC) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (IncSearchPath, CA65_INC); #endif diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 834edfb5f..89162c3c6 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -120,9 +120,21 @@ static void PutJMP (const InsDesc* Ins); ** to check for this case and is otherwise identical to PutAll. */ +static void PutJMP816 (const InsDesc* Ins); +/* Handle the JMP instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JMP. +** Note that JMP [abs] and JML [abs] are always both permitted for instruction $DC, +** because the [] notation for long indirection makes the generated instruction unambiguous. +*/ + +static void PutJSR816 (const InsDesc* Ins); +/* Handle the JSR instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JSR. +*/ + static void PutRTS (const InsDesc* Ins attribute ((unused))); /* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if -** the enclosing scope is FAR. +** the enclosing scope is FAR, but only if the long_jsr_jmp_rts feature applies. */ static void PutAll (const InsDesc* Ins); @@ -169,7 +181,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -240,7 +252,7 @@ static const struct { { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -330,7 +342,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x12, 0, PutPCRel8 }, /* DTV */ - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -406,7 +418,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -498,7 +510,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, { "CLC", 0x0000001, 0x18, 0, PutAll }, @@ -610,7 +622,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BSR", 0x0040000, 0x63, 0, PutPCRel4510 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -735,7 +747,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000005, 0x00, 6, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BRL", 0x0040000, 0x82, 0, PutPCRel16 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -744,7 +756,7 @@ static const struct { { "CLI", 0x0000001, 0x58, 0, PutAll }, { "CLV", 0x0000001, 0xb8, 0, PutAll }, { "CMP", 0x0b8f6fc, 0xc0, 0, PutAll }, - { "COP", 0x0000004, 0x02, 6, PutAll }, + { "COP", 0x0800005, 0x02, 6, PutAll }, { "CPA", 0x0b8f6fc, 0xc0, 0, PutAll }, /* == CMP */ { "CPX", 0x0c0000c, 0xe0, 1, PutAll }, { "CPY", 0x0c0000c, 0xc0, 1, PutAll }, @@ -758,9 +770,9 @@ static const struct { { "INX", 0x0000001, 0xe8, 0, PutAll }, { "INY", 0x0000001, 0xc8, 0, PutAll }, { "JML", 0x4000010, 0x5c, 1, PutAll }, - { "JMP", 0x4010818, 0x4c, 6, PutAll }, + { "JMP", 0x4010818, 0x4c, 6, PutJMP816 }, { "JSL", 0x0000010, 0x20, 7, PutAll }, - { "JSR", 0x0010018, 0x20, 7, PutAll }, + { "JSR", 0x0010018, 0x20, 7, PutJSR816 }, { "LDA", 0x0b8f6fc, 0xa0, 0, PutAll }, { "LDX", 0x0c0030c, 0xa2, 1, PutAll }, { "LDY", 0x0c0006c, 0xa0, 1, PutAll }, @@ -821,7 +833,7 @@ static const struct { { "TYA", 0x0000001, 0x98, 0, PutAll }, { "TYX", 0x0000001, 0xbb, 0, PutAll }, { "WAI", 0x0000001, 0xcb, 0, PutAll }, - { "WDM", 0x0000004, 0x42, 6, PutAll }, + { "WDM", 0x0800004, 0x42, 6, PutAll }, { "XBA", 0x0000001, 0xeb, 0, PutAll }, { "XCE", 0x0000001, 0xfb, 0, PutAll } } @@ -897,7 +909,7 @@ static const struct { { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, { "BRA", 0x0020000, 0x80, 0, PutPCRel8 }, - { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BRK", 0x0800005, 0x00, 6, PutAll }, { "BSR", 0x0020000, 0x44, 0, PutPCRel8 }, { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, @@ -1257,7 +1269,8 @@ static int EvalEA (const InsDesc* Ins, EffAddr* A) ExprNode* Left = A->Expr->Left; if ((A->Expr->Op == EXPR_BYTE0 || A->Expr->Op == EXPR_BYTE1) && Left->Op == EXPR_SYMBOL && - GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP) { + GetSymAddrSize (Left->V.Sym) != ADDR_SIZE_ZP && + !(A->Flags & EFFADDR_OVERRIDE_ZP)) { /* Output a warning */ Warning (1, "Suspicious address expression"); @@ -1298,7 +1311,7 @@ static void EmitCode (EffAddr* A) break; case 2: - if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y))) { + if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y | AM65_ABS_X_IND))) { /* This is a 16 bit mode that uses an address. If in 65816, ** mode, force this address into 16 bit range to allow ** addressing inside a 64K segment. @@ -1605,11 +1618,12 @@ static void PutJMP (const InsDesc* Ins) if (EvalEA (Ins, &A)) { /* Check for indirect addressing */ - if (A.AddrModeBit & AM65_ABS_IND) { + if ((A.AddrModeBit & AM65_ABS_IND) && (CPU < CPU_65SC02)) { /* 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 - ** things will go weird later. + ** things will go weird later. This only affects the 6502 CPU, + ** and was corrected in 65C02 and later CPUs in this family. */ ExprNode* E = GenNE (GenByteExpr (CloneExpr (A.Expr)), 0xFF); @@ -1627,12 +1641,46 @@ static void PutJMP (const InsDesc* Ins) -static void PutRTS (const InsDesc* Ins attribute ((unused))) -/* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if -** the enclosing scope is FAR. +static void PutJMP816 (const InsDesc* Ins) +/* Handle the JMP instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JMP. +** Note that JMP [abs] and JML [abs] are always both permitted for instruction $DC, +** because the [] notation for long indirection makes the generated instruction unambiguous. */ { - if (SmartMode && CurrentScope->AddrSize == ADDR_SIZE_FAR) { + if (LongJsrJmpRts) { + PutJMP (Ins); + } else { + InsDesc InsAbs = *Ins; + InsAbs.AddrMode &= ~(AM65_ABS_LONG); + PutJMP (&InsAbs); + } +} + + + +static void PutJSR816 (const InsDesc* Ins) +/* Handle the JSR instruction for the 816. +** Allowing the long_jsr_jmp_rts feature to permit a long JSR. +*/ +{ + if (LongJsrJmpRts) { + PutAll (Ins); + } else { + InsDesc InsAbs = *Ins; + InsAbs.AddrMode &= ~(AM65_ABS_LONG); + PutJMP (&InsAbs); + } +} + + + +static void PutRTS (const InsDesc* Ins attribute ((unused))) +/* Handle the RTS instruction for the 816. In smart mode emit a RTL opcode if +** the enclosing scope is FAR, but only if the long_jsr_jmp_rts feature applies. +*/ +{ + if (LongJsrJmpRts && SmartMode && CurrentScope->AddrSize == ADDR_SIZE_FAR) { Emit0 (0x6B); /* RTL */ } else { Emit0 (0x60); /* RTS */ diff --git a/src/ca65/macro.c b/src/ca65/macro.c index d9f1f385c..904c80756 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -390,7 +390,20 @@ void MacDef (unsigned Style) { Macro* M; TokNode* N; + FilePos Pos; int HaveParams; + int LastTokWasSep; + + /* For classic macros, remember if we are at the beginning of the line. + ** If the macro name and parameters pass our checks then we will be on a + ** new line, so set it now + */ + LastTokWasSep = 1; + + /* Save the position of the start of the macro definition to allow + ** using Perror to display the error if .endmacro isn't found + */ + Pos = CurTok.Pos; /* We expect a macro name here */ if (CurTok.Tok != TOK_IDENT) { @@ -489,33 +502,18 @@ void MacDef (unsigned Style) ** the .LOCAL command is detected and removed, at this time. */ while (1) { - /* Check for include */ - if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) { - /* Include another file */ - NextTok (); - /* Name must follow */ - if (CurTok.Tok != TOK_STRCON) { - ErrorSkip ("String constant expected"); - } else { - SB_Terminate (&CurTok.SVal); - if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) { - /* Error opening the file, skip remainder of line */ - SkipUntilSep (); - } - } - NextTok (); - } - /* Check for end of macro */ if (Style == MAC_STYLE_CLASSIC) { - /* In classic macros, only .endmacro is allowed */ - if (CurTok.Tok == TOK_ENDMACRO) { + /* In classic macros, if .endmacro is not at the start of the line + ** it will be added to the macro definition instead of closing it. + */ + if (CurTok.Tok == TOK_ENDMACRO && LastTokWasSep) { /* Done */ break; } /* May not have end of file in a macro definition */ if (CurTok.Tok == TOK_EOF) { - Error ("'.ENDMACRO' expected"); + PError (&Pos, "'.ENDMACRO' expected for macro '%m%p'", &M->Name); goto Done; } } else { @@ -590,6 +588,11 @@ void MacDef (unsigned Style) } ++M->TokCount; + /* Save if last token was a separator to know if .endmacro is at + ** the start of a line + */ + LastTokWasSep = TokIsSep(CurTok.Tok); + /* Read the next token */ NextTok (); } diff --git a/src/ca65/main.c b/src/ca65/main.c index b1ef3a3db..fedbb0d4b 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -342,6 +342,10 @@ static void SetSys (const char* Sys) NewSymbol ("__SYM1__", 1); break; + case TGT_KIM1: + NewSymbol ("__KIM1__", 1); + break; + default: AbEnd ("Invalid target name: '%s'", Sys); @@ -485,12 +489,15 @@ static void OptDebugInfo (const char* Opt attribute ((unused)), static void OptFeature (const char* Opt attribute ((unused)), const char* Arg) /* Set an emulation feature */ { - /* Make a string buffer from Arg */ - StrBuf Feature; + /* Make a string buffer from Arg and use it to find the feature. */ + StrBuf StrFeature; + feature_t Feature = FindFeature (SB_InitFromString (&StrFeature, Arg)); - /* Set the feature, check for errors */ - if (SetFeature (SB_InitFromString (&Feature, Arg)) == FEAT_UNKNOWN) { + /* Enable the feature, check for errors */ + if (Feature == FEAT_UNKNOWN) { AbEnd ("Illegal emulation feature: '%s'", Arg); + } else { + SetFeature (Feature, 1); } } @@ -652,6 +659,15 @@ static void OptVersion (const char* Opt attribute ((unused)), +static void OptWarningsAsErrors (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Generate an error if any warnings occur */ +{ + WarningsAsErrors = 1; +} + + + static void DoPCAssign (void) /* Start absolute code */ { @@ -842,7 +858,12 @@ static void OneLine (void) /* The line has switched the segment */ Size = 0; } - DefSizeOfSymbol (Sym, Size); + /* Suppress .size Symbol if this Symbol already has a multiply-defined error, + ** as it will only create its own additional unnecessary error. + */ + if ((Sym->Flags & SF_MULTDEF) == 0) { + DefSizeOfSymbol (Sym, Size); + } } /* Line separator must come here */ @@ -915,27 +936,28 @@ int main (int argc, char* argv []) { /* Program long options */ static const LongOpt OptTab[] = { - { "--auto-import", 0, OptAutoImport }, - { "--bin-include-dir", 1, OptBinIncludeDir }, - { "--cpu", 1, OptCPU }, - { "--create-dep", 1, OptCreateDep }, - { "--create-full-dep", 1, OptCreateFullDep }, - { "--debug", 0, OptDebug }, - { "--debug-info", 0, OptDebugInfo }, - { "--feature", 1, OptFeature }, - { "--help", 0, OptHelp }, - { "--ignore-case", 0, OptIgnoreCase }, - { "--include-dir", 1, OptIncludeDir }, - { "--large-alignment", 0, OptLargeAlignment }, - { "--list-bytes", 1, OptListBytes }, - { "--listing", 1, OptListing }, - { "--memory-model", 1, OptMemoryModel }, - { "--pagelength", 1, OptPageLength }, - { "--relax-checks", 0, OptRelaxChecks }, - { "--smart", 0, OptSmart }, - { "--target", 1, OptTarget }, - { "--verbose", 0, OptVerbose }, - { "--version", 0, OptVersion }, + { "--auto-import", 0, OptAutoImport }, + { "--bin-include-dir", 1, OptBinIncludeDir }, + { "--cpu", 1, OptCPU }, + { "--create-dep", 1, OptCreateDep }, + { "--create-full-dep", 1, OptCreateFullDep }, + { "--debug", 0, OptDebug }, + { "--debug-info", 0, OptDebugInfo }, + { "--feature", 1, OptFeature }, + { "--help", 0, OptHelp }, + { "--ignore-case", 0, OptIgnoreCase }, + { "--include-dir", 1, OptIncludeDir }, + { "--large-alignment", 0, OptLargeAlignment }, + { "--list-bytes", 1, OptListBytes }, + { "--listing", 1, OptListing }, + { "--memory-model", 1, OptMemoryModel }, + { "--pagelength", 1, OptPageLength }, + { "--relax-checks", 0, OptRelaxChecks }, + { "--smart", 0, OptSmart }, + { "--target", 1, OptTarget }, + { "--verbose", 0, OptVerbose }, + { "--version", 0, OptVersion }, + { "--warnings-as-errors", 0, OptWarningsAsErrors }, }; /* Name of the global name space */ @@ -1140,6 +1162,10 @@ int main (int argc, char* argv []) SegDump (); } + if (WarningCount > 0 && WarningsAsErrors) { + Error("Warnings as errors"); + } + /* If we didn't have an errors, finish off the line infos */ DoneLineInfo (); diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 843f5b9d2..2ce1ae087 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1023,7 +1023,10 @@ static void DoFatal (void) static void DoFeature (void) /* Switch the Feature option */ { - /* Allow a list of comma separated keywords */ + feature_t Feature; + unsigned char On; + + /* Allow a list of comma separated feature keywords with optional +/- or ON/OFF */ while (1) { /* We expect an identifier */ @@ -1034,18 +1037,29 @@ static void DoFeature (void) /* Make the string attribute lower case */ LocaseSVal (); - - /* Set the feature and check for errors */ - if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) { + Feature = FindFeature(&CurTok.SVal); + if (Feature == FEAT_UNKNOWN) { /* Not found */ ErrorSkip ("Invalid feature: '%m%p'", &CurTok.SVal); return; - } else { - /* Skip the keyword */ - NextTok (); } - /* Allow more than one keyword */ + if (Feature == FEAT_ADDRSIZE) { + Warning (1, "Deprecated feature: '.feature addrsize'. Pseudo function .addrsize is always available."); + } + + NextTok (); + + /* Optional +/- or ON/OFF */ + On = 1; + if (CurTok.Tok != TOK_COMMA && !TokIsSep (CurTok.Tok)) { + SetBoolOption(&On); + } + + /* Apply feature setting. */ + SetFeature (Feature, On); + + /* Allow more than one feature separated by commas. */ if (CurTok.Tok == TOK_COMMA) { NextTok (); } else { diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index bf0a85183..185100025 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -112,6 +112,7 @@ struct CharSource { CharSource* Next; /* Linked list of char sources */ token_t Tok; /* Last token */ int C; /* Last character */ + int SkipN; /* For '\r\n' line endings, skip '\n\ if next */ const CharSourceFunctions* Func; /* Pointer to function table */ union { InputFile File; /* File data */ @@ -325,6 +326,7 @@ static void UseCharSource (CharSource* S) Source = S; /* Read the first character from the new file */ + S->SkipN = 0; S->Func->NextChar (S); /* Setup the next token so it will be skipped on the next call to @@ -386,6 +388,11 @@ static void IFNextChar (CharSource* S) while (1) { int N = fgetc (S->V.File.F); + if (N == '\n' && S->SkipN) { + N = fgetc (S->V.File.F); + } + S->SkipN = 0; + if (N == EOF) { /* End of file. Accept files without a newline at the end */ if (SB_NotEmpty (&S->V.File.Line)) { @@ -401,9 +408,12 @@ static void IFNextChar (CharSource* S) /* Check for end of line */ } else if (N == '\n') { - /* End of line */ break; + } else if (N == '\r') { + /* End of line, skip '\n' if it's the next character */ + S->SkipN = 1; + break; /* Collect other stuff */ } else { @@ -738,24 +748,7 @@ static token_t FindDotKeyword (void) R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]), sizeof (DotKeywords [0]), CmpDotKeyword); if (R != 0) { - - /* By default, disable any somewhat experiemental DotKeyword. */ - - switch (R->Tok) { - - case TOK_ADDRSIZE: - /* Disallow .ADDRSIZE function by default */ - if (AddrSize == 0) { - return TOK_NONE; - } - break; - - default: - break; - } - return R->Tok; - } else { return TOK_NONE; } diff --git a/src/ca65/segment.c b/src/ca65/segment.c index 26b568711..aadc35009 100644 --- a/src/ca65/segment.c +++ b/src/ca65/segment.c @@ -306,7 +306,7 @@ void SegAlign (unsigned long Alignment, int FillVal) ActiveSeg->Align = CombinedAlignment; /* Output a warning for larger alignments if not suppressed */ - if (CombinedAlignment >= LARGE_ALIGNMENT && !LargeAlignment) { + if (CombinedAlignment >= LARGE_ALIGNMENT && CombinedAlignment > ActiveSeg->Align && CombinedAlignment > Alignment && !LargeAlignment) { Warning (0, "Combined alignment is suspiciously large (%lu)", CombinedAlignment); } diff --git a/src/ca65/symtab.c b/src/ca65/symtab.c index afe7b3ad6..a01d14bd8 100644 --- a/src/ca65/symtab.c +++ b/src/ca65/symtab.c @@ -570,7 +570,18 @@ void SymCheck (void) /* Check for open scopes */ if (CurrentScope->Parent != 0) { - Error ("Local scope was not closed"); + if (CurrentScope->Label) { + /* proc has a label indicating the line it was opened. */ + LIError (&CurrentScope->Label->DefLines, + "Local proc '%s' was not closed", + GetString (CurrentScope->Name)); + } else { + /* scope has no label to track a line number, uses end-of-document line instead. + ** Anonymous scopes will reveal their internal automatic name. + */ + Error ("Local scope '%s' was not closed", + GetString (CurrentScope->Name)); + } } /* First pass: Walk through all symbols, checking for undefined's and diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 5cddc1862..7e2e8ea8c 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -103,6 +103,7 @@ <ClInclude Include="cc65\macrotab.h" /> <ClInclude Include="cc65\opcodes.h" /> <ClInclude Include="cc65\output.h" /> + <ClInclude Include="cc65\ppexpr.h" /> <ClInclude Include="cc65\pragma.h" /> <ClInclude Include="cc65\preproc.h" /> <ClInclude Include="cc65\reginfo.h" /> @@ -182,6 +183,7 @@ <ClCompile Include="cc65\main.c" /> <ClCompile Include="cc65\opcodes.c" /> <ClCompile Include="cc65\output.c" /> + <ClCompile Include="cc65\ppexpr.c" /> <ClCompile Include="cc65\pragma.c" /> <ClCompile Include="cc65\preproc.c" /> <ClCompile Include="cc65\reginfo.c" /> diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 9834ae5d1..30e2f0fca 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -315,12 +315,41 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op } else if (Gen->Func == g_sub) { g_dec (Flags | CF_CONST, Expr2.IVal); } else { - if (Expr2.IVal == 0) { - /* Check for div by zero/mod by zero */ - if (Gen->Func == g_div) { - Error ("Division by zero"); - } else if (Gen->Func == g_mod) { - Error ("Modulo operation with zero"); + if (!ED_IsUneval (Expr)) { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Warning ("Division by zero"); + } else if (Gen->Func == g_mod) { + Warning ("Modulo operation with zero"); + } + } else if (Gen->Func == g_asl || Gen->Func == g_asr) { + const Type* CalType = IntPromotion (Expr->Type); + unsigned ExprBits = BitSizeOf (CalType); + + /* If the shift count is greater than or equal to the width of the + ** promoted left operand, the behaviour is undefined according to + ** the standard. + */ + if (Expr2.IVal < 0) { + Warning ("Negative shift count %ld treated as %u for %s", + Expr2.IVal, + (unsigned)Expr2.IVal & (ExprBits - 1), + GetBasicTypeName (CalType)); + } else if (Expr2.IVal >= (long)ExprBits) { + Warning ("Shift count %ld >= width of %s treated as %u", + Expr2.IVal, + GetBasicTypeName (CalType), + (unsigned)Expr2.IVal & (ExprBits - 1)); + } + + /* Here we simply "wrap" the shift count around the width */ + Expr2.IVal &= ExprBits - 1; + + /* Additional check for bit-fields */ + if (Expr2.IVal >= (long)Expr->Type->A.B.Width) { + Warning ("Shift count %ld >= width of bit-field", Expr2.IVal); + } } } @@ -495,12 +524,42 @@ static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* } else if (Gen->Func == g_sub) { g_dec (Flags | CF_CONST, Expr2.IVal); } else { - if (Expr2.IVal == 0) { - /* Check for div by zero/mod by zero */ - if (Gen->Func == g_div) { - Error ("Division by zero"); - } else if (Gen->Func == g_mod) { - Error ("Modulo operation with zero"); + if (!ED_IsUneval (Expr)) { + if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Warning ("Division by zero"); + } else if (Gen->Func == g_mod) { + Warning ("Modulo operation with zero"); + } + } else if (Gen->Func == g_asl || Gen->Func == g_asr) { + const Type* CalType = IntPromotion (Expr->Type); + unsigned ExprBits = BitSizeOf (CalType); + + /* If the shift count is greater than or equal to the width of the + ** promoted left operand, the behaviour is undefined according to + ** the standard. + */ + if (Expr2.IVal < 0) { + Warning ("Negative shift count %ld treated as %u for %s", + Expr2.IVal, + (unsigned)Expr2.IVal & (ExprBits - 1), + GetBasicTypeName (CalType)); + } else if (Expr2.IVal >= (long)ExprBits) { + Warning ("Shift count %ld >= width of %s treated as %u", + Expr2.IVal, + GetBasicTypeName (CalType), + (unsigned)Expr2.IVal & (ExprBits - 1)); + } + + /* Here we simply "wrap" the shift count around the width */ + Expr2.IVal &= ExprBits - 1; + + /* Additional check for bit width */ + if (Expr2.IVal >= (long)BitSizeOf (Expr->Type)) { + Warning ("Shift count %ld >= width of %s", + Expr2.IVal, GetBasicTypeName (Expr->Type)); + } } } Gen->Func (Flags | CF_CONST, Expr2.IVal); @@ -548,7 +607,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Only "=" accept struct/union */ - if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) { + if (IsClassStruct (ltype) ? Gen != 0 : !IsScalarType (ltype)) { Error ("Invalid left operand for binary operator '%s'", Op); /* Continue. Wrong code will be generated, but the compiler won't ** break, so this is the best error recovery. diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 326d751b6..480cc32ea 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -41,6 +41,7 @@ /* common */ #include "addrsize.h" +#include "attrib.h" #include "check.h" #include "cpu.h" #include "shift.h" @@ -64,7 +65,16 @@ #include "util.h" #include "codegen.h" - +/* This is a terrible hack that tries to combat the ever reoccuring issue with + Mingw and PRIXPTR - the macro should have been defined like this for us in + the first place. + NOTE: "I64x" works in the github actions now, so if your local mingw64 fails, + you probably have to update. +*/ +#if defined(__MINGW64__) +#undef PRIXPTR +#define PRIXPTR "I64x" +#endif /*****************************************************************************/ /* Helpers */ @@ -1282,34 +1292,49 @@ void g_tosint (unsigned flags) -static void g_regchar (unsigned Flags) -/* Make sure, the value in the primary register is in the range of char. Truncate if necessary */ +static void g_regchar (unsigned to) +/* Treat the value in the primary register as a char with specified signedness +** and convert it to an int (whose representation is irrelevent of signedness). +*/ { - unsigned L; - - AddCodeLine ("ldx #$00"); - - if ((Flags & CF_UNSIGNED) == 0) { - /* Sign extend */ - L = GetLocalLabel(); - AddCodeLine ("cmp #$80"); - AddCodeLine ("bcc %s", LocalLabelName (L)); - AddCodeLine ("dex"); - g_defcodelabel (L); - } + /* Since char is the smallest type supported here, we never need any info + ** about the original type to "promote from it". However, we have to make + ** sure the entire AX contains the correct char value as an int, since we + ** will almost always use the char value as an int in AX directly in code + ** generation (unless CF_FORCECHAR is specified). That is to say, we don't + ** need the original "from" flags for the first conversion to char, but do + ** need the original "to" flags as the new "from" flags for the conversion + ** to int. + */ + g_regint (to | CF_FORCECHAR); } -void g_regint (unsigned Flags) -/* Make sure, the value in the primary register an int. Convert if necessary */ +void g_regint (unsigned from) +/* Convert the value in the primary register to an int (whose representation +** is irrelevent of signedness). +*/ { - switch (Flags & CF_TYPEMASK) { + switch (from & CF_TYPEMASK) { case CF_CHAR: - if (Flags & CF_FORCECHAR) { - /* Conversion is from char */ - g_regchar (Flags); + /* If the original value was forced to use only A, it must be + ** extended from char to fill AX. Otherwise nothing to do here + ** since AX would already have the correct int value. + */ + if (from & CF_FORCECHAR) { + AddCodeLine ("ldx #$00"); + + if ((from & CF_UNSIGNED) == 0) { + /* Sign extend */ + unsigned L = GetLocalLabel(); + AddCodeLine ("cmp #$80"); + AddCodeLine ("bcc %s", LocalLabelName (L)); + AddCodeLine ("dex"); + g_defcodelabel (L); + } + break; } /* FALLTHROUGH */ @@ -1318,21 +1343,27 @@ void g_regint (unsigned Flags) break; default: - typeerror (Flags); + typeerror (from); } } -void g_reglong (unsigned Flags) -/* Make sure, the value in the primary register a long. Convert if necessary */ +void g_reglong (unsigned from) +/* Convert the value in the primary register to a long (whose representation +** is irrelevent of signedness). +*/ { - switch (Flags & CF_TYPEMASK) { + switch (from & CF_TYPEMASK) { case CF_CHAR: - if (Flags & CF_FORCECHAR) { + /* If the original value was forced to use only A, it must be + ** extended from char to long. Otherwise AX would already have + ** the correct int value to be extened to long. + */ + if (from & CF_FORCECHAR) { /* Conversion is from char */ - if (Flags & CF_UNSIGNED) { + if (from & CF_UNSIGNED) { if (IS_Get (&CodeSizeFactor) >= 200) { AddCodeLine ("ldx #$00"); AddCodeLine ("stx sreg"); @@ -1342,18 +1373,19 @@ void g_reglong (unsigned Flags) } } else { if (IS_Get (&CodeSizeFactor) >= 366) { - g_regchar (Flags); + g_regint (from); AddCodeLine ("stx sreg"); AddCodeLine ("stx sreg+1"); } else { AddCodeLine ("jsr along"); } } + break; } /* FALLTHROUGH */ case CF_INT: - if (Flags & CF_UNSIGNED) { + if (from & CF_UNSIGNED) { if (IS_Get (&CodeSizeFactor) >= 200) { AddCodeLine ("ldy #$00"); AddCodeLine ("sty sreg"); @@ -1370,7 +1402,7 @@ void g_reglong (unsigned Flags) break; default: - typeerror (Flags); + typeerror (from); } } @@ -1507,48 +1539,49 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) -unsigned g_typecast (unsigned lhs, unsigned rhs) -/* Cast the value in the primary register to the operand size that is flagged -** by the lhs value. Return the result value. +unsigned g_typecast (unsigned to, unsigned from) +/* Cast the value in the primary register to the specified operand size and +** signedness. Return the result flags. */ { /* Check if a conversion is needed */ - if ((rhs & CF_CONST) == 0) { - switch (lhs & CF_TYPEMASK) { + if ((from & CF_CONST) == 0) { + switch (to & CF_TYPEMASK) { case CF_LONG: - /* We must promote the primary register to long */ - g_reglong (rhs); + /* We must promote the primary register to long in EAX */ + g_reglong (from); break; case CF_INT: - /* We must promote the primary register to int */ - g_regint (rhs); + /* We must promote the primary register to int in AX */ + g_regint (from); break; case CF_CHAR: - /* We must truncate the primary register to char */ - g_regchar (lhs); + /* We must truncate the primary register to char and then + ** sign-extend it to signed int in AX. + */ + g_regchar (to); break; default: - typeerror (lhs); + /* Since we are switching on "to", report an error on it */ + typeerror (to); } } - /* Do not need any other action. If the left type is int, and the primary + /* Do not need any other action. If the "to" type is int, and the primary ** register is long, it will be automagically truncated. If the right hand ** side is const, it is not located in the primary register and handled by ** the expression parser code. */ /* Result is const if the right hand side was const */ - lhs |= (rhs & CF_CONST); + to |= (from & CF_CONST); - /* The resulting type is that of the left hand side (that's why you called - ** this function :-) - */ - return lhs; + /* The resulting type is "to" (that's why you called this function :-) */ + return to; } @@ -4561,7 +4594,7 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size) -void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth) +void g_testbitfield (ATTR_UNUSED(unsigned Flags), unsigned BitOffs, unsigned BitWidth) /* Test bit-field in primary. */ { /* Since the end is inclusive and cannot be negative here, we subtract 1 from the sum */ @@ -4921,5 +4954,25 @@ void g_switch (Collection* Nodes, unsigned DefaultLabel, unsigned Depth) void g_asmcode (struct StrBuf* B) /* Output one line of assembler code. */ { - AddCodeLine ("%.*s", (int) SB_GetLen (B), SB_GetConstBuf (B)); + int len = (int) SB_GetLen(B); + const char *buf = SB_GetConstBuf(B); + + /* remove whitespace at end of line */ + /* NOTE: This masks problems in ParseInsn(), which in some cases seems to + rely on no whitespace being present at the end of a line in generated + code (see issue #1252). However, it generally seems to be a good + idea to remove trailing whitespace from (inline) assembly, so we + do it anyway. */ + while (len) { + switch (buf[len - 1]) { + case '\n': + case ' ': + case '\t': + --len; + continue; + } + break; + } + + AddCodeLine ("%.*s", len, buf); } diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index cb62d78bd..8e04b45e4 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -208,11 +208,15 @@ void g_toslong (unsigned flags); void g_tosint (unsigned flags); /* Make sure, the value on TOS is an int. Convert if necessary */ -void g_regint (unsigned Flags); -/* Make sure, the value in the primary register an int. Convert if necessary */ +void g_regint (unsigned from); +/* Convert the value in the primary register to an int (whose representation +** is irrelevent of signedness). +*/ -void g_reglong (unsigned Flags); -/* Make sure, the value in the primary register a long. Convert if necessary */ +void g_reglong (unsigned from); +/* Convert the value in the primary register to a long (whose representation +** is irrelevent of signedness). +*/ unsigned g_typeadjust (unsigned lhs, unsigned rhs); /* Adjust the integer operands before doing a binary operation. lhs is a flags @@ -220,9 +224,9 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs); ** in (e)ax. The return value is the flags value for the resulting type. */ -unsigned g_typecast (unsigned lhs, unsigned rhs); -/* Cast the value in the primary register to the operand size that is flagged -** by the lhs value. Return the result value. +unsigned g_typecast (unsigned to, unsigned from); +/* Cast the value in the primary register to the specified operand size and +** signedness. Return the result flags. */ void g_scale (unsigned flags, long val); diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 440b10751..208ada134 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -850,6 +850,10 @@ static unsigned RunOptGroup7 (CodeSeg* S) C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptBranchDist2, 1); + /* Adjust branch distances again, since the previous step may change code + between branches */ + C += RunOptFunc (S, &DOptBranchDist, 3); + Changes += C; /* If we had changes, we must run dead code elimination again, ** since the changes may have introduced dead code. diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index e621147ab..9f1bf4cc5 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -521,7 +521,7 @@ CodeSeg* NewCodeSeg (const char* SegName, SymEntry* Func) /* If we have a function given, get the return type of the function. ** Assume ANY return type besides void will use the A and X registers. */ - if (S->Func && !IsTypeVoid ((RetType = GetFuncReturn (Func->Type)))) { + if (S->Func && !IsTypeVoid ((RetType = GetFuncReturnType (Func->Type)))) { if (SizeOf (RetType) == SizeOf (type_long)) { S->ExitRegs = REG_EAX; } else { diff --git a/src/cc65/compile.c b/src/cc65/compile.c index d1f78098d..73380f3df 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -79,11 +79,14 @@ static void Parse (void) /* Top level parser routine. */ { int comma; - SymEntry* Entry; + SymEntry* Sym; FuncDesc* FuncDef = 0; - /* Go... */ - NextToken (); + /* Initialization for deferred operations */ + InitDeferredOps (); + + /* Fill up the next token with a bogus semicolon and start the tokenizer */ + NextTok.Tok = TOK_SEMI; NextToken (); /* Parse until end of input */ @@ -120,7 +123,7 @@ static void Parse (void) } /* Read variable defs and functions */ - ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC); /* Don't accept illegal storage classes */ if ((Spec.StorageClass & SC_TYPEMASK) == 0) { @@ -139,11 +142,11 @@ static void Parse (void) } /* Read declarations for this type */ - Entry = 0; + Sym = 0; comma = 0; while (1) { - Declaration Decl; + Declarator Decl; /* Read the next declaration */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); @@ -193,10 +196,10 @@ static void Parse (void) } /* Add an entry to the symbol table */ - Entry = AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); + Sym = AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); /* Add declaration attributes */ - SymUseAttr (Entry, &Decl); + SymUseAttr (Sym, &Decl); /* Reserve storage for the variable if we need to */ if (Decl.StorageClass & SC_STORAGE) { @@ -207,12 +210,12 @@ static void Parse (void) /* Allow initialization */ if (CurTok.Tok == TOK_ASSIGN) { - /* This is a definition */ - if (SymIsDef (Entry)) { + /* This is a definition with storage */ + if (SymIsDef (Sym)) { Error ("Global variable '%s' has already been defined", - Entry->Name); + Sym->Name); } - Entry->Flags |= SC_DEF; + Sym->Flags |= SC_DEF; /* We cannot initialize types of unknown size, or ** void types in ISO modes. @@ -242,25 +245,35 @@ static void Parse (void) } /* Define a label */ - g_defgloblabel (Entry->Name); + g_defgloblabel (Sym->Name); /* Skip the '=' */ NextToken (); /* Parse the initialization */ - ParseInit (Entry->Type); + 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); - Entry->Flags &= ~(SC_STORAGE | SC_DEF); - } else if (Size == 0 && SymIsDef (Entry) && !IsEmptiableObjectType (Decl.Type)) { + Sym->Flags &= ~(SC_STORAGE | 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); } } else { + /* Check for enum forward declaration. + ** Warn about it when extensions are not allowed. + */ + if (Size == 0 && IsTypeEnum (Decl.Type)) { + if (IS_Get (&Standard) != STD_CC65) { + Warning ("ISO C forbids forward references to 'enum' types"); + } + } + /* A global (including static) uninitialized variable is ** only a tentative definition. For example, this is valid: ** int i; @@ -273,11 +286,11 @@ static void Parse (void) */ const char* bssName = GetSegName (SEG_BSS); - if (Entry->V.BssName && strcmp (Entry->V.BssName, bssName) != 0) { + if (Sym->V.BssName && strcmp (Sym->V.BssName, bssName) != 0) { Error ("Global variable '%s' already was defined in the '%s' segment.", - Entry->Name, Entry->V.BssName); + Sym->Name, Sym->V.BssName); } - Entry->V.BssName = xstrdup (bssName); + Sym->V.BssName = xstrdup (bssName); /* This is to make the automatical zeropage setting of the symbol ** work right. @@ -287,17 +300,9 @@ static void Parse (void) } /* Make the symbol zeropage according to the segment address size */ - if ((Entry->Flags & SC_EXTERN) != 0) { + if ((Sym->Flags & SC_STATIC) != 0) { if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { - Entry->Flags |= SC_ZEROPAGE; - /* Check for enum forward declaration. - ** Warn about it when extensions are not allowed. - */ - if (Size == 0 && IsTypeEnum (Decl.Type)) { - if (IS_Get (&Standard) != STD_CC65) { - Warning ("ISO C forbids forward references to 'enum' types"); - } - } + Sym->Flags |= SC_ZEROPAGE; } } @@ -313,7 +318,7 @@ static void Parse (void) } /* Function declaration? */ - if (Entry && IsTypeFunc (Entry->Type)) { + if (Sym && IsTypeFunc (Sym->Type)) { /* Function */ if (!comma) { @@ -322,7 +327,7 @@ static void Parse (void) NextToken (); } else { /* Parse the function body */ - NewFunc (Entry, FuncDef); + NewFunc (Sym, FuncDef); /* Make sure we aren't omitting any work */ CheckDeferredOpAllDone (); @@ -336,6 +341,9 @@ static void Parse (void) } } + + /* Done with deferred operations */ + DoneDeferredOps (); } @@ -388,6 +396,11 @@ void Compile (const char* FileName) DefineNumericMacro ("__EAGERLY_INLINE_FUNCS__", 1); } + /* Placeholders for __FILE__, __LINE__ and __COUNTER__ macros */ + DefineTextMacro ("__FILE__", ""); + DefineTextMacro ("__LINE__", ""); + DefineTextMacro ("__COUNTER__", ""); + /* __TIME__ and __DATE__ macros */ Time = time (0); TM = localtime (&Time); @@ -401,7 +414,13 @@ void Compile (const char* FileName) /* DefineNumericMacro ("__STDC__", 1); <- not now */ DefineNumericMacro ("__STDC_HOSTED__", 1); - InitDeferredOps (); + /* Stuff unsupported */ + if (IS_Get (&Standard) > STD_C99) { + DefineNumericMacro ("__STDC_NO_ATOMICS__", 1); + DefineNumericMacro ("__STDC_NO_COMPLEX__", 1); + DefineNumericMacro ("__STDC_NO_THREADS__", 1); + DefineNumericMacro ("__STDC_NO_VLA__", 1); + } /* Create the base lexical level */ EnterGlobalLevel (); @@ -421,6 +440,9 @@ void Compile (const char* FileName) /* Generate the code generator preamble */ g_preamble (); + /* Init preprocessor */ + InitPreprocess (); + /* Open the input file */ OpenMainFile (FileName); @@ -431,10 +453,8 @@ void Compile (const char* FileName) OpenOutputFile (); /* Preprocess each line and write it to the output file */ - while (NextLine ()) { - Preprocess (); - WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); - } + while (PreprocessNextLine ()) + { /* Nothing */ } /* Close the output file */ CloseOutputFile (); @@ -458,8 +478,9 @@ void Compile (const char* FileName) for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { /* Assembly definition of uninitialized global variable */ - SymEntry* Sym = GetSymType (Entry->Type); + SymEntry* TagSym = GetESUTagSym (Entry->Type); unsigned Size = SizeOf (Entry->Type); + if (Size == 0 && IsTypeArray (Entry->Type)) { if (GetElementCount (Entry->Type) == UNSPECIFIED) { /* Assume array size of 1 */ @@ -468,11 +489,11 @@ void Compile (const char* FileName) Warning ("Incomplete array '%s[]' assumed to have one element", Entry->Name); } - Sym = GetSymType (GetElementType (Entry->Type)); + TagSym = GetESUTagSym (GetElementType (Entry->Type)); } /* For non-ESU types, Size != 0 */ - if (Size != 0 || (Sym != 0 && SymIsDef (Sym))) { + if (Size != 0 || (TagSym != 0 && SymIsDef (TagSym))) { /* Set the segment name only when it changes */ if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { SetSegName (SEG_BSS, Entry->V.BssName); @@ -492,9 +513,11 @@ void Compile (const char* FileName) } } } + } - DoneDeferredOps (); + /* Done with preprocessor */ + DonePreprocess (); if (Debug) { PrintMacroStats (stdout); diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 08f6c820e..ae40a55e9 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1153,6 +1153,8 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) /* Save lhs into zeropage */ AddStoreLhsA (D); + /* AddStoreLhsA may have moved the OpIndex, recalculate insertion point to prevent label migration. */ + D->IP = D->OpIndex + 1; /* cmp */ X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); @@ -1206,6 +1208,8 @@ static unsigned Opt_a_tosicmp (StackOpData* D) /* RHS src is not directly comparable */ X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); InsertEntry (D, X, D->Rhs.A.ChgIndex + 1); + /* RHS insertion may have moved the OpIndex, recalculate insertion point to prevent label migration. */ + D->IP = D->OpIndex + 1; /* Cmp with stored RHS */ X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); @@ -1329,7 +1333,7 @@ static unsigned Opt_a_tosult (StackOpData* D) /* The first column of these two tables must be sorted in lexical order */ static const OptFuncDesc FuncTable[] = { - { "__bzero", Opt___bzero, REG_NONE, OP_X_ZERO | OP_A_KNOWN }, + { "___bzero", Opt___bzero, REG_NONE, OP_X_ZERO | OP_A_KNOWN }, { "staspidx", Opt_staspidx, REG_NONE, OP_NONE }, { "staxspidx", Opt_staxspidx, REG_AX, OP_NONE }, { "tosaddax", Opt_tosaddax, REG_NONE, OP_NONE }, diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6d9afa403..caa41a7a4 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -48,6 +48,7 @@ #include "fp.h" #include "funcdesc.h" #include "global.h" +#include "ident.h" #include "symtab.h" @@ -86,6 +87,1224 @@ const Type type_c_void_p[] = { TYPE(T_PTR), TYPE(T_C_VOID), TYPE(T_END) }; +unsigned TypeLen (const Type* T) +/* Return the length of the type string */ +{ + const Type* Start = T; + while (T->C != T_END) { + ++T; + } + return T - Start; +} + + + +Type* TypeCopy (Type* Dest, const Type* Src) +/* Copy a type string */ +{ + Type* Orig = Dest; + while (1) { + *Dest = *Src; + if (Src->C == T_END) { + break; + } + Src++; + Dest++; + } + return Orig; +} + + + +Type* TypeDup (const Type* T) +/* Create a copy of the given type on the heap */ +{ + unsigned Len = (TypeLen (T) + 1) * sizeof (Type); + return memcpy (xmalloc (Len), T, Len); +} + + + +Type* TypeAlloc (unsigned Len) +/* Allocate memory for a type string of length Len. Len *must* include the +** trailing T_END. +*/ +{ + return xmalloc (Len * sizeof (Type)); +} + + + +void TypeFree (Type* T) +/* Free a type string */ +{ + xfree (T); +} + + + +/*****************************************************************************/ +/* Type info extraction */ +/*****************************************************************************/ + + + +int SignExtendChar (int C) +/* Do correct sign extension of a character to an int */ +{ + if (IS_Get (&SignedChars) && (C & 0x80) != 0) { + return C | ~0xFF; + } else { + return C & 0xFF; + } +} + + + +long GetIntegerTypeMin (const Type* Type) +/* Get the smallest possible value of the integer type. +** The type must have a known size. +*/ +{ + if (SizeOf (Type) == 0) { + Internal ("Incomplete type used in GetIntegerTypeMin"); + } + + if (IsSignSigned (Type)) { + /* The smallest possible signed value of N-byte integer is -pow(2, 8*N-1) */ + return (long)((unsigned long)(-1L) << (CHAR_BITS * SizeOf (Type) - 1U)); + } else { + return 0; + } +} + + + +unsigned long GetIntegerTypeMax (const Type* Type) +/* Get the largest possible value of the integer type. +** The type must have a known size. +*/ +{ + if (SizeOf (Type) == 0) { + Internal ("Incomplete type used in GetIntegerTypeMax"); + } + + if (IsSignSigned (Type)) { + /* Min signed value of N-byte integer is pow(2, 8*N-1) - 1 */ + return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; + } else { + /* Max signed value of N-byte integer is pow(2, 8*N) - 1. However, + ** workaround is needed as in ISO C it is UB if the shift count is + ** equal to the bit width of the left operand type. + */ + return (1UL << 1U << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; + } +} + + + +unsigned BitSizeOf (const Type* T) +/* Return the size (in bit-width) of a data type */ +{ + return IsTypeBitField (T) ? T->A.B.Width : CHAR_BITS * SizeOf (T); +} + + + +unsigned SizeOf (const Type* T) +/* Compute size (in bytes) of object represented by type array */ +{ + switch (GetUnqualTypeCode (T)) { + + case T_VOID: + /* A void variable is a cc65 extension. + ** Get its size (in bytes). + */ + return T->A.U; + + /* Beware: There's a chance that this triggers problems in other parts + ** of the compiler. The solution is to fix the callers, because calling + ** SizeOf() with a function type as argument is bad. + */ + case T_FUNC: + return 0; /* Size of function is unknown */ + + case T_SCHAR: + case T_UCHAR: + return SIZEOF_CHAR; + + case T_SHORT: + case T_USHORT: + return SIZEOF_SHORT; + + case T_INT: + case T_UINT: + return SIZEOF_INT; + + case T_PTR: + return SIZEOF_PTR; + + case T_LONG: + case T_ULONG: + return SIZEOF_LONG; + + case T_LONGLONG: + case T_ULONGLONG: + return SIZEOF_LONGLONG; + + case T_FLOAT: + return SIZEOF_FLOAT; + + case T_DOUBLE: + return SIZEOF_DOUBLE; + + case T_STRUCT: + case T_UNION: + return T->A.S->V.S.Size; + + case T_ARRAY: + if (T->A.L == UNSPECIFIED) { + /* Array with unspecified size */ + return 0; + } else { + return T->A.U * SizeOf (T + 1); + } + + case T_ENUM: + /* Incomplete enum type */ + return 0; + + default: + + Internal ("Unknown type in SizeOf: %04lX", T->C); + return 0; + + } +} + + + +unsigned PSizeOf (const Type* T) +/* Compute size (in bytes) of pointee object */ +{ + /* We are expecting a pointer expression */ + CHECK (IsClassPtr (T)); + + /* Skip the pointer or array token itself */ + return SizeOf (T + 1); +} + + + +unsigned CheckedBitSizeOf (const Type* T) +/* Return the size (in bit-width) of a data type. If the size is zero, emit an +** error and return some valid size instead (so the rest of the compiler +** doesn't have to work with invalid sizes). +*/ +{ + return IsTypeBitField (T) ? T->A.B.Width : CHAR_BITS * CheckedSizeOf (T); +} + + + +unsigned CheckedSizeOf (const Type* T) +/* Return the size (in bytes) of a data type. If the size is zero, emit an +** error and return some valid size instead (so the rest of the compiler +** doesn't have to work with invalid sizes). +*/ +{ + unsigned Size = SizeOf (T); + if (Size == 0) { + if (HasUnknownSize (T + 1)) { + Error ("Size of type '%s' is unknown", GetFullTypeName (T)); + } else { + Error ("Size of type '%s' is 0", GetFullTypeName (T)); + } + Size = SIZEOF_CHAR; /* Don't return zero */ + } + return Size; +} + + + +unsigned CheckedPSizeOf (const Type* T) +/* Return the size (in bytes) of a data type that is pointed to by a pointer. +** If the size is zero, emit an error and return some valid size instead (so +** the rest of the compiler doesn't have to work with invalid sizes). +*/ +{ + unsigned Size = PSizeOf (T); + if (Size == 0) { + if (HasUnknownSize (T + 1)) { + Error ("Pointer to type '%s' of unknown size", GetFullTypeName (T + 1)); + } else { + Error ("Pointer to type '%s' of 0 size", GetFullTypeName (T + 1)); + } + Size = SIZEOF_CHAR; /* Don't return zero */ + } + return Size; +} + + + +static unsigned GetMinimalTypeSizeByBitWidth (unsigned BitWidth) +/* Return the size of the smallest integer type that may have BitWidth bits */ +{ + /* Since all integer types supported in cc65 for bit-fields have sizes that + ** are powers of 2, we can just use this bit-twiddling trick. + */ + unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS; + V |= V >> 1; + V |= V >> 2; + V |= V >> 4; + V |= V >> 8; + V |= V >> 16; + + /* Return the result size */ + return V + 1U; +} + + + +TypeCode GetUnqualTypeCode (const Type* Type) +/* Get the type code of the unqualified underlying type of Type. +** Return GetUnqualRawTypeCode (Type) if Type is not scalar. +*/ +{ + TypeCode Underlying = GetUnqualRawTypeCode (Type); + + if (IsDeclTypeChar (Type)) { + + return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; + + } else if (IsTypeEnum (Type)) { + TypeCode TCode; + + /* This should not happen, but just in case */ + if (Type->A.S == 0) { + Internal ("Enum tag type error in GetUnqualTypeCode"); + } + + /* Inspect the underlying type of the enum */ + if (Type->A.S->V.E.Type == 0) { + /* Incomplete enum type is used */ + return Underlying; + } + TCode = GetUnqualRawTypeCode (Type->A.S->V.E.Type); + + /* Replace the type code with integer */ + Underlying = (TCode & ~T_MASK_RANK); + switch (TCode & T_MASK_SIZE) { + case T_SIZE_INT: Underlying |= T_RANK_INT; break; + case T_SIZE_LONG: Underlying |= T_RANK_LONG; break; + case T_SIZE_SHORT: Underlying |= T_RANK_SHORT; break; + case T_SIZE_CHAR: Underlying |= T_RANK_CHAR; break; + case T_SIZE_LONGLONG: Underlying |= T_RANK_LONGLONG; break; + default: Underlying |= T_RANK_INT; break; + } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + switch (GetMinimalTypeSizeByBitWidth (Type->A.B.Width)) { + case SIZEOF_CHAR: Underlying = T_CHAR; break; + case SIZEOF_INT: Underlying = T_INT; break; + case SIZEOF_LONG: Underlying = T_LONG; break; + case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break; + default: Underlying = T_INT; break; + } + Underlying &= ~T_MASK_SIGN; + Underlying |= Type->C & T_MASK_SIGN; + } + + return Underlying; +} + + + +/*****************************************************************************/ +/* Type manipulation */ +/*****************************************************************************/ + + + +Type* GetImplicitFuncType (void) +/* Return a type string for an implicitly declared function */ +{ + /* Get a new function descriptor */ + FuncDesc* F = NewFuncDesc (); + + /* Allocate memory for the type string */ + Type* T = TypeAlloc (3); /* func/returns int/terminator */ + + /* Prepare the function descriptor */ + F->Flags = FD_EMPTY; + F->SymTab = &EmptySymTab; + F->TagTab = &EmptySymTab; + + /* Fill the type string */ + T[0].C = T_FUNC | CodeAddrSizeQualifier (); + T[0].A.F = F; + T[1].C = T_INT; + T[2].C = T_END; + + /* Return the new type */ + return T; +} + + + +Type* GetCharArrayType (unsigned Len) +/* Return the type for a char array of the given length */ +{ + /* Allocate memory for the type string */ + Type* T = TypeAlloc (3); /* array/char/terminator */ + + /* Fill the type string */ + T[0].C = T_ARRAY; + T[0].A.L = Len; /* Array length is in the L attribute */ + T[1].C = T_CHAR; + T[2].C = T_END; + + /* Return the new type */ + return T; +} + + + +Type* NewPointerTo (const Type* T) +/* Return a type string that is "pointer to T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + +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. +*/ +{ + Type* P; + + /* The type specifier must be integeral */ + CHECK (IsClassInt (T)); + + /* Allocate the new type string */ + P = TypeAlloc (3); + + /* Create the return type... */ + P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD; + P[0].C |= (T[0].C & T_QUAL_ADDRSIZE); + P[0].A.B.Offs = BitOffs; + P[0].A.B.Width = BitWidth; + + /* Get the declaration type */ + memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1])); + + /* Get done... */ + P[2].C = T_END; + + /* ...and return it */ + return P; +} + + + +const Type* AddressOf (const Type* T) +/* Return a type string that is "address of T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + +const Type* Indirect (const Type* T) +/* Do one indirection for the given type, that is, return the type where the +** given type points to. +*/ +{ + /* We are expecting a pointer expression */ + CHECK (IsClassPtr (T)); + + /* Skip the pointer or array token itself */ + return T + 1; +} + + + +Type* ArrayToPtr (const Type* T) +/* Convert an array to a pointer to it's first element */ +{ + /* Return pointer to first element */ + return NewPointerTo (GetElementType (T)); +} + + + +const Type* PtrConversion (const Type* T) +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. Otherwise +** return T. +*/ +{ + if (IsTypeFunc (T)) { + return AddressOf (T); + } else if (IsTypeArray (T)) { + return AddressOf (GetElementType (T)); + } else { + return T; + } +} + + + +const Type* StdConversion (const Type* T) +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. If the +** type is an integer, do integeral promotion. Otherwise return T. +*/ +{ + if (IsTypeFunc (T)) { + return AddressOf (T); + } else if (IsTypeArray (T)) { + return AddressOf (GetElementType (T)); + } else if (IsClassInt (T)) { + return IntPromotion (T); + } else { + return T; + } +} + + + +const Type* IntPromotion (const Type* T) +/* Apply the integer promotions to T and return the result. The returned type +** string may be T if there is no need to change it. +*/ +{ + /* We must have an int to apply int promotions */ + PRECONDITION (IsClassInt (T)); + + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 + ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or + ** an object that has enumeration type, may be used in an expression wherever an int or + ** unsigned int may be used. If an int can represent all values of the original type, + ** the value is converted to an int; otherwise it is converted to an unsigned int. + ** These are called the integral promotions. + */ + + if (IsTypeBitField (T)) { + /* As we now support long bit-fields, we need modified rules for them: + ** - If an int can represent all values of the bit-field, the bit-field is converted + ** to an int; + ** - Otherwise, if an unsigned int can represent all values of the bit-field, the + ** bit-field is converted to an unsigned int; + ** - Otherwise, the bit-field will have its declared integer type. + ** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's. + */ + if (T->A.B.Width > INT_BITS) { + return IsSignUnsigned (T) ? type_ulong : type_long; + } + return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; + } else if (IsRankChar (T)) { + /* An integer can represent all values from either signed or unsigned char, so convert + ** chars to int. + */ + return type_int; + } else if (IsRankShort (T)) { + /* An integer cannot represent all values from unsigned short, so convert unsigned short + ** to unsigned int. + */ + return IsSignUnsigned (T) ? type_uint : type_int; + } else if (!IsIncompleteESUType (T)) { + /* The type is a complete type not smaller than int, so leave it alone. */ + return T; + } else { + /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. + ** Assume int to avoid further errors. + */ + return type_int; + } +} + + + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) +/* Perform the usual arithmetic conversions for binary operators. */ +{ + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 + ** Many binary operators that expect operands of arithmetic type cause conversions and yield + ** result types in a similar way. The purpose is to yield a common type, which is also the type + ** of the result. This pattern is called the usual arithmetic conversions. + */ + + /* There are additional rules for floating point types that we don't bother with, since + ** floating point types are not (yet) supported. + ** The integral promotions are performed on both operands. + */ + lhst = IntPromotion (lhst); + rhst = IntPromotion (rhst); + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((IsRankLong (lhst) && IsSignUnsigned (lhst)) || + (IsRankLong (rhst) && IsSignUnsigned (rhst))) { + return type_ulong; + } + + /* Otherwise, if one operand has type long int and the other has type unsigned int, + ** if a long int can represent all values of an unsigned int, the operand of type unsigned int + ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, + ** both operands are converted to unsigned long int. + */ + if ((IsRankLong (lhst) && IsRankInt (rhst) && IsSignUnsigned (rhst)) || + (IsRankLong (rhst) && IsRankInt (lhst) && IsSignUnsigned (lhst))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return type_long; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. + */ + if (IsRankLong (lhst) || IsRankLong (rhst)) { + return type_long; + } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((IsRankInt (lhst) && IsSignUnsigned (lhst)) || + (IsRankInt (rhst) && IsSignUnsigned (rhst))) { + return type_uint; + } + + /* Otherwise, both operands have type int. */ + CHECK (IsRankInt (lhst)); + CHECK (IsSignSigned (lhst)); + CHECK (IsRankInt (rhst)); + CHECK (IsSignSigned (rhst)); + return type_int; +} + + + +const Type* GetSignedType (const Type* T) +/* Get signed counterpart of the integral type */ +{ + switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + case T_RANK_CHAR: + return type_schar; + + case T_RANK_INT: + case T_RANK_SHORT: + return type_int; + + case T_RANK_LONG: + return type_long; + + default: + Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + return T; + } +} + + + +const Type* GetUnsignedType (const Type* T) +/* Get unsigned counterpart of the integral type */ +{ + switch (GetUnqualTypeCode (T) & T_MASK_RANK) { + case T_RANK_CHAR: + return type_uchar; + + case T_RANK_INT: + case T_RANK_SHORT: + return type_uint; + + case T_RANK_LONG: + return type_ulong; + + default: + Internal ("Unknown type code: %lX", GetUnqualTypeCode (T)); + return T; + } +} + + + +const Type* GetUnderlyingType (const Type* Type) +/* Get the underlying type of an enum or other integer class type */ +{ + if (IsDeclTypeChar (Type)) { + return IS_Get (&SignedChars) ? type_schar : type_uchar; + } else if (IsTypeEnum (Type)) { + /* This should not happen, but just in case */ + if (Type->A.S == 0) { + Internal ("Enum tag type error in GetUnderlyingType"); + } + + /* If incomplete enum type is used, just return its raw type */ + if (Type->A.S->V.E.Type != 0) { + return Type->A.S->V.E.Type; + } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + switch (GetMinimalTypeSizeByBitWidth (Type->A.B.Width)) { + case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break; + case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break; + case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break; + default: Type = IsSignSigned (Type) ? type_int : type_uint; break; + } + } + + return Type; +} + + + +const Type* GetStructReplacementType (const Type* SType) +/* Get a replacement type for passing a struct/union by value in the primary */ +{ + const Type* NewType; + /* If the size is less than or equal to that of a long, we will copy the + ** struct using the primary register, otherwise we will use memcpy. + */ + switch (SizeOf (SType)) { + case 1: NewType = type_uchar; break; + case 2: NewType = type_uint; break; + case 4: NewType = type_ulong; break; + default: NewType = SType; break; + } + + return NewType; +} + + + +const Type* GetBitFieldChunkType (const Type* Type) +/* Get the type needed to operate on the byte chunk containing the bit-field */ +{ + unsigned ChunkSize; + if ((Type->A.B.Width - 1U) / CHAR_BITS == + (Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) { + /* T bit-field fits within its underlying type */ + return GetUnderlyingType (Type); + } + + ChunkSize = GetMinimalTypeSizeByBitWidth (Type->A.B.Offs + Type->A.B.Width); + if (ChunkSize < SizeOf (Type + 1)) { + /* The end of the bit-field is offset by some bits so that it requires + ** more bytes to be accessed as a whole than its underlying type does. + ** Note: In cc65 the bit offset is always less than CHAR_BITS. + */ + switch (ChunkSize) { + case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar; + case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint; + case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong; + default: return IsSignSigned (Type) ? type_int : type_uint; + } + } + + /* We can always use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than that + ** of its declaration type in cc65. So this is OK. + */ + return Type + 1; +} + + + +/*****************************************************************************/ +/* Type Predicates */ +/*****************************************************************************/ + + + +int IsTypeFragBitField (const Type* T) +/* Return true if this is a bit-field that shares byte space with other fields */ +{ + return IsTypeBitField (T) && + (T->A.B.Offs != 0 || T->A.B.Width != CHAR_BITS * SizeOf (T)); +} + + + +int IsObjectType (const Type* T) +/* Return true if this is a fully described object type */ +{ + return !IsTypeFunc (T) && !IsIncompleteType (T); +} + + + +int IsIncompleteType (const Type* T) +/* Return true if this is an object type lacking size info */ +{ + if (IsTypeArray (T)) { + return GetElementCount (T) == UNSPECIFIED || IsIncompleteType (T + 1); + } + return IsTypeVoid (T) || IsIncompleteESUType (T); +} + + + +int IsArithmeticType (const Type* T) +/* Return true if this is an integer or floating type */ +{ + return IsClassInt (T) || IsClassFloat (T); +} + + + +int IsBasicType (const Type* T) +/* Return true if this is a character, integer or floating type */ +{ + return IsDeclRankChar (T) || IsClassInt (T) || IsClassFloat (T); +} + + + +int IsScalarType (const Type* T) +/* Return true if this is an arithmetic or pointer type */ +{ + return IsArithmeticType (T) || IsTypePtr (T); +} + + + +int IsDerivedType (const Type* T) +/* Return true if this is an array, struct, union, function or pointer type */ +{ + return IsTypeArray (T) || IsClassStruct (T) || IsClassFunc (T) || IsTypePtr (T); +} + + + +int IsAggregateType (const Type* T) +/* Return true if this is an array or struct type */ +{ + return IsTypeArray (T) || IsTypeStruct (T); +} + + + +int IsRelationType (const Type* T) +/* Return true if this is an arithmetic, array or pointer type */ +{ + return IsArithmeticType (T) || IsClassPtr (T); +} + + + +int IsCastType (const Type* T) +/* Return true if this type can be used for casting */ +{ + return IsScalarType (T) || IsTypeVoid (T); +} + + + +int IsESUType (const Type* T) +/* Return true if this is an enum/struct/union type */ +{ + return IsClassStruct (T) || IsTypeEnum (T); +} + + + +int IsIncompleteESUType (const Type* T) +/* Return true if this is an incomplete ESU type */ +{ + SymEntry* TagSym = GetESUTagSym (T); + + return TagSym != 0 && !SymIsDef (TagSym); +} + + + +int IsEmptiableObjectType (const Type* T) +/* Return true if this is a struct/union/void type that can have zero size */ +{ + return IsClassStruct (T) || IsTypeVoid (T); +} + + + +int HasUnknownSize (const Type* T) +/* Return true if this is an incomplete ESU type or an array of unknown size */ +{ + if (IsTypeArray (T)) { + return GetElementCount (T) == UNSPECIFIED || HasUnknownSize (T + 1); + } + return IsIncompleteESUType (T); +} + + + +int TypeHasAttrData (const Type* T) +/* Return true if the given type has attribute data */ +{ + return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); +} + + + +/*****************************************************************************/ +/* Qualifier helpers */ +/*****************************************************************************/ + + + +TypeCode AddrSizeQualifier (unsigned AddrSize) +/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ +{ + switch (AddrSize) { + + case ADDR_SIZE_ABS: + return T_QUAL_NEAR; + + case ADDR_SIZE_FAR: + return T_QUAL_FAR; + + default: + Error ("Invalid address size"); + return T_QUAL_NEAR; + + } +} + + + +/*****************************************************************************/ +/* Function type helpers */ +/*****************************************************************************/ + + + +int IsVariadicFunc (const Type* T) +/* Return true if this is a function type or pointer to function type with +** variable parameter list. +** Check fails if the type is not a function or a pointer to function. +*/ +{ + return (GetFuncDesc (T)->Flags & FD_VARIADIC) != 0; +} + + + +int IsFastcallFunc (const Type* T) +/* Return true if this is a function type or pointer to function type by +** __fastcall__ calling convention. +** Check fails if the type is not a function or a pointer to function. +*/ +{ + if (GetUnqualRawTypeCode (T) == T_PTR) { + /* Pointer to function */ + ++T; + } + return !IsVariadicFunc (T) && (AutoCDecl ? IsQualFastcall (T) : !IsQualCDecl (T)); +} + + + +FuncDesc* GetFuncDesc (const Type* T) +/* Get the FuncDesc pointer from a function or pointer-to-function type */ +{ + if (GetUnqualRawTypeCode (T) == T_PTR) { + /* Pointer to function */ + ++T; + } + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + /* Get the function descriptor from the type attributes */ + return T->A.F; +} + + + +void SetFuncDesc (Type* T, FuncDesc* F) +/* Set the FuncDesc pointer in a function or pointer-to-function type */ +{ + if (GetUnqualRawTypeCode (T) == T_PTR) { + /* Pointer to function */ + ++T; + } + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + /* Set the function descriptor */ + T->A.F = F; +} + + + +const Type* GetFuncReturnType (const Type* T) +/* Return a pointer to the return type of a function or pointer-to-function type */ +{ + if (GetUnqualRawTypeCode (T) == T_PTR) { + /* Pointer to function */ + ++T; + } + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + /* Return a pointer to the return type */ + return T + 1; +} + + + +Type* GetFuncReturnTypeModifiable (Type* T) +/* Return a non-const pointer to the return type of a function or pointer-to-function type */ +{ + if (GetUnqualRawTypeCode (T) == T_PTR) { + /* Pointer to function */ + ++T; + } + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + /* Return a pointer to the return type */ + return T + 1; +} + + + +const FuncDesc* GetFuncDefinitionDesc (const Type* T) +/* Get the function descriptor of the function definition */ +{ + const FuncDesc* D; + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + D = GetFuncDesc (T); + return D->FuncDef != 0 ? D->FuncDef : D; +} + + + +/*****************************************************************************/ +/* Array type helpers */ +/*****************************************************************************/ + + + +long GetElementCount (const Type* T) +/* Get the element count of the array specified in T (which must be of +** array type). +*/ +{ + CHECK (IsTypeArray (T)); + return T->A.L; +} + + + +void SetElementCount (Type* T, long Count) +/* Set the element count of the array specified in T (which must be of +** array type). +*/ +{ + CHECK (IsTypeArray (T)); + T->A.L = Count; +} + + + +const Type* GetElementType (const Type* T) +/* Return the element type of the given array type */ +{ + CHECK (IsTypeArray (T)); + return T + 1; +} + + + +Type* GetElementTypeModifiable (Type* T) +/* Return the element type of the given array type */ +{ + CHECK (IsTypeArray (T)); + return T + 1; +} + + + +const Type* GetBaseElementType (const Type* T) +/* Return the base element type of a given type. If T is not an array, this +** will return. Otherwise it will return the base element type, which means +** the element type that is not an array. +*/ +{ + while (IsTypeArray (T)) { + ++T; + } + return T; +} + + + +/*****************************************************************************/ +/* ESU types helpers */ +/*****************************************************************************/ + + + +struct SymEntry* GetESUTagSym (const Type* T) +/* Get the tag symbol entry of the enum/struct/union type. +** Return 0 if it is not an enum/struct/union. +*/ +{ + if ((IsClassStruct (T) || IsTypeEnum (T))) { + return T->A.S; + } + return 0; +} + + + +void SetESUTagSym (Type* T, struct SymEntry* S) +/* Set the tag symbol entry of the enum/struct/union type */ +{ + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); + + /* Set the attribute */ + T->A.S = S; +} + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +const char* GetBasicTypeName (const Type* T) +/* Return a const name string of the basic type. +** Return "type" for unknown basic types. +*/ +{ + switch (GetRawTypeRank (T)) { + case T_RANK_ENUM: return "enum"; + case T_RANK_BITFIELD: return "bit-field"; + case T_RANK_FLOAT: return "float"; + case T_RANK_DOUBLE: return "double"; + case T_RANK_VOID: return "void"; + case T_RANK_STRUCT: return "struct"; + case T_RANK_UNION: return "union"; + case T_RANK_ARRAY: return "array"; + case T_RANK_PTR: return "pointer"; + case T_RANK_FUNC: return "function"; + case T_RANK_NONE: /* FALLTHROUGH */ + default: break; + } + if (IsClassInt (T)) { + if (IsRawSignSigned (T)) { + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "signed char"; + case T_RANK_SHORT: return "short"; + case T_RANK_INT: return "int"; + case T_RANK_LONG: return "long"; + case T_RANK_LONGLONG: return "long long"; + default: + return "signed integer"; + } + } else if (IsRawSignUnsigned (T)) { + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "unsigned char"; + case T_RANK_SHORT: return "unsigned short"; + case T_RANK_INT: return "unsigned int"; + case T_RANK_LONG: return "unsigned long"; + case T_RANK_LONGLONG: return "unsigned long long"; + default: + return "unsigned integer"; + } + } else { + switch (GetRawTypeRank (T)) { + case T_RANK_CHAR: return "char"; + case T_RANK_SHORT: return "short"; + case T_RANK_INT: return "int"; + case T_RANK_LONG: return "long"; + case T_RANK_LONGLONG: return "long long"; + default: + return "integer"; + } + } + } + return "type"; +} + + + +static const char* GetTagSymName (const Type* T) +/* Return a name string of the type or the symbol name if it is an ESU type. +** Note: This may use a static buffer that could be overwritten by other calls. +*/ +{ + static char TypeName [IDENTSIZE + 16]; + SymEntry* Sym; + + Sym = GetESUTagSym (T); + if (Sym == 0) { + return GetBasicTypeName (T); + } + sprintf (TypeName, "%s %s", GetBasicTypeName (T), + Sym->Name[0] != '\0' ? Sym->Name : "<unknown>"); + + return TypeName; +} + + + +const char* GetFullTypeName (const Type* T) +/* Return the full name string of the given type */ +{ + struct StrBuf* Buf = NewDiagnosticStrBuf (); + GetFullTypeNameBuf (Buf, T); + + return SB_GetConstBuf (Buf); +} + + + 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. @@ -208,7 +1427,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } if (!IsTypeBitField (T)) { - SB_AppendStr (&Buf, GetSymTypeName (T)); + SB_AppendStr (&Buf, GetTagSymName (T)); } else { SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); } @@ -228,74 +1447,6 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu -const char* GetBasicTypeName (const Type* T) -/* Return a const name string of the basic type. -** Return "type" for unknown basic types. -*/ -{ - switch (GetRawType (T)) { - case T_TYPE_ENUM: return "enum"; - case T_TYPE_BITFIELD: return "bit-field"; - case T_TYPE_FLOAT: return "float"; - case T_TYPE_DOUBLE: return "double"; - case T_TYPE_VOID: return "void"; - case T_TYPE_STRUCT: return "struct"; - case T_TYPE_UNION: return "union"; - case T_TYPE_ARRAY: return "array"; - case T_TYPE_PTR: return "pointer"; - case T_TYPE_FUNC: return "function"; - case T_TYPE_NONE: /* FALLTHROUGH */ - default: break; - } - if (IsClassInt (T)) { - if (IsRawSignSigned (T)) { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "signed char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "int"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; - default: - return "signed integer"; - } - } else if (IsRawSignUnsigned (T)) { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "unsigned char"; - case T_TYPE_SHORT: return "unsigned short"; - case T_TYPE_INT: return "unsigned int"; - case T_TYPE_LONG: return "unsigned long"; - case T_TYPE_LONGLONG: return "unsigned long long"; - default: - return "unsigned integer"; - } - } else { - switch (GetRawType (T)) { - case T_TYPE_CHAR: return "char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "int"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; - default: - return "integer"; - } - } - } - return "type"; -} - - - -const char* GetFullTypeName (const Type* T) -/* Return the full name string of the given type */ -{ - struct StrBuf* Buf = NewDiagnosticStrBuf (); - GetFullTypeNameBuf (Buf, T); - - return SB_GetConstBuf (Buf); -} - - - struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T) /* Return the full name string of the given type */ { @@ -377,1167 +1528,6 @@ int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode Ignor -unsigned TypeLen (const Type* T) -/* Return the length of the type string */ -{ - const Type* Start = T; - while (T->C != T_END) { - ++T; - } - return T - Start; -} - - - -Type* TypeCopy (Type* Dest, const Type* Src) -/* Copy a type string */ -{ - Type* Orig = Dest; - while (1) { - *Dest = *Src; - if (Src->C == T_END) { - break; - } - Src++; - Dest++; - } - return Orig; -} - - - -Type* TypeDup (const Type* T) -/* Create a copy of the given type on the heap */ -{ - unsigned Len = (TypeLen (T) + 1) * sizeof (Type); - return memcpy (xmalloc (Len), T, Len); -} - - - -Type* TypeAlloc (unsigned Len) -/* Allocate memory for a type string of length Len. Len *must* include the -** trailing T_END. -*/ -{ - return xmalloc (Len * sizeof (Type)); -} - - - -void TypeFree (Type* T) -/* Free a type string */ -{ - xfree (T); -} - - - -int SignExtendChar (int C) -/* Do correct sign extension of a character */ -{ - if (IS_Get (&SignedChars) && (C & 0x80) != 0) { - return C | ~0xFF; - } else { - return C & 0xFF; - } -} - - - -Type* GetCharArrayType (unsigned Len) -/* Return the type for a char array of the given length */ -{ - /* Allocate memory for the type string */ - Type* T = TypeAlloc (3); /* array/char/terminator */ - - /* Fill the type string */ - T[0].C = T_ARRAY; - T[0].A.L = Len; /* Array length is in the L attribute */ - T[1].C = T_CHAR; - T[2].C = T_END; - - /* Return the new type */ - return T; -} - - - -Type* GetImplicitFuncType (void) -/* Return a type string for an inplicitly declared function */ -{ - /* Get a new function descriptor */ - FuncDesc* F = NewFuncDesc (); - - /* Allocate memory for the type string */ - Type* T = TypeAlloc (3); /* func/returns int/terminator */ - - /* Prepare the function descriptor */ - F->Flags = FD_EMPTY; - F->SymTab = &EmptySymTab; - F->TagTab = &EmptySymTab; - - /* Fill the type string */ - T[0].C = T_FUNC | CodeAddrSizeQualifier (); - T[0].A.F = F; - T[1].C = T_INT; - T[2].C = T_END; - - /* Return the new type */ - return T; -} - - - -const Type* GetStructReplacementType (const Type* SType) -/* Get a replacement type for passing a struct/union in the primary register */ -{ - const Type* NewType; - /* If the size is less than or equal to that of a long, we will copy the - ** struct using the primary register, otherwise we will use memcpy. - */ - switch (SizeOf (SType)) { - case 1: NewType = type_uchar; break; - case 2: NewType = type_uint; break; - case 3: /* FALLTHROUGH */ - case 4: NewType = type_ulong; break; - default: NewType = SType; break; - } - - return NewType; -} - - - -long GetIntegerTypeMin (const Type* Type) -/* Get the smallest possible value of the integer type. -** The type must have a known size. -*/ -{ - if (SizeOf (Type) == 0) { - Internal ("Incomplete type used in GetIntegerTypeMin"); - } - - if (IsSignSigned (Type)) { - /* The smallest possible signed value of N-byte integer is -pow(2, 8*N-1) */ - return (long)((unsigned long)(-1L) << (CHAR_BITS * SizeOf (Type) - 1U)); - } else { - return 0; - } -} - - - -unsigned long GetIntegerTypeMax (const Type* Type) -/* Get the largest possible value of the integer type. -** The type must have a known size. -*/ -{ - if (SizeOf (Type) == 0) { - Internal ("Incomplete type used in GetIntegerTypeMax"); - } - - if (IsSignSigned (Type)) { - /* Min signed value of N-byte integer is pow(2, 8*N-1) - 1 */ - return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; - } else { - /* Max signed value of N-byte integer is pow(2, 8*N) - 1. However, - ** workaround is needed as in ISO C it is UB if the shift count is - ** equal to the bit width of the left operand type. - */ - return (1UL << 1U << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; - } -} - - - -static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth) -/* Return the size of the smallest integer type that may have BitWidth bits */ -{ - /* Since all integer types supported in cc65 for bit-fields have sizes that - ** are powers of 2, we can just use this bit-twiddling trick. - */ - unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS; - V |= V >> 1; - V |= V >> 2; - V |= V >> 4; - V |= V >> 8; - V |= V >> 16; - - /* Return the result size */ - return V + 1U; -} - - -static unsigned TypeOfBySize (unsigned Size) -/* Get the code generator replacement type of the object by its size */ -{ - unsigned NewType; - /* 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 3: /* FALLTHROUGH */ - case 4: NewType = CF_LONG; break; - default: NewType = CF_NONE; break; - } - - return NewType; -} - - - -const Type* GetUnderlyingType (const Type* Type) -/* Get the underlying type of an enum or other integer class type */ -{ - if (IsISOChar (Type)) { - return IS_Get (&SignedChars) ? type_schar : type_uchar; - } else if (IsTypeEnum (Type)) { - /* This should not happen, but just in case */ - if (Type->A.S == 0) { - Internal ("Enum tag type error in GetUnderlyingTypeCode"); - } - - /* If incomplete enum type is used, just return its raw type */ - if (Type->A.S->V.E.Type != 0) { - return Type->A.S->V.E.Type; - } - } else if (IsTypeBitField (Type)) { - /* We consider the smallest type that can represent all values of the - ** bit-field, instead of the type used in the declaration, the truly - ** underlying of the bit-field. - */ - switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) { - case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break; - case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break; - case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break; - default: Type = IsSignSigned (Type) ? type_int : type_uint; break; - } - } - - return Type; -} - - - -TypeCode GetUnderlyingTypeCode (const Type* Type) -/* Get the type code of the unqualified underlying type of TCode. -** Return UnqualifiedType (TCode) if TCode is not scalar. -*/ -{ - TypeCode Underlying = UnqualifiedType (Type->C); - - if (IsISOChar (Type)) { - - return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; - - } else if (IsTypeEnum (Type)) { - TypeCode TCode; - - /* This should not happen, but just in case */ - if (Type->A.S == 0) { - Internal ("Enum tag type error in GetUnderlyingTypeCode"); - } - - /* Inspect the underlying type of the enum */ - if (Type->A.S->V.E.Type == 0) { - /* Incomplete enum type is used */ - return Underlying; - } - TCode = UnqualifiedType (Type->A.S->V.E.Type->C); - - /* Replace the type code with integer */ - Underlying = (TCode & ~T_MASK_TYPE); - switch (TCode & T_MASK_SIZE) { - case T_SIZE_INT: Underlying |= T_TYPE_INT; break; - case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break; - case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break; - case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break; - case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; - default: Underlying |= T_TYPE_INT; break; - } - } else if (IsTypeBitField (Type)) { - /* We consider the smallest type that can represent all values of the - ** bit-field, instead of the type used in the declaration, the truly - ** underlying of the bit-field. - */ - switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) { - case SIZEOF_CHAR: Underlying = T_CHAR; break; - case SIZEOF_INT: Underlying = T_INT; break; - case SIZEOF_LONG: Underlying = T_LONG; break; - case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break; - default: Underlying = T_INT; break; - } - Underlying &= ~T_MASK_SIGN; - Underlying |= Type->C & T_MASK_SIGN; - } - - return Underlying; -} - - - -const Type* GetBitFieldChunkType (const Type* Type) -/* Get the type needed to operate on the byte chunk containing the bit-field */ -{ - unsigned ChunkSize; - if ((Type->A.B.Width - 1U) / CHAR_BITS == - (Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) { - /* T bit-field fits within its underlying type */ - return GetUnderlyingType (Type); - } - - ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width); - if (ChunkSize < SizeOf (Type + 1)) { - /* The end of the bit-field is offset by some bits so that it requires - ** more bytes to be accessed as a whole than its underlying type does. - ** Note: In cc65 the bit offset is always less than CHAR_BITS. - */ - switch (ChunkSize) { - case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar; - case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint; - case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong; - default: return IsSignSigned (Type) ? type_int : type_uint; - } - } - - /* We can always use the declarartion integer type as the chunk type. - ** Note: A bit-field will not occupy bits located in bytes more than that - ** of its declaration type in cc65. So this is OK. - */ - return Type + 1; -} - - - -unsigned SizeOf (const Type* T) -/* Compute size of object represented by type array. */ -{ - switch (GetUnderlyingTypeCode (T)) { - - case T_VOID: - /* A void variable is a cc65 extension. - ** Get its size (in bytes). - */ - return T->A.U; - - /* Beware: There's a chance that this triggers problems in other parts - ** of the compiler. The solution is to fix the callers, because calling - ** SizeOf() with a function type as argument is bad. - */ - case T_FUNC: - return 0; /* Size of function is unknown */ - - case T_SCHAR: - case T_UCHAR: - return SIZEOF_CHAR; - - case T_SHORT: - case T_USHORT: - return SIZEOF_SHORT; - - case T_INT: - case T_UINT: - return SIZEOF_INT; - - case T_PTR: - return SIZEOF_PTR; - - case T_LONG: - case T_ULONG: - return SIZEOF_LONG; - - case T_LONGLONG: - case T_ULONGLONG: - return SIZEOF_LONGLONG; - - case T_FLOAT: - return SIZEOF_FLOAT; - - case T_DOUBLE: - return SIZEOF_DOUBLE; - - case T_STRUCT: - case T_UNION: - return T->A.S->V.S.Size; - - case T_ARRAY: - if (T->A.L == UNSPECIFIED) { - /* Array with unspecified size */ - return 0; - } else { - return T->A.U * SizeOf (T + 1); - } - - case T_ENUM: - /* Incomplete enum type */ - return 0; - - default: - - Internal ("Unknown type in SizeOf: %04lX", T->C); - return 0; - - } -} - - - -unsigned PSizeOf (const Type* T) -/* Compute size of pointer object. */ -{ - /* We are expecting a pointer expression */ - CHECK (IsClassPtr (T)); - - /* Skip the pointer or array token itself */ - return SizeOf (T + 1); -} - - - -unsigned CheckedSizeOf (const Type* T) -/* Return the size of a data type. If the size is zero, emit an error and -** return some valid size instead (so the rest of the compiler doesn't have -** to work with invalid sizes). -*/ -{ - unsigned Size = SizeOf (T); - if (Size == 0) { - if (HasUnknownSize (T + 1)) { - Error ("Size of type '%s' is unknown", GetFullTypeName (T)); - } else { - Error ("Size of type '%s' is 0", GetFullTypeName (T)); - } - Size = SIZEOF_CHAR; /* Don't return zero */ - } - return Size; -} - - - -unsigned CheckedPSizeOf (const Type* T) -/* Return the size of a data type that is pointed to by a pointer. If the -** size is zero, emit an error and return some valid size instead (so the -** rest of the compiler doesn't have to work with invalid sizes). -*/ -{ - unsigned Size = PSizeOf (T); - if (Size == 0) { - if (HasUnknownSize (T + 1)) { - Error ("Pointer to type '%s' of unknown size", GetFullTypeName (T + 1)); - } else { - Error ("Pointer to type '%s' of 0 size", GetFullTypeName (T + 1)); - } - Size = SIZEOF_CHAR; /* Don't return zero */ - } - return Size; -} - - - -unsigned TypeOf (const Type* T) -/* Get the code generator base type of the object */ -{ - unsigned NewType; - - switch (GetUnderlyingTypeCode (T)) { - - case T_SCHAR: - return CF_CHAR; - - case T_UCHAR: - return CF_CHAR | CF_UNSIGNED; - - case T_SHORT: - case T_INT: - return CF_INT; - - case T_USHORT: - case T_UINT: - case T_PTR: - case T_ARRAY: - return CF_INT | CF_UNSIGNED; - - case T_LONG: - return CF_LONG; - - case T_ULONG: - return CF_LONG | CF_UNSIGNED; - - case T_FLOAT: - case T_DOUBLE: - /* These two are identical in the backend */ - return CF_FLOAT; - - case T_FUNC: - /* Treat this as a function pointer */ - return CF_INT | CF_UNSIGNED; - - case T_STRUCT: - case T_UNION: - NewType = TypeOfBySize (SizeOf (T)); - if (NewType != CF_NONE) { - return NewType; - } - /* Address of ... */ - return CF_INT | CF_UNSIGNED; - - case T_VOID: - case T_ENUM: - /* Incomplete enum type */ - Error ("Incomplete type '%s'", GetFullTypeName (T)); - return CF_INT; - - default: - Error ("Illegal type %04lX", T->C); - return CF_INT; - } -} - - - -unsigned FuncTypeOf (const Type* T) -/* Get the code generator flag for calling the function */ -{ - if (GetUnderlyingTypeCode (T) == T_FUNC) { - return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; - } else { - Error ("Illegal function type %04lX", T->C); - return 0; - } -} - - - -const Type* Indirect (const Type* T) -/* Do one indirection for the given type, that is, return the type where the -** given type points to. -*/ -{ - /* We are expecting a pointer expression */ - CHECK (IsClassPtr (T)); - - /* Skip the pointer or array token itself */ - return T + 1; -} - - - -Type* IndirectModifiable (Type* T) -/* Do one indirection for the given type, that is, return the type where the -** given type points to. -*/ -{ - /* We are expecting a pointer expression */ - CHECK (IsClassPtr (T)); - - /* Skip the pointer or array token itself */ - return T + 1; -} - - - -Type* NewPointerTo (const Type* T) -/* Return a type string that is "pointer to T". The type string is allocated -** on the heap and may be freed after use. -*/ -{ - /* Get the size of the type string including the terminator */ - unsigned Size = TypeLen (T) + 1; - - /* Allocate the new type string */ - Type* P = TypeAlloc (Size + 1); - - /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); - memcpy (P+1, T, Size * sizeof (Type)); - - /* ...and return it */ - return P; -} - - - -const Type* AddressOf (const Type* T) -/* Return a type string that is "address of T". The type string is allocated -** on the heap and may be freed after use. -*/ -{ - /* Get the size of the type string including the terminator */ - unsigned Size = TypeLen (T) + 1; - - /* Allocate the new type string */ - Type* P = TypeAlloc (Size + 1); - - /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; - memcpy (P+1, T, Size * sizeof (Type)); - - /* ...and return it */ - return P; -} - - - -Type* ArrayToPtr (const Type* T) -/* Convert an array to a pointer to it's first element */ -{ - /* Return pointer to first element */ - return NewPointerTo (GetElementType (T)); -} - - - -const Type* PtrConversion (const Type* T) -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. Otherwise -** return T. -*/ -{ - if (IsTypeFunc (T)) { - return AddressOf (T); - } else if (IsTypeArray (T)) { - return AddressOf (GetElementType (T)); - } else { - return T; - } -} - - - -const Type* StdConversion (const Type* T) -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. If the -** type is an integer, do integeral promotion. Otherwise return T. -*/ -{ - if (IsTypeFunc (T)) { - return AddressOf (T); - } else if (IsTypeArray (T)) { - return AddressOf (GetElementType (T)); - } else if (IsClassInt (T)) { - return IntPromotion (T); - } else { - return T; - } -} - - - -const Type* IntPromotion (const Type* T) -/* Apply the integer promotions to T and return the result. The returned type -** string may be T if there is no need to change it. -*/ -{ - /* We must have an int to apply int promotions */ - PRECONDITION (IsClassInt (T)); - - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 - ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or - ** an object that has enumeration type, may be used in an expression wherever an int or - ** unsigned int may be used. If an int can represent all values of the original type, - ** the value is converted to an int; otherwise it is converted to an unsigned int. - ** These are called the integral promotions. - */ - - if (IsTypeBitField (T)) { - /* As we now support long bit-fields, we need modified rules for them: - ** - If an int can represent all values of the bit-field, the bit-field is converted - ** to an int; - ** - Otherwise, if an unsigned int can represent all values of the bit-field, the - ** bit-field is converted to an unsigned int; - ** - Otherwise, the bit-field will have its declared integer type. - ** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's. - */ - if (T->A.B.Width > INT_BITS) { - return IsSignUnsigned (T) ? type_ulong : type_long; - } - return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; - } else if (IsTypeChar (T)) { - /* An integer can represent all values from either signed or unsigned char, so convert - ** chars to int. - */ - return type_int; - } else if (IsTypeShort (T)) { - /* An integer cannot represent all values from unsigned short, so convert unsigned short - ** to unsigned int. - */ - return IsSignUnsigned (T) ? type_uint : type_int; - } else if (!IsIncompleteESUType (T)) { - /* The type is a complete type not smaller than int, so leave it alone. */ - return T; - } else { - /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. - ** Assume int to avoid further errors. - */ - return type_int; - } -} - - - -const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) -/* Perform the usual arithmetic conversions for binary operators. */ -{ - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 - ** Many binary operators that expect operands of arithmetic type cause conversions and yield - ** result types in a similar way. The purpose is to yield a common type, which is also the type - ** of the result. This pattern is called the usual arithmetic conversions. - */ - - /* There are additional rules for floating point types that we don't bother with, since - ** floating point types are not (yet) supported. - ** The integral promotions are performed on both operands. - */ - lhst = IntPromotion (lhst); - rhst = IntPromotion (rhst); - - /* If either operand has type unsigned long int, the other operand is converted to - ** unsigned long int. - */ - if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || - (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { - return type_ulong; - } - - /* Otherwise, if one operand has type long int and the other has type unsigned int, - ** if a long int can represent all values of an unsigned int, the operand of type unsigned int - ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, - ** both operands are converted to unsigned long int. - */ - if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || - (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { - /* long can represent all unsigneds, so we are in the first sub-case. */ - return type_long; - } - - /* Otherwise, if either operand has type long int, the other operand is converted to long int. - */ - if (IsTypeLong (lhst) || IsTypeLong (rhst)) { - return type_long; - } - - /* Otherwise, if either operand has type unsigned int, the other operand is converted to - ** unsigned int. - */ - if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || - (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { - return type_uint; - } - - /* Otherwise, both operands have type int. */ - CHECK (IsTypeInt (lhst)); - CHECK (IsSignSigned (lhst)); - CHECK (IsTypeInt (rhst)); - CHECK (IsSignSigned (rhst)); - return type_int; -} - - - -const Type* SignedType (const Type* T) -/* Get signed counterpart of the integral type */ -{ - switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { - case T_TYPE_CHAR: - return type_schar; - - case T_TYPE_INT: - case T_TYPE_SHORT: - return type_int; - - case T_TYPE_LONG: - return type_long; - - default: - Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); - return T; - } -} - - - -const Type* UnsignedType (const Type* T) -/* Get unsigned counterpart of the integral type */ -{ - switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { - case T_TYPE_CHAR: - return type_uchar; - - case T_TYPE_INT: - case T_TYPE_SHORT: - return type_uint; - - case T_TYPE_LONG: - return type_ulong; - - default: - Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); - return T; - } -} - - - -Type* NewBitFieldType (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. -*/ -{ - Type* P; - - /* The type specifier must be integeral */ - CHECK (IsClassInt (T)); - - /* Allocate the new type string */ - P = TypeAlloc (3); - - /* Create the return type... */ - P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD; - P[0].C |= (T[0].C & T_QUAL_ADDRSIZE); - P[0].A.B.Offs = BitOffs; - P[0].A.B.Width = BitWidth; - - /* Get the declaration type */ - memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1])); - - /* Get done... */ - P[2].C = T_END; - - /* ...and return it */ - return P; -} - - - -int IsTypeFragBitField (const Type* T) -/* Return true if this is a bit-field that shares byte space with other fields */ -{ - return IsTypeBitField (T) && - (T->A.B.Offs != 0 || T->A.B.Width != CHAR_BITS * SizeOf (T)); -} - - - -int IsClassObject (const Type* T) -/* Return true if this is a fully described object type */ -{ - return !IsTypeFunc (T) && !IsClassIncomplete (T); -} - - - -int IsClassIncomplete (const Type* T) -/* Return true if this is an object type lacking size info */ -{ - if (IsTypeArray (T)) { - return GetElementCount (T) == UNSPECIFIED || IsClassIncomplete (T + 1); - } - return IsTypeVoid (T) || IsIncompleteESUType (T); -} - - - -int IsClassArithmetic (const Type* T) -/* Return true if this is an integer or real floating type */ -{ - return IsClassInt (T) || IsClassFloat (T); -} - - - -int IsClassBasic (const Type* T) -/* Return true if this is a char, integer or floating type */ -{ - return IsClassChar (T) || IsClassInt (T) || IsClassFloat (T); -} - - - -int IsClassScalar (const Type* T) -/* Return true if this is an arithmetic or pointer type */ -{ - return IsClassArithmetic (T) || IsTypePtr (T); -} - - - -int IsClassDerived (const Type* T) -/* Return true if this is an array, struct, union, function or pointer type */ -{ - return IsTypeArray (T) || IsClassStruct (T) || IsClassFunc (T) || IsTypePtr (T); -} - - - -int IsClassAggregate (const Type* T) -/* Return true if this is an array or struct type */ -{ - return IsTypeArray (T) || IsTypeStruct (T); -} - - - -int IsRelationType (const Type* T) -/* Return true if this is an arithmetic, array or pointer type */ -{ - return IsClassArithmetic (T) || IsClassPtr (T); -} - - - -int IsCastType (const Type* T) -/* Return true if this type can be used for casting */ -{ - return IsClassScalar (T) || IsTypeVoid (T); -} - - - -int IsESUType (const Type* T) -/* Return true if this is an enum/struct/union type */ -{ - return IsClassStruct (T) || IsTypeEnum (T); -} - - - -int IsIncompleteESUType (const Type* T) -/* Return true if this is an incomplete ESU type */ -{ - SymEntry* Sym = GetSymType (T); - - return Sym != 0 && !SymIsDef (Sym); -} - - - -int IsEmptiableObjectType (const Type* T) -/* Return true if this is a struct/union/void type that can have zero size */ -{ - return IsClassStruct (T) || IsTypeVoid (T); -} - - - -int HasUnknownSize (const Type* T) -/* Return true if this is an incomplete ESU type or an array of unknown size */ -{ - if (IsTypeArray (T)) { - return GetElementCount (T) == UNSPECIFIED || HasUnknownSize (T + 1); - } - return IsIncompleteESUType (T); -} - - - -int IsVariadicFunc (const Type* T) -/* Return true if this is a function type or pointer to function type with -** variable parameter list. -** Check fails if the type is not a function or a pointer to function. -*/ -{ - return (GetFuncDesc (T)->Flags & FD_VARIADIC) != 0; -} - - - -int IsFastcallFunc (const Type* T) -/* Return true if this is a function type or pointer to function type by -** __fastcall__ calling convention. -** Check fails if the type is not a function or a pointer to function. -*/ -{ - if (UnqualifiedType (T->C) == T_PTR) { - /* Pointer to function */ - ++T; - } - return !IsVariadicFunc (T) && (AutoCDecl ? IsQualFastcall (T) : !IsQualCDecl (T)); -} - - - -FuncDesc* GetFuncDesc (const Type* T) -/* Get the FuncDesc pointer from a function or pointer-to-function type */ -{ - if (UnqualifiedType (T->C) == T_PTR) { - /* Pointer to function */ - ++T; - } - - /* Be sure it's a function type */ - CHECK (IsClassFunc (T)); - - /* Get the function descriptor from the type attributes */ - return T->A.F; -} - - - -void SetFuncDesc (Type* T, FuncDesc* F) -/* Set the FuncDesc pointer in a function or pointer-to-function type */ -{ - if (UnqualifiedType (T->C) == T_PTR) { - /* Pointer to function */ - ++T; - } - - /* Be sure it's a function type */ - CHECK (IsClassFunc (T)); - - /* Set the function descriptor */ - T->A.F = F; -} - - - -const Type* GetFuncReturn (const Type* T) -/* Return a pointer to the return type of a function or pointer-to-function type */ -{ - if (UnqualifiedType (T->C) == T_PTR) { - /* Pointer to function */ - ++T; - } - - /* Be sure it's a function type */ - CHECK (IsClassFunc (T)); - - /* Return a pointer to the return type */ - return T + 1; -} - - - -Type* GetFuncReturnModifiable (Type* T) -/* Return a non-const pointer to the return type of a function or pointer-to-function type */ -{ - if (UnqualifiedType (T->C) == T_PTR) { - /* Pointer to function */ - ++T; - } - - /* Be sure it's a function type */ - CHECK (IsClassFunc (T)); - - /* Return a pointer to the return type */ - return T + 1; -} - - - -const FuncDesc* GetFuncDefinitionDesc (const Type* T) -/* Get the function descriptor of the function definition */ -{ - const FuncDesc* D; - - /* Be sure it's a function type */ - CHECK (IsClassFunc (T)); - - D = GetFuncDesc (T); - return D->FuncDef != 0 ? D->FuncDef : D; -} - - - -long GetElementCount (const Type* T) -/* Get the element count of the array specified in T (which must be of -** array type). -*/ -{ - CHECK (IsTypeArray (T)); - return T->A.L; -} - - - -void SetElementCount (Type* T, long Count) -/* Set the element count of the array specified in T (which must be of -** array type). -*/ -{ - CHECK (IsTypeArray (T)); - T->A.L = Count; -} - - - -const Type* GetElementType (const Type* T) -/* Return the element type of the given array type. */ -{ - CHECK (IsTypeArray (T)); - return T + 1; -} - - - -const Type* GetBaseElementType (const Type* T) -/* Return the base element type of a given type. If T is not an array, this -** will return. Otherwise it will return the base element type, which means -** the element type that is not an array. -*/ -{ - while (IsTypeArray (T)) { - ++T; - } - return T; -} - - - -struct SymEntry* GetESUSymEntry (const Type* T) -/* Return a SymEntry pointer from an enum/struct/union type */ -{ - /* Only enums, structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T) || IsTypeEnum (T)); - - /* Return the attribute */ - return T->A.S; -} - - - -void SetESUSymEntry (Type* T, struct SymEntry* S) -/* Set the SymEntry pointer for an enum/struct/union type */ -{ - /* Only enums, structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T) || IsTypeEnum (T)); - - /* Set the attribute */ - T->A.S = S; -} - - - -TypeCode AddrSizeQualifier (unsigned AddrSize) -/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ -{ - switch (AddrSize) { - - case ADDR_SIZE_ABS: - return T_QUAL_NEAR; - - case ADDR_SIZE_FAR: - return T_QUAL_FAR; - - default: - Error ("Invalid address size"); - return T_QUAL_NEAR; - - } -} - - - -int TypeHasAttr (const Type* T) -/* Return true if the given type has attribute data */ -{ - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); -} - - - void PrintType (FILE* F, const Type* T) /* Print fulle name of the type */ { @@ -1570,7 +1560,7 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) if (SymIsRegVar (Param)) { SB_AppendStr (&ParamList, "register "); } - if (!HasAnonName (Param)) { + if (!SymHasAnonName (Param)) { SB_AppendStr (&Buf, Param->Name); } SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); @@ -1604,7 +1594,7 @@ void PrintFuncSig (FILE* F, const char* Name, const Type* T) SB_Done (&ParamList); /* Complete with the return type */ - GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + GetFullTypeNameWestEast (&West, &East, GetFuncReturnType (T)); SB_Append (&West, &East); SB_Terminate (&West); diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index e8ba7b6c0..eebd3abd8 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -71,24 +71,24 @@ struct SymEntry; enum { T_END = 0x000000, - /* Basic types */ - T_TYPE_NONE = 0x000000, - T_TYPE_CHAR = 0x000001, - T_TYPE_SHORT = 0x000002, - T_TYPE_INT = 0x000003, - T_TYPE_LONG = 0x000004, - T_TYPE_LONGLONG = 0x000005, - T_TYPE_ENUM = 0x000008, - T_TYPE_BITFIELD = 0x000009, - T_TYPE_FLOAT = 0x00000A, - T_TYPE_DOUBLE = 0x00000B, - T_TYPE_VOID = 0x000010, - T_TYPE_STRUCT = 0x000011, - T_TYPE_UNION = 0x000012, - T_TYPE_ARRAY = 0x000018, - T_TYPE_PTR = 0x000019, - T_TYPE_FUNC = 0x00001A, - T_MASK_TYPE = 0x00001F, + /* Basic type ranks */ + T_RANK_NONE = 0x000000, + T_RANK_CHAR = 0x000001, + T_RANK_SHORT = 0x000002, + T_RANK_INT = 0x000003, + T_RANK_LONG = 0x000004, + T_RANK_LONGLONG = 0x000005, + T_RANK_ENUM = 0x000008, + T_RANK_BITFIELD = 0x000009, + T_RANK_FLOAT = 0x00000A, + T_RANK_DOUBLE = 0x00000B, + T_RANK_VOID = 0x000010, + T_RANK_STRUCT = 0x000011, + T_RANK_UNION = 0x000012, + T_RANK_ARRAY = 0x000018, + T_RANK_PTR = 0x000019, + T_RANK_FUNC = 0x00001A, + T_MASK_RANK = 0x00001F, /* Type classes */ T_CLASS_NONE = 0x000000, @@ -129,28 +129,28 @@ enum { T_MASK_QUAL = 0x7F0000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, - T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, - T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, - T_SHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, - T_USHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, - T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, - T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, - T_LONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, - T_ULONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, - T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, - T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, - T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, - T_SBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, - T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, - T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, - T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, - T_STRUCT = T_TYPE_STRUCT | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, - T_UNION = T_TYPE_UNION | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, - T_ARRAY = T_TYPE_ARRAY | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, - T_PTR = T_TYPE_PTR | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, - T_FUNC = T_TYPE_FUNC | T_CLASS_FUNC | T_SIGN_NONE | T_SIZE_NONE, + T_CHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, + T_SCHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, + T_UCHAR = T_RANK_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, + T_SHORT = T_RANK_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, + T_USHORT = T_RANK_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, + T_INT = T_RANK_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, + T_UINT = T_RANK_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, + T_LONG = T_RANK_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, + T_ULONG = T_RANK_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, + T_LONGLONG = T_RANK_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, + T_ULONGLONG = T_RANK_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, + T_ENUM = T_RANK_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, + T_SBITFIELD = T_RANK_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_UBITFIELD = T_RANK_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_FLOAT = T_RANK_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, + T_DOUBLE = T_RANK_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, + T_VOID = T_RANK_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, + T_STRUCT = T_RANK_STRUCT | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, + T_UNION = T_RANK_UNION | T_CLASS_STRUCT | T_SIGN_NONE | T_SIZE_NONE, + T_ARRAY = T_RANK_ARRAY | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, + T_PTR = T_RANK_PTR | T_CLASS_PTR | T_SIGN_NONE | T_SIZE_NONE, + T_FUNC = T_RANK_FUNC | T_CLASS_FUNC | T_SIGN_NONE | T_SIZE_NONE, /* More types for convenience */ T_C_CHAR = T_CHAR | T_QUAL_CONST, @@ -239,23 +239,6 @@ extern const Type type_c_void_p[]; -const char* GetBasicTypeName (const Type* T); -/* Return a const name string of the basic type. -** Return "type" for unknown basic types. -*/ - -const char* GetFullTypeName (const Type* T); -/* Return the full name string of the given type */ - -struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T); -/* Return the full name string of the given type */ - -int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual); -/* Return the names of the qualifiers of the type. -** Qualifiers to be ignored can be specified with the IgnoredQual flags. -** Return the count of added qualifier names. -*/ - unsigned TypeLen (const Type* T); /* Return the length of the type string */ @@ -273,17 +256,26 @@ Type* TypeAlloc (unsigned Len); void TypeFree (Type* T); /* Free a type string */ +#if defined(HAVE_INLINE) +INLINE void CopyTypeAttr (const Type* Src, Type* Dest) +/* Copy attribute data from Src to Dest */ +{ + Dest->A = Src->A; +} +#else +# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) +#endif + + + +/*****************************************************************************/ +/* Type info extraction */ +/*****************************************************************************/ + + + int SignExtendChar (int C); -/* Do correct sign extension of a character */ - -Type* GetCharArrayType (unsigned Len); -/* Return the type for a char array of the given length */ - -Type* GetImplicitFuncType (void); -/* Return a type string for an inplicitly declared function */ - -const Type* GetStructReplacementType (const Type* SType); -/* Get a replacement type for passing a struct/union in the primary register */ +/* Do correct sign extension of a character to an int */ long GetIntegerTypeMin (const Type* Type); /* Get the smallest possible value of the integer type. @@ -295,70 +287,166 @@ unsigned long GetIntegerTypeMax (const Type* Type); ** The type must have a known size. */ -#if defined(HAVE_INLINE) -INLINE TypeCode UnqualifiedType (TypeCode T) -/* Return the unqalified type code */ -{ - return (T & ~T_MASK_QUAL); -} -#else -# define UnqualifiedType(T) ((T) & ~T_MASK_QUAL) -#endif - -const Type* GetUnderlyingType (const Type* Type); -/* Get the underlying type of an enum or other integer class type */ - -TypeCode GetUnderlyingTypeCode (const Type* Type); -/* Get the type code of the unqualified underlying type of TCode. -** Return TCode if it is not scalar. -*/ - -const Type* GetBitFieldChunkType (const Type* Type); -/* Get the type needed to operate on the byte chunk containing the bit-field */ +unsigned BitSizeOf (const Type* T); +/* Return the size (in bit-width) of a data type */ unsigned SizeOf (const Type* T); -/* Compute size of object represented by type array. */ +/* Compute size (in bytes) of object represented by type array */ unsigned PSizeOf (const Type* T); -/* Compute size of pointer object. */ +/* Compute size (in bytes) of pointee object */ + +unsigned CheckedBitSizeOf (const Type* T); +/* Return the size (in bit-width) of a data type. If the size is zero, emit an +** error and return some valid size instead (so the rest of the compiler +** doesn't have to work with invalid sizes). +*/ unsigned CheckedSizeOf (const Type* T); -/* Return the size of a data type. If the size is zero, emit an error and -** return some valid size instead (so the rest of the compiler doesn't have -** to work with invalid sizes). +/* Return the size (in bytes) of a data type. If the size is zero, emit an +** error and return some valid size instead (so the rest of the compiler +** doesn't have to work with invalid sizes). */ + unsigned CheckedPSizeOf (const Type* T); -/* Return the size of a data type that is pointed to by a pointer. If the -** size is zero, emit an error and return some valid size instead (so the -** rest of the compiler doesn't have to work with invalid sizes). +/* Return the size (in bytes) of a data type that is pointed to by a pointer. +** If the size is zero, emit an error and return some valid size instead (so +** the rest of the compiler doesn't have to work with invalid sizes). */ -unsigned TypeOf (const Type* T); -/* Get the code generator base type of the object */ +#if defined(HAVE_INLINE) +INLINE TypeCode GetQualifier (const Type* T) +/* Get the qualifier from the given type. This doesn't have a "raw" version +** since an underlying type can never be qualified. +*/ +{ + return (T->C & T_MASK_QUAL); +} +#else +# define GetQualifier(T) ((T)->C & T_MASK_QUAL) +#endif -unsigned FuncTypeOf (const Type* T); -/* Get the code generator flag for calling the function */ - -const Type* Indirect (const Type* T); -/* Do one indirection for the given type, that is, return the type where the -** given type points to. +TypeCode GetUnqualTypeCode (const Type* Type); +/* Get the type code of the unqualified underlying type of Type. +** Return GetUnqualRawTypeCode (Type) if Type is not scalar. */ -Type* IndirectModifiable (Type* T); -/* Do one indirection for the given type, that is, return the type where the -** given type points to. +#if defined(HAVE_INLINE) +INLINE TypeCode GetUnqualRawTypeCode (const Type* T) +/* Return the unqualified raw type code */ +{ + return (T->C & ~T_MASK_QUAL); +} +#else +# define GetUnqualRawTypeCode(T) ((T)->C & ~T_MASK_QUAL) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetTypeClass (const Type* T) +/* Get the class of a type. This doesn't have a "raw" version since an +** underlying type can never be in a different class. */ +{ + return (T->C & T_MASK_CLASS); +} +#else +# define GetTypeClass(T) ((T)->C & T_MASK_CLASS) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetTypeRank (const Type* T) +/* Get the type rank of a type */ +{ + return (GetUnqualTypeCode (T) & T_MASK_RANK); +} +#else +# define GetTypeRank(T) (GetUnqualTypeCode (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); +} +#else +# define GetSignedness(T) (GetUnqualTypeCode (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); +} +#else +# define GetSizeModifier(T) (GetUnqualTypeCode (T) & T_MASK_SIZE) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetRawTypeRank (const Type* T) +/* Get the raw type rank of a type */ +{ + return (T->C & T_MASK_RANK); +} +#else +# define GetRawTypeRank(T) ((T)->C & T_MASK_RANK) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetRawSignedness (const Type* T) +/* Get the raw signedness of a type */ +{ + return (T->C & T_MASK_SIGN); +} +#else +# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetRawSizeModifier (const Type* T) +/* Get the raw size modifier of a type */ +{ + return (T->C & T_MASK_SIZE); +} +#else +# define GetRawSizeModifier(T) ((T)->C & T_MASK_SIZE) +#endif + + + +/*****************************************************************************/ +/* Type manipulation */ +/*****************************************************************************/ + + + +Type* GetImplicitFuncType (void); +/* Return a type string for an implicitly declared function */ + +Type* GetCharArrayType (unsigned Len); +/* Return the type for a char array of the given length */ Type* NewPointerTo (const Type* T); /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. */ +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. +*/ + const Type* AddressOf (const Type* T); /* Return a type string that is "address of T". The type string is allocated ** on the heap and may be freed after use. */ +const Type* Indirect (const Type* T); +/* Do one indirection for the given type, that is, return the type where the +** given type points to. +*/ + Type* ArrayToPtr (const Type* T); /* Convert an array to a pointer to it's first element */ @@ -382,177 +470,151 @@ const Type* IntPromotion (const Type* T); const Type* ArithmeticConvert (const Type* lhst, const Type* rhst); /* Perform the usual arithmetic conversions for binary operators. */ -const Type* SignedType (const Type* T); +const Type* GetSignedType (const Type* T); /* Get signed counterpart of the integral type */ -const Type* UnsignedType (const Type* T); +const Type* GetUnsignedType (const Type* T); /* Get unsigned counterpart of the integral type */ -Type* NewBitFieldType (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. -*/ +const Type* GetUnderlyingType (const Type* Type); +/* Get the underlying type of an enum or other integer class type */ + +const Type* GetStructReplacementType (const Type* SType); +/* Get a replacement type for passing a struct/union by value in the primary */ + +const Type* GetBitFieldChunkType (const Type* Type); +/* Get the type needed to operate on the byte chunk containing the bit-field */ + + + +/*****************************************************************************/ +/* Type Predicates */ +/*****************************************************************************/ + + #if defined(HAVE_INLINE) -INLINE TypeCode GetRawType (const Type* T) -/* Get the raw type */ +INLINE int IsRankChar (const Type* T) +/* Return true if this is a character type */ { - return (T->C & T_MASK_TYPE); + return (GetTypeRank (T) == T_RANK_CHAR); } #else -# define GetRawType(T) ((T)->C & T_MASK_TYPE) +# define IsRankChar(T) (GetTypeRank (T) == T_RANK_CHAR) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeChar (const Type* T) -/* Return true if this is a char type */ -{ - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); -} -#else -# define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsTypeShort (const Type* T) +INLINE int IsRankShort (const Type* T) /* Return true if this is a short type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT); + return (GetTypeRank (T) == T_RANK_SHORT); } #else -# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT) +# define IsRankShort(T) (GetTypeRank (T) == T_RANK_SHORT) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeInt (const Type* T) +INLINE int IsRankInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT); + return (GetTypeRank (T) == T_RANK_INT); } #else -# define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT) +# define IsRankInt(T) (GetTypeRank (T) == T_RANK_INT) #endif #if defined(HAVE_INLINE) -INLINE int IsTypeLong (const Type* T) +INLINE int IsRankLong (const Type* T) /* Return true if this is a long int type (signed or unsigned) */ { - return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); + return (GetTypeRank (T) == T_RANK_LONG); } #else -# define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG) +# define IsRankLong(T) (GetTypeRank (T) == T_RANK_LONG) #endif #if defined(HAVE_INLINE) -INLINE int IsISOChar (const Type* T) -/* Return true if this is a narrow character type (without signed/unsigned) */ -{ - return (UnqualifiedType (T->C) == T_CHAR); -} -#else -# define IsISOChar(T) (UnqualifiedType ((T)->C) == T_CHAR) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsClassChar (const Type* T) -/* Return true if this is a narrow character type (including signed/unsigned). -** For now this is the same as IsRawTypeChar(T). +INLINE int IsDeclTypeChar (const Type* T) +/* Return true if this is declared as a char type (without signed/unsigned). +** This function is to exclude enums whose underlying type is char. */ { - return (GetRawType (T) == T_TYPE_CHAR); + return (GetUnqualRawTypeCode (T) == T_CHAR); } #else -# define IsClassChar(T) (GetRawType (T) == T_TYPE_CHAR) +# define IsDeclTypeChar(T) (GetUnqualRawTypeCode (T) == T_CHAR) #endif #if defined(HAVE_INLINE) -INLINE int IsRawTypeChar (const Type* T) -/* Return true if this is a char raw type (including signed/unsigned) */ +INLINE int IsDeclRankChar (const Type* T) +/* Return true if this is declared as a character type (including signed/unsigned). +** This function is to exclude enums whose underlying types are character types. +*/ { - return (GetRawType (T) == T_TYPE_CHAR); + return (GetRawTypeRank (T) == T_RANK_CHAR); } #else -# define IsRawTypeChar(T) (GetRawType (T) == T_TYPE_CHAR) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsRawTypeInt (const Type* T) -/* Return true if this is an int raw type (signed or unsigned) */ -{ - return (GetRawType (T) == T_TYPE_INT); -} -#else -# define IsRawTypeInt(T) (GetRawType (T) == T_TYPE_INT) -#endif - -#if defined(HAVE_INLINE) -INLINE int IsRawTypeLong (const Type* T) -/* Return true if this is a long raw type (signed or unsigned) */ -{ - return (GetRawType (T) == T_TYPE_LONG); -} -#else -# define IsRawTypeLong(T) (GetRawType (T) == T_TYPE_LONG) +# define IsDeclRankChar(T) (GetRawTypeRank (T) == T_RANK_CHAR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFloat (const Type* T) /* Return true if this is a float type */ { - return (GetRawType (T) == T_TYPE_FLOAT); + return (GetRawTypeRank (T) == T_RANK_FLOAT); } #else -# define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT) +# define IsTypeFloat(T) (GetRawTypeRank (T) == T_RANK_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeDouble (const Type* T) /* Return true if this is a double type */ { - return (GetRawType (T) == T_TYPE_DOUBLE); + return (GetRawTypeRank (T) == T_RANK_DOUBLE); } #else -# define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE) +# define IsTypeDouble(T) (GetRawTypeRank (T) == T_RANK_DOUBLE) #endif #if defined(HAVE_INLINE) INLINE int IsTypePtr (const Type* T) /* Return true if this is a pointer type */ { - return (GetRawType (T) == T_TYPE_PTR); + return (GetRawTypeRank (T) == T_RANK_PTR); } #else -# define IsTypePtr(T) (GetRawType (T) == T_TYPE_PTR) +# define IsTypePtr(T) (GetRawTypeRank (T) == T_RANK_PTR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeEnum (const Type* T) /* Return true if this is an enum type */ { - return (GetRawType (T) == T_TYPE_ENUM); + return (GetRawTypeRank (T) == T_RANK_ENUM); } #else -# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) +# define IsTypeEnum(T) (GetRawTypeRank (T) == T_RANK_ENUM) #endif #if defined(HAVE_INLINE) INLINE int IsTypeSignedBitField (const Type* T) /* Return true if this is a signed bit-field */ { - return (UnqualifiedType (T->C) == T_SBITFIELD); + return (GetUnqualRawTypeCode (T) == T_SBITFIELD); } #else -# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == T_SBITFIELD) +# define IsTypeSignedBitField(T) (GetUnqualRawTypeCode (T) == T_SBITFIELD) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnsignedBitField (const Type* T) /* Return true if this is an unsigned bit-field */ { - return (UnqualifiedType (T->C) == T_UBITFIELD); + return (GetUnqualRawTypeCode (T) == T_UBITFIELD); } #else -# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +# define IsTypeUnsignedBitField(T) (GetUnqualRawTypeCode (T) == T_UBITFIELD) #endif #if defined(HAVE_INLINE) @@ -572,55 +634,55 @@ int IsTypeFragBitField (const Type* T); INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ { - return (GetRawType (T) == T_TYPE_STRUCT); + return (GetRawTypeRank (T) == T_RANK_STRUCT); } #else -# define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT) +# define IsTypeStruct(T) (GetRawTypeRank (T) == T_RANK_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnion (const Type* T) /* Return true if this is a union type */ { - return (GetRawType (T) == T_TYPE_UNION); + return (GetRawTypeRank (T) == T_RANK_UNION); } #else -# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION) +# define IsTypeUnion(T) (GetRawTypeRank (T) == T_RANK_UNION) #endif #if defined(HAVE_INLINE) INLINE int IsTypeArray (const Type* T) /* Return true if this is an array type */ { - return (GetRawType (T) == T_TYPE_ARRAY); + return (GetRawTypeRank (T) == T_RANK_ARRAY); } #else -# define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY) +# define IsTypeArray(T) (GetRawTypeRank (T) == T_RANK_ARRAY) #endif #if defined(HAVE_INLINE) INLINE int IsTypeVoid (const Type* T) /* Return true if this is a void type */ { - return (GetRawType (T) == T_TYPE_VOID); + return (GetRawTypeRank (T) == T_RANK_VOID); } #else -# define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID) +# define IsTypeVoid(T) (GetRawTypeRank (T) == T_RANK_VOID) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFunc (const Type* T) -/* Return true if this is a function class */ +/* Return true if this is a function type */ { - return (GetRawType (T) == T_TYPE_FUNC); + return (GetRawTypeRank (T) == T_RANK_FUNC); } #else -# define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC) +# define IsTypeFunc(T) (GetRawTypeRank (T) == T_RANK_FUNC) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFuncPtr (const Type* T) -/* Return true if this is a function pointer */ +/* Return true if this is a function pointer type */ { return (IsTypePtr (T) && IsTypeFunc (T+1)); } @@ -628,85 +690,75 @@ INLINE int IsTypeFuncPtr (const Type* T) # define IsTypeFuncPtr(T) (IsTypePtr (T) && IsTypeFunc (T+1)) #endif -#if defined(HAVE_INLINE) -INLINE TypeCode GetClass (const Type* T) -/* Get the class of a type string */ -{ - return (T->C & T_MASK_CLASS); -} -#else -# define GetClass(T) ((T)->C & T_MASK_CLASS) -#endif - #if defined(HAVE_INLINE) INLINE int IsClassInt (const Type* T) /* Return true if this is an integer type */ { - return (GetClass (T) == T_CLASS_INT); + return (GetTypeClass (T) == T_CLASS_INT); } #else -# define IsClassInt(T) (GetClass (T) == T_CLASS_INT) +# define IsClassInt(T) (GetTypeClass (T) == T_CLASS_INT) #endif #if defined(HAVE_INLINE) INLINE int IsClassFloat (const Type* T) -/* Return true if this is a float type */ +/* Return true if this is a floating type */ { - return (GetClass (T) == T_CLASS_FLOAT); + return (GetTypeClass (T) == T_CLASS_FLOAT); } #else -# define IsClassFloat(T) (GetClass (T) == T_CLASS_FLOAT) +# define IsClassFloat(T) (GetTypeClass (T) == T_CLASS_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsClassPtr (const Type* T) -/* Return true if this is a pointer type */ +/* Return true if this is a pointer or array type */ { - return (GetClass (T) == T_CLASS_PTR); + return (GetTypeClass (T) == T_CLASS_PTR); } #else -# define IsClassPtr(T) (GetClass (T) == T_CLASS_PTR) +# define IsClassPtr(T) (GetTypeClass (T) == T_CLASS_PTR) #endif #if defined(HAVE_INLINE) INLINE int IsClassStruct (const Type* T) /* Return true if this is a struct or union type */ { - return (GetClass (T) == T_CLASS_STRUCT); + return (GetTypeClass (T) == T_CLASS_STRUCT); } #else -# define IsClassStruct(T) (GetClass (T) == T_CLASS_STRUCT) +# define IsClassStruct(T) (GetTypeClass (T) == T_CLASS_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsClassFunc (const Type* T) /* Return true if this is a function type */ { - return (GetClass (T) == T_CLASS_FUNC); + return (GetTypeClass (T) == T_CLASS_FUNC); } #else -# define IsClassFunc(T) (GetClass (T) == T_CLASS_FUNC) +# define IsClassFunc(T) (GetTypeClass (T) == T_CLASS_FUNC) #endif -int IsClassObject (const Type* T); +int IsObjectType (const Type* T); /* Return true if this is a fully described object type */ -int IsClassIncomplete (const Type* T); +int IsIncompleteType (const Type* T); /* Return true if this is an object type lacking size info */ -int IsClassArithmetic (const Type* T); -/* Return true if this is an integer or real floating type */ +int IsArithmeticType (const Type* T); +/* Return true if this is an integer or floating type */ -int IsClassBasic (const Type* T); +int IsBasicType (const Type* T); /* Return true if this is a char, integer or floating type */ -int IsClassScalar (const Type* T); +int IsScalarType (const Type* T); /* Return true if this is an arithmetic or pointer type */ -int IsClassDerived (const Type* T); +int IsDerivedType (const Type* T); /* Return true if this is an array, struct, union, function or pointer type */ -int IsClassAggregate (const Type* T); +int IsAggregateType (const Type* T); /* Return true if this is an array or struct type */ int IsRelationType (const Type* T); @@ -727,25 +779,8 @@ int IsEmptiableObjectType (const Type* T); int HasUnknownSize (const Type* T); /* Return true if this is an incomplete ESU type or an array of unknown size */ -#if defined(HAVE_INLINE) -INLINE TypeCode GetRawSignedness (const Type* T) -/* Get the raw signedness of a type */ -{ - return ((T)->C & T_MASK_SIGN); -} -#else -# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN) -#endif - -#if defined(HAVE_INLINE) -INLINE TypeCode GetSignedness (const Type* T) -/* Get the signedness of a type */ -{ - return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); -} -#else -# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) -#endif +int TypeHasAttrData (const Type* T); +/* Return true if the given type has attribute data */ #if defined(HAVE_INLINE) INLINE int IsRawSignUnsigned (const Type* T) @@ -787,35 +822,13 @@ INLINE int IsSignSigned (const Type* T) # define IsSignSigned(T) (GetSignedness (T) == T_SIGN_SIGNED) #endif -#if defined(HAVE_INLINE) -INLINE TypeCode GetRawSizeModifier (const Type* T) -/* Get the size modifier of a raw type */ -{ - return (T->C & T_MASK_SIZE); -} -#else -# define GetRawSizeModifier(T) ((T)->C & T_MASK_SIZE) -#endif -#if defined(HAVE_INLINE) -INLINE TypeCode GetSizeModifier (const Type* T) -/* Get the size modifier of a type */ -{ - return (GetUnderlyingTypeCode (T) & T_MASK_SIZE); -} -#else -# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE) -#endif -#if defined(HAVE_INLINE) -INLINE TypeCode GetQualifier (const Type* T) -/* Get the qualifier from the given type string */ -{ - return (T->C & T_MASK_QUAL); -} -#else -# define GetQualifier(T) ((T)->C & T_MASK_QUAL) -#endif +/*****************************************************************************/ +/* Qualifier helpers */ +/*****************************************************************************/ + + #if defined(HAVE_INLINE) INLINE int IsQualConst (const Type* T) @@ -897,58 +910,6 @@ INLINE int IsQualCConv (const Type* T) # define IsQualCConv(T) (((T)->C & T_QUAL_CCONV) != 0) #endif -int IsVariadicFunc (const Type* T) attribute ((const)); -/* Return true if this is a function type or pointer to function type with -** variable parameter list. -** Check fails if the type is not a function or a pointer to function. -*/ - -int IsFastcallFunc (const Type* T) attribute ((const)); -/* Return true if this is a function type or pointer to function type with -** __fastcall__ calling convention. -** Check fails if the type is not a function or a pointer to function. -*/ - -FuncDesc* GetFuncDesc (const Type* T) attribute ((const)); -/* Get the FuncDesc pointer from a function or pointer-to-function type */ - -void SetFuncDesc (Type* T, FuncDesc* F); -/* Set the FuncDesc pointer in a function or pointer-to-function type */ - -const Type* GetFuncReturn (const Type* T) attribute ((const)); -/* Return a pointer to the return type of a function or pointer-to-function type */ - -Type* GetFuncReturnModifiable (Type* T) attribute ((const)); -/* Return a non-const pointer to the return type of a function or pointer-to-function type */ - -const FuncDesc* GetFuncDefinitionDesc (const Type* T) attribute ((const)); -/* Get the function descriptor of the function definition */ - -long GetElementCount (const Type* T); -/* Get the element count of the array specified in T (which must be of -** array type). -*/ - -void SetElementCount (Type* T, long Count); -/* Set the element count of the array specified in T (which must be of -** array type). -*/ - -const Type* GetElementType (const Type* T); -/* Return the element type of the given array type. */ - -const Type* GetBaseElementType (const Type* T); -/* Return the base element type of a given type. If T is not an array, this -** will return. Otherwise it will return the base element type, which means -** the element type that is not an array. -*/ - -struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const)); -/* Return a SymEntry pointer from an enum/struct/union type */ - -void SetESUSymEntry (Type* T, struct SymEntry* S); -/* Set the SymEntry pointer for an enum/struct/union type */ - TypeCode AddrSizeQualifier (unsigned AddrSize); /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ @@ -972,18 +933,111 @@ INLINE TypeCode DataAddrSizeQualifier (void) # define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize)) #endif -int TypeHasAttr (const Type* T); -/* Return true if the given type has attribute data */ -#if defined(HAVE_INLINE) -INLINE void CopyTypeAttr (const Type* Src, Type* Dest) -/* Copy attribute data from Src to Dest */ -{ - Dest->A = Src->A; -} -#else -# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) -#endif + +/*****************************************************************************/ +/* Function type helpers */ +/*****************************************************************************/ + + + +int IsVariadicFunc (const Type* T) attribute ((const)); +/* Return true if this is a function type or pointer to function type with +** variable parameter list. +** Check fails if the type is not a function or a pointer to function. +*/ + +int IsFastcallFunc (const Type* T) attribute ((const)); +/* Return true if this is a function type or pointer to function type with +** __fastcall__ calling convention. +** Check fails if the type is not a function or a pointer to function. +*/ + +FuncDesc* GetFuncDesc (const Type* T) attribute ((const)); +/* Get the FuncDesc pointer from a function or pointer-to-function type */ + +void SetFuncDesc (Type* T, FuncDesc* F); +/* Set the FuncDesc pointer in a function or pointer-to-function type */ + +const Type* GetFuncReturnType (const Type* T) attribute ((const)); +/* Return a pointer to the return type of a function or pointer-to-function type */ + +Type* GetFuncReturnTypeModifiable (Type* T) attribute ((const)); +/* Return a non-const pointer to the return type of a function or pointer-to-function type */ + +const FuncDesc* GetFuncDefinitionDesc (const Type* T) attribute ((const)); +/* Get the function descriptor of the function definition */ + + + +/*****************************************************************************/ +/* Array type helpers */ +/*****************************************************************************/ + + + +long GetElementCount (const Type* T); +/* Get the element count of the array specified in T (which must be of +** array type). +*/ + +void SetElementCount (Type* T, long Count); +/* Set the element count of the array specified in T (which must be of +** array type). +*/ + +const Type* GetElementType (const Type* T); +/* Return the element type of the given array type */ + +Type* GetElementTypeModifiable (Type* T); +/* Return the element type of the given array type */ + +const Type* GetBaseElementType (const Type* T); +/* Return the base element type of a given type. If T is not an array, this +** will return. Otherwise it will return the base element type, which means +** the element type that is not an array. +*/ + + + +/*****************************************************************************/ +/* ESU types helpers */ +/*****************************************************************************/ + + + +struct SymEntry* GetESUTagSym (const Type* T) attribute ((const)); +/* Get the tag symbol entry of the enum/struct/union type. +** Return 0 if it is not an enum/struct/union. +*/ + +void SetESUTagSym (Type* T, struct SymEntry* S); +/* Set the tag symbol entry of the enum/struct/union type */ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +const char* GetBasicTypeName (const Type* T); +/* Return a const name string of the basic type. +** Return "type" for unknown basic types. +*/ + +const char* GetFullTypeName (const Type* T); +/* Return the full name string of the given type */ + +struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T); +/* Return the full name string of the given type */ + +int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual); +/* Return the names of the qualifiers of the type. +** Qualifiers to be ignored can be specified with the IgnoredQual flags. +** Return the count of added qualifier names. +*/ void PrintType (FILE* F, const Type* T); /* Print fulle name of the type */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 7cc7444b6..59eb555c4 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -72,8 +72,7 @@ -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, - int* SignednessSpecified); +static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified); /* Parse a type specifier */ @@ -84,6 +83,75 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, +static unsigned ParseOneStorageClass (void) +/* Parse and return a storage class specifier */ +{ + unsigned StorageClass = 0; + + /* Check the storage class given */ + switch (CurTok.Tok) { + + case TOK_EXTERN: + StorageClass = SC_EXTERN | SC_STATIC; + NextToken (); + break; + + case TOK_STATIC: + StorageClass = SC_STATIC; + NextToken (); + break; + + case TOK_REGISTER: + StorageClass = SC_REGISTER | SC_STATIC; + NextToken (); + break; + + case TOK_AUTO: + StorageClass = SC_AUTO; + NextToken (); + break; + + case TOK_TYPEDEF: + StorageClass = SC_TYPEDEF; + NextToken (); + break; + + default: + break; + } + + return StorageClass; +} + + + +static int ParseStorageClass (DeclSpec* D) +/* Parse storage class specifiers. Return true if a specifier is read even if +** it was duplicated or disallowed. */ +{ + /* Check the storage class given */ + unsigned StorageClass = ParseOneStorageClass (); + + if (StorageClass == 0) { + return 0; + } + + while (StorageClass != 0) { + if (D->StorageClass == 0) { + D->StorageClass = StorageClass; + } else if (D->StorageClass == StorageClass) { + Warning ("Duplicate storage class specifier"); + } else { + Error ("Conflicting storage class specifier"); + } + StorageClass = ParseOneStorageClass (); + } + + return 1; +} + + + static void DuplicateQualifier (const char* Name) /* Print an error message */ { @@ -92,9 +160,9 @@ static void DuplicateQualifier (const char* Name) -static TypeCode OptionalQualifiers (TypeCode Allowed) +static TypeCode OptionalQualifiers (TypeCode Qualifiers, TypeCode Allowed) /* Read type qualifiers if we have any. Allowed specifies the allowed -** qualifiers. +** qualifiers. Return any read qualifiers even if they caused errors. */ { /* We start without any qualifiers */ @@ -107,7 +175,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_CONST: if (Allowed & T_QUAL_CONST) { - if (Q & T_QUAL_CONST) { + if (Qualifiers & T_QUAL_CONST) { DuplicateQualifier ("const"); } Q |= T_QUAL_CONST; @@ -118,7 +186,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_VOLATILE: if (Allowed & T_QUAL_VOLATILE) { - if (Q & T_QUAL_VOLATILE) { + if (Qualifiers & T_QUAL_VOLATILE) { DuplicateQualifier ("volatile"); } Q |= T_QUAL_VOLATILE; @@ -129,7 +197,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_RESTRICT: if (Allowed & T_QUAL_RESTRICT) { - if (Q & T_QUAL_RESTRICT) { + if (Qualifiers & T_QUAL_RESTRICT) { DuplicateQualifier ("restrict"); } Q |= T_QUAL_RESTRICT; @@ -140,7 +208,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_NEAR: if (Allowed & T_QUAL_NEAR) { - if (Q & T_QUAL_NEAR) { + if (Qualifiers & T_QUAL_NEAR) { DuplicateQualifier ("near"); } Q |= T_QUAL_NEAR; @@ -151,7 +219,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_FAR: if (Allowed & T_QUAL_FAR) { - if (Q & T_QUAL_FAR) { + if (Qualifiers & T_QUAL_FAR) { DuplicateQualifier ("far"); } Q |= T_QUAL_FAR; @@ -162,7 +230,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_FASTCALL: if (Allowed & T_QUAL_FASTCALL) { - if (Q & T_QUAL_FASTCALL) { + if (Qualifiers & T_QUAL_FASTCALL) { DuplicateQualifier ("fastcall"); } Q |= T_QUAL_FASTCALL; @@ -173,7 +241,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_CDECL: if (Allowed & T_QUAL_CDECL) { - if (Q & T_QUAL_CDECL) { + if (Qualifiers & T_QUAL_CDECL) { DuplicateQualifier ("cdecl"); } Q |= T_QUAL_CDECL; @@ -187,13 +255,16 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) } + /* Combine with newly read qualifiers */ + Qualifiers |= Q; + /* Skip the token */ NextToken (); } Done: /* We cannot have more than one address size far qualifier */ - switch (Q & T_QUAL_ADDRSIZE) { + switch (Qualifiers & T_QUAL_ADDRSIZE) { case T_QUAL_NONE: case T_QUAL_NEAR: @@ -202,11 +273,11 @@ Done: default: Error ("Cannot specify more than one address size qualifier"); - Q &= ~T_QUAL_ADDRSIZE; + Qualifiers &= ~T_QUAL_ADDRSIZE; } /* We cannot have more than one calling convention specifier */ - switch (Q & T_QUAL_CCONV) { + switch (Qualifiers & T_QUAL_CCONV) { case T_QUAL_NONE: case T_QUAL_FASTCALL: @@ -215,15 +286,41 @@ Done: default: Error ("Cannot specify more than one calling convention qualifier"); - Q &= ~T_QUAL_CCONV; + Qualifiers &= ~T_QUAL_CCONV; } - /* Return the qualifiers read */ + /* Return any qualifiers just read */ return Q; } +static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t TSFlags) +/* Read storage specifiers and/or type qualifiers if we have any. Storage class +** specifiers require the corresponding typespec_t flag set to be allowed, and +** only const and volatile type qualifiers are allowed under any circumstance. +** Read storage class specifiers are output in *Spec and type qualifiers are +** output in *Qualifiers with error checking. +*/ +{ + TypeCode Q = T_QUAL_NONE; + int Continue; + + do { + /* There may be type qualifiers *before* any storage class specifiers */ + Q = OptionalQualifiers (*Qualifiers, T_QUAL_CONST | T_QUAL_VOLATILE); + *Qualifiers |= Q; + + /* Parse storage class specifiers anyway then check */ + Continue = ParseStorageClass (Spec); + if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) { + Error ("Unexpected storage class specified"); + } + } while (Continue || Q != T_QUAL_NONE); +} + + + static void OptionalInt (void) /* Eat an optional "int" token */ { @@ -259,8 +356,8 @@ void InitDeclSpec (DeclSpec* D) -static void InitDeclaration (Declaration* D) -/* Initialize the Declaration struct for use */ +static void InitDeclarator (Declarator* D) +/* Initialize the Declarator struct for use */ { D->Ident[0] = '\0'; D->Type[0].C = T_END; @@ -270,7 +367,7 @@ static void InitDeclaration (Declaration* D) -static void NeedTypeSpace (Declaration* D, unsigned Count) +static void NeedTypeSpace (Declarator* D, unsigned Count) /* Check if there is enough space for Count type specifiers within D */ { if (D->Index + Count >= MAXTYPELEN) { @@ -284,8 +381,8 @@ static void NeedTypeSpace (Declaration* D, unsigned Count) -static void AddTypeToDeclaration (Declaration* D, TypeCode T) -/* Add a type specifier to the type of a declaration */ +static void AddTypeCodeToDeclarator (Declarator* D, TypeCode T) +/* Add a type specifier to the type of a declarator */ { NeedTypeSpace (D, 1); D->Type[D->Index++].C = T; @@ -309,7 +406,7 @@ static void FixQualifiers (Type* DataType) if (IsTypeArray (T)) { /* Extract any type qualifiers */ Q |= GetQualifier (T); - T->C = UnqualifiedType (T->C); + T->C = GetUnqualRawTypeCode (T); } else { /* Add extracted type qualifiers here */ T->C |= Q; @@ -396,48 +493,6 @@ static void FixQualifiers (Type* DataType) -static unsigned ParseOneStorageClass (void) -/* Parse and return a storage class */ -{ - unsigned StorageClass = 0; - - /* Check the storage class given */ - switch (CurTok.Tok) { - - case TOK_EXTERN: - StorageClass = SC_EXTERN | SC_STATIC; - NextToken (); - break; - - case TOK_STATIC: - StorageClass = SC_STATIC; - NextToken (); - break; - - case TOK_REGISTER: - StorageClass = SC_REGISTER | SC_STATIC; - NextToken (); - break; - - case TOK_AUTO: - StorageClass = SC_AUTO; - NextToken (); - break; - - case TOK_TYPEDEF: - StorageClass = SC_TYPEDEF; - NextToken (); - break; - - default: - break; - } - - return StorageClass; -} - - - static void CheckArrayElementType (Type* DataType) /* Check if data type consists of arrays of incomplete element types */ { @@ -469,51 +524,24 @@ static void CheckArrayElementType (Type* DataType) -static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) -/* Parse a storage class */ -{ - /* Assume we're using an explicit storage class */ - D->Flags &= ~DS_DEF_STORAGE; - - /* Check the storage class given */ - D->StorageClass = ParseOneStorageClass (); - if (D->StorageClass == 0) { - /* No storage class given, use default */ - D->Flags |= DS_DEF_STORAGE; - D->StorageClass = DefStorage; - } else { - unsigned StorageClass = ParseOneStorageClass (); - while (StorageClass != 0) { - if (D->StorageClass == StorageClass) { - Warning ("Duplicate storage class specifier"); - } else { - Error ("Conflicting storage class specifier"); - } - StorageClass = ParseOneStorageClass (); - } - } -} - - - -static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags) -/* Handle an enum, struct or union forward decl */ +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* Entry = FindTagSym (Name); - if (Entry == 0) { + SymEntry* TagEntry = FindTagSym (Name); + if (TagEntry == 0) { if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { - Entry = AddStructSym (Name, Flags, 0, 0, DSFlags); + TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags); } else { - Entry = AddEnumSym (Name, Flags, 0, 0, DSFlags); + TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags); } - } else if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { + } 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 Entry; + return TagEntry; } @@ -556,8 +584,8 @@ static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) -static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) -/* Process an enum declaration */ +static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) +/* Process an enum specifier */ { SymTable* FieldTab; long EnumVal; @@ -574,7 +602,7 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward definition */ - return ESUForwardDecl (Name, SC_ENUM, DSFlags); + return ForwardESU (Name, SC_ENUM, DSFlags); } /* Add a forward declaration for the enum tag in the current lexical level */ @@ -611,20 +639,20 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) } else { - /* Defaulted with the same signedness as the previous member's */ + /* Defaulted with the same signedness as the previous member's */ IsSigned = IsSignSigned (MemberType) && (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); - /* Enumerate. Signed integer overflow is UB but unsigned integers - ** are guaranteed to wrap around. - */ - EnumVal = (long)((unsigned long)EnumVal + 1UL); + /* Enumerate by adding one to the previous value */ + EnumVal = (long)(((unsigned long)EnumVal + 1UL) & 0xFFFFFFFFUL); - if (UnqualifiedType (MemberType->C) == T_ULONG && EnumVal == 0) { - /* Warn on 'unsigned long' overflow in enumeration */ - Warning ("Enumerator '%s' overflows the range of '%s'", - Ident, - GetBasicTypeName (type_ulong)); + 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; @@ -657,11 +685,12 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) /* Warn if the incremented value exceeds the range of the previous ** type. */ - if (IsIncremented && - EnumVal >= 0 && - NewType->C != UnqualifiedType (MemberType->C)) { + 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) is of type '%s'", + Warning ("Enumerator '%s' (value = %lu) implies type '%s'", Ident, (unsigned long)EnumVal, GetBasicTypeName (NewType)); @@ -725,7 +754,7 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) -static int ParseFieldWidth (Declaration* D) +static int ParseFieldWidth (Declarator* D) /* Parse an optional field width. Returns -1 if no field width is specified, ** otherwise the width of the field. */ @@ -803,21 +832,19 @@ static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) -static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon) +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 pointer to the symbol table entry of the anon struct */ - SymEntry* Entry = GetESUSymEntry (D->Type); - /* Get the symbol table containing the fields. If it is empty, there has ** been an error before, so bail out. */ - SymTable* Tab = Entry->V.S.SymTab; + SymTable* Tab = GetESUTagSym (D->Type)->V.S.SymTab; if (Tab == 0) { /* Incomplete definition - has been flagged before */ return 0; @@ -826,24 +853,24 @@ static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon) /* Get a pointer to the list of symbols. Then walk the list adding copies ** of the embedded struct to the current level. */ - Entry = Tab->SymHead; - while (Entry) { + Field = Tab->SymHead; + while (Field) { /* Enter an alias of this symbol */ - if (!IsAnonName (Entry->Name)) { - Alias = AddLocalSym (Entry->Name, Entry->Type, SC_STRUCTFIELD|SC_ALIAS, 0); - Alias->V.A.Field = Entry; - Alias->V.A.Offs = Anon->V.Offs + Entry->V.Offs; + 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 (Entry->Attr == 0); + CHECK (Field->Attr == 0); /* Next entry */ - Entry = Entry->NextSym; + Field = Field->NextSym; } /* Return the count of created aliases */ @@ -852,8 +879,8 @@ static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon) -static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) -/* Parse a union declaration. */ +static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) +/* Parse a union specifier */ { unsigned UnionSize; @@ -861,14 +888,14 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; SymEntry* UnionTagEntry; - SymEntry* Entry; + SymEntry* Field; unsigned Flags = 0; unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration */ - return ESUForwardDecl (Name, SC_UNION, DSFlags); + return ForwardESU (Name, SC_UNION, DSFlags); } /* Add a forward declaration for the union tag in the current lexical level */ @@ -883,19 +910,26 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) EnterStructLevel (); /* Parse union fields */ - UnionSize = 0; + 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, -1, T_QUAL_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { - Declaration Decl; + Declarator Decl; /* Get type and name of the struct field */ ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); @@ -925,7 +959,7 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) } /* Check for incomplete types including 'void' */ - if (IsClassIncomplete (Decl.Type)) { + if (IsIncompleteType (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -945,17 +979,17 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, SignednessSpecified); } else if (Decl.Ident[0] != '\0') { - Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); if (IsAnonName (Decl.Ident)) { - Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Entry); + 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* Sym = GetSymType (Decl.Type); - if (Sym && SymHasFlexibleArrayMember (Sym)) { - Entry->Flags |= SC_HAVEFAM; + SymEntry* TagEntry = GetESUTagSym (Decl.Type); + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; } } @@ -992,8 +1026,8 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) -/* Parse a struct declaration. */ +static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) +/* Parse a struct specifier */ { unsigned StructSize; @@ -1002,14 +1036,14 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; SymEntry* StructTagEntry; - SymEntry* Entry; + SymEntry* Field; unsigned Flags = 0; unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration */ - return ESUForwardDecl (Name, SC_STRUCT, DSFlags); + return ForwardESU (Name, SC_STRUCT, DSFlags); } /* Add a forward declaration for the struct tag in the current lexical level */ @@ -1031,6 +1065,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) /* Get the type of the entry */ DeclSpec Spec; + int SignednessSpecified = 0; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -1038,14 +1073,13 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) continue; } - int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { - Declaration Decl; + Declarator Decl; /* If we had a flexible array member before, no other fields can ** follow. @@ -1125,7 +1159,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) } /* Check for incomplete types including 'void' */ - if (IsClassIncomplete (Decl.Type)) { + if (IsIncompleteType (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -1147,17 +1181,17 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; } else if (Decl.Ident[0] != '\0') { - Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); if (IsAnonName (Decl.Ident)) { - Entry->V.A.ANumber = StructTagEntry->V.S.ACount++; - AliasAnonStructFields (&Decl, Entry); + 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* Sym = GetSymType (Decl.Type); - if (Sym && SymHasFlexibleArrayMember (Sym)) { - Entry->Flags |= SC_HAVEFAM; + SymEntry* TagEntry = GetESUTagSym (Decl.Type); + if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { + Field->Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM; } } @@ -1206,15 +1240,15 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, - int* SignednessSpecified) +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* Entry; + SymEntry* TagEntry; + TypeCode Qualifiers = T_QUAL_NONE; if (SignednessSpecified != NULL) { *SignednessSpecified = 0; @@ -1223,8 +1257,8 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Assume we have an explicit type */ D->Flags &= ~DS_DEF_TYPE; - /* Read type qualifiers if we have any */ - Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); + /* Read storage specifiers and/or type qualifiers if we have any */ + OptionalSpecifiers (D, &Qualifiers, TSFlags); /* Look at the data type */ switch (CurTok.Tok) { @@ -1384,10 +1418,10 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Declare the union in the current scope */ - Entry = ParseUnionDecl (Ident, &D->Flags); + TagEntry = ParseUnionSpec (Ident, &D->Flags); /* Encode the union entry into the type */ D->Type[0].C = T_UNION; - SetESUSymEntry (D->Type, Entry); + SetESUTagSym (D->Type, TagEntry); D->Type[1].C = T_END; break; @@ -1403,10 +1437,10 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Declare the struct in the current scope */ - Entry = ParseStructDecl (Ident, &D->Flags); + TagEntry = ParseStructSpec (Ident, &D->Flags); /* Encode the struct entry into the type */ D->Type[0].C = T_STRUCT; - SetESUSymEntry (D->Type, Entry); + SetESUTagSym (D->Type, TagEntry); D->Type[1].C = T_END; break; @@ -1419,17 +1453,16 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, } else { if (CurTok.Tok != TOK_LCURLY) { Error ("Identifier expected"); - } else { - AnonName (Ident, "enum"); } + AnonName (Ident, "enum"); } /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - Entry = ParseEnumDecl (Ident, &D->Flags); + TagEntry = ParseEnumSpec (Ident, &D->Flags); /* Encode the enum entry into the type */ D->Type[0].C |= T_ENUM; - SetESUSymEntry (D->Type, Entry); + 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. @@ -1442,11 +1475,11 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, case TOK_IDENT: /* This could be a label */ if (NextTok.Tok != TOK_COLON || GetLexicalLevel () == LEX_LEVEL_STRUCT) { - Entry = FindSym (CurTok.Ident); - if (Entry && SymIsTypeDef (Entry)) { + TagEntry = FindSym (CurTok.Ident); + if (TagEntry && SymIsTypeDef (TagEntry)) { /* It's a typedef */ NextToken (); - TypeCopy (D->Type, Entry->Type); + 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. @@ -1471,20 +1504,21 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* FALL THROUGH */ default: - if (Default < 0) { + 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 = (TypeCode) Default; + D->Type[0].C = T_INT; D->Type[1].C = T_END; } break; } - /* There may also be qualifiers *after* the initial type */ - D->Type[0].C |= (Qualifiers | OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE)); + /* There may also be specifiers/qualifiers *after* the initial type */ + OptionalSpecifiers (D, &Qualifiers, TSFlags); + D->Type[0].C |= Qualifiers; } @@ -1564,7 +1598,7 @@ static void ParseOldStyleParamList (FuncDesc* F) DeclSpec Spec; /* Read the declaration specifier */ - ParseDeclSpec (&Spec, SC_AUTO, T_INT); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. @@ -1577,7 +1611,7 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Parse a comma separated variable list */ while (1) { - Declaration Decl; + Declarator Decl; /* Read the parameter */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); @@ -1591,19 +1625,19 @@ static void ParseOldStyleParamList (FuncDesc* F) if (Decl.Ident[0] != '\0') { /* We have a name given. Search for the symbol */ - SymEntry* Sym = FindLocalSym (Decl.Ident); - if (Sym) { + SymEntry* Param = FindLocalSym (Decl.Ident); + if (Param) { /* Check if we already changed the type for this ** parameter */ - if (Sym->Flags & SC_DEFTYPE) { + if (Param->Flags & SC_DEFTYPE) { /* Found it, change the default type to the one given */ - ChangeSymType (Sym, ParamTypeCvt (Decl.Type)); + SymChangeType (Param, ParamTypeCvt (Decl.Type)); /* Reset the "default type" flag */ - Sym->Flags &= ~SC_DEFTYPE; + Param->Flags &= ~SC_DEFTYPE; } else { /* Type has already been changed */ - Error ("Redefinition for parameter '%s'", Sym->Name); + Error ("Redefinition for parameter '%s'", Param->Name); } } else { Error ("Unknown identifier: '%s'", Decl.Ident); @@ -1632,8 +1666,8 @@ static void ParseAnsiParamList (FuncDesc* F) while (CurTok.Tok != TOK_RPAREN) { DeclSpec Spec; - Declaration Decl; - SymEntry* Sym; + Declarator Decl; + SymEntry* Param; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { @@ -1643,7 +1677,7 @@ static void ParseAnsiParamList (FuncDesc* F) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, SC_AUTO, T_INT); + 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) { @@ -1681,10 +1715,10 @@ static void ParseAnsiParamList (FuncDesc* F) ParseAttribute (&Decl); /* Create a symbol table entry */ - Sym = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0); + Param = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0); /* Add attributes if we have any */ - SymUseAttr (Sym, &Decl); + SymUseAttr (Param, &Decl); /* If the parameter is a struct or union, emit a warning */ if (IsClassStruct (Decl.Type)) { @@ -1713,7 +1747,7 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) -/* Parse the argument list of a function. */ +/* Parse the argument list of a function with the enclosing parentheses */ { SymEntry* Sym; SymEntry* WrappedCall; @@ -1725,6 +1759,9 @@ static FuncDesc* ParseFuncDecl (void) /* 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) */ @@ -1782,16 +1819,16 @@ static FuncDesc* ParseFuncDecl (void) -static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) -/* Recursively process declarators. Build a type array in reverse order. */ +static void DirectDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +/* Recursively process direct declarators. Build a type array in reverse order. */ { - /* Read optional function or pointer qualifiers. They 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. + /* 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. */ - TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_CCONV); + TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { @@ -1800,19 +1837,19 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) NextToken (); /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (T_QUAL_CVR); + Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); /* Parse the type that the pointer points to */ - Declarator (Spec, D, Mode); + DirectDecl (Spec, D, Mode); /* Add the type */ - AddTypeToDeclaration (D, T_PTR | Qualifiers); + AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); return; } if (CurTok.Tok == TOK_LPAREN) { NextToken (); - Declarator (Spec, D, Mode); + DirectDecl (Spec, D, Mode); ConsumeRParen (); } else { /* Things depend on Mode now: @@ -1832,7 +1869,13 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) NextToken (); } 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 }; + Error ("Identifier expected"); + + /* Try some smart error recovery */ + SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); } D->Ident[0] = '\0'; } @@ -1841,14 +1884,11 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { - /* Function declaration */ + /* Function declarator */ FuncDesc* F; SymEntry* PrevEntry; - /* Skip the opening paren */ - NextToken (); - - /* Parse the function declaration */ + /* Parse the function declarator */ F = ParseFuncDecl (); /* We cannot specify fastcall for variadic functions */ @@ -1877,7 +1917,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) Qualifiers = T_QUAL_NONE; } else { - /* Array declaration. */ + /* Array declarator */ long Size = UNSPECIFIED; /* We cannot have any qualifiers for an array */ @@ -1941,11 +1981,11 @@ Type* ParseType (Type* T) /* Parse a complete type specification */ { DeclSpec Spec; - Declaration Decl; + Declarator Decl; /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL); /* Parse additional declarators */ ParseDecl (&Spec, &Decl, DM_NO_IDENT); @@ -1959,19 +1999,19 @@ Type* ParseType (Type* T) -void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) -/* Parse a variable, type or function declaration */ +void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode) +/* Parse a variable, type or function declarator */ { /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; - /* Initialize the Declaration struct */ - InitDeclaration (D); + /* Initialize the Declarator struct */ + InitDeclarator (D); - /* Get additional declarators and the identifier */ - Declarator (Spec, D, Mode); + /* Get additional derivation of the declarator and the identifier */ + DirectDecl (Spec, D, Mode); - /* Add the base type. */ + /* Add the base type */ NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */ TypeCopy (D->Type + D->Index, Spec->Type); @@ -1989,14 +2029,14 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) D->StorageClass |= SC_FUNC; } - /* Parse attributes for this declaration */ + /* 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 = GetFuncReturnModifiable (D->Type); + Type* RetType = GetFuncReturnTypeModifiable (D->Type); /* Functions may not return functions or arrays */ if (IsTypeFunc (RetType)) { @@ -2008,13 +2048,13 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* The return type must not be qualified */ if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - if (GetRawType (RetType) == T_TYPE_VOID) { + 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 = UnqualifiedType (RetType[0].C); + RetType[0].C = GetUnqualRawTypeCode (RetType); } } @@ -2070,22 +2110,23 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) -void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType) +void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage) /* Parse a declaration specification */ { - TypeCode Qualifiers; - /* Initialize the DeclSpec struct */ InitDeclSpec (D); - /* There may be qualifiers *before* the storage class specifier */ - Qualifiers = OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); + /* Assume we're using an explicit storage class */ + D->Flags &= ~DS_DEF_STORAGE; - /* Now get the storage class specifier for this declaration */ - ParseStorageClass (D, DefStorage); + /* Parse the type specifiers */ + ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); - /* Parse the type specifiers passing any initial type qualifiers */ - ParseTypeSpec (D, DefType, Qualifiers, NULL); + /* If no explicit storage class is given, use the default */ + if (D->StorageClass == 0) { + D->Flags |= DS_DEF_STORAGE; + D->StorageClass = DefStorage; + } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 2b8b36f1c..ee9e1fc63 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -53,6 +53,22 @@ +/* Type specifier parser flags */ +typedef enum typespec_t typespec_t; +enum typespec_t { + TS_NONE = 0x00, + + /* Default type */ + TS_MASK_DEFAULT_TYPE = 0x03, + TS_DEFAULT_TYPE_NONE = 0x00, /* No default type */ + TS_DEFAULT_TYPE_INT = 0x01, /* Good old int */ + 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 */ +}; + /* Masks for the Flags field in DeclSpec */ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ #define DS_DEF_TYPE 0x0002U /* Default type used */ @@ -70,8 +86,8 @@ struct DeclSpec { }; /* Result of ParseDecl */ -typedef struct Declaration Declaration; -struct Declaration { +typedef struct Declarator Declarator; +struct Declarator { unsigned StorageClass; /* A set of SC_xxx flags */ Type Type[MAXTYPELEN]; /* The type */ ident Ident; /* The identifier, if any*/ @@ -102,10 +118,10 @@ void InitDeclSpec (DeclSpec* D); Type* ParseType (Type* Type); /* Parse a complete type specification */ -void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode); -/* Parse a variable, type or function declaration */ +void ParseDecl (const DeclSpec* Spec, Declarator* D, declmode_t Mode); +/* Parse a variable, type or function declarator */ -void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType); +void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage); /* Parse a declaration specification */ void CheckEmptyDecl (const DeclSpec* D); diff --git a/src/cc65/declattr.c b/src/cc65/declattr.c index 37048e69b..eec89552e 100644 --- a/src/cc65/declattr.c +++ b/src/cc65/declattr.c @@ -2,7 +2,7 @@ /* */ /* declattr.c */ /* */ -/* Declaration attributes */ +/* Declarator attributes */ /* */ /* */ /* */ @@ -55,8 +55,8 @@ /* Forwards for attribute handlers */ -static void NoReturnAttr (Declaration* D); -static void UnusedAttr (Declaration* D); +static void NoReturnAttr (Declarator* D); +static void UnusedAttr (Declarator* D); @@ -64,7 +64,7 @@ static void UnusedAttr (Declaration* D); typedef struct AttrDesc AttrDesc; struct AttrDesc { const char Name[15]; - void (*Handler) (Declaration*); + void (*Handler) (Declarator*); }; static const AttrDesc AttrTable [] = { { "__noreturn__", NoReturnAttr }, @@ -141,8 +141,8 @@ static void ErrorSkip (void) -static void AddAttr (Declaration* D, DeclAttr* A) -/* Add an attribute to a declaration */ +static void AddAttr (Declarator* D, DeclAttr* A) +/* Add an attribute to a declarator */ { /* Allocate the list if necessary, the add the attribute */ if (D->Attributes == 0) { @@ -159,7 +159,7 @@ static void AddAttr (Declaration* D, DeclAttr* A) -static void NoReturnAttr (Declaration* D) +static void NoReturnAttr (Declarator* D) /* Parse the "noreturn" attribute */ { /* Add the noreturn attribute */ @@ -168,7 +168,7 @@ static void NoReturnAttr (Declaration* D) -static void UnusedAttr (Declaration* D) +static void UnusedAttr (Declarator* D) /* Parse the "unused" attribute */ { /* Add the noreturn attribute */ @@ -177,7 +177,7 @@ static void UnusedAttr (Declaration* D) -void ParseAttribute (Declaration* D) +void ParseAttribute (Declarator* D) /* Parse an additional __attribute__ modifier */ { /* Do we have an attribute? */ diff --git a/src/cc65/declattr.h b/src/cc65/declattr.h index 63669cee7..930cd71ff 100644 --- a/src/cc65/declattr.h +++ b/src/cc65/declattr.h @@ -2,7 +2,7 @@ /* */ /* declattr.h */ /* */ -/* Declaration attributes */ +/* Declarator attributes */ /* */ /* */ /* */ @@ -45,7 +45,7 @@ /* Forward */ -struct Declaration; +struct Declarator; /* Supported attribute types */ typedef enum { @@ -67,7 +67,7 @@ struct DeclAttr { -void ParseAttribute (struct Declaration* D); +void ParseAttribute (struct Declarator* D); /* Parse an additional __attribute__ modifier */ diff --git a/src/cc65/error.c b/src/cc65/error.c index f0e023969..39b067825 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -79,6 +79,7 @@ IntStack WarnUnusedLabel = INTSTACK(1); /* - unused labels */ IntStack WarnUnusedParam = INTSTACK(1); /* - unused parameters */ IntStack WarnUnusedVar = INTSTACK(1); /* - unused variables */ IntStack WarnUnusedFunc = INTSTACK(1); /* - unused functions */ +IntStack WarnConstOverflow = INTSTACK(0); /* - overflow conversion of numerical constants */ /* Map the name of a warning to the intstack that holds its state */ typedef struct WarnMapEntry WarnMapEntry; @@ -102,12 +103,43 @@ static WarnMapEntry WarnMap[] = { { &WarnUnusedLabel, "unused-label" }, { &WarnUnusedParam, "unused-param" }, { &WarnUnusedVar, "unused-var" }, + { &WarnConstOverflow, "const-overflow" }, }; Collection DiagnosticStrBufs; +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static const char* GetDiagnosticFileName (void) +/* Get the source file name where the diagnostic info refers to */ +{ + if (CurTok.LI) { + return GetInputName (CurTok.LI); + } else { + return GetCurrentFilename (); + } +} + + + +static unsigned GetDiagnosticLineNum (void) +/* Get the source line number where the diagnostic info refers to */ +{ + if (CurTok.LI) { + return GetInputLine (CurTok.LI); + } else { + return GetCurrentLineNum (); + } +} + + + /*****************************************************************************/ /* Handling of fatal errors */ /*****************************************************************************/ @@ -119,17 +151,7 @@ void Fatal (const char* Format, ...) { va_list ap; - const char* FileName; - unsigned LineNum; - if (CurTok.LI) { - FileName = GetInputName (CurTok.LI); - LineNum = GetInputLine (CurTok.LI); - } else { - FileName = GetCurrentFile (); - LineNum = GetCurrentLine (); - } - - fprintf (stderr, "%s:%u: Fatal: ", FileName, LineNum); + fprintf (stderr, "%s:%u: Fatal: ", GetDiagnosticFileName (), GetDiagnosticLineNum ()); va_start (ap, Format); vfprintf (stderr, Format, ap); @@ -145,22 +167,12 @@ void Fatal (const char* Format, ...) void Internal (const char* Format, ...) -/* Print a message about an internal compiler error and die. */ +/* Print a message about an internal compiler error and die */ { va_list ap; - const char* FileName; - unsigned LineNum; - if (CurTok.LI) { - FileName = GetInputName (CurTok.LI); - LineNum = GetInputLine (CurTok.LI); - } else { - FileName = GetCurrentFile (); - LineNum = GetCurrentLine (); - } - fprintf (stderr, "%s:%u: Internal compiler error:\n", - FileName, LineNum); + GetDiagnosticFileName (), GetDiagnosticLineNum ()); va_start (ap, Format); vfprintf (stderr, Format, ap); @@ -184,7 +196,7 @@ void Internal (const char* Format, ...) static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) -/* Print an error message - internal function*/ +/* Print an error message - internal function */ { fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); vfprintf (stderr, Msg, ap); @@ -206,7 +218,7 @@ void Error (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Format, ap); + IntError (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); va_end (ap); } @@ -224,11 +236,11 @@ void LIError (const LineInfo* LI, const char* Format, ...) void PPError (const char* Format, ...) -/* Print an error message. For use within the preprocessor. */ +/* Print an error message. For use within the preprocessor */ { va_list ap; va_start (ap, Format); - IntError (GetCurrentFile(), GetCurrentLine(), Format, ap); + IntError (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); va_end (ap); } @@ -241,7 +253,7 @@ void PPError (const char* Format, ...) static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) -/* Print warning message - internal function. */ +/* Print a warning message - internal function */ { if (IS_Get (&WarningsAreErrors)) { @@ -265,11 +277,11 @@ static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, void Warning (const char* Format, ...) -/* Print warning message. */ +/* Print a warning message */ { va_list ap; va_start (ap, Format); - IntWarning (GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Format, ap); + IntWarning (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); va_end (ap); } @@ -287,11 +299,11 @@ void LIWarning (const LineInfo* LI, const char* Format, ...) void PPWarning (const char* Format, ...) -/* Print warning message. For use within the preprocessor. */ +/* Print a warning message. For use within the preprocessor */ { va_list ap; va_start (ap, Format); - IntWarning (GetCurrentFile(), GetCurrentLine(), Format, ap); + IntWarning (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); va_end (ap); } @@ -326,6 +338,55 @@ void ListWarnings (FILE* F) +/*****************************************************************************/ +/* Handling of other infos */ +/*****************************************************************************/ + + + +static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +/* Print a note message - internal function */ +{ + fprintf (stderr, "%s:%u: Note: ", Filename, LineNo); + 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); + va_end (ap); +} + + + +void PPNote (const char* Format, ...) +/* Print a note message. For use within the preprocessor */ +{ + va_list ap; + va_start (ap, Format); + IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + va_end (ap); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ diff --git a/src/cc65/error.h b/src/cc65/error.h index c4420c434..83be8c782 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -76,6 +76,7 @@ extern IntStack WarnUnusedLabel; /* - unused labels */ extern IntStack WarnUnusedParam; /* - unused parameters */ extern IntStack WarnUnusedVar; /* - unused variables */ extern IntStack WarnUnusedFunc; /* - unused functions */ +extern IntStack WarnConstOverflow; /* - overflow conversion of numerical constants */ /* Forward */ struct StrBuf; @@ -92,7 +93,7 @@ void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2) /* Print a message about a fatal error and die */ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2))); -/* Print a message about an internal compiler error and die. */ +/* Print a message about an internal compiler error and die */ void Error (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print an error message */ @@ -101,16 +102,16 @@ void LIError (const LineInfo* LI, const char* Format, ...) attribute ((format (p /* Print an error message with the line info given explicitly */ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); -/* Print an error message. For use within the preprocessor. */ +/* Print an error message. For use within the preprocessor */ void Warning (const char* Format, ...) attribute ((format (printf, 1, 2))); -/* Print warning message. */ +/* Print a warning message */ void LIWarning (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); /* Print a warning message with the line info given explicitly */ void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2))); -/* Print warning message. For use within the preprocessor. */ +/* Print a warning message. For use within the preprocessor */ IntStack* FindWarning (const char* Name); /* Search for a warning in the WarnMap table and return a pointer to the @@ -120,6 +121,15 @@ IntStack* FindWarning (const char* Name); void ListWarnings (FILE* F); /* Print a list of warning types/names to the given file */ +void Note (const char* Format, ...) attribute ((format (printf, 1, 2))); +/* Print a note message */ + +void LINote (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3))); +/* Print a note message with the line info given explicitly */ + +void PPNote (const char* Format, ...) attribute ((format (printf, 1, 2))); +/* Print a note message. For use within the preprocessor */ + void ErrorReport (void); /* Report errors (called at end of compile) */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index eb0afb38b..9460569ed 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -103,6 +103,100 @@ unsigned GlobalModeFlags (const ExprDesc* Expr) +static unsigned TypeOfBySize (unsigned Size) +/* Get the code generator replacement type of the object by its size */ +{ + unsigned NewType; + /* 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 3: /* FALLTHROUGH */ + case 4: NewType = CF_LONG; break; + default: NewType = CF_NONE; break; + } + + return NewType; +} + + + +unsigned TypeOf (const Type* T) +/* Get the code generator base type of the object */ +{ + unsigned NewType; + + switch (GetUnqualTypeCode (T)) { + + case T_SCHAR: + return CF_CHAR; + + case T_UCHAR: + return CF_CHAR | CF_UNSIGNED; + + case T_SHORT: + case T_INT: + return CF_INT; + + case T_USHORT: + case T_UINT: + case T_PTR: + case T_ARRAY: + return CF_INT | CF_UNSIGNED; + + case T_LONG: + return CF_LONG; + + case T_ULONG: + return CF_LONG | CF_UNSIGNED; + + case T_FLOAT: + case T_DOUBLE: + /* These two are identical in the backend */ + return CF_FLOAT; + + case T_FUNC: + /* Treat this as a function pointer */ + return CF_INT | CF_UNSIGNED; + + case T_STRUCT: + case T_UNION: + NewType = TypeOfBySize (SizeOf (T)); + if (NewType != CF_NONE) { + return NewType; + } + /* Address of ... */ + return CF_INT | CF_UNSIGNED; + + case T_VOID: + case T_ENUM: + /* Incomplete enum type */ + Error ("Incomplete type '%s'", GetFullTypeName (T)); + return CF_INT; + + default: + Error ("Illegal type %04lX", T->C); + return CF_INT; + } +} + + + +unsigned FuncTypeOf (const Type* T) +/* Get the code generator flag for calling the function */ +{ + if (GetUnqualTypeCode (T) == T_FUNC) { + return (T->A.F->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; + } else { + Error ("Illegal function type %04lX", T->C); + return 0; + } +} + + + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) /* Call an expression function with checks. */ { @@ -193,12 +287,15 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) -void LimitExprValue (ExprDesc* Expr) +void LimitExprValue (ExprDesc* Expr, int WarnOverflow) /* Limit the constant value of the expression to the range of its type */ { - switch (GetUnderlyingTypeCode (Expr->Type)) { + switch (GetUnqualTypeCode (Expr->Type)) { case T_INT: case T_SHORT: + if (WarnOverflow && ((Expr->IVal < -0x8000) || (Expr->IVal > 0x7FFF))) { + Warning ("Signed integer constant overflow"); + } Expr->IVal = (int16_t)Expr->IVal; break; @@ -218,6 +315,9 @@ void LimitExprValue (ExprDesc* Expr) break; case T_SCHAR: + if (WarnOverflow && ((Expr->IVal < -0x80) || (Expr->IVal > 0x7F))) { + Warning ("Signed character constant overflow"); + } Expr->IVal = (int8_t)Expr->IVal; break; @@ -274,11 +374,10 @@ static unsigned ExprCheckedSizeOf (const Type* T) /* Specially checked SizeOf() used in 'sizeof' expressions */ { unsigned Size = SizeOf (T); - SymEntry* Sym; if (Size == 0) { - Sym = GetSymType (T); - if (Sym == 0 || !SymIsDef (Sym)) { + SymEntry* TagSym = GetESUTagSym (T); + if (TagSym == 0 || !SymIsDef (TagSym)) { Error ("Cannot apply 'sizeof' to incomplete type '%s'", GetFullTypeName (T)); } } @@ -304,13 +403,9 @@ void PushAddr (const ExprDesc* Expr) static void WarnConstCompareResult (const ExprDesc* Expr) -/* If the result of a comparison is constant, this is suspicious when not in -** preprocessor mode. -*/ +/* If the result of a comparison is constant, this is suspicious */ { - if (!Preprocessing && - !ED_NeedsConst (Expr) && - IS_Get (&WarnConstComparison) != 0) { + if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) { Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); } } @@ -1051,7 +1146,7 @@ static void FunctionCall (ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - ReturnType = GetFuncReturn (Expr->Type); + ReturnType = GetFuncReturnType (Expr->Type); /* Handle struct/union specially */ if (IsClassStruct (ReturnType)) { @@ -1075,58 +1170,19 @@ static void Primary (ExprDesc* E) /* This is the lowest level of the expression parser. */ { SymEntry* Sym; - - /* Character and integer constants. */ - if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { - E->IVal = CurTok.IVal; - E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; - NextToken (); - return; - } - - /* Floating point constant */ - if (CurTok.Tok == TOK_FCONST) { - E->V.FVal = CurTok.FVal; - E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; - NextToken (); - return; - } - - /* Process parenthesized subexpression by calling the whole parser - ** recursively. - */ - if (CurTok.Tok == TOK_LPAREN) { - NextToken (); - hie0 (E); - ConsumeRParen (); - return; - } - - /* If we run into an identifier in preprocessing mode, we assume that this - ** is an undefined macro and replace it by a constant value of zero. - */ - if (Preprocessing && CurTok.Tok == TOK_IDENT) { - NextToken (); - ED_MakeConstAbsInt (E, 0); - return; - } - - /* All others may only be used if the expression evaluation is not called - ** recursively by the preprocessor. - */ - if (Preprocessing) { - /* Illegal expression in PP mode */ - Error ("Preprocessor expression expected"); - ED_MakeConstAbsInt (E, 1); - return; - } - unsigned Flags = E->Flags & E_MASK_KEEP_MAKE; switch (CurTok.Tok) { + case TOK_LPAREN: + /* Process parenthesized subexpression by calling the whole parser + ** recursively. + */ + NextToken (); + hie0 (E); + ConsumeRParen (); + break; + case TOK_BOOL_AND: /* A computed goto label address */ if (IS_Get (&Standard) >= STD_CC65) { @@ -1160,9 +1216,9 @@ static void Primary (ExprDesc* E) /* 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; - return; + E->Flags = E_LOC_STACK | E_RTYPE_LVAL; + E->Type = type_int; + break; } /* Mark the symbol as referenced */ @@ -1175,11 +1231,7 @@ static void Primary (ExprDesc* E) if ((Sym->Flags & SC_CONST) == SC_CONST) { /* Enum or some other numeric constant */ E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->IVal = Sym->V.ConstVal; - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { - /* Function */ - E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; + E->IVal = Sym->V.ConstVal; } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic ** function, we have to add some address calculations, and the @@ -1194,6 +1246,10 @@ 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) { + /* Function */ + E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; + E->Name = (uintptr_t) Sym->Name; } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) { /* Register variable, zero page based */ E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL; @@ -1202,10 +1258,10 @@ static void Primary (ExprDesc* E) /* 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; + E->Name = (uintptr_t) Sym->Name; } else { E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; - E->Name = Sym->V.L.Label; + E->Name = Sym->V.L.Label; } } else { /* Local static variable */ @@ -1251,7 +1307,7 @@ static void Primary (ExprDesc* E) /* Undeclared Variable */ Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0); E->Flags = E_LOC_STACK | E_RTYPE_LVAL; - E->Type = type_int; + E->Type = type_int; Error ("Undefined symbol: '%s'", Ident); } @@ -1263,6 +1319,8 @@ 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; } @@ -1273,6 +1331,24 @@ static void Primary (ExprDesc* E) NextToken (); break; + case TOK_ICONST: + case TOK_CCONST: + case TOK_WCCONST: + /* Character and integer constants */ + E->IVal = CurTok.IVal; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; + NextToken (); + break; + + case TOK_FCONST: + /* Floating point constant */ + E->V.FVal = CurTok.FVal; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; + NextToken (); + break; + case TOK_ASM: /* ASM statement */ AsmStatement (); @@ -1319,13 +1395,13 @@ static void Primary (ExprDesc* E) /* Let's see if this is a C99-style declaration */ DeclSpec Spec; InitDeclSpec (&Spec); - ParseDeclSpec (&Spec, -1, T_QUAL_NONE); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); if (Spec.Type->C != T_END) { Error ("Mixed declarations and code are not supported in cc65"); while (CurTok.Tok != TOK_SEMI) { - Declaration Decl; + Declarator Decl; /* Parse one declaration */ ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); @@ -1845,7 +1921,7 @@ static void UnaryOp (ExprDesc* Expr) Expr->Type = IntPromotion (Expr->Type); /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr); + LimitExprValue (Expr, 1); } else { unsigned Flags; @@ -2101,6 +2177,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Check for const operands */ if (lconst && rconst) { + /* Evaluate the result for operands */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Expr2.IVal; + /* Both operands are constant, remove the generated code */ RemoveCode (&Mark1); @@ -2108,84 +2188,55 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Handle the op differently for signed and unsigned types */ - if (IsSignSigned (Expr->Type)) { - - /* Evaluate the result for signed operands */ - signed long Val1 = Expr->IVal; - signed long Val2 = Expr2.IVal; - switch (Tok) { - case TOK_OR: - Expr->IVal = (Val1 | Val2); - break; - case TOK_XOR: - Expr->IVal = (Val1 ^ Val2); - break; - case TOK_AND: - Expr->IVal = (Val1 & Val2); - break; - case TOK_STAR: - Expr->IVal = (Val1 * Val2); - break; - case TOK_DIV: - if (Val2 == 0) { - Error ("Division by zero"); - Expr->IVal = 0x7FFFFFFF; + switch (Tok) { + case TOK_OR: + Expr->IVal = (Val1 | Val2); + break; + case TOK_XOR: + Expr->IVal = (Val1 ^ Val2); + break; + case TOK_AND: + Expr->IVal = (Val1 & Val2); + break; + case TOK_STAR: + Expr->IVal = (Val1 * Val2); + break; + case TOK_DIV: + if (Val2 == 0) { + if (!ED_IsUneval (Expr)) { + Warning ("Division by zero"); + } + Expr->IVal = 0xFFFFFFFF; + } else { + /* Handle signed and unsigned operands differently */ + if (IsSignSigned (Expr->Type)) { + Expr->IVal = ((long)Val1 / (long)Val2); } else { Expr->IVal = (Val1 / Val2); } - break; - case TOK_MOD: - if (Val2 == 0) { - Error ("Modulo operation with zero"); - Expr->IVal = 0; + } + break; + case TOK_MOD: + if (Val2 == 0) { + if (!ED_IsUneval (Expr)) { + Warning ("Modulo operation with zero"); + } + Expr->IVal = 0; + } else { + /* Handle signed and unsigned operands differently */ + if (IsSignSigned (Expr->Type)) { + Expr->IVal = ((long)Val1 % (long)Val2); } else { Expr->IVal = (Val1 % Val2); } - break; - default: - Internal ("hie_internal: got token 0x%X\n", Tok); - } - } else { - - /* Evaluate the result for unsigned operands */ - unsigned long Val1 = Expr->IVal; - unsigned long Val2 = Expr2.IVal; - switch (Tok) { - case TOK_OR: - Expr->IVal = (Val1 | Val2); - break; - case TOK_XOR: - Expr->IVal = (Val1 ^ Val2); - break; - case TOK_AND: - Expr->IVal = (Val1 & Val2); - break; - case TOK_STAR: - Expr->IVal = (Val1 * Val2); - break; - case TOK_DIV: - if (Val2 == 0) { - Error ("Division by zero"); - Expr->IVal = 0xFFFFFFFF; - } else { - Expr->IVal = (Val1 / Val2); - } - break; - case TOK_MOD: - if (Val2 == 0) { - Error ("Modulo operation with zero"); - Expr->IVal = 0; - } else { - Expr->IVal = (Val1 % Val2); - } - break; - default: - Internal ("hie_internal: got token 0x%X\n", Tok); - } + } + break; + default: + Internal ("hie_internal: got token 0x%X\n", Tok); } /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr); + LimitExprValue (Expr, 1); } else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) { /* If the LHS constant is an int that fits into an unsigned char, change the @@ -2238,10 +2289,12 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Second value is constant - check for div */ type |= CF_CONST; rtype |= CF_CONST; - if (Tok == TOK_DIV && Expr2.IVal == 0) { - Error ("Division by zero"); - } else if (Tok == TOK_MOD && Expr2.IVal == 0) { - Error ("Modulo operation with zero"); + if (Expr2.IVal == 0 && !ED_IsUneval (Expr)) { + if (Tok == TOK_DIV) { + Warning ("Division by zero"); + } else if (Tok == TOK_MOD) { + Warning ("Modulo operation with zero"); + } } if ((Gen->Flags & GEN_NOPUSH) != 0) { RemoveCode (&Mark2); @@ -2364,7 +2417,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } else if (IsClassPtr (Expr->Type)) { if (IsClassPtr (Expr2.Type)) { /* Pointers are allowed in comparison */ - if (TypeCmp (Expr->Type, Expr2.Type).C < TC_STRICT_COMPATIBLE) { + if (TypeCmp (Expr->Type, Expr2.Type).C < TC_VOID_PTR) { /* Warn about distinct pointer types */ TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0, "Distinct pointer types comparing '%s' with '%s'"); @@ -2515,7 +2568,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } /* Determine the type of the operation. */ - if (IsTypeChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) { + if (IsRankChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) { /* Left side is unsigned char, right side is constant. ** Determine the minimum and maximum values @@ -2598,7 +2651,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_UNSIGNED; } - } else if (IsTypeChar (Expr->Type) && IsTypeChar (Expr2.Type) && + } else if (IsRankChar (Expr->Type) && IsRankChar (Expr2.Type) && GetSignedness (Expr->Type) == GetSignedness (Expr2.Type)) { /* Both are chars with the same signedness. We can encode the @@ -2812,7 +2865,7 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) Expr->Type = rhst; } else { /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr); + LimitExprValue (Expr, 1); } /* The result is always an rvalue */ @@ -3037,9 +3090,10 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef) Expr->Type = Expr2.Type; } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer addition */ - flags = typeadjust (Expr, &Expr2, 0); /* Load rhs into the primary */ LoadExpr (CF_NONE, &Expr2); + /* Adjust rhs primary if needed */ + flags = typeadjust (Expr, &Expr2, 0); } else { /* OOPS */ AddDone = -1; @@ -3283,7 +3337,7 @@ static void parsesub (ExprDesc* Expr) /* Just adjust the result type */ Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* And limit the calculated value to the range of it */ - LimitExprValue (Expr); + LimitExprValue (Expr, 1); } /* The result is always an rvalue */ ED_MarkExprAsRVal (Expr); @@ -3474,48 +3528,6 @@ static void hie2 (ExprDesc* Expr) -static void hieAndPP (ExprDesc* Expr) -/* Process "exp && exp" in preprocessor mode (that is, when the parser is -** called recursively from the preprocessor. -*/ -{ - *Expr = NoCodeConstAbsIntExpr (hie2); - while (CurTok.Tok == TOK_BOOL_AND) { - - /* Skip the && */ - NextToken (); - - /* Get rhs */ - ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2); - - /* Combine the two */ - Expr->IVal = (Expr->IVal && Expr2.IVal); - } -} - - - -static void hieOrPP (ExprDesc *Expr) -/* Process "exp || exp" in preprocessor mode (that is, when the parser is -** called recursively from the preprocessor. -*/ -{ - *Expr = NoCodeConstAbsIntExpr (hieAndPP); - while (CurTok.Tok == TOK_BOOL_OR) { - - /* Skip the && */ - NextToken (); - - /* Get rhs */ - ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP); - - /* Combine the two */ - Expr->IVal = (Expr->IVal || Expr2.IVal); - } -} - - - static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Process "exp && exp". This should only be called within hieOr. ** Return true if logic AND does occur. @@ -3865,11 +3877,7 @@ static void hieQuest (ExprDesc* Expr) Type* ResultType; /* Type of result */ /* Call the lower level eval routine */ - if (Preprocessing) { - ExprWithCheck (hieOrPP, Expr); - } else { - ExprWithCheck (hieOr, Expr); - } + ExprWithCheck (hieOr, Expr); /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { @@ -3883,17 +3891,6 @@ static void hieQuest (ExprDesc* Expr) ED_Init (&Expr3); Expr3.Flags = Flags; - NextToken (); - - /* Convert non-integer constant to boolean constant, so that we may just - ** check it in the same way. - */ - if (ED_IsConstTrue (Expr)) { - ED_MakeConstBool (Expr, 1); - } else if (ED_IsConstFalse (Expr)) { - ED_MakeConstBool (Expr, 0); - } - if (!ConstantCond) { /* Condition codes not set, request a test */ ED_RequireTest (Expr); @@ -3905,6 +3902,15 @@ static void hieQuest (ExprDesc* Expr) FalseLab = GetLocalLabel (); g_falsejump (CF_NONE, FalseLab); } else { + /* Convert non-integer constant to boolean constant, so that we + ** may just check it in an easier way later. + */ + if (ED_IsConstTrue (Expr)) { + ED_MakeConstBool (Expr, 1); + } else if (ED_IsConstFalse (Expr)) { + ED_MakeConstBool (Expr, 0); + } + /* Constant boolean subexpression could still have deferred inc/dec ** operations, so just flush their side-effects at this sequence point. */ @@ -3913,9 +3919,18 @@ static void hieQuest (ExprDesc* Expr) if (Expr->IVal == 0) { /* Remember the current code position */ GetCodePos (&SkippedBranch); + + /* Expr2 is unevaluated when the condition is false */ + Expr2.Flags |= E_EVAL_UNEVAL; + } else { + /* Expr3 is unevaluated when the condition is true */ + Expr3.Flags |= E_EVAL_UNEVAL; } } + /* Skip the question mark */ + NextToken (); + /* Parse second expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. */ @@ -3932,9 +3947,9 @@ static void hieQuest (ExprDesc* Expr) ED_FinalizeRValLoad (&Expr2); } else { - /* Constant boolean subexpression could still have deferred inc/ - ** dec operations, so just flush their side-effects at this - ** sequence point. + /* Constant subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. */ DoDeferred (SQP_KEEP_NONE, &Expr2); } @@ -3947,30 +3962,26 @@ static void hieQuest (ExprDesc* Expr) /* Jump around the evaluation of the third expression */ TrueLab = GetLocalLabel (); - ConsumeColon (); - g_jump (TrueLab); /* Jump here if the first expression was false */ g_defcodelabel (FalseLab); } else { if (Expr->IVal == 0) { - /* Expr2 is unevaluated when the condition is false */ - Expr2.Flags |= E_EVAL_UNEVAL; - /* Remove the load code of Expr2 */ RemoveCode (&SkippedBranch); } else { /* Remember the current code position */ GetCodePos (&SkippedBranch); } - ConsumeColon(); } + ConsumeColon (); + /* Parse third expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. */ - ExprWithCheck (hie1, &Expr3); + ExprWithCheck (hieQuest, &Expr3); Expr3IsNULL = ED_IsNullPtr (&Expr3); if (!IsTypeVoid (Expr3.Type) && ED_YetToLoad (&Expr3) && @@ -3983,18 +3994,15 @@ static void hieQuest (ExprDesc* Expr) ED_FinalizeRValLoad (&Expr3); } else { - /* Constant boolean subexpression could still have deferred inc/ - ** dec operations, so just flush their side-effects at this - ** sequence point. + /* Constant subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. */ DoDeferred (SQP_KEEP_NONE, &Expr3); } Expr3.Type = PtrConversion (Expr3.Type); if (ConstantCond && Expr->IVal != 0) { - /* Expr3 is unevaluated when the condition is true */ - Expr3.Flags |= E_EVAL_UNEVAL; - /* Remove the load code of Expr3 */ RemoveCode (&SkippedBranch); } @@ -4099,6 +4107,8 @@ static void hieQuest (ExprDesc* Expr) } else { *Expr = Expr3; } + /* The result expression is always an rvalue */ + ED_MarkExprAsRVal (Expr); } /* Setup the target expression */ diff --git a/src/cc65/expr.h b/src/cc65/expr.h index abdf8ab0d..5644fb82d 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -51,6 +51,12 @@ typedef struct GenDesc { unsigned GlobalModeFlags (const ExprDesc* Expr); /* Return the addressing mode flags for the given expression */ +unsigned 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 */ + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ @@ -59,7 +65,7 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); ** generated code. */ -void LimitExprValue (ExprDesc* Expr); +void LimitExprValue (ExprDesc* Expr, int WarnOverflow); /* Limit the constant value of the expression to the range of its type */ void PushAddr (const ExprDesc* Expr); diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 7d0ace004..5924ab6cf 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -67,74 +67,9 @@ ExprDesc* ED_Init (ExprDesc* Expr) -#if !defined(HAVE_INLINE) -int ED_IsLocQuasiConst (const ExprDesc* Expr) -/* Return true if the expression is a constant location of some sort or on the -** stack. -*/ -{ - return ED_IsLocConst (Expr) || ED_IsLocStack (Expr); -} -#endif - - - -#if !defined(HAVE_INLINE) -int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) -/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ -{ - return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); -} -#endif - - - -#if !defined(HAVE_INLINE) -int ED_IsIndExpr (const ExprDesc* Expr) -/* Check if the expression is a reference to its value */ -{ - return (Expr->Flags & E_ADDRESS_OF) == 0 && !ED_IsLocNone (Expr); -} -#endif - - - -int ED_YetToLoad (const ExprDesc* Expr) -/* Check if the expression needs to be loaded somehow. */ -{ - return ED_NeedsPrimary (Expr) || - ED_YetToTest (Expr) || - (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)); -} - - - -void ED_MarkForUneval (ExprDesc* Expr) -/* Mark the expression as not to be evaluated */ -{ - Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL; -} - - - -void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) -/* Set the code range for this expression */ -{ - Expr->Flags |= E_HAVE_MARKS; - Expr->Start = *Start; - Expr->End = *End; -} - - - -int ED_CodeRangeIsEmpty (const ExprDesc* Expr) -/* Return true if no code was output for this expression */ -{ - /* We must have code marks */ - PRECONDITION (Expr->Flags & E_HAVE_MARKS); - - return CodeRangeIsEmpty (&Expr->Start, &Expr->End); -} +/*****************************************************************************/ +/* Info Extraction */ +/*****************************************************************************/ @@ -214,132 +149,51 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) -ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) -/* Replace Expr with an absolute const with the given value and type */ +/*****************************************************************************/ +/* Predicates */ +/*****************************************************************************/ + + + +#if !defined(HAVE_INLINE) +int ED_IsLocQuasiConst (const ExprDesc* Expr) +/* Return true if the expression is a constant location of some sort or on the +** stack. +*/ { - Expr->Type = Type; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); - Expr->Name = 0; - Expr->Sym = 0; - Expr->IVal = Value; - memset (&Expr->V, 0, sizeof (Expr->V)); - return Expr; + return ED_IsLocConst (Expr) || ED_IsLocStack (Expr); } +#endif -ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) -/* Replace Expr with a constant integer expression with the given value */ +#if !defined(HAVE_INLINE) +int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ { - Expr->Type = type_int; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); - Expr->Name = 0; - Expr->Sym = 0; - Expr->IVal = Value; - memset (&Expr->V, 0, sizeof (Expr->V)); - return Expr; + return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); } +#endif -ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) -/* Replace Expr with a constant boolean expression with the given value */ +#if !defined(HAVE_INLINE) +int ED_IsIndExpr (const ExprDesc* Expr) +/* Check if the expression is a reference to its value */ { - Expr->Sym = 0; - Expr->Type = type_bool; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); - Expr->Name = 0; - Expr->IVal = Value; - memset (&Expr->V, 0, sizeof (Expr->V)); - return Expr; + return (Expr->Flags & E_ADDRESS_OF) == 0 && + !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr); } +#endif -ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) -/* Finalize the result of LoadExpr to be an rvalue in the primary register */ +int ED_YetToLoad (const ExprDesc* Expr) +/* Check if the expression needs to be loaded somehow. */ { - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF); - Expr->Flags &= ~E_CC_SET; - Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); - Expr->Sym = 0; - Expr->Name = 0; - Expr->IVal = 0; /* No offset */ - memset (&Expr->V, 0, sizeof (Expr->V)); - return Expr; -} - - - -ExprDesc* ED_AddrExpr (ExprDesc* Expr) -/* Take address of Expr. The result is always an rvalue */ -{ - switch (Expr->Flags & E_MASK_LOC) { - case E_LOC_NONE: - Error ("Cannot get the address of a numeric constant"); - break; - - case E_LOC_EXPR: - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); - Expr->Flags |= E_ADDRESS_OF | E_LOC_PRIMARY | E_RTYPE_RVAL; - break; - - default: - if ((Expr->Flags & E_ADDRESS_OF) == 0) { - Expr->Flags &= ~E_MASK_RTYPE; - Expr->Flags |= E_ADDRESS_OF | E_RTYPE_RVAL; - } else { - /* Due to the way we handle arrays, this may happen if we take - ** the address of a pointer to an array element. - */ - if (!IsTypePtr (Expr->Type)) { - Error ("Cannot get the address of an address"); - } - Expr->Flags &= ~E_MASK_RTYPE; - Expr->Flags |= E_RTYPE_RVAL; - } - break; - } - return Expr; -} - - - -ExprDesc* ED_IndExpr (ExprDesc* Expr) -/* Dereference Expr */ -{ - switch (Expr->Flags & E_MASK_LOC) { - case E_LOC_NONE: - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); - Expr->Flags |= E_LOC_ABS | E_RTYPE_LVAL; - break; - - case E_LOC_PRIMARY: - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); - Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; - break; - - default: - if ((Expr->Flags & E_ADDRESS_OF) != 0) { - Expr->Flags &= ~(E_MASK_RTYPE | E_ADDRESS_OF); - Expr->Flags |= E_RTYPE_LVAL; - } else { - /* Due to the limitation of LoadExpr, this may happen after we - ** have loaded the value from a referenced address, in which - ** case the content in the primary no longer refers to the - ** original address. We simply mark this as E_LOC_EXPR so that - ** some info about the original location can be retained. - ** If it's really meant to dereference a "pointer value", it - ** should be done in two steps where the pointervalue should - ** be the manually loaded first before a call into this, and - ** the offset should be manually cleared somewhere outside. - */ - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); - Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; - } - break; - } - return Expr; + return ED_NeedsPrimary (Expr) || + ED_YetToTest (Expr) || + (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)); } @@ -427,6 +281,7 @@ int ED_IsQuasiConst (const ExprDesc* Expr) } + int ED_IsConstAddr (const ExprDesc* Expr) /* Return true if the expression denotes a constant address of some sort. This ** can be the address of a global variable (maybe with offset) or similar. @@ -472,6 +327,166 @@ int ED_IsBool (const ExprDesc* Expr) +/*****************************************************************************/ +/* Manipulation */ +/*****************************************************************************/ + + + +ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) +/* Replace Expr with an absolute const with the given value and type */ +{ + Expr->Type = Type; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); + Expr->Name = 0; + Expr->Sym = 0; + Expr->IVal = Value; + memset (&Expr->V, 0, sizeof (Expr->V)); + return Expr; +} + + + +ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) +/* Replace Expr with a constant integer expression with the given value */ +{ + Expr->Type = type_int; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); + Expr->Name = 0; + Expr->Sym = 0; + Expr->IVal = Value; + memset (&Expr->V, 0, sizeof (Expr->V)); + return Expr; +} + + + +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) +/* Replace Expr with a constant boolean expression with the given value */ +{ + Expr->Sym = 0; + Expr->Type = type_bool; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); + Expr->Name = 0; + Expr->IVal = Value; + memset (&Expr->V, 0, sizeof (Expr->V)); + return Expr; +} + + + +ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) +/* Finalize the result of LoadExpr to be an rvalue in the primary register */ +{ + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF); + Expr->Flags &= ~E_CC_SET; + Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); + Expr->Sym = 0; + Expr->Name = 0; + Expr->IVal = 0; /* No offset */ + memset (&Expr->V, 0, sizeof (Expr->V)); + return Expr; +} + + + +ExprDesc* ED_AddrExpr (ExprDesc* Expr) +/* Take address of Expr. The result is always an rvalue */ +{ + switch (Expr->Flags & E_MASK_LOC) { + case E_LOC_NONE: + Error ("Cannot get the address of a numeric constant"); + break; + + case E_LOC_EXPR: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_PRIMARY | E_RTYPE_RVAL; + break; + + default: + if ((Expr->Flags & E_ADDRESS_OF) == 0) { + Expr->Flags &= ~E_MASK_RTYPE; + Expr->Flags |= E_ADDRESS_OF | E_RTYPE_RVAL; + } else { + /* Due to the way we handle arrays, this may happen if we take + ** the address of a pointer to an array element. + */ + if (!IsTypePtr (Expr->Type)) { + Error ("Cannot get the address of an address"); + } + Expr->Flags &= ~E_MASK_RTYPE; + Expr->Flags |= E_RTYPE_RVAL; + } + break; + } + return Expr; +} + + + +ExprDesc* ED_IndExpr (ExprDesc* Expr) +/* Dereference Expr */ +{ + switch (Expr->Flags & E_MASK_LOC) { + case E_LOC_NONE: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_ABS | E_RTYPE_LVAL; + break; + + case E_LOC_PRIMARY: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; + break; + + default: + if ((Expr->Flags & E_ADDRESS_OF) != 0) { + Expr->Flags &= ~(E_MASK_RTYPE | E_ADDRESS_OF); + Expr->Flags |= E_RTYPE_LVAL; + } else { + /* Due to the limitation of LoadExpr, this may happen after we + ** have loaded the value from a referenced address, in which + ** case the content in the primary no longer refers to the + ** original address. We simply mark this as E_LOC_EXPR so that + ** some info about the original location can be retained. + ** If it's really meant to dereference a "pointer value", it + ** should be done in two steps where the pointer value should + ** be the manually loaded first before a call into this, and + ** the offset should be manually cleared somewhere outside. + */ + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; + } + break; + } + return Expr; +} + + + +void ED_MarkForUneval (ExprDesc* Expr) +/* Mark the expression as not to be evaluated */ +{ + Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL; +} + + + +const Type* ReplaceType (ExprDesc* Expr, const Type* NewType) +/* Replace the type of Expr by a copy of Newtype and return the old type string */ +{ + const Type* OldType = Expr->Type; + Expr->Type = TypeDup (NewType); + return OldType; +} + + + +/*****************************************************************************/ +/* Other Helpers */ +/*****************************************************************************/ + + + void PrintExprDesc (FILE* F, ExprDesc* E) /* Print an ExprDesc */ { @@ -576,10 +591,21 @@ void PrintExprDesc (FILE* F, ExprDesc* E) -const Type* ReplaceType (ExprDesc* Expr, const Type* NewType) -/* Replace the type of Expr by a copy of Newtype and return the old type string */ +void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) +/* Set the code range for this expression */ { - const Type* OldType = Expr->Type; - Expr->Type = TypeDup (NewType); - return OldType; + Expr->Flags |= E_HAVE_MARKS; + Expr->Start = *Start; + Expr->End = *End; +} + + + +int ED_CodeRangeIsEmpty (const ExprDesc* Expr) +/* Return true if no code was output for this expression */ +{ + /* We must have code marks */ + PRECONDITION (Expr->Flags & E_HAVE_MARKS); + + return CodeRangeIsEmpty (&Expr->Start, &Expr->End); } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index a1674a7cc..2ef8b617f 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -227,6 +227,14 @@ struct ExprDesc { ExprDesc* ED_Init (ExprDesc* Expr); /* Initialize an ExprDesc */ + + +/*****************************************************************************/ +/* Info Extraction */ +/*****************************************************************************/ + + + #if defined(HAVE_INLINE) INLINE int ED_GetLoc (const ExprDesc* Expr) /* Return the location flags from the expression */ @@ -237,6 +245,35 @@ INLINE int ED_GetLoc (const ExprDesc* Expr) # define ED_GetLoc(Expr) ((Expr)->Flags & E_MASK_LOC) #endif +#if defined(HAVE_INLINE) +INLINE int ED_GetNeeds (const ExprDesc* Expr) +/* Get flags about what the expression needs. */ +{ + return (Expr->Flags & E_MASK_NEED); +} +#else +# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED) +#endif + +const char* ED_GetLabelName (const ExprDesc* Expr, long Offs); +/* Return the assembler label name of the given expression. Beware: This +** function may use a static buffer, so the name may get "lost" on the second +** call to the function. +*/ + +int ED_GetStackOffs (const ExprDesc* Expr, int Offs); +/* Get the stack offset of an address on the stack in Expr taking into account +** an additional offset in Offs. +*/ + + + +/*****************************************************************************/ +/* Predicates */ +/*****************************************************************************/ + + + #if defined(HAVE_INLINE) INLINE int ED_IsLocNone (const ExprDesc* Expr) /* Return true if the expression is an absolute value */ @@ -279,7 +316,7 @@ INLINE int ED_IsLocStack (const ExprDesc* Expr) #if defined(HAVE_INLINE) INLINE int ED_IsLocPrimary (const ExprDesc* Expr) -/* Return true if the expression is an expression in the register pseudo variable */ +/* Return true if the expression is an expression in the primary */ { return (Expr->Flags & E_MASK_LOC) == E_LOC_PRIMARY; } @@ -289,7 +326,7 @@ INLINE int ED_IsLocPrimary (const ExprDesc* Expr) #if defined(HAVE_INLINE) INLINE int ED_IsLocExpr (const ExprDesc* Expr) -/* Return true if the expression is an expression in the primary */ +/* Return true if the expression is an expression referenced in the primary */ { return (Expr->Flags & E_MASK_LOC) == E_LOC_EXPR; } @@ -333,33 +370,14 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); #endif #if defined(HAVE_INLINE) -INLINE void ED_RequireTest (ExprDesc* Expr) -/* Mark the expression for a test. */ +INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ { - Expr->Flags |= E_NEED_TEST; + return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); } #else -# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_RequireNoTest (ExprDesc* Expr) -/* Mark the expression not for a test. */ -{ - Expr->Flags &= ~E_NEED_TEST; -} -#else -# define ED_RequireNoTest(Expr) do { (Expr)->Flags &= ~E_NEED_TEST; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE int ED_GetNeeds (const ExprDesc* Expr) -/* Get flags about what the expression needs. */ -{ - return (Expr->Flags & E_MASK_NEED); -} -#else -# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED) +int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr); +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ #endif #if defined(HAVE_INLINE) @@ -382,27 +400,6 @@ INLINE int ED_NeedsTest (const ExprDesc* Expr) # define ED_NeedsTest(Expr) (((Expr)->Flags & E_NEED_TEST) != 0) #endif -#if defined(HAVE_INLINE) -INLINE int ED_YetToTest (const ExprDesc* Expr) -/* Check if the expression needs to be tested but not yet. */ -{ - return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST; -} -#else -# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_TestDone (ExprDesc* Expr) -/* Mark the expression as tested and condition codes set. */ -{ - Expr->Flags |= E_CC_SET; -} -#else -# define ED_TestDone(Expr) \ - do { (Expr)->Flags |= E_CC_SET; } while (0) -#endif - #if defined(HAVE_INLINE) INLINE int ED_IsTested (const ExprDesc* Expr) /* Check if the expression has set the condition codes. */ @@ -414,13 +411,13 @@ INLINE int ED_IsTested (const ExprDesc* Expr) #endif #if defined(HAVE_INLINE) -INLINE void ED_MarkAsUntested (ExprDesc* Expr) -/* Mark the expression as not tested (condition codes not set). */ +INLINE int ED_YetToTest (const ExprDesc* Expr) +/* Check if the expression needs to be tested but not yet. */ { - Expr->Flags &= ~E_CC_SET; + return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST; } #else -# define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0) +# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST) #endif #if defined(HAVE_INLINE) @@ -448,9 +445,6 @@ INLINE int ED_NeedsConst (const ExprDesc* Expr) # define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT) #endif -void ED_MarkForUneval (ExprDesc* Expr); -/* Mark the expression as not to be evaluated */ - #if defined(HAVE_INLINE) INLINE int ED_IsUneval (const ExprDesc* Expr) /* Check if the expression is not to be evaluated */ @@ -471,27 +465,6 @@ INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr) # define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED) #endif -#if defined(HAVE_INLINE) -INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr) -/* Propagate viral flags from subexpression */ -{ - Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL; -} -#else -# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL) -#endif - -#if defined(HAVE_INLINE) -INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) -/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ -{ - return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); -} -#else -int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr); -/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ -#endif - #if defined(HAVE_INLINE) INLINE int ED_IsAddrExpr (const ExprDesc* Expr) /* Check if the expression is taken address of instead of its value. @@ -507,42 +480,14 @@ INLINE int ED_IsAddrExpr (const ExprDesc* Expr) INLINE int ED_IsIndExpr (const ExprDesc* Expr) /* Check if the expression is a reference to its value */ { - return (Expr->Flags & E_ADDRESS_OF) == 0 && !ED_IsLocNone (Expr); + return (Expr->Flags & E_ADDRESS_OF) == 0 && + !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr); } #else int ED_IsIndExpr (const ExprDesc* Expr); /* Check if the expression is a reference to its value */ #endif -void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End); -/* Set the code range for this expression */ - -int ED_CodeRangeIsEmpty (const ExprDesc* Expr); -/* Return true if no code was output for this expression */ - -const char* ED_GetLabelName (const ExprDesc* Expr, long Offs); -/* Return the assembler label name of the given expression. Beware: This -** function may use a static buffer, so the name may get "lost" on the second -** call to the function. -*/ - -int ED_GetStackOffs (const ExprDesc* Expr, int Offs); -/* Get the stack offset of an address on the stack in Expr taking into account -** an additional offset in Offs. -*/ - -ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type); -/* Replace Expr with an absolute const with the given value and type */ - -ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value); -/* Replace Expr with an constant integer with the given value */ - -ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value); -/* Replace Expr with a constant boolean expression with the given value */ - -ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr); -/* Finalize the result of LoadExpr to be an rvalue in the primary register */ - #if defined(HAVE_INLINE) INLINE int ED_IsLVal (const ExprDesc* Expr) /* Return true if the expression is a reference */ @@ -563,40 +508,6 @@ INLINE int ED_IsRVal (const ExprDesc* Expr) # define ED_IsRVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL) #endif -#if defined(HAVE_INLINE) -INLINE void ED_MarkExprAsLVal (ExprDesc* Expr) -/* Mark the expression as an lvalue. -** HINT: Consider using ED_IndExpr instead of this, unless you know what -** consequence there will be, as there are both a big part in the code -** assuming rvalue = const and a big part assuming rvalue = address. -*/ -{ - Expr->Flags |= E_RTYPE_LVAL; -} -#else -# define ED_MarkExprAsLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_MarkExprAsRVal (ExprDesc* Expr) -/* Mark the expression as an rvalue. -** HINT: Consider using ED_AddrExpr instead of this, unless you know what -** consequence there will be, as there are both a big part in the code -** assuming rvalue = const and a big part assuming rvalue = address. -*/ -{ - Expr->Flags &= ~E_RTYPE_LVAL; -} -#else -# define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) -#endif - -ExprDesc* ED_AddrExpr (ExprDesc* Expr); -/* Take address of Expr */ - -ExprDesc* ED_IndExpr (ExprDesc* Expr); -/* Dereference Expr */ - #if defined(HAVE_INLINE) INLINE int ED_IsAbs (const ExprDesc* Expr) /* Return true if the expression denotes a numeric value or address. */ @@ -669,14 +580,136 @@ int ED_IsBool (const ExprDesc* Expr); ** be an operand to a compare operation with 0/NULL. */ -void PrintExprDesc (FILE* F, ExprDesc* Expr); -/* Print an ExprDesc */ + + +/*****************************************************************************/ +/* Manipulation */ +/*****************************************************************************/ + + + +ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type); +/* Replace Expr with an absolute const with the given value and type */ + +ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value); +/* Replace Expr with an constant integer with the given value */ + +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value); +/* Replace Expr with a constant boolean expression with the given value */ + +ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr); +/* Finalize the result of LoadExpr to be an rvalue in the primary register */ + +#if defined(HAVE_INLINE) +INLINE void ED_MarkExprAsLVal (ExprDesc* Expr) +/* Mark the expression as an lvalue. +** HINT: Consider using ED_IndExpr instead of this, unless you know what +** consequence there will be, as there are both a big part in the code +** assuming rvalue = const and a big part assuming rvalue = address. +*/ +{ + Expr->Flags |= E_RTYPE_LVAL; +} +#else +# define ED_MarkExprAsLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_MarkExprAsRVal (ExprDesc* Expr) +/* Mark the expression as an rvalue. +** HINT: Consider using ED_AddrExpr instead of this, unless you know what +** consequence there will be, as there are both a big part in the code +** assuming rvalue = const and a big part assuming rvalue = address. +*/ +{ + Expr->Flags &= ~E_RTYPE_LVAL; +} +#else +# define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) +#endif + +ExprDesc* ED_AddrExpr (ExprDesc* Expr); +/* Take address of Expr */ + +ExprDesc* ED_IndExpr (ExprDesc* Expr); +/* Dereference Expr */ + +#if defined(HAVE_INLINE) +INLINE void ED_RequireTest (ExprDesc* Expr) +/* Mark the expression for a test. */ +{ + Expr->Flags |= E_NEED_TEST; +} +#else +# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_RequireNoTest (ExprDesc* Expr) +/* Mark the expression not for a test. */ +{ + Expr->Flags &= ~E_NEED_TEST; +} +#else +# define ED_RequireNoTest(Expr) do { (Expr)->Flags &= ~E_NEED_TEST; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_TestDone (ExprDesc* Expr) +/* Mark the expression as tested and condition codes set. */ +{ + Expr->Flags |= E_CC_SET; +} +#else +# define ED_TestDone(Expr) \ + do { (Expr)->Flags |= E_CC_SET; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_MarkAsUntested (ExprDesc* Expr) +/* Mark the expression as not tested (condition codes not set). */ +{ + Expr->Flags &= ~E_CC_SET; +} +#else +# define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0) +#endif + +void ED_MarkForUneval (ExprDesc* Expr); +/* Mark the expression as not to be evaluated */ + +#if defined(HAVE_INLINE) +INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr) +/* Propagate viral flags from subexpression */ +{ + Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL; +} +#else +# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL) +#endif const Type* ReplaceType (ExprDesc* Expr, const Type* NewType); /* Replace the type of Expr by a copy of Newtype and return the old type string */ +/*****************************************************************************/ +/* Other Helpers */ +/*****************************************************************************/ + + + +void PrintExprDesc (FILE* F, ExprDesc* Expr); +/* Print an ExprDesc */ + +void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End); +/* Set the code range for this expression */ + +int ED_CodeRangeIsEmpty (const ExprDesc* Expr); +/* Return true if no code was output for this expression */ + + + /* End of exprdesc.h */ #endif diff --git a/src/cc65/function.c b/src/cc65/function.c index 452181af9..38a8f45aa 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -42,6 +42,7 @@ #include "asmlabel.h" #include "codegen.h" #include "error.h" +#include "expr.h" #include "funcdesc.h" #include "global.h" #include "litpool.h" @@ -80,7 +81,7 @@ static Function* NewFunction (struct SymEntry* Sym, FuncDesc* D) /* Initialize the fields */ F->FuncEntry = Sym; - F->ReturnType = GetFuncReturn (Sym->Type); + F->ReturnType = GetFuncReturnType (Sym->Type); F->Desc = D; F->Reserved = 0; F->RetLab = 0; @@ -539,7 +540,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Determine if this is a main function in a C99 environment that ** returns an int. */ - if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) && + if (GetUnqualRawTypeCode (ReturnType) == T_INT && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } @@ -600,7 +601,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) ** We don't currently support this case. */ if (RType == Param->Type) { - Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + Error ("Passing '%s' of this size (%d) by value is not supported", GetFullTypeName (Param->Type), SizeOf (RType)); } } @@ -613,7 +614,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Could we allocate a register? */ if (Reg < 0) { /* No register available: Convert parameter to auto */ - CvtRegVarToAuto (Param); + SymCvtRegVarToAuto (Param); } else { /* Remember the register offset */ Param->V.R.RegOffs = Reg; diff --git a/src/cc65/global.c b/src/cc65/global.c index 8b9838dc5..b2c3ef0a0 100644 --- a/src/cc65/global.c +++ b/src/cc65/global.c @@ -49,6 +49,7 @@ unsigned char DebugInfo = 0; /* Add debug info to the obj */ unsigned char PreprocessOnly = 0; /* Just preprocess the input */ unsigned char DebugOptOutput = 0; /* Output debug stuff */ unsigned RegisterSpace = 6; /* Space available for register vars */ +unsigned AllowNewComments = 0; /* Allow new style comments in C89 mode */ /* Stackable options */ IntStack WritableStrings = INTSTACK(0); /* Literal strings are r/w */ diff --git a/src/cc65/global.h b/src/cc65/global.h index 266035346..ba7105130 100644 --- a/src/cc65/global.h +++ b/src/cc65/global.h @@ -57,6 +57,7 @@ extern unsigned char DebugInfo; /* Add debug info to the obj */ extern unsigned char PreprocessOnly; /* Just preprocess the input */ extern unsigned char DebugOptOutput; /* Output debug stuff */ extern unsigned RegisterSpace; /* Space available for register vars */ +extern unsigned AllowNewComments; /* Allow new style comments in C89 mode */ /* Stackable options */ extern IntStack WritableStrings; /* Literal strings are r/w */ diff --git a/src/cc65/incpath.c b/src/cc65/incpath.c index 85f6e070b..3069079ac 100644 --- a/src/cc65/incpath.c +++ b/src/cc65/incpath.c @@ -76,7 +76,7 @@ void FinishIncludePaths (void) AddSubSearchPathFromEnv (SysIncSearchPath, "CC65_HOME", "include"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(CC65_INC) && !defined(_WIN32) +#if defined(CC65_INC) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (SysIncSearchPath, CC65_INC); #endif diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 99dacdca9..619fe4897 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -330,12 +330,12 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) int HasCurly = 0; /* Get the array data */ - Type* ElementType = IndirectModifiable (T); + Type* ElementType = GetElementTypeModifiable (T); unsigned ElementSize = SizeOf (ElementType); long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsClassChar (ElementType) && + if (IsDeclRankChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { @@ -437,7 +437,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of a struct or union. Return the number of data bytes. */ { - SymEntry* Sym; + SymEntry* TagSym; SymTable* Tab; StructInitData SI; int HasCurly = 0; @@ -452,15 +452,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the struct entry from the type */ - Sym = GetESUSymEntry (T); + TagSym = GetESUTagSym (T); /* Get the size of the struct from the symbol table entry */ - SI.Size = Sym->V.S.Size; + SI.Size = TagSym->V.S.Size; /* Check if this struct definition has a field table. If it doesn't, it ** is an incomplete definition. */ - Tab = Sym->V.S.SymTab; + Tab = TagSym->V.S.SymTab; if (Tab == 0) { Error ("Cannot initialize variables with incomplete type"); /* Try error recovery */ @@ -470,7 +470,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the list of symbols */ - Sym = Tab->SymHead; + TagSym = Tab->SymHead; /* Initialize fields */ SI.Offs = 0; @@ -479,7 +479,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) while (CurTok.Tok != TOK_RCURLY) { /* Check for excess elements */ - if (Sym == 0) { + if (TagSym == 0) { /* Is there just one trailing comma before a closing curly? */ if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) { /* Skip comma and exit scope */ @@ -495,7 +495,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Check for special members that don't consume the initializer */ - if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) { + if ((TagSym->Flags & SC_ALIAS) == SC_ALIAS) { /* Just skip */ goto NextMember; } @@ -503,13 +503,13 @@ 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 (Sym) && (IsAnonName (Sym->Name))) { + 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 += Sym->Type->A.B.Width; + 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); @@ -526,7 +526,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SkipComma = 0; } - if (SymIsBitField (Sym)) { + if (SymIsBitField (TagSym)) { /* Parse initialization of one field. Bit-fields need a special ** handling. @@ -537,14 +537,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned long Mask = shl_l (1UL, Sym->Type->A.B.Width) - 1UL; + unsigned long Mask = shl_l (1UL, TagSym->Type->A.B.Width) - 1UL; /* Safety ... */ - CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == + CHECK (TagSym->V.Offs * CHAR_BITS + TagSym->Type->A.B.Offs == SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - Field = ParseScalarInitInternal (IntPromotion (Sym->Type)); + Field = ParseScalarInitInternal (IntPromotion (TagSym->Type)); if (!ED_IsConstAbsInt (&Field)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&Field, 1); @@ -554,19 +554,19 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** any useful bits. */ Val = (unsigned long) Field.IVal & Mask; - if (IsSignUnsigned (Sym->Type)) { + if (IsSignUnsigned (TagSym->Type)) { if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) { Warning (IsSignUnsigned (Field.Type) ? "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %lu to %lu" : "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %lu", - GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type), - Sym->Type->A.B.Width, Field.IVal, Val); + GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type), + TagSym->Type->A.B.Width, Field.IVal, Val); } } else { /* Sign extend back to full width of host long. */ - unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; + unsigned ShiftBits = sizeof (long) * CHAR_BIT - TagSym->Type->A.B.Width; long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits); if (Field.IVal != RestoredVal) { Warning (IsSignUnsigned (Field.Type) ? @@ -574,17 +574,17 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) " changes value from %lu to %ld" : "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %ld", - GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type), - Sym->Type->A.B.Width, Field.IVal, RestoredVal); + GetFullTypeName (Field.Type), GetFullTypeName (TagSym->Type), + TagSym->Type->A.B.Width, Field.IVal, RestoredVal); } } /* Add the value to the currently stored bit-field value */ - Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs; + Shift = (TagSym->V.Offs - SI.Offs) * CHAR_BITS + TagSym->Type->A.B.Offs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Sym->Type->A.B.Width; + SI.ValBits += TagSym->Type->A.B.Width; /* Make sure unsigned is big enough to hold the value, 32 bits. ** This cannot be more than 32 bits because a 16-bit or 32-bit ** bit-field will always be byte-aligned with padding before it @@ -602,14 +602,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Standard member. We should never have stuff from a ** bit-field left because an anonymous member was added - ** for padding by ParseStructDecl. + ** for padding by ParseStructSpec. */ CHECK (SI.ValBits == 0); /* Flexible array members may only be initialized if they are ** the last field (or part of the last struct field). */ - SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0); + SI.Offs += ParseInitInternal (TagSym->Type, Braces, AllowFlexibleMembers && TagSym->NextSym == 0); } /* More initializers? */ @@ -624,10 +624,10 @@ NextMember: /* Next member. For unions, only the first one can be initialized */ if (IsTypeUnion (T)) { /* Union */ - Sym = 0; + TagSym = 0; } else { /* Struct */ - Sym = Sym->NextSym; + TagSym = TagSym->NextSym; } } @@ -669,7 +669,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ExprDesc Expr = NoCodeConstExpr (hie1); - switch (GetUnderlyingTypeCode (&Expr.Type[0])) { + switch (GetUnqualTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -737,7 +737,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 (GetUnderlyingTypeCode (T)) { + switch (GetUnqualTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/input.c b/src/cc65/input.c index 22a66e1f7..89c471687 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -54,6 +54,7 @@ #include "input.h" #include "lineinfo.h" #include "output.h" +#include "preproc.h" @@ -66,6 +67,9 @@ /* The current input line */ StrBuf* Line; +/* The input line to reuse as the next line */ +static StrBuf* CurReusedLine; + /* Current and next input character */ char CurC = '\0'; char NextC = '\0'; @@ -91,6 +95,9 @@ struct AFile { FILE* F; /* Input file stream */ IFile* Input; /* Points to corresponding IFile */ int SearchPath; /* True if we've added a path for this file */ + char* PName; /* Presumed name of the file */ + PPIfStack IfStack; /* PP #if stack */ + int MissingNL; /* Last input line was missing a newline */ }; /* List of all input files */ @@ -99,8 +106,11 @@ static Collection IFiles = STATIC_COLLECTION_INITIALIZER; /* List of all active files */ static Collection AFiles = STATIC_COLLECTION_INITIALIZER; -/* Input stack used when preprocessing. */ -static Collection InputStack = STATIC_COLLECTION_INITIALIZER; +/* Input stack used when preprocessing */ +static Collection* CurrentInputStack; + +/* Counter for the __COUNTER__ macro */ +static unsigned MainFileCounter; @@ -156,6 +166,9 @@ static AFile* NewAFile (IFile* IF, FILE* F) AF->Line = 0; AF->F = F; AF->Input = IF; + AF->PName = 0; + AF->IfStack.Index = -1; + AF->MissingNL = 0; /* Increment the usage counter of the corresponding IFile. If this ** is the first use, set the file data and output debug info if @@ -204,6 +217,9 @@ static AFile* NewAFile (IFile* IF, FILE* F) static void FreeAFile (AFile* AF) /* Free an AFile structure */ { + if (AF->PName != 0) { + xfree (AF->PName); + } xfree (AF); } @@ -257,6 +273,12 @@ void OpenMainFile (const char* Name) /* Allocate a new AFile structure for the file */ MainFile = NewAFile (IF, F); + /* Use this file with PP */ + SetPPIfStack (&MainFile->IfStack); + + /* Begin PP for this file */ + PreprocessBegin (); + /* Allocate the input line buffer */ Line = NewStrBuf (); @@ -264,6 +286,9 @@ void OpenMainFile (const char* Name) ** the main file before the first line is read. */ UpdateLineInfo (MainFile->Input, MainFile->Line, Line); + + /* Initialize the __COUNTER__ counter */ + MainFileCounter = 0; } @@ -274,6 +299,7 @@ void OpenIncludeFile (const char* Name, InputType IT) char* N; FILE* F; IFile* IF; + AFile* AF; /* Check for the maximum include nesting */ if (CollCount (&AFiles) > MAX_INC_NESTING) { @@ -311,12 +337,18 @@ void OpenIncludeFile (const char* Name, InputType IT) Print (stdout, 1, "Opened include file '%s'\n", IF->Name); /* Allocate a new AFile structure */ - (void) NewAFile (IF, F); + AF = NewAFile (IF, F); + + /* Use this file with PP */ + SetPPIfStack (&AF->IfStack); + + /* Begin PP for this file */ + PreprocessBegin (); } -static void CloseIncludeFile (void) +void CloseIncludeFile (void) /* Close an include file and switch to the higher level file. Set Input to ** NULL if this was the main file. */ @@ -329,14 +361,18 @@ static void CloseIncludeFile (void) /* Must have an input file when called */ PRECONDITION (AFileCount > 0); + /* End preprocessor in this file */ + PreprocessEnd (); + /* Get the current active input file */ - Input = (AFile*) CollLast (&AFiles); + Input = CollLast (&AFiles); /* Close the current input file (we're just reading so no error check) */ fclose (Input->F); /* Delete the last active file from the active file collection */ - CollDelete (&AFiles, AFileCount-1); + --AFileCount; + CollDelete (&AFiles, AFileCount); /* If we had added an extra search path for this AFile, remove it */ if (Input->SearchPath) { @@ -345,6 +381,12 @@ static void CloseIncludeFile (void) /* Delete the active file structure */ FreeAFile (Input); + + /* Use previous file with PP if it is not the main file */ + if (AFileCount > 0) { + Input = CollLast (&AFiles); + SetPPIfStack (&Input->IfStack); + } } @@ -355,34 +397,19 @@ static void GetInputChar (void) ** are read by this function. */ { - /* Drop all pushed fragments that don't have data left */ - while (SB_GetIndex (Line) >= SB_GetLen (Line)) { - /* Cannot read more from this line, check next line on stack if any */ - if (CollCount (&InputStack) == 0) { - /* This is THE line */ - break; - } - FreeStrBuf (Line); - Line = CollPop (&InputStack); + /* Get the next-next character from the line */ + if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { + /* CurC and NextC come from this fragment */ + CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); + NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); + } else { + /* NextC is '\0' by default */ + NextC = '\0'; + + /* Get CurC from the line */ + CurC = SB_LookAt (Line, SB_GetIndex (Line)); } - /* Now get the next characters from the line */ - if (SB_GetIndex (Line) >= SB_GetLen (Line)) { - CurC = NextC = '\0'; - } else { - CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); - if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { - /* NextC comes from this fragment */ - NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); - } else { - /* NextC comes from next fragment */ - if (CollCount (&InputStack) > 0) { - NextC = ' '; - } else { - NextC = '\0'; - } - } - } } @@ -402,17 +429,41 @@ void NextChar (void) +Collection* UseInputStack (Collection* InputStack) +/* Use the provided input stack for incoming input. Return the previously used +** InputStack. +*/ +{ + Collection* OldInputStack = CurrentInputStack; + + CurrentInputStack = InputStack; + return OldInputStack; +} + + + +void PushLine (StrBuf* L) +/* Save the current input line and use a new one */ +{ + PRECONDITION (CurrentInputStack != 0); + CollAppend (CurrentInputStack, Line); + Line = L; + GetInputChar (); +} + + + +void ReuseInputLine (void) +/* Save and reuse the current line as the next line */ +{ + CurReusedLine = Line; +} + + + void ClearLine (void) /* Clear the current input line */ { - unsigned I; - - /* Remove all pushed fragments from the input stack */ - for (I = 0; I < CollCount (&InputStack); ++I) { - FreeStrBuf (CollAtUnchecked (&InputStack, I)); - } - CollDeleteAll (&InputStack); - /* Clear the contents of Line */ SB_Clear (Line); CurC = '\0'; @@ -436,47 +487,84 @@ StrBuf* InitLine (StrBuf* Buf) int NextLine (void) -/* Get a line from the current input. Returns 0 on end of file. */ +/* Get a line from the current input. Returns 0 on end of file with no new +** input bytes. +*/ { + int C; AFile* Input; - /* Clear the current line */ + /* Overwrite the next input line with the pushed line if there is one */ + if (CurReusedLine != 0) { + /* Use data move to resolve the issue that Line may be impersistent */ + if (Line != CurReusedLine) { + SB_Move (Line, CurReusedLine); + } + /* Continue with this Line */ + InitLine (Line); + CurReusedLine = 0; + + return 1; + } + + /* If there are pushed input lines, read from them */ + if (CurrentInputStack != 0 && CollCount (CurrentInputStack) > 0) { + /* Drop all pushed fragments that have no data left until one can be + ** used as input. + */ + do { + /* Use data move to resolve the issue that Line may be impersistent */ + if (Line != CollLast (CurrentInputStack)) { + SB_Move (Line, CollPop (CurrentInputStack)); + } else { + CollPop (CurrentInputStack); + } + } while (CollCount (CurrentInputStack) > 0 && + SB_GetIndex (Line) >= SB_GetLen (Line)); + + if (SB_GetIndex (Line) < SB_GetLen (Line)) { + InitLine (Line); + + /* Successive */ + return 1; + } + } + + /* Otherwise, clear the current line */ ClearLine (); - /* If there is no file open, bail out, otherwise get the current input file */ + /* Must have an input file when going on */ if (CollCount (&AFiles) == 0) { return 0; } + + /* Get the current input file */ Input = CollLast (&AFiles); /* Read characters until we have one complete line */ while (1) { /* Read the next character */ - int C = fgetc (Input->F); + C = fgetc (Input->F); /* Check for EOF */ if (C == EOF) { - /* Accept files without a newline at the end */ - if (SB_NotEmpty (Line)) { + if (!Input->MissingNL || SB_NotEmpty (Line)) { + + /* Accept files without a newline at the end */ ++Input->Line; - break; - } - /* Leave the current file */ - CloseIncludeFile (); + /* Assume no new line */ + Input->MissingNL = 1; - /* If there is no file open, bail out, otherwise get the - ** previous input file and start over. - */ - if (CollCount (&AFiles) == 0) { - return 0; } - Input = CollLast (&AFiles); - continue; + break; } + /* Assume no new line */ + Input->MissingNL = 1; + /* Check for end of line */ if (C == '\n') { @@ -490,14 +578,16 @@ int NextLine (void) SB_Drop (Line, 1); } - /* If we don't have a line continuation character at the end, - ** we're done with this line. Otherwise replace the character - ** by a newline and continue reading. + /* If we don't have a line continuation character at the end, we + ** are done with this line. Otherwise just skip the character and + ** continue reading. */ - if (SB_LookAtLast (Line) == '\\') { - Line->Buf[Line->Len-1] = '\n'; - } else { + if (SB_LookAtLast (Line) != '\\') { + Input->MissingNL = 0; break; + } else { + SB_Drop (Line, 1); + ContinueLine (); } } else if (C != '\0') { /* Ignore embedded NULs */ @@ -517,6 +607,38 @@ int NextLine (void) /* Create line information for this line */ UpdateLineInfo (Input->Input, Input->Line, Line); + /* Done */ + return C != EOF || SB_NotEmpty (Line); +} + + + +int PreprocessNextLine (void) +/* Get a line from opened input files and do preprocess. Returns 0 on end of +** main file. +*/ +{ + while (NextLine() == 0) { + + /* If there is no input file open, bail out. Otherwise get the previous + ** input file and start over. + */ + if (CollCount (&AFiles) == 0) { + return 0; + } + + /* Leave the current file */ + CloseIncludeFile (); + } + + /* Do preprocess anyways */ + Preprocess (); + + /* Write it to the output file if in preprocess-only mode */ + if (PreprocessOnly) { + WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); + } + /* Done */ return 1; } @@ -531,33 +653,27 @@ const char* GetInputFile (const struct IFile* IF) -const char* GetCurrentFile (void) +const char* GetCurrentFilename (void) /* Return the name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { - const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1); - return AF->Input->Name; + const AFile* AF = CollLast (&AFiles); + return AF->PName == 0 ? AF->Input->Name : AF->PName; } else { - /* No open file. Use the main file if we have one. */ - unsigned IFileCount = CollCount (&IFiles); - if (IFileCount > 0) { - const IFile* IF = (const IFile*) CollAt (&IFiles, 0); - return IF->Name; - } else { - return "(outside file scope)"; - } + /* No open file */ + return "(outside file scope)"; } } -unsigned GetCurrentLine (void) +unsigned GetCurrentLineNum (void) /* Return the line number in the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { - const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1); + const AFile* AF = CollLast (&AFiles); return AF->Line; } else { /* No open file */ @@ -567,6 +683,43 @@ unsigned GetCurrentLine (void) +void SetCurrentLineNum (unsigned LineNum) +/* Set the line number in the current input file */ +{ + unsigned AFileCount = CollCount (&AFiles); + if (AFileCount > 0) { + AFile* AF = CollLast (&AFiles); + AF->Line = LineNum; + } +} + + + +void SetCurrentFilename (const char* Name) +/* Set the presumed name of the current input file */ +{ + unsigned AFileCount = CollCount (&AFiles); + if (AFileCount > 0) { + size_t Len = strlen (Name); + AFile* AF = CollLast (&AFiles); + if (AF->PName != 0) { + xfree (AF->PName); + } + AF->PName = xmalloc (Len + 1); + memcpy (AF->PName, Name, Len + 1); + } +} + + + +unsigned GetCurrentCounter (void) +/* Return the counter number in the current input file */ +{ + return MainFileCounter++; +} + + + static void WriteEscaped (FILE* F, const char* Name) /* Write a file name to a dependency file escaping spaces */ { diff --git a/src/cc65/input.h b/src/cc65/input.h index a643800ba..9457bdf9b 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -41,12 +41,13 @@ #include <stdio.h> /* common */ +#include "coll.h" #include "strbuf.h" /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ @@ -84,12 +85,28 @@ void OpenMainFile (const char* Name); void OpenIncludeFile (const char* Name, InputType IT); /* Open an include file and insert it into the tables. */ +void CloseIncludeFile (void); +/* Close an include file and switch to the higher level file. Set Input to +** NULL if this was the main file. +*/ + void NextChar (void); /* Read the next character from the input stream and make CurC and NextC ** valid. If end of line is reached, both are set to NUL, no more lines ** are read by this function. */ +Collection* UseInputStack (Collection* InputStack); +/* Use the provided input stack for incoming input. Return the previously used +** InputStack. +*/ + +void PushLine (StrBuf* L); +/* Save the current input line and use a new one */ + +void ReuseInputLine (void); +/* Save and reuse the current line as the next line */ + void ClearLine (void); /* Clear the current input line */ @@ -99,17 +116,33 @@ StrBuf* InitLine (StrBuf* Buf); */ int NextLine (void); -/* Get a line from the current input. Returns 0 on end of file. */ +/* Get a line from the current input. Returns 0 on end of file with no new +** input bytes. +*/ + +int PreprocessNextLine (void); +/* Get a line from opened input files and do preprocess. Returns 0 on end of +** main file. +*/ const char* GetInputFile (const struct IFile* IF); /* Return a filename from an IFile struct */ -const char* GetCurrentFile (void); +const char* GetCurrentFilename (void); /* Return the name of the current input file */ -unsigned GetCurrentLine (void); +unsigned GetCurrentLineNum (void); /* Return the line number in the current input file */ +void SetCurrentLineNum (unsigned LineNum); +/* Set the line number in the current input file */ + +void SetCurrentFilename (const char* Name); +/* Set the presumed name of the current input file */ + +unsigned GetCurrentCounter (void); +/* Return the counter number in the current input file */ + void CreateDependencies (void); /* Create dependency files requested by the user */ diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index 95228179d..d741f87d0 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -126,9 +126,6 @@ static void FreeLiteral (Literal* L) static void OutputLiteral (Literal* L) /* Output one literal to the currently active data segment */ { - /* Translate the literal into the target charset */ - TranslateLiteral (L); - /* Define the label for the literal */ g_defliterallabel (L->Label); @@ -387,9 +384,6 @@ static void OutputReadOnlyLiterals (Collection* Literals) continue; } - /* Translate the literal into the target charset */ - TranslateLiteral (L); - /* Check if this literal is part of another one. Since the literals ** are sorted by size (larger ones first), it can only be part of a ** literal with a smaller index. diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 0b75e0a9c..c5ac43f78 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -36,6 +36,7 @@ /* cc65 */ #include "codegen.h" #include "error.h" +#include "expr.h" #include "exprdesc.h" #include "global.h" #include "loadexpr.h" @@ -92,7 +93,6 @@ static void LoadAddress (unsigned Flags, ExprDesc* Expr) g_leasp (Expr->IVal); break; - case E_LOC_PRIMARY: case E_LOC_EXPR: if (Expr->IVal != 0) { /* We have an expression in the primary plus a constant diff --git a/src/cc65/locals.c b/src/cc65/locals.c index ad36bded0..68ac00e62 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -97,8 +97,8 @@ static void AllocStorage (unsigned DataLabel, void (*UseSeg) (), unsigned Size) -static void ParseRegisterDecl (Declaration* Decl, int Reg) -/* Parse the declaration of a register variable. Reg is the offset of the +static void ParseRegisterDecl (Declarator* Decl, int Reg) +/* Parse the declarator of a register variable. Reg is the offset of the ** variable in the register bank. */ { @@ -186,8 +186,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) -static void ParseAutoDecl (Declaration* Decl) -/* Parse the declaration of an auto variable. */ +static void ParseAutoDecl (Declarator* Decl) +/* Parse the declarator of an auto variable. */ { unsigned Flags; SymEntry* Sym; @@ -287,7 +287,7 @@ static void ParseAutoDecl (Declaration* Decl) ** We abuse the Collection somewhat by using it to store line ** numbers. */ - CollReplace (&CurrentFunc->LocalsBlockStack, (void *)(size_t)GetCurrentLine (), + CollReplace (&CurrentFunc->LocalsBlockStack, (void *)(size_t)GetCurrentLineNum (), CollCount (&CurrentFunc->LocalsBlockStack) - 1); } else { @@ -382,8 +382,8 @@ static void ParseAutoDecl (Declaration* Decl) -static void ParseStaticDecl (Declaration* Decl) -/* Parse the declaration of a static variable. */ +static void ParseStaticDecl (Declarator* Decl) +/* Parse the declarator of a static variable. */ { unsigned Size; @@ -441,12 +441,12 @@ static void ParseStaticDecl (Declaration* Decl) static void ParseOneDecl (const DeclSpec* Spec) -/* Parse one variable declaration */ +/* Parse one variable declarator. */ { - Declaration Decl; /* Declaration data structure */ + Declarator Decl; /* Declarator data structure */ - /* Read the declaration */ + /* Read the declarator */ ParseDecl (Spec, &Decl, DM_NEED_IDENT); /* Check if there are any non-extern storage classes set for function @@ -465,8 +465,8 @@ static void ParseOneDecl (const DeclSpec* Spec) /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; - /* This is always a declaration */ - Decl.StorageClass |= SC_DECL; + /* This is always an extern declaration */ + Decl.StorageClass |= SC_DECL | SC_EXTERN; } /* If we don't have a name, this was flagged as an error earlier. @@ -524,7 +524,9 @@ static void ParseOneDecl (const DeclSpec* Spec) if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || (Decl.StorageClass & SC_FUNC) == SC_FUNC) { - /* Add the global symbol to the local symbol table */ + /* Add the global symbol to both of the global and local symbol + ** tables. + */ AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); } else { /* Add the local symbol to the local symbol table */ @@ -566,7 +568,7 @@ void DeclareLocals (void) continue; } - ParseDeclSpec (&Spec, SC_AUTO, T_INT); + 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 */ diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c index c04024dc8..3bfae0811 100644 --- a/src/cc65/macrotab.c +++ b/src/cc65/macrotab.c @@ -42,6 +42,7 @@ /* cc65 */ #include "error.h" +#include "preproc.h" #include "macrotab.h" @@ -56,6 +57,9 @@ #define MACRO_TAB_SIZE 211 static Macro* MacroTab[MACRO_TAB_SIZE]; +/* The undefined macros list head */ +static Macro* UndefinedMacrosListHead; + /*****************************************************************************/ @@ -70,19 +74,17 @@ Macro* NewMacro (const char* Name) */ { /* Get the length of the macro name */ - unsigned Len = strlen(Name); + unsigned Len = strlen (Name); /* Allocate the structure */ Macro* M = (Macro*) xmalloc (sizeof(Macro) + Len); /* Initialize the data */ - M->Next = 0; - M->Expanding = 0; - M->ArgCount = -1; /* Flag: Not a function like macro */ - M->MaxArgs = 0; - InitCollection (&M->FormalArgs); + M->Next = 0; + M->ParamCount = -1; /* Flag: Not a function-like macro */ + InitCollection (&M->Params); SB_Init (&M->Replacement); - M->Variadic = 0; + M->Variadic = 0; memcpy (M->Name, Name, Len+1); /* Return the new macro */ @@ -98,16 +100,39 @@ void FreeMacro (Macro* M) { unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - xfree (CollAtUnchecked (&M->FormalArgs, I)); + for (I = 0; I < CollCount (&M->Params); ++I) { + xfree (CollAtUnchecked (&M->Params, I)); } - DoneCollection (&M->FormalArgs); + DoneCollection (&M->Params); SB_Done (&M->Replacement); xfree (M); } +Macro* CloneMacro (const Macro* M) +/* Clone a macro definition. The function is not insert the macro into the +** macro table, thus the cloned instance cannot be freed with UndefineMacro. +** Use FreeMacro for that. +*/ +{ + Macro* New = NewMacro (M->Name); + unsigned I; + + for (I = 0; I < CollCount (&M->Params); ++I) { + /* Copy the parameter */ + const char* Param = CollAtUnchecked (&M->Params, I); + CollAppend (&New->Params, xstrdup (Param)); + } + New->ParamCount = M->ParamCount; + New->Variadic = M->Variadic; + SB_Copy (&New->Replacement, &M->Replacement); + + return New; +} + + + void DefineNumericMacro (const char* Name, long Val) /* Define a macro for a numeric constant */ { @@ -150,10 +175,11 @@ void InsertMacro (Macro* M) -int UndefineMacro (const char* Name) -/* Search for the macro with the given name and remove it from the macro -** table if it exists. Return 1 if a macro was found and deleted, return -** 0 otherwise. +Macro* UndefineMacro (const char* Name) +/* Search for the macro with the given name, if it exists, remove it from +** the defined macro table and insert it to a list for pending deletion. +** Return the macro if it was found and removed, return 0 otherwise. +** To safely free the removed macro, use FreeUndefinedMacros(). */ { /* Get the hash value of the macro name */ @@ -173,11 +199,12 @@ int UndefineMacro (const char* Name) L->Next = M->Next; } - /* Delete the macro */ - FreeMacro (M); + /* Add this macro to pending deletion list */ + M->Next = UndefinedMacrosListHead; + UndefinedMacrosListHead = M; /* Done */ - return 1; + return M; } /* Next macro */ @@ -191,6 +218,23 @@ int UndefineMacro (const char* Name) +void FreeUndefinedMacros (void) +/* Free all undefined macros */ +{ + Macro* Next; + + while (UndefinedMacrosListHead != 0) { + Next = UndefinedMacrosListHead->Next; + + /* Delete the macro */ + FreeMacro (UndefinedMacrosListHead); + + UndefinedMacrosListHead = Next; + } +} + + + Macro* FindMacro (const char* Name) /* Find a macro with the given name. Return the macro definition or NULL */ { @@ -201,6 +245,10 @@ Macro* FindMacro (const char* Name) Macro* M = MacroTab[Hash]; while (M) { if (strcmp (M->Name, Name) == 0) { + /* Check for some special macro names */ + if (Name[0] == '_') { + HandleSpecialMacro (M, Name); + } /* Found it */ return M; } @@ -215,14 +263,14 @@ Macro* FindMacro (const char* Name) -int FindMacroArg (Macro* M, const char* Arg) -/* Search for a formal macro argument. If found, return the index of the -** argument. If the argument was not found, return -1. +int FindMacroParam (const Macro* M, const char* Param) +/* Search for a macro parameter. If found, return the index of the parameter. +** If the parameter was not found, return -1. */ { unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { + for (I = 0; I < CollCount (&M->Params); ++I) { + if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) { /* Found */ return I; } @@ -234,25 +282,25 @@ int FindMacroArg (Macro* M, const char* Arg) -void AddMacroArg (Macro* M, const char* Arg) -/* Add a formal macro argument. */ +void AddMacroParam (Macro* M, const char* Param) +/* Add a macro parameter. */ { - /* Check if we have a duplicate macro argument, but add it anyway. - ** Beware: Don't use FindMacroArg here, since the actual argument array + /* Check if we have a duplicate macro parameter, but add it anyway. + ** Beware: Don't use FindMacroParam here, since the actual argument array ** may not be initialized. */ unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { + for (I = 0; I < CollCount (&M->Params); ++I) { + if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) { /* Found */ - PPError ("Duplicate macro parameter: '%s'", Arg); + PPError ("Duplicate macro parameter: '%s'", Param); break; } } - /* Add the new argument */ - CollAppend (&M->FormalArgs, xstrdup (Arg)); - ++M->ArgCount; + /* Add the new parameter */ + CollAppend (&M->Params, xstrdup (Param)); + ++M->ParamCount; } @@ -263,14 +311,14 @@ int MacroCmp (const Macro* M1, const Macro* M2) int I; /* Argument count must be identical */ - if (M1->ArgCount != M2->ArgCount) { + if (M1->ParamCount != M2->ParamCount) { return 1; } - /* Compare the arguments */ - for (I = 0; I < M1->ArgCount; ++I) { - if (strcmp (CollConstAt (&M1->FormalArgs, I), - CollConstAt (&M2->FormalArgs, I)) != 0) { + /* Compare the parameters */ + for (I = 0; I < M1->ParamCount; ++I) { + if (strcmp (CollConstAt (&M1->Params, I), + CollConstAt (&M2->Params, I)) != 0) { return 1; } } diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h index c3ff20ceb..52b812b2f 100644 --- a/src/cc65/macrotab.h +++ b/src/cc65/macrotab.h @@ -55,10 +55,8 @@ typedef struct Macro Macro; struct Macro { Macro* Next; /* Next macro with same hash value */ - int Expanding; /* Are we currently expanding this macro? */ - int ArgCount; /* Number of parameters, -1 = no parens */ - unsigned MaxArgs; /* Size of formal argument list */ - Collection FormalArgs; /* Formal argument list (char*) */ + int ParamCount; /* Number of parameters, -1 = no parens */ + Collection Params; /* Parameter list (char*) */ StrBuf Replacement; /* Replacement text */ unsigned char Variadic; /* C99 variadic macro */ char Name[1]; /* Name, dynamically allocated */ @@ -82,6 +80,12 @@ void FreeMacro (Macro* M); ** table, use UndefineMacro for that. */ +Macro* CloneMacro (const Macro* M); +/* Clone a macro definition. The function is not insert the macro into the +** macro table, thus the cloned instance cannot be freed with UndefineMacro. +** Use FreeMacro for that. +*/ + void DefineNumericMacro (const char* Name, long Val); /* Define a macro for a numeric constant */ @@ -91,12 +95,16 @@ void DefineTextMacro (const char* Name, const char* Val); void InsertMacro (Macro* M); /* Insert the given macro into the macro table. */ -int UndefineMacro (const char* Name); -/* Search for the macro with the given name and remove it from the macro -** table if it exists. Return 1 if a macro was found and deleted, return -** 0 otherwise. +Macro* UndefineMacro (const char* Name); +/* Search for the macro with the given name, if it exists, remove it from +** the defined macro table and insert it to a list for pending deletion. +** Return the macro if it was found and removed, return 0 otherwise. +** To safely free the removed macro, use FreeUndefinedMacros(). */ +void FreeUndefinedMacros (void); +/* Free all undefined macros */ + Macro* FindMacro (const char* Name); /* Find a macro with the given name. Return the macro definition or NULL */ @@ -110,13 +118,13 @@ INLINE int IsMacro (const char* Name) # define IsMacro(Name) (FindMacro (Name) != 0) #endif -int FindMacroArg (Macro* M, const char* Arg); -/* Search for a formal macro argument. If found, return the index of the -** argument. If the argument was not found, return -1. +int FindMacroParam (const Macro* M, const char* Param); +/* Search for a macro parameter. If found, return the index of the parameter. +** If the parameter was not found, return -1. */ -void AddMacroArg (Macro* M, const char* Arg); -/* Add a formal macro argument. */ +void AddMacroParam (Macro* M, const char* Param); +/* Add a macro parameter. */ int MacroCmp (const Macro* M1, const Macro* M2); /* Compare two macros and return zero if both are identical. */ diff --git a/src/cc65/main.c b/src/cc65/main.c index c08616efa..f800ac43e 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -299,6 +299,10 @@ static void SetSys (const char* Sys) DefineNumericMacro ("__SYM1__", 1); break; + case TGT_KIM1: + DefineNumericMacro ("__KIM1__", 1); + break; + default: AbEnd ("Unknown target system '%s'", Sys); } diff --git a/src/cc65/ppexpr.c b/src/cc65/ppexpr.c new file mode 100644 index 000000000..8d8c0b65d --- /dev/null +++ b/src/cc65/ppexpr.c @@ -0,0 +1,849 @@ +/*****************************************************************************/ +/* */ +/* ppexpr.h */ +/* */ +/* Expressions for C preprocessor */ +/* */ +/* */ +/* */ +/* (C) 2022 The cc65 Authors */ +/* */ +/* */ +/* 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 "error.h" +#include "scanner.h" +#include "ppexpr.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* PP expression parser status */ +static int PPEvaluationEnabled = 0; +static int PPEvaluationFailed = 0; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void PPhie0 (PPExpr* Expr); +static void PPhie1 (PPExpr* Expr); + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static token_t PPFindTok (token_t Tok, const token_t* Table) +/* Find a token in a generator table */ +{ + while (*Table != TOK_INVALID) { + if (*Table == Tok) { + return Tok; + } + ++Table; + } + return TOK_INVALID; +} + + + +static void PPExprInit (PPExpr* Expr) +/* Initialize the expression */ +{ + Expr->IVal = 0; + Expr->Flags = PPEXPR_NONE; +} + + + +static void PPErrorSkipLine (void) +/* Set the expression parser error flag, skip the remain tokens till the end +** of the line, clear the current and the next tokens. +*/ +{ + PPEvaluationFailed = 1; + SkipTokens (0, 0); + CurTok.Tok = TOK_CEOF; + NextTok.Tok = TOK_CEOF; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void PPhiePrimary (PPExpr* Expr) +/* This is the lowest level of the PP expression parser */ +{ + switch (CurTok.Tok) { + case TOK_ICONST: + case TOK_CCONST: + case TOK_WCCONST: + /* Character and integer constants */ + Expr->IVal = CurTok.IVal; + /* According to the C standard, all signed types act as intmax_t + ** and all unsigned types act as uintmax_t. + */ + if (IsSignUnsigned (CurTok.Type)) { + Expr->Flags |= PPEXPR_UNSIGNED; + } + NextToken (); + break; + + case TOK_FCONST: + /* Floating point constant */ + PPError ("Floating constant in preprocessor expression"); + Expr->IVal = 0; + NextToken (); + break; + + case TOK_LPAREN: + /* Parse parenthesized subexpression by calling the whole parser + ** recursively. + */ + NextToken (); + PPhie0 (Expr); + ConsumeRParen (); + break; + + case TOK_IDENT: + /* Assume that this identifier is an undefined macro and replace + ** it by a constant value of zero. + */ + NextToken (); + Expr->Flags |= PPEXPR_UNDEFINED; + Expr->IVal = 0; + break; + + case TOK_CEOF: + /* Error recovery */ + break; + + default: + /* Illegal expression in PP mode */ + PPError ("Preprocessor expression expected"); + PPErrorSkipLine (); + break; + } +} + + + +static void PPhie11 (PPExpr* Expr) +/* Handle compound types (structs and arrays) etc which are invalid in PP */ +{ + /* Evaluate the lhs */ + PPhiePrimary (Expr); + + /* Check for a rhs */ + while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC || + CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || + CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) { + + switch (CurTok.Tok) { + + case TOK_LBRACK: + PPError ("Token \".\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_LPAREN: + /* Function call syntax is not recognized in preprocessor + ** expressions. + */ + PPError ("Missing binary operator before token \"(\""); + PPErrorSkipLine (); + break; + + case TOK_DOT: + PPError ("Token \".\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_PTR_REF: + PPError ("Token \"->\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_INC: + PPError ("Token \"++\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_DEC: + PPError ("Token \"--\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + default: + Internal ("Invalid token in PPhie11: %d", CurTok.Tok); + + } + } + + /* Check for excessive expressions */ + if (!TokIsPunc (&CurTok)) { + PPError ("Missing binary operator"); + PPErrorSkipLine (); + } +} + + + +void PPhie10 (PPExpr* Expr) +/* Handle prefixing unary operators */ +{ + switch (CurTok.Tok) { + + case TOK_INC: + PPError ("Token \"++\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_DEC: + PPError ("Token \"--\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_PLUS: + NextToken (); + PPhie10 (Expr); + Expr->IVal = +Expr->IVal; + break; + + case TOK_MINUS: + NextToken (); + PPhie10 (Expr); + Expr->IVal = -Expr->IVal; + break; + + case TOK_COMP: + NextToken (); + PPhie10 (Expr); + Expr->IVal = ~Expr->IVal; + break; + + case TOK_BOOL_NOT: + NextToken (); + PPhie10 (Expr); + Expr->IVal = !Expr->IVal; + break; + + case TOK_CEOF: + /* Error recovery */ + break; + + case TOK_STAR: + case TOK_AND: + case TOK_SIZEOF: + default: + /* Type cast, sizeof, *, &, are not recognized in preprocessor + ** expressions. So everything is treated as as expression here. + */ + PPhie11 (Expr); + break; + } +} + + + +static void PPhie_internal (const token_t* Ops, /* List of generators */ + PPExpr* Expr, + void (*hienext) (PPExpr*)) +/* Helper function */ +{ + token_t Tok; + + hienext (Expr); + + while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) { + + PPExpr Rhs; + PPExprInit (&Rhs); + + /* Remember the operator token, then skip it */ + NextToken (); + + /* Get the right hand side */ + hienext (&Rhs); + + if (PPEvaluationEnabled && !PPEvaluationFailed) { + + /* Evaluate the result for operands */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Rhs.IVal; + + /* If either side is unsigned, the result is unsigned */ + Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; + + switch (Tok) { + case TOK_OR: + Expr->IVal = (Val1 | Val2); + break; + case TOK_XOR: + Expr->IVal = (Val1 ^ Val2); + break; + case TOK_AND: + Expr->IVal = (Val1 & Val2); + break; + case TOK_PLUS: + Expr->IVal = (Val1 + Val2); + break; + case TOK_MINUS: + Expr->IVal = (Val1 - Val2); + break; + case TOK_MUL: + Expr->IVal = (Val1 * Val2); + break; + case TOK_DIV: + if (Val2 == 0) { + PPError ("Division by zero"); + Expr->IVal = 0; + } else { + /* Handle signed and unsigned operands differently */ + if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { + Expr->IVal = ((long)Val1 / (long)Val2); + } else { + Expr->IVal = (Val1 / Val2); + } + } + break; + case TOK_MOD: + if (Val2 == 0) { + PPError ("Modulo operation with zero"); + Expr->IVal = 0; + } else { + /* Handle signed and unsigned operands differently */ + if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { + Expr->IVal = ((long)Val1 % (long)Val2); + } else { + Expr->IVal = (Val1 % Val2); + } + } + break; + default: + Internal ("PPhie_internal: got token 0x%X\n", Tok); + } + } + } +} + + + +static void PPhie_compare (const token_t* Ops, /* List of generators */ + PPExpr* Expr, + void (*hienext) (PPExpr*)) +/* Helper function for the compare operators */ +{ + token_t Tok; + + hienext (Expr); + + while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) { + + PPExpr Rhs; + + PPExprInit (&Rhs); + + /* Skip the operator token */ + NextToken (); + + /* Get the right hand side */ + hienext (&Rhs); + + if (PPEvaluationEnabled && !PPEvaluationFailed) { + + /* If either side is unsigned, the comparison is unsigned */ + Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; + + /* Determine if this is a signed or unsigned compare */ + if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) { + + /* Evaluate the result for signed operands */ + signed long Val1 = Expr->IVal; + signed long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_EQ: Expr->IVal = (Val1 == Val2); break; + case TOK_NE: Expr->IVal = (Val1 != Val2); break; + case TOK_LT: Expr->IVal = (Val1 < Val2); break; + case TOK_LE: Expr->IVal = (Val1 <= Val2); break; + case TOK_GE: Expr->IVal = (Val1 >= Val2); break; + case TOK_GT: Expr->IVal = (Val1 > Val2); break; + default: Internal ("PPhie_compare: got token 0x%X\n", Tok); + } + + } else { + + /* Evaluate the result for unsigned operands */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Rhs.IVal; + switch (Tok) { + case TOK_EQ: Expr->IVal = (Val1 == Val2); break; + case TOK_NE: Expr->IVal = (Val1 != Val2); break; + case TOK_LT: Expr->IVal = (Val1 < Val2); break; + case TOK_LE: Expr->IVal = (Val1 <= Val2); break; + case TOK_GE: Expr->IVal = (Val1 >= Val2); break; + case TOK_GT: Expr->IVal = (Val1 > Val2); break; + default: Internal ("PPhie_compare: got token 0x%X\n", Tok); + } + } + } + } + + /* The result is signed */ + Expr->Flags &= ~PPEXPR_UNSIGNED; +} + + + +static void PPhie9 (PPExpr* Expr) +/* Handle "*", "/" and "%" operators */ +{ + static const token_t PPhie9_ops[] = { + TOK_STAR, + TOK_DIV, + TOK_MOD, + TOK_INVALID + }; + + PPhie_internal (PPhie9_ops, Expr, PPhie10); +} + + + +static void PPhie8 (PPExpr* Expr) +/* Handle "+" and "-" binary operators */ +{ + static const token_t PPhie8_ops[] = { + TOK_PLUS, + TOK_MINUS, + TOK_INVALID + }; + + PPhie_internal (PPhie8_ops, Expr, PPhie9); +} + + + +static void PPhie7 (PPExpr* Expr) +/* Handle the "<<" and ">>" shift operators */ +{ + /* Evaluate the lhs */ + PPhie8 (Expr); + + while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) { + + token_t Op; /* The operator token */ + PPExpr Rhs; + PPExprInit (&Rhs); + + /* Remember the operator, then skip its token */ + Op = CurTok.Tok; + NextToken (); + + /* Get the right hand side */ + PPhie8 (&Rhs); + + /* Evaluate */ + if (PPEvaluationEnabled && !PPEvaluationFailed) { + /* For now we use 32-bit integer types for PP integer constants */ + if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0) { + if ((unsigned long)Rhs.IVal > LONG_BITS) { + Rhs.IVal = (long)LONG_BITS; + } + } else if (Rhs.IVal > (long)LONG_BITS) { + Rhs.IVal = (long)LONG_BITS; + } else if (Rhs.IVal < -(long)LONG_BITS) { + Rhs.IVal = -(long)LONG_BITS; + } + + /* Positive count for left-shift and negative for right-shift. So + ** to shift by a count is equivalent to shift to the opposite + ** direction by the negated count. + */ + if (Op == TOK_SHR) { + Rhs.IVal = -Rhs.IVal; + } + + /* Evaluate the result */ + if ((Expr->Flags & PPEXPR_UNSIGNED) != 0) { + if (Rhs.IVal >= (long)LONG_BITS) { + PPWarning ("Integer overflow in preprocessor expression"); + Expr->IVal = 0; + } else if (Rhs.IVal > 0) { + Expr->IVal <<= Rhs.IVal; + } else if (Rhs.IVal <= -(long)LONG_BITS) { + Expr->IVal = 0; + } else if (Rhs.IVal < 0) { + Expr->IVal = (unsigned long)Expr->IVal >> -Rhs.IVal; + } + } else { + /* -1 for sign bit */ + if (Rhs.IVal >= (long)(LONG_BITS - 1)) { + PPWarning ("Integer overflow in preprocessor expression"); + Expr->IVal = 0; + } else if (Rhs.IVal > 0) { + Expr->IVal <<= Rhs.IVal; + } else if (Rhs.IVal <= -(long)LONG_BITS) { + Expr->IVal = Expr->IVal >= 0 ? 0 : -1; + } else if (Rhs.IVal < 0) { + Expr->IVal = (long)Expr->IVal >> -Rhs.IVal; + } + } + } + } +} + + + +static void PPhie6 (PPExpr* Expr) +/* Handle greater-than type relational operators */ +{ + static const token_t PPhie6_ops [] = { + TOK_LT, + TOK_LE, + TOK_GE, + TOK_GT, + TOK_INVALID + }; + + PPhie_compare (PPhie6_ops, Expr, PPhie7); +} + + + +static void PPhie5 (PPExpr* Expr) +/* Handle "==" and "!=" relational operators */ +{ + static const token_t PPhie5_ops[] = { + TOK_EQ, + TOK_NE, + TOK_INVALID + }; + + PPhie_compare (PPhie5_ops, Expr, PPhie6); +} + + + +static void PPhie4 (PPExpr* Expr) +/* Handle the bitwise AND "&" operator */ +{ + static const token_t PPhie4_ops[] = { + TOK_AND, + TOK_INVALID + }; + + PPhie_internal (PPhie4_ops, Expr, PPhie5); +} + + + +static void PPhie3 (PPExpr* Expr) +/* Handle the bitwise exclusive OR "^" operator */ +{ + static const token_t PPhie3_ops[] = { + TOK_XOR, + TOK_INVALID + }; + + PPhie_internal (PPhie3_ops, Expr, PPhie4); +} + + + +static void PPhie2 (PPExpr* Expr) +/* Handle the bitwise OR "|" operator */ +{ + static const token_t PPhie2_ops[] = { + TOK_OR, + TOK_INVALID + }; + + PPhie_internal (PPhie2_ops, Expr, PPhie3); +} + + + +static void PPhieAnd (PPExpr* Expr) +/* Handle the logical AND "expr1 && expr2" operator */ +{ + /* Get one operand */ + PPhie2 (Expr); + + if (CurTok.Tok == TOK_BOOL_AND) { + + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr One; + + /* Do logical and */ + Expr->IVal = (Expr->IVal != 0); + if (Expr->IVal == 0) { + PPEvaluationEnabled = 0; + } + + /* While there are more expressions */ + while (CurTok.Tok == TOK_BOOL_AND) { + /* Skip the && */ + NextToken (); + + /* Get one operand */ + PPExprInit (&One); + PPhie2 (&One); + + /* Evaluate */ + if (PPEvaluationEnabled) { + if (One.IVal == 0) { + /* Skip evaluating remaining */ + PPEvaluationEnabled = 0; + /* The value of the result will be false */ + Expr->IVal = 0; + } + } + } + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhieOr (PPExpr* Expr) +/* Handle the logical OR "||" operator */ +{ + /* Call the next level parser */ + PPhieAnd (Expr); + + if (CurTok.Tok == TOK_BOOL_OR) { + + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr One; + + /* Do logical or */ + Expr->IVal = (Expr->IVal != 0); + if (Expr->IVal != 0) { + PPEvaluationEnabled = 0; + } + + /* While there are more expressions */ + while (CurTok.Tok == TOK_BOOL_OR) { + /* Skip the || */ + NextToken (); + + /* Get rhs subexpression */ + PPExprInit (&One); + PPhieAnd (&One); + + /* Evaluate */ + if (PPEvaluationEnabled) { + if (One.IVal != 0) { + /* Skip evaluating remaining */ + PPEvaluationEnabled = 0; + /* The value of the result will be true */ + Expr->IVal = 1; + } + } + } + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhieQuest (PPExpr* Expr) +/* Handle the ternary "expr1 ? expr2 : expr3 " operator */ +{ + /* Call the lower level eval routine */ + PPhieOr (Expr); + + /* Check if it's a ternary expression */ + if (CurTok.Tok == TOK_QUEST) { + int PPEvaluationEnabledPrev = PPEvaluationEnabled; + PPExpr Expr2; /* Expression 2 */ + PPExpr Expr3; /* Expression 3 */ + + /* Skip the question mark */ + NextToken (); + + /* Disable evaluation for Expr2 if the condition is false */ + if (Expr->IVal == 0) { + PPEvaluationEnabled = 0; + } + + /* Parse second expression */ + PPExprInit (&Expr2); + PPhie0 (&Expr2); + + /* Skip the colon */ + ConsumeColon (); + + /* Disable evaluation for Expr3 if the condition is true */ + if (Expr->IVal != 0) { + PPEvaluationEnabled = 0; + } + + /* Parse third expression */ + PPExprInit (&Expr3); + PPhieQuest (&Expr3); + + /* Set the result */ + Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0; + + /* Restore evaluation as before */ + PPEvaluationEnabled = PPEvaluationEnabledPrev; + } +} + + + +static void PPhie1 (PPExpr* Expr) +/* Handle first level of expression hierarchy */ +{ + PPhieQuest (Expr); + + if (!PPEvaluationEnabled) { + /* Skip evaluation */ + return; + } + + switch (CurTok.Tok) { + + case TOK_ASSIGN: + PPError ("Token \"=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_PLUS_ASSIGN: + PPError ("Token \"+=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_MINUS_ASSIGN: + PPError ("Token \"-=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_MUL_ASSIGN: + PPError ("Token \"*=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_DIV_ASSIGN: + PPError ("Token \"/=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_MOD_ASSIGN: + PPError ("Token \"%%=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_SHL_ASSIGN: + PPError ("Token \"<<=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_SHR_ASSIGN: + PPError ("Token \">>=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_AND_ASSIGN: + PPError ("Token \"&=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_OR_ASSIGN: + PPError ("Token \"|=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + case TOK_XOR_ASSIGN: + PPError ("Token \"^=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); + break; + + default: + break; + } +} + + + +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 */ +{ + /* Initialize the parser status */ + PPEvaluationFailed = 0; + PPEvaluationEnabled = 1; + PPParserRunning = 1; + + /* Parse */ + PPExprInit (Expr); + PPhie0 (Expr); + + /* If the evaluation fails, the result is always zero */ + if (PPEvaluationFailed) { + Expr->IVal = 0; + PPEvaluationFailed = 0; + } + + /* Restore parser status */ + PPParserRunning = 0; +} diff --git a/src/cc65/ppexpr.h b/src/cc65/ppexpr.h new file mode 100644 index 000000000..5e9968a2b --- /dev/null +++ b/src/cc65/ppexpr.h @@ -0,0 +1,76 @@ +/*****************************************************************************/ +/* */ +/* ppexpr.h */ +/* */ +/* Expressions for C preprocessor */ +/* */ +/* */ +/* */ +/* (C) 2022 The cc65 Authors */ +/* */ +/* */ +/* 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 PPEXPR_H +#define PPEXPR_H + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* PPExpr data struct */ +typedef struct PPExpr PPExpr; +struct PPExpr +{ + long IVal; + unsigned Flags; +}; + +/* PPExpr initializers */ +#define AUTO_PPEXPR_INITIALIZER { 0, 0 } +#define STATIC_PPEXPR_INITIALIZER { 0, 0 } + +/* PPExpr flags */ +#define PPEXPR_NONE 0U +#define PPEXPR_UNSIGNED 1U +#define PPEXPR_UNDEFINED 2U + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ParsePPExprInLine (PPExpr* Expr); +/* Parse a line for PP expression */ + + + +/* End of ppexpr.h */ + +#endif diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index b0478ce2a..83ed362c8 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -784,7 +784,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) static void MakeMessage (const char* Message) { - fprintf (stderr, "%s:%u: Note: %s\n", GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Message); + Note ("%s", Message); } diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index a607e3217..0a9b94bf2 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -48,15 +48,16 @@ /* cc65 */ #include "codegen.h" #include "error.h" -#include "expr.h" #include "global.h" #include "ident.h" #include "incpath.h" #include "input.h" #include "lineinfo.h" #include "macrotab.h" +#include "ppexpr.h" #include "preproc.h" #include "scanner.h" +#include "scanstrbuf.h" #include "standard.h" @@ -67,27 +68,81 @@ -/* Set when the preprocessor calls expr() recursively */ -unsigned char Preprocessing = 0; +/* Macro scanner mode flags */ +#define MSM_NONE 0x00U /* Default */ +#define MSM_MULTILINE 0x01U /* Read from multiple lines */ +#define MSM_IN_DIRECTIVE 0x02U /* In PP directives scan */ +#define MSM_IN_ARG_LIST 0x04U /* In macro argument scan */ +#define MSM_IN_ARG_EXPANSION 0x08U /* In expansion on arguments */ +#define MSM_OP_DEFINED 0x10U /* Handle the "defined" operator */ +#define MSM_OP_HAS_INCLUDE 0x20U /* Handle the "__has_include" operator */ +#define MSM_OP_HAS_C_ATTRIBUTE 0x40U /* Handle the "__has_c_attribute" operator */ +#define MSM_TOK_HEADER 0x80U /* Support header tokens */ + +/* Macro expansion state flags */ +#define MES_NONE 0x00U /* Nothing */ +#define MES_FIRST_TOKEN 0x01U /* Mark for detecting pp-token count in the sequence */ +#define MES_MULTIPLE_TOKEN 0x02U /* Multiple pp-tokens are detected in the sequence */ +#define MES_BEGIN_WITH_IDENT 0x04U /* The first pp-token of the sequence is an identifier */ +#define MES_HAS_REPLACEMENT 0x10U /* Macro argument has cached replacement result */ +#define MES_NO_VA_COMMA 0x20U /* Variadic macro called w/o the ',' in front of variable argument */ +#define MES_ERROR 0x80U /* Error has occurred in macro expansion */ /* Management data for #if */ -#define MAX_IFS 256 #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U #define IFCOND_NEEDTERM 0x04U -static unsigned char IfStack[MAX_IFS]; -static int IfIndex = -1; -/* Buffer for macro expansion */ -static StrBuf* MLine; +/* Current PP if stack */ +static PPIfStack* PPStack; + +/* Struct for rescan */ +typedef struct RescanInputStack RescanInputStack; +struct RescanInputStack { + Collection Lines; + Collection LastTokLens; + StrBuf* PrevTok; +}; + +/* Input backup for rescan */ +static RescanInputStack* CurRescanStack; + +/* Intermediate input buffers */ +static StrBuf* PLine; /* Buffer for macro expansion */ +static StrBuf* MLine; /* Buffer for macro expansion in #pragma */ +static StrBuf* OLine; /* Buffer for #pragma output */ + +/* Newlines to be added to preprocessed text */ +static unsigned PendingNewLines; +static unsigned ContinuedLines; +static int FileChanged; /* Structure used when expanding macros */ typedef struct MacroExp MacroExp; struct MacroExp { - Collection ActualArgs; /* Actual arguments */ - StrBuf Replacement; /* Replacement with arguments substituted */ - Macro* M; /* The macro we're handling */ + Collection Args; /* Actual arguments (for function-like) */ + Collection HideSets; /* Macros hidden from expansion */ + StrBuf Tokens; /* Originally read sequence */ + unsigned IdentCount; /* Count of identifiers in the pp-token sequence */ + unsigned Flags; /* Macro argument flags */ + MacroExp* Replaced; /* Macro-replaced version of this pp-token sequence */ + unsigned FirstTokLen; /* Length of the first pp-token */ + unsigned LastTokLen; /* Length of the last pp-token */ +}; + +typedef struct HideRange HideRange; +struct HideRange +{ + HideRange* Next; + unsigned Start; + unsigned End; +}; + +typedef struct HiddenMacro HiddenMacro; +struct HiddenMacro { + const Macro* M; + HideRange* HS; }; @@ -98,13 +153,39 @@ struct MacroExp { -static unsigned Pass1 (StrBuf* Source, StrBuf* Target); -/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments -** and the "defined" operator. +static void TranslationPhase3 (StrBuf* Source, StrBuf* Target); +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. */ -static void MacroReplacement (StrBuf* Source, StrBuf* Target); -/* Perform macro replacement. */ +static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFlags); +/* Preprocess a single line. Handle specified tokens and operators, remove +** whitespace and comments, then do macro replacement. +*/ + +static int ParseDirectives (unsigned ModeFlags); +/* Handle directives. Return 1 if any whitespace or newlines are parsed. */ + +static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags); +/* Scan for and perform macro replacement. Return the count of identifiers and +** right parentheses in the replacement result. +*/ + +static MacroExp* InitMacroExp (MacroExp* E); +/* Initialize a MacroExp structure */ + +static void DoneMacroExp (MacroExp* E); +/* Cleanup after use of a MacroExp structure */ + +static int CheckPastePPTok (StrBuf* Target, unsigned TokLen, char Next); +/* Return 1 if the last pp-tokens from Source could be concatenated with any +** characters from Appended to form a new valid one. +*/ + +static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen); +/* Memorize the previous pp-token(s) to later check for potential pp-token +** concatenation. +*/ @@ -114,67 +195,128 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target); -/* Types of preprocessor tokens */ +/* Types of preprocessor directives */ typedef enum { - PP_ILLEGAL = -1, - PP_DEFINE, - PP_ELIF, - PP_ELSE, - PP_ENDIF, - PP_ERROR, - PP_IF, - PP_IFDEF, - PP_IFNDEF, - PP_INCLUDE, - PP_LINE, - PP_PRAGMA, - PP_UNDEF, - PP_WARNING, -} pptoken_t; + PPD_ILLEGAL = -1, + PPD_DEFINE, + PPD_ELIF, + PPD_ELSE, + PPD_ENDIF, + PPD_ERROR, + PPD_IF, + PPD_IFDEF, + PPD_IFNDEF, + PPD_INCLUDE, + PPD_LINE, + PPD_PRAGMA, + PPD_UNDEF, + PPD_WARNING, +} ppdirective_t; -/* Preprocessor keyword to token mapping table */ -static const struct PPToken { - const char* Key; /* Keyword */ - pptoken_t Tok; /* Token */ -} PPTokens[] = { - { "define", PP_DEFINE }, - { "elif", PP_ELIF }, - { "else", PP_ELSE }, - { "endif", PP_ENDIF }, - { "error", PP_ERROR }, - { "if", PP_IF }, - { "ifdef", PP_IFDEF }, - { "ifndef", PP_IFNDEF }, - { "include", PP_INCLUDE }, - { "line", PP_LINE }, - { "pragma", PP_PRAGMA }, - { "undef", PP_UNDEF }, - { "warning", PP_WARNING }, +/* Preprocessor directive tokens mapping table */ +static const struct PPDType { + const char* Tok; /* Token */ + ppdirective_t Type; /* Type */ +} PPDTypes[] = { + { "define", PPD_DEFINE }, + { "elif", PPD_ELIF }, + { "else", PPD_ELSE }, + { "endif", PPD_ENDIF }, + { "error", PPD_ERROR }, + { "if", PPD_IF }, + { "ifdef", PPD_IFDEF }, + { "ifndef", PPD_IFNDEF }, + { "include", PPD_INCLUDE }, + { "line", PPD_LINE }, + { "pragma", PPD_PRAGMA }, + { "undef", PPD_UNDEF }, + { "warning", PPD_WARNING }, }; -/* Number of preprocessor tokens */ -#define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0])) +/* Number of preprocessor directive types */ +#define PPDTOKEN_COUNT (sizeof(PPDTypes) / sizeof(PPDTypes[0])) static int CmpToken (const void* Key, const void* Elem) /* Compare function for bsearch */ { - return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key); + return strcmp ((const char*) Key, ((const struct PPDType*) Elem)->Tok); } -static pptoken_t FindPPToken (const char* Ident) -/* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier -** is not a valid preprocessor token. +static ppdirective_t FindPPDirectiveType (const char* Ident) +/* Find a preprocessor directive type and return it. Return PPD_ILLEGAL if the +** identifier is not a valid preprocessor directive token. */ { - struct PPToken* P; - P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken); - return P? P->Tok : PP_ILLEGAL; + struct PPDType* P; + P = bsearch (Ident, PPDTypes, PPDTOKEN_COUNT, sizeof (PPDTypes[0]), CmpToken); + return P? P->Type : PPD_ILLEGAL; +} + + + +/*****************************************************************************/ +/* MacroExp helpers */ +/*****************************************************************************/ + + + +static HideRange* NewHideRange (unsigned Start, unsigned Len) +/* Create a hide range */ +{ + HideRange* HS = xmalloc (sizeof (HideRange)); + + HS->Next = 0; + HS->Start = Start; + HS->End = Start + Len; + + return HS; +} + + + +static void FreeHideRange (HideRange* HS) +/* Free a hide range */ +{ + xfree (HS); +} + + + +static HiddenMacro* NewHiddenMacro (const Macro* M) +/* Create a new struct for the hidden macro */ +{ + HiddenMacro* MHS = xmalloc (sizeof (HiddenMacro)); + + MHS->M = M; + MHS->HS = 0; + + return MHS; +} + + + +static void FreeHiddenMacro (HiddenMacro* MHS) +/* Free the struct and all ranges of the hidden macro */ +{ + HideRange* This; + HideRange* Next; + + if (MHS == 0) { + return; + } + + for (This = MHS->HS; This != 0; This = Next) { + Next = This->Next; + FreeHideRange (This); + } + + xfree (MHS); } @@ -185,12 +327,384 @@ static pptoken_t FindPPToken (const char* Ident) -static MacroExp* InitMacroExp (MacroExp* E, Macro* M) +static HiddenMacro* ME_FindHiddenMacro (const MacroExp* E, const Macro* M) +/* Find the macro hide set */ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + if (MHS->M == M) { + return MHS; + } + } + + return 0; +} + + + +static void ME_HideMacro (unsigned Idx, unsigned Count, MacroExp* E, const Macro* M) +/* Hide the macro from the Idx'th identifier */ +{ + if (Count > 0) { + /* Find the macro hideset */ + HiddenMacro* MHS = ME_FindHiddenMacro (E, M); + HideRange** This; + + /* New hidden section */ + HideRange* NewHS = NewHideRange (Idx, Count); + + /* New macro to hide */ + if (MHS == 0) { + MHS = NewHiddenMacro (M); + CollAppend (&E->HideSets, MHS); + } + This = &MHS->HS; + + if (*This == 0) { + *This = NewHS; + } else { + /* Insert */ + while (1) { + if (*This == 0 || NewHS->Start <= (*This)->Start) { + /* Insert before */ + NewHS->Next = *This; + *This = NewHS; + break; + } else if (NewHS->Start <= (*This)->End) { + /* Insert after */ + NewHS->Next = (*This)->Next; + (*This)->Next = NewHS; + break; + } + /* Advance */ + This = &(*This)->Next; + } + + /* Merge */ + while (*This != 0) { + HideRange* Next = (*This)->Next; + + if (Next != 0 && (*This)->End >= Next->Start) { + /* Expand this to the next */ + if ((*This)->End < Next->End) { + (*This)->End = Next->End; + } + + /* Remove next */ + (*This)->Next = Next->Next; + FreeHideRange (Next); + + /* Advance */ + This = &(*This)->Next; + } else { + /* No more */ + break; + } + } + } + } +} + + + +static int ME_CanExpand (unsigned Idx, const MacroExp* E, const Macro* M) +/* Return 1 if the macro can be expanded with the Idx'th identifier */ +{ + if (E != 0) { + /* Find the macro hideset */ + HiddenMacro* MHS = ME_FindHiddenMacro (E, M); + if (MHS != 0) { + /* Check if the macro is hidden from this identifier */ + HideRange* HS = MHS->HS; + while (HS != 0) { + /* If the macro name overlaps with the range where the macro is hidden, + ** the macro cannot be expanded. + */ + if (Idx >= HS->Start && Idx < HS->End) { + return 0; + } + HS = HS->Next; + } + } + } + + return 1; +} + + + +static void ME_OffsetHideSets (unsigned Idx, unsigned Offs, MacroExp* E) +/* Adjust all macro hide set ranges for the macro expansion when the identifier +** at Idx is replaced with a count of Offs + 1 (if Offs > 0) of identifiers. +*/ +{ + if (Offs != 0) { + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + + for (This = MHS->HS; This != 0; This = This->Next) { + if (Idx < This->Start) { + This->Start += Offs; + This->End += Offs; + } else if (Idx < This->End) { + This->End += Offs; + } + } + } + } +} + + + +static void ME_RemoveToken (unsigned Idx, unsigned Count, MacroExp* E) +/* Remove the Idx'th identifier token from tracking and offset all hidden +** ranges accordingly. +*/ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + HideRange** Prev; + + for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) { + if (Idx < This->Start) { + if (This->Start - Idx >= Count) { + This->Start -= Count; + This->End -= Count; + } else { + if (This->End - Idx > Count) { + This->Start = Idx; + This->End -= Count; + } else { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + } else if (Idx < This->End) { + if (This->End - Idx > Count) { + This->End -= Count; + } else { + This->End = Idx; + } + + if (This->End == This->Start) { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + + Prev = &This->Next; + } + } +} + + + +static void ME_HandleSemiNestedMacro (unsigned NameIdx, unsigned LastIdx, MacroExp* E) +/* Unhide the macro name from all hidesets if it was expanded with an unhidden +** right parenthesis. This is unspecified but allowed behavior according to +** ISO/IEC 9899:2018, 6.10.3.4ff. +*/ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + HideRange** Prev; + + for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) { + if (NameIdx < This->End) { + if (NameIdx >= This->Start && LastIdx >= This->End) { + This->End = NameIdx; + if (This->End == This->Start) { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + Prev = &This->Next; + } else { + break; + } + } + } +} + + + +static void ME_AddArgHideSets (unsigned Idx, const MacroExp* A, MacroExp* Parent) +/* Propagate the macro hide sets of the substituted argument starting as the +** Idx'th identifier of the result. +*/ +{ + unsigned I; + + /* Move the hide set generated with in the argument as it will be freed later */ + for (I = 0; I < CollCount (&A->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&A->HideSets, I); + HideRange* HS; + + for (HS = MHS->HS; HS != 0; HS = HS->Next) { + ME_HideMacro (Idx + HS->Start, HS->End - HS->Start, Parent, MHS->M); + } + } +} + + + +static void ME_DoneHideSets (MacroExp* E) +/* Free all of hidden macros for the macro expansion */ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + FreeHiddenMacro (MHS); + } + DoneCollection (&E->HideSets); +} + + + +static void ME_SetTokLens (MacroExp* E, unsigned TokLen) +/* Set token lengths and flags for macro expansion struct */ +{ + E->LastTokLen = TokLen; + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags &= ~MES_FIRST_TOKEN; + E->FirstTokLen = E->LastTokLen; + } else { + E->Flags |= MES_MULTIPLE_TOKEN; + } +} + + + +static MacroExp* ME_MakeReplaced (MacroExp* A) +/* Make a replaced version of the argument */ +{ + /* Replace the parameter with actual argument tokens */ + if ((A->Flags & MES_HAS_REPLACEMENT) == 0) { + A->Replaced = xmalloc (sizeof (MacroExp)); + + InitMacroExp (A->Replaced); + SB_Reset (&A->Tokens); + + /* Propagate the hide sets */ + ME_AddArgHideSets (0, A, A->Replaced); + + /* Do macro expansion on the argument */ + A->Replaced->IdentCount = ReplaceMacros (&A->Tokens, + &A->Replaced->Tokens, + A->Replaced, + MSM_IN_ARG_EXPANSION); + + A->Flags |= MES_HAS_REPLACEMENT; + } + + return A->Replaced != 0 ? A->Replaced : A; +} + + + +static MacroExp* ME_GetOriginalArg (const MacroExp* E, unsigned Index) +/* Return an actual macro argument with the given index */ +{ + return CollAt (&E->Args, Index); +} + + + +static MacroExp* ME_GetReplacedArg (const MacroExp* E, unsigned Index) +/* Return a replaced macro argument with the given index */ +{ + return ME_MakeReplaced (CollAt (&E->Args, Index)); +} + + + +static MacroExp* ME_AppendArg (MacroExp* E, MacroExp* Arg) +/* Add a copy of Arg to the list of actual macro arguments. +** NOTE: This function will clear the token sequence of Arg! +*/ +{ + MacroExp* A = xmalloc (sizeof (MacroExp)); + + /* Initialize our MacroExp structure */ + InitMacroExp (A); + + /* Copy info about the original strings */ + A->IdentCount = Arg->IdentCount; + A->Flags = Arg->Flags; + A->FirstTokLen = Arg->FirstTokLen; + A->LastTokLen = Arg->LastTokLen; + + /* Move the contents of Arg to A */ + SB_Move (&A->Tokens, &Arg->Tokens); + + /* Add A to the list of actual arguments */ + CollAppend (&E->Args, A); + + return A; +} + + + +static void ME_ClearArgs (MacroExp* E) +/* Clear all read arguments for macro expansion */ +{ + unsigned I; + + /* Delete the list with actual arguments */ + for (I = 0; I < CollCount (&E->Args); ++I) { + MacroExp* A = CollAtUnchecked (&E->Args, I); + + /* Destroy the macro expansion structure and then free memory allocated + ** for it. + */ + DoneMacroExp (A); + xfree (A); + } + + DoneCollection (&E->Args); + InitCollection (&E->Args); +} + + + +static int ME_IsNextArgVariadic (const MacroExp* E, const Macro* M) +/* Return true if the next actual argument we will add is a variadic one */ +{ + return (M->Variadic && + M->ParamCount == (int) CollCount (&E->Args) + 1); +} + + + +static MacroExp* InitMacroExp (MacroExp* E) /* Initialize a MacroExp structure */ { - InitCollection (&E->ActualArgs); - SB_Init (&E->Replacement); - E->M = M; + InitCollection (&E->Args); + InitCollection (&E->HideSets); + SB_Init (&E->Tokens); + E->IdentCount = 0; + E->Flags = MES_FIRST_TOKEN; + E->Replaced = 0; + E->FirstTokLen = 0; + E->LastTokLen = 0; return E; } @@ -199,48 +713,70 @@ static MacroExp* InitMacroExp (MacroExp* E, Macro* M) static void DoneMacroExp (MacroExp* E) /* Cleanup after use of a MacroExp structure */ { - unsigned I; - - /* Delete the list with actual arguments */ - for (I = 0; I < CollCount (&E->ActualArgs); ++I) { - FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I)); + ME_ClearArgs (E); + ME_DoneHideSets (E); + SB_Done (&E->Tokens); + if (E->Replaced != 0) { + DoneMacroExp (E->Replaced); } - DoneCollection (&E->ActualArgs); - SB_Done (&E->Replacement); } -static void ME_AppendActual (MacroExp* E, StrBuf* Arg) -/* Add a copy of Arg to the list of actual macro arguments. -** NOTE: This function will clear Arg! -*/ +/*****************************************************************************/ +/* Rescan input stack */ +/*****************************************************************************/ + + + +static void PushRescanLine (RescanInputStack* RIS, StrBuf* L, unsigned LastTokLen) +/* Push an input line to the rescan input stack */ { - /* Create a new string buffer */ - StrBuf* A = NewStrBuf (); - - /* Move the contents of Arg to A */ - SB_Move (A, Arg); - - /* Add A to the actual arguments */ - CollAppend (&E->ActualArgs, A); + CollAppend (&RIS->Lines, L); + /* Abuse the pointer to store an unsigned */ + CollAppend (&RIS->LastTokLens, (void*)(uintptr_t)LastTokLen); } -static StrBuf* ME_GetActual (MacroExp* E, unsigned Index) -/* Return an actual macro argument with the given index */ +static void PopRescanLine (void) +/* Pop and free a rescan input line if it reaches the end */ { - return CollAt (&E->ActualArgs, Index); + if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) { + FreeStrBuf (CollPop (&CurRescanStack->Lines)); + InitLine (CollLast (&CurRescanStack->Lines)); + CollPop (&CurRescanStack->LastTokLens); + } } -static int ME_ArgIsVariadic (const MacroExp* E) -/* Return true if the next actual argument we will add is a variadic one */ +static void InitRescanInputStack (RescanInputStack* RIS) +/* Init a RescanInputStack struct */ { - return (E->M->Variadic && - E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1); + InitCollection (&RIS->Lines); + InitCollection (&RIS->LastTokLens); + RIS->PrevTok = 0; +} + + + +static void DoneRescanInputStack (RescanInputStack* RIS) +/* Free a RescanInputStack struct. RIS must be non-NULL. */ +{ + /* Free pushed input lines */ + while (CollCount (&RIS->Lines) > 1) { + FreeStrBuf (CollPop (&RIS->Lines)); + } + /* Switch back to the old input stack */ + InitLine (CollPop (&RIS->Lines)); + + /* Free any remaining pp-tokens used for concatenation check */ + FreeStrBuf (RIS->PrevTok); + + /* Done */ + DoneCollection (&RIS->Lines); + DoneCollection (&RIS->LastTokLens); } @@ -251,6 +787,88 @@ static int ME_ArgIsVariadic (const MacroExp* E) +static int MacName (char* Ident) +/* Get a macro symbol name into Ident. If we have an error, print a +** diagnostic message and clear the line. +*/ +{ + if (IsSym (Ident) == 0) { + if (CurC != '\0') { + PPError ("Macro name must be an identifier"); + } else { + PPError ("Missing macro name"); + } + ClearLine (); + return 0; + } else { + return 1; + } +} + + + +static void CheckForBadIdent (const char* Ident, int Std, const Macro* M) +/* Check for and warning on problematic identifiers */ +{ + if (Std >= STD_C99 && + (M == 0 || !M->Variadic) && + strcmp (Ident, "__VA_ARGS__") == 0) { + /* __VA_ARGS__ cannot be used as a macro parameter name in post-C89 + ** mode. + */ + PPWarning ("__VA_ARGS__ can only appear in the expansion of a C99 variadic macro"); + } +} + + + +static void AddPreLine (StrBuf* Str) +/* Add newlines to the string buffer */ +{ + /* No need to prettify the non-exist output */ + if (!PreprocessOnly) { + PendingNewLines = 0; + ContinuedLines = 0; + return; + } + + /* We'll adjust the line number later if necessary */ + PendingNewLines += ContinuedLines; + + if (FileChanged || PendingNewLines > 6) { + /* Output #line directives as source info */ + StrBuf Comment = AUTO_STRBUF_INITIALIZER; + if (SB_NotEmpty (Str) && SB_LookAtLast (Str) != '\n') { + SB_AppendChar (Str, '\n'); + } + SB_Printf (&Comment, "#line %u \"%s\"\n", + GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ()); + SB_Append (Str, &Comment); + } else { + /* Output new lines */ + while (PendingNewLines > 0) { + SB_AppendChar (Str, '\n'); + --PendingNewLines; + } + } + FileChanged = 0; + PendingNewLines = 0; + ContinuedLines = 0; +} + + + +static void AppendIndent (StrBuf* Str, int Count) +/* Add Count of spaces ' ' to the string buffer */ +{ + while (Count > 0) { + SB_AppendChar (Str, ' '); + --Count; + } +} + + + static void Stringize (StrBuf* Source, StrBuf* Target) /* Stringize the given string: Add double quotes at start and end and preceed ** each occurance of " and \ by a backslash. @@ -283,12 +901,12 @@ static void Stringize (StrBuf* Source, StrBuf* Target) static void OldStyleComment (void) -/* Remove an old style C comment from line. */ +/* Remove an old style C comment from line */ { /* Remember the current line number, so we can output better error ** messages if the comment is not terminated in the current file. */ - unsigned StartingLine = GetCurrentLine(); + unsigned StartingLine = GetCurrentLineNum (); /* Skip the start of comment chars */ NextChar (); @@ -302,6 +920,7 @@ static void OldStyleComment (void) StartingLine); return; } + ++PendingNewLines; } else { if (CurC == '/' && NextC == '*') { PPWarning ("'/*' found inside a comment"); @@ -318,8 +937,15 @@ static void OldStyleComment (void) static void NewStyleComment (void) -/* Remove a new style C comment from line. */ +/* Remove a new style C comment from line */ { + /* Diagnose if this is unsupported */ + if (IS_Get (&Standard) < STD_C99 && !AllowNewComments) { + PPError ("C++ style comments are not allowed in C89"); + PPNote ("(this will be reported only once per input file)"); + AllowNewComments = 1; + } + /* Beware: Because line continuation chars are handled when reading ** lines, we may only skip until the end of the source line, which ** may not be the same as the end of the input line. The end of the @@ -336,22 +962,72 @@ static void NewStyleComment (void) static int SkipWhitespace (int SkipLines) -/* Skip white space in the input stream. Do also skip newlines if SkipLines -** is true. Return zero if nothing was skipped, otherwise return a -** value != zero. +/* Skip white space and comments in the input stream. If skipLines is true, +** also skip newlines and add that count to global PendingNewLines. Return 1 +** if the last skipped character was a white space other than a newline '\n', +** otherwise return -1 if there were any newline characters skipped, otherwise +** return 0 if nothing was skipped. */ { int Skipped = 0; + int NewLine = 0; + + /* Rescanning */ while (1) { if (IsSpace (CurC)) { NextChar (); Skipped = 1; - } else if (CurC == '\0' && SkipLines) { - /* End of line, read next */ - if (NextLine () != 0) { - Skipped = 1; + } else if (CurC == '/' && NextC == '*') { + OldStyleComment (); + Skipped = 1; + } else if (CurC == '/' && NextC == '/') { + NewStyleComment (); + Skipped = 1; + } else if (CurC == '\0') { + /* End of line */ + if (CurRescanStack != 0 && + CollCount (&CurRescanStack->Lines) > 1 && + Line == CollLast (&CurRescanStack->Lines)) { + + unsigned LastTokLen = (unsigned)(uintptr_t)CollLast (&CurRescanStack->LastTokLens); + + /* Check for potentially merged tokens */ + if (Skipped == 0 && LastTokLen != 0) { + /* Get the following input */ + StrBuf* Next = CollAtUnchecked (&CurRescanStack->Lines, + CollCount (&CurRescanStack->Lines) - 2); + char C = SB_Peek (Next); + + /* We cannot check right now if the next pp-token may be a + ** macro. + */ + if (IsIdent (C)) { + /* Memorize the previous pp-token and check it later */ + LazyCheckNextPPTok (Line, LastTokLen); + } else if (C != '\0' && !IsSpace (C)) { + /* If the two adjacent pp-tokens could be put together + ** to form a new one, we have to separate them with an + ** additional space. + */ + Skipped = CheckPastePPTok (Line, LastTokLen, SB_Peek (Next)); + } + + } + + /* switch back to previous input */ + PopRescanLine (); + + } else if (SkipLines) { + /* Read next line */ + if (NextLine () != 0) { + ++PendingNewLines; + NewLine = 1; + Skipped = 0; + } else { + /* End of input */ + break; + } } else { - /* End of input */ break; } } else { @@ -359,16 +1035,17 @@ static int SkipWhitespace (int SkipLines) break; } } - return Skipped; + + return Skipped != 0 ? Skipped : -(NewLine != 0); } -static void CopyQuotedString (StrBuf* Target) -/* Copy a single or double quoted string from the input to Target. */ +static void CopyHeaderNameToken (StrBuf* Target) +/* Copy a header name from the input to Target. */ { /* Remember the quote character, copy it to the target buffer and skip it */ - char Quote = CurC; + char Quote = CurC == '"' ? '"' : '>'; SB_AppendChar (Target, CurC); NextChar (); @@ -393,378 +1070,1518 @@ static void CopyQuotedString (StrBuf* Target) +static int IsQuotedString (void) +/* Retrun 1 if the incoming characters indicate a string literal or character +** constant, otherwise return 0. +*/ +{ + return IsQuote (CurC) || IsWideQuoted (CurC, NextC); +} + + + +static void CopyQuotedString (StrBuf* Target) +/* Copy a single or double quoted string from the input to Target. */ +{ + /* Remember the quote character, copy it to the target buffer and skip it */ + char Quote; + + if (CurC == 'L') { + SB_AppendChar (Target, CurC); + NextChar (); + } + + Quote = CurC; + SB_AppendChar (Target, CurC); + NextChar (); + + /* Copy the characters inside the string */ + while (CurC != '\0' && CurC != Quote) { + /* Keep an escaped char */ + if (CurC == '\\') { + SB_AppendChar (Target, CurC); + NextChar (); + } + /* Copy the character */ + SB_AppendChar (Target, CurC); + NextChar (); + } + + /* If we had a terminating quote, copy it */ + if (CurC != '\0') { + SB_AppendChar (Target, CurC); + NextChar (); + } else { + PPWarning ("Missing terminating %c character", Quote); + } +} + + + +static int GetPunc (char* S) +/* Parse a punctuator token. Return 1 and store the parsed token string into S +** on success, otherwise just return 0. +*/ +{ + char C; + switch (CurC) { + + case '[': + case ']': + case '(': + case ')': + case '{': + case '}': + case '~': + case '?': + case ':': + case ';': + case ',': + /* C */ + *S++ = CurC; + NextChar (); + break; + + case '=': + case '#': + /* C or CC */ + C = *S++ = CurC; + NextChar (); + if (CurC == C) { + *S++ = C; + NextChar (); + } + break; + + case '*': + case '/': + case '%': + case '^': + case '!': + /* C or C= */ + *S++ = CurC; + NextChar (); + if (CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '+': + case '&': + case '|': + /* C, CC or C= */ + C = *S++ = CurC; + NextChar (); + if (CurC == C || CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '<': + case '>': + /* C, CC, C= or CC= */ + C = *S++ = CurC; + NextChar (); + if (CurC == C) { + *S++ = CurC; + if (NextC == '=') { + *S++ = NextC; + NextChar (); + } + NextChar (); + } else if (CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '-': + /* C, CC, C= or C> */ + *S++ = CurC; + NextChar (); + switch (CurC) { + case '-': + case '=': + case '>': + *S++ = CurC; + NextChar (); + break; + default: + break; + } + break; + + case '.': + /* C or CCC */ + *S++ = CurC; + NextChar (); + if (CurC == '.' && NextC == '.') { + *S++ = CurC; + *S++ = NextC; + NextChar (); + NextChar (); + } + break; + + default: + return 0; + + } + + *S = '\0'; + return 1; +} + + + +static int CheckPastePPTok (StrBuf* Source, unsigned TokLen, char Next) +/* Return 1 if the last pp-tokens from Source could be concatenated with any +** characters from Appended to form a new valid one. +*/ +{ + char C; + unsigned NewTokLen; + StrBuf* OldSource; + StrBuf Src = AUTO_STRBUF_INITIALIZER; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + if (TokLen == 0 || IsBlank (SB_LookAtLast (Source))) { + return 0; + } + + PRECONDITION (SB_GetLen (Source) >= TokLen); + + /* Special casing "..", "/ /" and "/ *" that are not pp-tokens but still + ** need be separated. + */ + C = SB_LookAt (Source, SB_GetLen (Source) - TokLen); + if ((C == '.' && Next == '.') || (C == '/' && (Next == '/' || Next == '*'))) { + return 1; + } + + SB_CopyBuf (&Src, SB_GetConstBuf (Source) + SB_GetLen (Source) - TokLen, TokLen); + SB_AppendChar (&Src, Next); + + SB_Reset (&Src); + OldSource = InitLine (&Src); + + if (IsPPNumber (CurC, NextC)) { + /* PP-number */ + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + /* Quoted string */ + CopyQuotedString (&Buf); + } else { + ident Ident; + if (GetPunc (Ident)) { + /* Punctuator */ + SB_CopyStr (&Buf, Ident); + } else if (IsSym (Ident)) { + /* Identifier */ + SB_CopyStr (&Buf, Ident); + } + } + + NewTokLen = SB_GetLen (&Buf); + + SB_Done (&Buf); + SB_Done (&Src); + + /* Restore old source */ + InitLine (OldSource); + + /* Return if concatenation succeeded */ + return NewTokLen != TokLen; +} + + + +static int TryPastePPTok (StrBuf* Target, + StrBuf* Appended, + unsigned FirstTokLen, + unsigned SecondTokLen) +/* Paste the whole appened pp-token sequence onto the end of the target +** pp-token sequence. Diagnose if it fails to form a valid pp-token with the +** two pp-tokens pasted together. Return 1 if succeeds. +*/ +{ + unsigned TokLen; + StrBuf* OldSource; + StrBuf Src = AUTO_STRBUF_INITIALIZER; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + if (FirstTokLen == 0 || SecondTokLen == 0) { + SB_Append (Target, Appended); + return 1; + } + + /* Since we need to concatenate the token sequences, remove the + ** last whitespace that was added to target, since it must come + ** from the input. + */ + if (IsBlank (SB_LookAtLast (Target))) { + SB_Drop (Target, 1); + } + + PRECONDITION (SB_GetLen (Target) >= FirstTokLen && + SB_GetLen (Appended) >= SecondTokLen); + + /* Special casing "..", "/ /" and "/ *" */ + if (FirstTokLen == 1) { + char C = SB_LookAt (Target, SB_GetLen (Target) - FirstTokLen); + char N = SB_LookAt (Appended, 0); + /* Avoid forming a comment introducer or an ellipsis. Note that an + ** ellipsis pp-token cannot be formed with macros anyway. + */ + if ((C == '.' && N == '.') || (C == '/' && (N == '/' || N == '*'))) { + SB_AppendChar (Target, ' '); + SB_Append (Target, Appended); + PPWarning ("Pasting formed \"%c%c\", an invalid preprocessing token", C, N); + + return 0; + } + } + + SB_CopyBuf (&Src, SB_GetConstBuf (Target) + SB_GetLen (Target) - FirstTokLen, FirstTokLen); + if (SecondTokLen == SB_GetLen (Appended) || IsSpace (SB_LookAt (Appended, SecondTokLen))) { + SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen); + } else { + SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen + 1); + } + + SB_Reset (&Src); + OldSource = InitLine (&Src); + + if (IsPPNumber (CurC, NextC)) { + /* PP-number */ + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + /* Quoted string */ + CopyQuotedString (&Buf); + } else { + ident Ident; + if (GetPunc (Ident)) { + /* Punctuator */ + SB_CopyStr (&Buf, Ident); + } else if (IsSym (Ident)) { + /* Identifier */ + SB_CopyStr (&Buf, Ident); + } else { + /* Unknown */ + } + } + + TokLen = SB_GetLen (&Buf); + if (TokLen < FirstTokLen + SecondTokLen) { + /* The pasting doesn't form a valid pp-token */ + while (SB_GetLen (&Buf) < FirstTokLen + SecondTokLen) { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + SB_Terminate (&Buf); + PPWarning ("Pasting formed \"%s\", an invalid preprocessing token", + SB_GetConstBuf (&Buf)); + + /* Add a space between the tokens to avoid problems in rescanning */ + if (TokLen > FirstTokLen) { + SB_AppendChar (Target, ' '); + } + /* Append all remaining tokens */ + SB_Append (Target, Appended); + + /* No concatenation */ + TokLen = FirstTokLen; + } else { + /* Add a space after the merged token if necessary */ + SB_AppendBuf (Target, SB_GetConstBuf (Appended), SecondTokLen); + if (TokLen > FirstTokLen + SecondTokLen) { + SB_AppendChar (Target, ' '); + } + /* Append all remaining tokens */ + SB_AppendBuf (Target, + SB_GetConstBuf (Appended) + SecondTokLen, + SB_GetLen (Appended) - SecondTokLen); + } + + SB_Done (&Buf); + SB_Done (&Src); + + /* Restore old source */ + InitLine (OldSource); + + /* Return if concatenation succeeded */ + return TokLen != FirstTokLen; +} + + + +static void SeparatePPTok (StrBuf* Target, char Next) +/* Add a space to target if the previous pp-token could be concatenated with +** the following character. +*/ +{ + if (CurRescanStack->PrevTok != 0) { + unsigned Len = SB_GetLen (CurRescanStack->PrevTok) - SB_GetIndex (CurRescanStack->PrevTok); + + /* Check for pp-token pasting */ + if (CheckPastePPTok (CurRescanStack->PrevTok, Len, Next)) { + SB_AppendChar (Target, ' '); + } + FreeStrBuf (CurRescanStack->PrevTok); + CurRescanStack->PrevTok = 0; + } +} + + + +static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen) +/* Memorize the previous pp-token(s) to later check for potential pp-token +** concatenation. +*/ +{ + char C; + int CheckEllipsis = 0; + unsigned NewIndex = SB_GetLen (Prev) - LastTokLen; + + PRECONDITION (SB_GetLen (Prev) >= LastTokLen); + + /* Check for some special cases */ + C = SB_AtUnchecked (Prev, NewIndex); + + /* We may exclude certain punctuators for speedups. As newer C standards + ** could add more punctuators such as "[[", "]]", "::" and so on, this + ** check might need changes accordingly. + */ + if (C == '[' || C == ']' || C == '(' || C == ')' || + C == '{' || C == '}' || C == '~' || C == '?' || + C == ':' || C == ';' || C == ',') { + /* These punctuators cannot be concatenated */ + return; + } + + /* Special check for .. */ + if (NewIndex > 0 && + C == '.' && + SB_AtUnchecked (Prev, NewIndex - 1) == '.') { + /* Save the preceding '.' as well */ + CheckEllipsis = 1; + } + + if (CurRescanStack->PrevTok != 0) { + unsigned OldIndex = SB_GetIndex (CurRescanStack->PrevTok); + unsigned OldLen = SB_GetLen (CurRescanStack->PrevTok) - OldIndex; + unsigned NewLen = SB_GetLen (Prev) - NewIndex; + if (OldLen == NewLen && + strncmp (SB_GetConstBuf (CurRescanStack->PrevTok) + OldIndex - CheckEllipsis, + SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis, + OldLen + CheckEllipsis) == 0) { + /* Same pp-token, keep using the old one */ + } else { + /* Logic error */ + SB_Terminate (CurRescanStack->PrevTok); + Internal ("Unchecked pp-token concatenation: \"%s\"", + SB_GetConstBuf (CurRescanStack->PrevTok) + SB_GetIndex (CurRescanStack->PrevTok)); + } + } else { + /* Memorize the current line */ + CurRescanStack->PrevTok = NewStrBuf (); + SB_CopyBuf (CurRescanStack->PrevTok, + SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis, + LastTokLen + CheckEllipsis); + SB_Reset (CurRescanStack->PrevTok); + } +} + + + +static int CheckExtraTokens (const char* Name) +/* Check for extra tokens at the end of the directive. Return 1 if there are +** extra tokens, otherwise 0. +*/ +{ + SkipWhitespace (0); + if (SB_GetIndex (Line) != SB_GetLen (Line)) { + PPWarning ("Extra tokens at end of #%s directive", Name); + return 1; + } + return 0; +} + + + /*****************************************************************************/ /* Macro stuff */ /*****************************************************************************/ -static int MacName (char* Ident) -/* Get a macro symbol name into Ident. If we have an error, print a -** diagnostic message and clear the line. +static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, int MultiLine) +/* Identify the arguments to a macro call as-is. Return the total count of +** identifiers and right parentheses in the read argument list. */ { - if (IsSym (Ident) == 0) { - PPError ("Identifier expected"); - ClearLine (); - return 0; - } else { - return 1; - } -} - - - -static void ReadMacroArgs (MacroExp* E) -/* Identify the arguments to a macro call */ -{ - unsigned Parens; /* Number of open parenthesis */ - StrBuf Arg = STATIC_STRBUF_INITIALIZER; - - /* Read the actual macro arguments */ - Parens = 0; - while (1) { - if (CurC == '(') { - - /* Nested parenthesis */ - SB_AppendChar (&Arg, CurC); - NextChar (); - ++Parens; - - } else if (IsQuote (CurC)) { - - /* Quoted string - just copy */ - CopyQuotedString (&Arg); - - } else if (CurC == ',' || CurC == ')') { - - if (Parens) { - /* Comma or right paren inside nested parenthesis */ - if (CurC == ')') { - --Parens; - } - SB_AppendChar (&Arg, CurC); - NextChar (); - } else if (CurC == ',' && ME_ArgIsVariadic (E)) { - /* It's a comma, but we're inside a variadic macro argument, so - ** just copy it and proceed. - */ - SB_AppendChar (&Arg, CurC); - NextChar (); - } else { - /* End of actual argument. Remove whitespace from the end. */ - while (IsSpace (SB_LookAtLast (&Arg))) { - SB_Drop (&Arg, 1); - } - - /* If this is not the single empty argument for a macro with - ** an empty argument list, remember it. - */ - if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { - ME_AppendActual (E, &Arg); - } - - /* Check for end of macro param list */ - if (CurC == ')') { - NextChar (); - break; - } - - /* Start the next param */ - NextChar (); - SB_Clear (&Arg); - } - } else if (SkipWhitespace (1)) { - /* Squeeze runs of blanks within an arg */ - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } - } else if (CurC == '/' && NextC == '*') { - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } - OldStyleComment (); - } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } - NewStyleComment (); - } else if (CurC == '\0') { - /* End of input inside macro argument list */ - PPError ("Unterminated argument list invoking macro '%s'", E->M->Name); - - ClearLine (); - break; - } else { - /* Just copy the character */ - SB_AppendChar (&Arg, CurC); - NextChar (); - } - } - - /* Deallocate string buf resources */ - SB_Done (&Arg); -} - - - -static void MacroArgSubst (MacroExp* E) -/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ -{ + unsigned Idx = 0; + unsigned CountInArg = 0; + unsigned Parens = 0; /* Number of open parenthesis */ ident Ident; - int ArgIdx; - StrBuf* OldSource; - StrBuf* Arg; - int HaveSpace; + MacroExp Arg; - - /* Remember the current input and switch to the macro replacement. */ - int OldIndex = SB_GetIndex (&E->M->Replacement); - SB_Reset (&E->M->Replacement); - OldSource = InitLine (&E->M->Replacement); - - /* Argument handling loop */ - while (CurC != '\0') { - - /* If we have an identifier, check if it's a macro */ - if (IsSym (Ident)) { - - /* Check if it's a macro argument */ - if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { - - /* A macro argument. Get the corresponding actual argument. */ - Arg = ME_GetActual (E, ArgIdx); - - /* Copy any following whitespace */ - HaveSpace = SkipWhitespace (0); - - /* If a ## operator follows, we have to insert the actual - ** argument as is, otherwise it must be macro replaced. - */ - if (CurC == '#' && NextC == '#') { - - /* ### Add placemarker if necessary */ - SB_Append (&E->Replacement, Arg); - - } else { - - /* Replace the formal argument by a macro replaced copy - ** of the actual. - */ - SB_Reset (Arg); - MacroReplacement (Arg, &E->Replacement); - - /* If we skipped whitespace before, re-add it now */ - if (HaveSpace) { - SB_AppendChar (&E->Replacement, ' '); - } - } - - - } else { - - /* An identifier, keep it */ - SB_AppendStr (&E->Replacement, Ident); - - } - - } else if (CurC == '#' && NextC == '#') { - - /* ## operator. */ - NextChar (); - NextChar (); - SkipWhitespace (0); - - /* Since we need to concatenate the token sequences, remove - ** any whitespace that was added to target, since it must come - ** from the input. - */ - while (IsSpace (SB_LookAtLast (&E->Replacement))) { - SB_Drop (&E->Replacement, 1); - } - - /* If the next token is an identifier which is a macro argument, - ** replace it, otherwise do nothing. - */ - if (IsSym (Ident)) { - - /* Check if it's a macro argument */ - if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { - - /* Get the corresponding actual argument and add it. */ - SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); - - } else { - - /* Just an ordinary identifier - add as is */ - SB_AppendStr (&E->Replacement, Ident); - - } - } - - } else if (CurC == '#' && E->M->ArgCount >= 0) { - - /* A # operator within a macro expansion of a function like - ** macro. Read the following identifier and check if it's a - ** macro parameter. - */ - NextChar (); - SkipWhitespace (0); - if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { - PPError ("'#' is not followed by a macro parameter"); - } else { - /* Make a valid string from Replacement */ - Arg = ME_GetActual (E, ArgIdx); - SB_Reset (Arg); - Stringize (Arg, &E->Replacement); - } - - } else if (IsQuote (CurC)) { - CopyQuotedString (&E->Replacement); - } else { - SB_AppendChar (&E->Replacement, CurC); - NextChar (); - } - } - -#if 0 - /* Remove whitespace from the end of the line */ - while (IsSpace (SB_LookAtLast (&E->Replacement))) { - SB_Drop (&E->Replacement, 1); - } -#endif - - /* Switch back the input */ - InitLine (OldSource); - SB_SetIndex (&E->M->Replacement, OldIndex); -} - - - -static void MacroCall (StrBuf* Target, Macro* M) -/* Process a function like macro */ -{ - MacroExp E; + InitMacroExp (&Arg); /* Eat the left paren */ NextChar (); - /* Initialize our MacroExp structure */ - InitMacroExp (&E, M); - /* Read the actual macro arguments */ - ReadMacroArgs (&E); + while (1) { + /* Squeeze runs of blanks within an arg */ + unsigned OldPendingNewLines = PendingNewLines; + int Skipped = SkipWhitespace (MultiLine); - /* Compare formal and actual argument count */ - if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) { + /* Directives can only be found in an argument list that spans + ** multiple lines. + */ + if (MultiLine && OldPendingNewLines < PendingNewLines && CurC == '#') { + unsigned Newlines = 0; - StrBuf Arg = STATIC_STRBUF_INITIALIZER; + while (OldPendingNewLines < PendingNewLines && CurC == '#') { + Newlines += PendingNewLines - OldPendingNewLines; + PendingNewLines = OldPendingNewLines; + OldPendingNewLines = 0; + Skipped = ParseDirectives (MSM_IN_ARG_LIST) || Skipped; + } + PendingNewLines += Newlines; + } - /* Argument count mismatch */ - PPError ("Macro argument count mismatch"); + /* Append a space as a separator */ + if (Skipped && SB_NotEmpty (&Arg.Tokens)) { + SB_AppendChar (&Arg.Tokens, ' '); + Skipped = 1; + } else { + Skipped = 0; + } - /* Be sure to make enough empty arguments available */ - while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) { - ME_AppendActual (&E, &Arg); + /* Finish reading the current argument if we are not inside nested + ** parentheses or a variadic macro argument. + */ + if (Parens == 0 && + ((CurC == ',' && !ME_IsNextArgVariadic (E, M)) || CurC == ')')) { + + /* End of actual argument. Remove whitespace from the end. */ + while (IsBlank (SB_LookAtLast (&Arg.Tokens))) { + SB_Drop (&Arg.Tokens, 1); + } + + /* If this is not the single empty argument for a macro with an + ** empty argument list, remember it. + */ + if (CurC != ')' || + CollCount (&E->Args) > 0 || + SB_NotEmpty (&Arg.Tokens) || + M->ParamCount > 0) { + MacroExp* A = ME_AppendArg (E, &Arg); + unsigned I; + + /* Copy the hide sets from the argument list */ + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* HS; + + for (HS = MHS->HS; HS != 0; HS = HS->Next) { + /* Get the correct hide range */ + unsigned Start = NameIdx + 1 + Idx; + unsigned Len; + if (HS->Start < Start) { + if (HS->End > Start) { + Len = HS->End - Start; + } else { + /* Out of the range */ + continue; + } + Start = 0; + } else { + Len = HS->End - HS->Start; + Start = HS->Start - Start; + } + if (Start + Len > Idx + CountInArg) { + if (Idx + CountInArg > Start) { + Len = Idx + CountInArg - Start; + } else { + /* Out of the range */ + break; + } + } + ME_HideMacro (Start, Len, A, MHS->M); + } + } + + /* More argument info */ + A->IdentCount = CountInArg; + } + + Idx += CountInArg; + + /* Check for end of macro param list */ + if (CurC == ')') { + /* Count right parens */ + ++Idx; + NextChar (); + break; + } + + /* Start the next param */ + NextChar (); + DoneMacroExp (&Arg); + InitMacroExp (&Arg); + CountInArg = 0; + + continue; + + } else if (CurC == '\0') { + + /* End of input inside macro argument list */ + PPError ("Unterminated argument list invoking macro '%s'", M->Name); + Parens = -1; + ClearLine (); + E->Flags |= MES_ERROR; + Idx = 0; + break; + + } else { + unsigned LastLen = SB_GetLen (&Arg.Tokens); + + if (IsSym (Ident)) { + /* Just copy the identifier */ + SB_AppendStr (&Arg.Tokens, Ident); + + /* Count identifiers */ + ++CountInArg; + + /* Used for concatentation check */ + if ((Arg.Flags & MES_FIRST_TOKEN) != 0) { + Arg.Flags |= MES_BEGIN_WITH_IDENT; + } + } else if (IsPPNumber (CurC, NextC)) { + /* Copy a pp-number */ + CopyPPNumber (&Arg.Tokens); + } else if (IsQuotedString ()) { + /* Quoted string - just copy */ + CopyQuotedString (&Arg.Tokens); + } else if (GetPunc (Ident)) { + /* Check nested parentheses */ + if (Ident[0] == '(') { + /* Opening nested parenthesis */ + ++Parens; + } else if (Ident[0] == ')') { + /* Closing nested parenthesis */ + --Parens; + + /* Count right parens */ + ++CountInArg; + } + /* Just copy the punctuator */ + SB_AppendStr (&Arg.Tokens, Ident); + } else { + /* Just copy the character */ + SB_AppendChar (&Arg.Tokens, CurC); + NextChar (); + + /* But don't count it */ + ++LastLen; + } + + /* Used for concatentation check */ + ME_SetTokLens (&Arg, SB_GetLen (&Arg.Tokens) - LastLen - Skipped); } } - /* Replace macro arguments handling the # and ## operators */ - MacroArgSubst (&E); + /* Compare formal and actual argument count */ + if (CollCount (&E->Args) < (unsigned) M->ParamCount) { + /* Check further only when the parentheses are paired */ + if (Parens == 0) { + /* Specially casing variable argument */ + if (M->Variadic && + M->ParamCount > 0 && + CollCount (&E->Args) + 1 == (unsigned) M->ParamCount) { + /* The variable argument is left out entirely */ + E->Flags |= MES_NO_VA_COMMA; + if (IS_Get (&Standard) < STD_CC65) { + PPWarning ("ISO C does not permit leaving out the comma before the variable argument"); + } + } else { + /* Too few argument */ + PPError ("Macro \"%s\" passed only %u arguments, but requires %u", + M->Name, CollCount (&E->Args), (unsigned) M->ParamCount); + } + } - /* Do macro replacement on the macro that already has the parameters - ** substituted. + /* Be sure to make enough empty arguments available */ + DoneMacroExp (&Arg); + InitMacroExp (&Arg); + Arg.Flags |= MES_HAS_REPLACEMENT; + while (CollCount (&E->Args) < (unsigned) M->ParamCount) { + ME_AppendArg (E, &Arg); + } + } else if (Parens == 0 && CollCount (&E->Args) > (unsigned) M->ParamCount) { + /* Too many arguments */ + PPError ("Macro \"%s\" passed %u arguments, but takes just %u", + M->Name, CollCount (&E->Args), (unsigned) M->ParamCount); + } + + /* Deallocate argument resources */ + DoneMacroExp (&Arg); + + /* Return the total count of identifiers and right parentheses in the + ** argument list. */ - M->Expanding = 1; - MacroReplacement (&E.Replacement, Target); - M->Expanding = 0; - - /* Free memory allocated for the macro expansion structure */ - DoneMacroExp (&E); + return Idx; } -static void ExpandMacro (StrBuf* Target, Macro* M) -/* Expand a macro into Target */ +static unsigned SubstMacroArgs (unsigned NameIdx, StrBuf* Target, MacroExp* E, Macro* M, unsigned* IdentCount) +/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff. +** Return the length of the last pp-token in the result and output the count +** of identifiers and right parentheses in the result to *IdentCount. +*/ { -#if 0 + unsigned Idx = NameIdx; + ident Ident; + unsigned TokLen = 0; + int ParamIdx; + StrBuf* OldSource; + int HaveSpace = 0; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; /* Temporary buffer */ + + /* Remember the current input stack and disable it for now */ + Collection* OldInputStack = UseInputStack (0); + + /* Remember the current input and switch to the macro replacement */ + unsigned OldIndex = SB_GetIndex (&M->Replacement); + + SB_Reset (&M->Replacement); + OldSource = InitLine (&M->Replacement); + + /* If the macro expansion replaces an function-like macro with an argument + ** list containing a right parenthesis outside the hidesets of previously + ** replaced macros, stop those hidesets from this replacement. This is not + ** required by the standard but just to match up with other major C + ** compilers. + */ + ME_HandleSemiNestedMacro (NameIdx, NameIdx + *IdentCount, E); + + /* Substitution loop */ + while (CurC != '\0') { + int NeedPaste = 0; + + /* If we have an identifier, check if it's a macro */ + if (IsSym (Ident)) { + + /* Remember and skip any following whitespace */ + HaveSpace = SkipWhitespace (0); + + /* Check if it's a macro parameter */ + if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) { + + /* If a ## operator follows, we have to insert the actual + ** argument as-is, otherwise it must be macro-replaced. + */ + if (CurC == '#' && NextC == '#') { + + /* Get the corresponding actual argument */ + const MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + + /* Separate with a white space if necessary */ + if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) { + SB_AppendChar (Target, ' '); + } + + /* For now we need no placemarkers */ + SB_Append (Target, &A->Tokens); + + /* Adjust tracking */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + Idx += A->IdentCount; + + /* This will be used for concatenation */ + TokLen = A->LastTokLen; + + } else { + + /* Get the corresponding macro-replaced argument */ + const MacroExp* A = ME_GetReplacedArg (E, ParamIdx); + + /* Separate with a white space if necessary */ + if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) { + SB_AppendChar (Target, ' '); + } + + /* Append the replaced string */ + SB_Append (Target, &A->Tokens); + + /* Insert the range of identifiers to parent preceding this argument */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + + /* Add hide range */ + ME_AddArgHideSets (Idx, A, E); + + /* Adjust tracking */ + Idx += A->IdentCount; + + /* May be used for later pp-token merge check */ + TokLen = A->LastTokLen; + } + + } else { + + /* An identifier, keep it */ + SB_AppendStr (Target, Ident); + + /* Adjust tracking */ + ME_OffsetHideSets (Idx, 1, E); + ++Idx; + + /* May be used for later concatenation */ + TokLen = strlen (Ident); + } + + /* Special casing for 'L' prefixing '#' */ + if (TokLen == 1 && SB_LookAtLast (Target) == 'L' && CurC == '#') { + HaveSpace = 1; + } + + /* Squeeze and add the skipped whitespace back for consistency */ + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + + /* Done with this substituted argument */ + continue; + + } else if (CurC == '#' && NextC == '#') { + + /* ## operator */ + NextChar (); + NextChar (); + SkipWhitespace (0); + + /* If the next token is an identifier which is a macro argument, + ** replace it, otherwise just add it. + */ + if (IsSym (Ident)) { + unsigned NewCount = 1; + + /* Check if it's a macro parameter */ + if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) { + + /* Get the corresponding actual argument */ + MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + + /* Insert the range of identifiers to parent preceding this argument */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + + /* Add hide range */ + ME_AddArgHideSets (Idx, A, E); + + /* Adjust tracking */ + NewCount = A->IdentCount; + + /* If the preceding pp-token is not a placemarker and is + ** concatenated to with an identifier, the count of tracked + ** identifiers is then one less. + */ + if (TryPastePPTok (Target, &A->Tokens, TokLen, A->FirstTokLen)) { + if (TokLen > 0 && (A->Flags & MES_BEGIN_WITH_IDENT) != 0) { + --NewCount; + ME_RemoveToken (Idx, 1, E); + } + if ((A->Flags & MES_MULTIPLE_TOKEN) == 0) { + TokLen += A->FirstTokLen; + } else { + TokLen = A->LastTokLen; + } + } else { + TokLen = A->LastTokLen; + } + + } else { + + unsigned Len; + + /* Just an ordinary identifier - add as-is */ + SB_CopyStr (&Buf, Ident); + + /* If the preceding pp-token is not a placemarker and is + ** concatenated to with an identifier, the count of tracked + ** identifiers is then one less. + */ + Len = SB_GetLen (&Buf); + if (TryPastePPTok (Target, &Buf, TokLen, Len)) { + if (TokLen > 0) { + --NewCount; + } + TokLen += Len; + } else { + TokLen = Len; + } + + /* Adjust tracking */ + ME_OffsetHideSets (Idx, NewCount, E); + + } + + /* Adjust tracking */ + Idx += NewCount; + + /* Keep the whitespace for consistency */ + HaveSpace = SkipWhitespace (0); + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + + /* Done with this concatenated identifier */ + continue; + } + + if (CurC != '\0') { + /* Non-identifiers may still be pasted together */ + NeedPaste = 1; + } + + } + + /* Use the temporary buffer */ + SB_Clear (&Buf); + if (IsPPNumber (CurC, NextC)) { + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + CopyQuotedString (&Buf); + } else { + if (CurC == '#' && M->ParamCount >= 0) { + /* A # operator within a macro expansion of a function-like + ** macro. Read the following identifier and check if it's a + ** macro parameter. + */ + NextChar (); + SkipWhitespace (0); + if (!IsSym (Ident) || (ParamIdx = FindMacroParam (M, Ident)) < 0) { + /* Should not happen, but still */ + Internal ("'#' is not followed by a macro parameter"); + } else { + /* Make a valid string from Replacement */ + MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + SB_Reset (&A->Tokens); + Stringize (&A->Tokens, &Buf); + } + } else if (GetPunc (Ident)) { + /* Count right parens. This is OK since they cannot be pasted + ** to form different punctuators with others. + */ + if (Ident[0] == ')') { + /* Adjust tracking */ + ME_OffsetHideSets (Idx, 1, E); + ++Idx; + } + SB_AppendStr (&Buf, Ident); + } else if (CurC != '\0') { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + } + + /* Squeeze any whitespace for consistency. Especially, comments must + ** be consumed before fed to the punctuator parser, or their leading + ** '/' characters would be parsed wrongly as division operators. + */ + HaveSpace = SkipWhitespace (0); + + if (NeedPaste) { + unsigned Len = SB_GetLen (&Buf); + + /* Concatenate pp-tokens */ + if (TryPastePPTok (Target, &Buf, TokLen, Len)) { + TokLen += Len; + } else { + TokLen = Len; + } + } else { + /* Just append the token */ + SB_Append (Target, &Buf); + TokLen = SB_GetLen (&Buf); + } + + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + + } + + /* Done with the temporary buffer */ + SB_Done (&Buf); + + /* Remove the macro name itself together with the arguments (if any) */ + ME_RemoveToken (Idx, 1 + *IdentCount, E); + + /* Hide this macro for the whole result of this expansion */ + ME_HideMacro (NameIdx, Idx - NameIdx, E, M); + + /* Switch back the input */ + UseInputStack (OldInputStack); + InitLine (OldSource); + SB_SetIndex (&M->Replacement, OldIndex); + + /* Set the count of identifiers and right parentheses in the result */ + *IdentCount = Idx - NameIdx; + + /* Return the length of the last pp-token */ + return TokLen; +} + + + +static unsigned ExpandMacro (unsigned Idx, StrBuf* Target, MacroExp* E, Macro* M, int MultiLine) +/* Expand a macro into Target. Return the length of the last pp-token in the +** result of the expansion. +*/ +{ + unsigned Count = 0; /* Count of identifiers and right parentheses */ + unsigned Len = 0; /* Length of the last pp-token in the result */ + + /* Disable previous pp-token spacing checking */ + StrBuf* PrevTok = CurRescanStack->PrevTok; + CurRescanStack->PrevTok = 0; + +#if DEV_CC65_DEBUG static unsigned V = 0; - printf ("Expanding %s(%u)\n", M->Name, ++V); + printf ("Expanding (%u) %s\n", ++V, M->Name); #endif /* Check if this is a function like macro */ - if (M->ArgCount >= 0) { - - int Whitespace = SkipWhitespace (1); - if (CurC != '(') { - /* Function like macro but no parameter list */ - SB_AppendStr (Target, M->Name); - if (Whitespace) { - SB_AppendChar (Target, ' '); - } - } else { - /* Function like macro */ - MacroCall (Target, M); - } - - } else { - - MacroExp E; - InitMacroExp (&E, M); - - /* Handle # and ## operators for object like macros */ - MacroArgSubst (&E); - - /* Do macro replacement on the macro that already has the parameters - ** substituted. - */ - M->Expanding = 1; - MacroReplacement (&E.Replacement, Target); - M->Expanding = 0; - - /* Free memory allocated for the macro expansion structure */ - DoneMacroExp (&E); - + if (M->ParamCount >= 0) { + /* Read the actual macro arguments (with the enclosing parentheses) */ + Count = ReadMacroArgs (Idx, E, M, MultiLine); } -#if 0 - printf ("Done with %s(%u)\n", M->Name, V--); + + if ((E->Flags & MES_ERROR) == 0) { + /* Replace macro parameters with arguments handling the # and ## operators */ + Len = SubstMacroArgs (Idx, Target, E, M, &Count); + } else { + SB_CopyStr (Target, M->Name); + } + + if (CollCount (&E->Args) > 0) { + /* Clear all arguments */ + ME_ClearArgs (E); + } + +#if DEV_CC65_DEBUG + printf ("Expanded (%u) %s to %d ident(s) at %u: %s\n", + V--, M->Name, Count, Idx, SB_GetConstBuf (Target)); #endif + + /* Reenable previous pp-token concatenation checking */ + FreeStrBuf (CurRescanStack->PrevTok); + CurRescanStack->PrevTok = PrevTok; + + /* Return the length of the last pp-token in the expansion result */ + return Len; } -static void DefineMacro (void) -/* Handle a macro definition. */ +static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags) +/* Scan for and perform macro replacement. Return the count of identifiers and +** right parentheses in the replacement result. +*/ +{ + unsigned Count = 0; + StrBuf* TmpTarget = NewStrBuf (); + + /* Remember the current input and switch to Source */ + StrBuf* OldSource = InitLine (Source); + RescanInputStack RescanStack; + RescanInputStack* OldRescanStack = CurRescanStack; + + InitRescanInputStack (&RescanStack); + PushRescanLine (&RescanStack, Line, 0); + CurRescanStack = &RescanStack; + + /* Loop substituting macros */ + while (CurC != '\0') { + int Skipped = 0; + ident Ident; + + /* If we have an identifier, check if it's a macro */ + if (IsSym (Ident)) { + /* Check for bad identifier names */ + if ((ModeFlags & (MSM_MULTILINE | MSM_IN_DIRECTIVE | MSM_IN_ARG_LIST)) != 0 && + (CollCount (&CurRescanStack->Lines) == 1 || CurC == '\0')) { + CheckForBadIdent (Ident, IS_Get (&Standard), 0); + } + + if ((ModeFlags & MSM_OP_DEFINED) != 0 && strcmp (Ident, "defined") == 0) { + /* Handle the "defined" operator */ + int HaveParen = 0; + + /* Eat the "defined" operator */ + ME_RemoveToken (Count, 1, E); + + SkipWhitespace (0); + if (CurC == '(') { + HaveParen = 1; + NextChar (); + SkipWhitespace (0); + } + + /* Add a space to separate the result if necessary */ + SeparatePPTok (Target, '0'); + + if (IsSym (Ident)) { + /* Eat the identifier */ + ME_RemoveToken (Count, 1, E); + SB_AppendChar (Target, IsMacro (Ident) ? '1' : '0'); + if (HaveParen) { + SkipWhitespace (0); + if (CurC != ')') { + PPError ("')' expected"); + ClearLine (); + } else { + /* Eat the right parenthesis */ + ME_RemoveToken (Count, 1, E); + NextChar (); + } + } + } else { + PPError ("Macro name must be an identifier"); + ClearLine (); + SB_AppendChar (Target, '0'); + } + } else { + Macro* M = FindMacro (Ident); + + /* Check if it's an expandable macro */ + if (M != 0 && ME_CanExpand (Count, E, M)) { + int MultiLine = (ModeFlags & MSM_MULTILINE) != 0; + unsigned LastTokLen; + + /* Check if this is a function-like macro */ + if (M->ParamCount >= 0) { + int HaveSpace = SkipWhitespace (MultiLine) > 0; + + /* A function-like macro name without an immediately + ** following argument list is not subject to expansion. + */ + if (CurC != '(') { + /* No expansion */ + ++Count; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, M->Name[0]); + SB_AppendStr (Target, M->Name); + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + /* Used for concatentation check */ + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags |= MES_BEGIN_WITH_IDENT; + } + ME_SetTokLens (E, strlen (M->Name)); + } + + /* Since we have already got on hold of the next + ** line, we have to reuse it as the next line + ** instead of reading a new line from the source. + */ + if (PendingNewLines > 0 && MultiLine) { + unsigned I = SB_GetIndex (Line); + + /* There is no way a function-like macro call + ** detection could span multiple lines within + ** the range of another just expanded macro. + */ + CHECK (CollCount (&CurRescanStack->Lines) == 1); + + /* Revert one newline */ + --PendingNewLines; + + /* Align indention */ + while (I > 0) { + --I; + if (SB_GetBuf (Line)[I] == '\n') { + ++I; + break; + } + SB_GetBuf (Line)[I] = ' '; + } + + /* Set start index */ + SB_SetIndex (Line, I); + + /* Add newlines */ + AddPreLine (Target); + + /* Reuse this line as the next line */ + ReuseInputLine (); + + /* Quit this loop */ + break; + } + + /* Append back the whitespace */ + if (HaveSpace) { + SB_AppendChar (Target, ' '); + } + + /* Loop */ + goto Loop; + } + } + + /* Either an object-like or function-like macro */ + MultiLine = MultiLine && M->ParamCount >= 0; + + /* If we were going to support #pragma in macro argument + ** list, it would be output to OLine. + */ + if (MultiLine && OLine == 0) { + OLine = TmpTarget; + LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine); + OLine = 0; + } else { + LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine); + } + + /* Check for errors in expansion */ + if ((E->Flags & MES_ERROR) != 0) { + break; + } + + /* Pop the current line if it is at the end */ + PopRescanLine (); + + if (SB_GetLen (TmpTarget) > 0) { + /* Start rescanning from the temporary result */ + SB_Reset (TmpTarget); + InitLine (TmpTarget); + PushRescanLine (CurRescanStack, TmpTarget, LastTokLen); + + /* Add a space before a '#' at the beginning of the line */ + if (CurC == '#' && + NextC != '#' && + (SB_IsEmpty (Target) || SB_LookAtLast (Target) == '\n')) { + SB_AppendChar (Target, ' '); + } + + /* Switch the buffers */ + TmpTarget = NewStrBuf (); + } else if (PendingNewLines > 0 && MultiLine) { + /* Cancel remaining check for pp-tokens separation + ** if there is since ther have been newlines that + ** can always separate them. + */ + if (CurRescanStack->PrevTok != 0) { + FreeStrBuf (CurRescanStack->PrevTok); + CurRescanStack->PrevTok = 0; + } + + /* Squeeze whitespace */ + SkipWhitespace (0); + + /* Add indention to preprocessor output if needed */ + if (CurC != '\0' && CollCount (&CurRescanStack->Lines) == 1) { + /* Add newlines */ + AddPreLine (Target); + + /* Align indention */ + AppendIndent (Target, SB_GetIndex (Line)); + } + } + + /* Since we are rescanning, we needn't add the + ** count of just replaced identifiers right now. + */ + continue; + } + + /* An unexpandable identifier. Keep it. */ + ++Count; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, Ident[0]); + SB_AppendStr (Target, Ident); + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + /* Used for concatentation check */ + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags |= MES_BEGIN_WITH_IDENT; + } + ME_SetTokLens (E, strlen (Ident)); + } + } + } else { + unsigned LastLen; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, CurC); + + LastLen = SB_GetLen (Target); + + if ((ModeFlags & MSM_TOK_HEADER) != 0 && (CurC == '<' || CurC == '\"')) { + CopyHeaderNameToken (Target); + } else if (IsPPNumber (CurC, NextC)) { + CopyPPNumber (Target); + } else if (IsQuotedString ()) { + CopyQuotedString (Target); + } else { + /* We want to squeeze whitespace until the end of the current + ** input line, so we have to deal with such cases specially. + */ + if (CollCount (&CurRescanStack->Lines) > 1) { + RescanInputStack* RIS = CurRescanStack; + + /* Temporarily disable input popping */ + CurRescanStack = 0; + Skipped = SkipWhitespace (0); + CurRescanStack = RIS; + + if (CurC == '\0') { + /* Now we are at the end of the input line */ + goto Loop; + } + } else { + Skipped = SkipWhitespace (0); + } + + /* Punctuators must be checked after whitespace since comments + ** introducers may be misinterpreted as division operators. + */ + if (!Skipped) { + if (GetPunc (Ident)) { + if (Ident[0] == ')') { + /* Count right parens */ + ++Count; + } + SB_AppendStr (Target, Ident); + + /* If an identifier follows immediately, it could be a macro + ** expanded later that occasionally need a space to separate. + */ + if (IsIdent (CurC)) { + /* Memorize the previous pp-token and check it later */ + LazyCheckNextPPTok (Target, strlen (Ident)); + } + } else { + SB_AppendChar (Target, CurC); + NextChar (); + + /* Don't count this character */ + ++LastLen; + } + } + } + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + ME_SetTokLens (E, SB_GetLen (Target) - LastLen); + } + } + +Loop: + /* Switch back to the previous input stream if we have finished + ** rescanning the current one. + */ + if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) { + /* Check for rescan sequence end and pp-token pasting */ + Skipped = SkipWhitespace (0) || Skipped; + + /* Add indention to preprocessor output if needed */ + if (CurC != '\0' && + PendingNewLines > 0 && + (ModeFlags & MSM_MULTILINE) != 0 && + CollCount (&CurRescanStack->Lines) == 1) { + /* Add newlines */ + AddPreLine (Target); + + /* Align indention */ + AppendIndent (Target, SB_GetIndex (Line)); + Skipped = 0; + } + } + + /* Append a space if there hasn't been one */ + if (Skipped && !IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + } + + /* Append the remaining result */ + SB_Append (Target, TmpTarget); + + /* Done with the temporary buffer */ + SB_Done (TmpTarget); + + /* Drop whitespace at the end */ + if (IsBlank (SB_LookAtLast (Target))) { + SB_Drop (Target, 1); + } + + /* Sanity check */ + if ((E->Flags & MES_ERROR) == 0) { + CHECK (CollCount (&CurRescanStack->Lines) == 1); + } + + /* Done with the current input stack */ + DoneRescanInputStack (CurRescanStack); + CurRescanStack = OldRescanStack; + + /* Switch back the input */ + InitLine (OldSource); + + /* Return the count of identifiers and right parentheses */ + return Count; +} + + + +/*****************************************************************************/ +/* Directives */ +/*****************************************************************************/ + + + +static int ParseMacroReplacement (StrBuf* Source, Macro* M) +/* Check correctness of macro definition while squeezing old and new style +** comments and other non-newline whitespace sequences. Return 1 on success +** or 0 on failure. +*/ +{ + /* Switch to the new input source */ + StrBuf* OldSource = InitLine (Source); + int HasWhiteSpace = 0; + unsigned Len; + ident Ident; + int Std = IS_Get (&Standard); + + /* Skip whitespace before the macro replacement */ + SkipWhitespace (0); + + /* Check for ## at start */ + if (CurC == '#' && NextC == '#') { + /* Diagnose and bail out */ + PPError ("'##' cannot appear at start of macro expansion"); + goto Error_Handler; + } + + /* Loop removing ws and comments */ + while (CurC != '\0') { + if (HasWhiteSpace) { + SB_AppendChar (&M->Replacement, ' '); + } else if (IsQuotedString ()) { + CopyQuotedString (&M->Replacement); + } else if (IsSym (Ident)) { + CheckForBadIdent (Ident, Std, M); + SB_AppendStr (&M->Replacement, Ident); + } else { + if (M->ParamCount >= 0 && GetPunc (Ident)) { + Len = strlen (Ident); + /* Check for # */ + if (Len == 1 && Ident[0] == '#') { + HasWhiteSpace = SkipWhitespace (0); + + /* Check next pp-token */ + if (!IsSym (Ident) || FindMacroParam (M, Ident) < 0) { + PPError ("'#' is not followed by a macro parameter"); + goto Error_Handler; + } + + /* Make the replacement */ + SB_AppendChar (&M->Replacement, '#'); + if (HasWhiteSpace) { + SB_AppendChar (&M->Replacement, ' '); + } + SB_AppendStr (&M->Replacement, Ident); + } else { + SB_AppendBuf (&M->Replacement, Ident, Len); + } + } else { + SB_AppendChar (&M->Replacement, CurC); + NextChar (); + } + } + + HasWhiteSpace = SkipWhitespace (0); + } + + /* Check for ## at end */ + Len = SB_GetLen (&M->Replacement); + if (Len >= 2) { + if (SB_LookAt (&M->Replacement, Len - 1) == '#' && + SB_LookAt (&M->Replacement, Len - 2) == '#') { + /* Diagnose and bail out */ + PPError ("'##' cannot appear at end of macro expansion"); + goto Error_Handler; + } + } + + /* Terminate the new input line */ + SB_Terminate (&M->Replacement); + + /* Switch back to the old source */ + InitLine (OldSource); + + /* Success */ + return 1; + +Error_Handler: + + /* Switch back to the old source */ + InitLine (OldSource); + + /* Failure */ + return 0; +} + + + +static void DoDefine (void) +/* Process #define directive */ { ident Ident; - Macro* M; + Macro* M = 0; Macro* Existing; - int C89; + int Std; /* Read the macro name */ SkipWhitespace (0); if (!MacName (Ident)) { - return; + goto Error_Handler; } - /* Remember if we're in C89 mode */ - C89 = (IS_Get (&Standard) == STD_C89); + /* Remember the language standard we are in */ + Std = IS_Get (&Standard); - /* Get an existing macro definition with this name */ - Existing = FindMacro (Ident); + /* Check for forbidden macro names */ + if (strcmp (Ident, "defined") == 0) { + PPError ("'%s' cannot be used as a macro name", Ident); + goto Error_Handler; + } + + /* Check for and warn on special identifiers */ + CheckForBadIdent (Ident, Std, 0); /* Create a new macro definition */ M = NewMacro (Ident); - /* Check if this is a function like macro */ + /* Check if this is a function-like macro */ if (CurC == '(') { /* Skip the left paren */ NextChar (); - /* Set the marker that this is a function like macro */ - M->ArgCount = 0; + /* Set the marker that this is a function-like macro */ + M->ParamCount = 0; /* Read the formal parameter list */ while (1) { @@ -778,41 +2595,37 @@ static void DefineMacro (void) /* The next token must be either an identifier, or - if not in ** C89 mode - the ellipsis. */ - if (!C89 && CurC == '.') { + if (Std >= STD_C99 && CurC == '.') { /* Ellipsis */ NextChar (); if (CurC != '.' || NextC != '.') { PPError ("'...' expected"); - ClearLine (); - return; + goto Error_Handler; } NextChar (); NextChar (); /* Remember that the macro is variadic and use __VA_ARGS__ as - ** the argument name. + ** the parameter name. */ - AddMacroArg (M, "__VA_ARGS__"); + AddMacroParam (M, "__VA_ARGS__"); M->Variadic = 1; } else { - /* Must be macro argument name */ + /* Must be macro parameter name */ if (MacName (Ident) == 0) { - return; + goto Error_Handler; } - /* __VA_ARGS__ is only allowed in C89 mode */ - if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) { - PPWarning ("'__VA_ARGS__' can only appear in the expansion " - "of a C99 variadic macro"); - } + /* Check for and warn on special identifiers */ + CheckForBadIdent (Ident, Std, 0); - /* Add the macro argument */ - AddMacroArg (M, Ident); + /* Add the macro parameter */ + AddMacroParam (M, Ident); } /* If we had an ellipsis, or the next char is not a comma, we've - ** reached the end of the macro argument list. + ** reached the end of the macro parameter list. */ SkipWhitespace (0); if (M->Variadic || CurC != ',') { @@ -823,180 +2636,51 @@ static void DefineMacro (void) /* Check for a right paren and eat it if we find one */ if (CurC != ')') { - PPError ("')' expected"); - ClearLine (); - return; + PPError ("')' expected for macro definition"); + goto Error_Handler; } NextChar (); } - /* Skip whitespace before the macro replacement */ - SkipWhitespace (0); - - /* Insert the macro into the macro table and allocate the ActualArgs array */ - InsertMacro (M); - /* Remove whitespace and comments from the line, store the preprocessed ** line into the macro replacement buffer. */ - Pass1 (Line, &M->Replacement); - - /* Remove whitespace from the end of the line */ - while (IsSpace (SB_LookAtLast (&M->Replacement))) { - SB_Drop (&M->Replacement, 1); + if (ParseMacroReplacement (Line, M) == 0) { + goto Error_Handler; } + #if 0 printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); #endif + /* Get an existing macro definition with this name */ + Existing = FindMacro (M->Name); + /* If we have an existing macro, check if the redefinition is identical. ** Print a diagnostic if not. */ - if (Existing && MacroCmp (M, Existing) != 0) { - PPError ("Macro redefinition is not identical"); - } -} - - - -/*****************************************************************************/ -/* Preprocessing */ -/*****************************************************************************/ - - - -static unsigned Pass1 (StrBuf* Source, StrBuf* Target) -/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments -** and the "defined" operator. -*/ -{ - unsigned IdentCount; - ident Ident; - int HaveParen; - - /* Switch to the new input source */ - StrBuf* OldSource = InitLine (Source); - - /* Loop removing ws and comments */ - IdentCount = 0; - while (CurC != '\0') { - if (SkipWhitespace (0)) { - /* Squeeze runs of blanks */ - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, ' '); - } - } else if (IsSym (Ident)) { - if (Preprocessing && strcmp (Ident, "defined") == 0) { - /* Handle the "defined" operator */ - SkipWhitespace (0); - HaveParen = 0; - if (CurC == '(') { - HaveParen = 1; - NextChar (); - SkipWhitespace (0); - } - if (IsSym (Ident)) { - SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); - if (HaveParen) { - SkipWhitespace (0); - if (CurC != ')') { - PPError ("')' expected"); - } else { - NextChar (); - } - } - } else { - PPError ("Identifier expected"); - SB_AppendChar (Target, '0'); - } - } else { - ++IdentCount; - SB_AppendStr (Target, Ident); - } - } else if (IsQuote (CurC)) { - CopyQuotedString (Target); - } else if (CurC == '/' && NextC == '*') { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, ' '); - } - OldStyleComment (); - } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, ' '); - } - NewStyleComment (); - } else { - SB_AppendChar (Target, CurC); - NextChar (); + if (Existing != 0) { + if (MacroCmp (M, Existing) != 0) { + PPError ("Macro redefinition is not identical"); } + /* Undefine the existing macro anyways */ + UndefineMacro (Existing->Name); } - /* Switch back to the old source */ - InitLine (OldSource); + /* Insert the new macro into the macro table */ + InsertMacro (M); - /* Return the number of identifiers found in the line */ - return IdentCount; -} + /* Success */ + return; +Error_Handler: + /* Cleanup */ + ClearLine (); -static void MacroReplacement (StrBuf* Source, StrBuf* Target) -/* Perform macro replacement. */ -{ - ident Ident; - Macro* M; - - /* Remember the current input and switch to Source */ - StrBuf* OldSource = InitLine (Source); - - /* Loop substituting macros */ - while (CurC != '\0') { - /* If we have an identifier, check if it's a macro */ - if (IsSym (Ident)) { - /* Check if it's a macro */ - if ((M = FindMacro (Ident)) != 0 && !M->Expanding) { - /* It's a macro, expand it */ - ExpandMacro (Target, M); - } else { - /* An identifier, keep it */ - SB_AppendStr (Target, Ident); - } - } else if (IsQuote (CurC)) { - CopyQuotedString (Target); - } else if (IsSpace (CurC)) { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, CurC); - } - NextChar (); - } else { - SB_AppendChar (Target, CurC); - NextChar (); - } + if (M != 0) { + FreeMacro (M); } - - /* Switch back the input */ - InitLine (OldSource); -} - - - -static void PreprocessLine (void) -/* Translate one line. */ -{ - /* Trim whitespace and remove comments. The function returns the number of - ** identifiers found. If there were any, we will have to check for macros. - */ - SB_Clear (MLine); - if (Pass1 (Line, MLine) > 0) { - MLine = InitLine (MLine); - SB_Reset (Line); - SB_Clear (MLine); - MacroReplacement (Line, MLine); - } - - /* Read from the new line */ - SB_Reset (MLine); - MLine = InitLine (MLine); } @@ -1005,18 +2689,18 @@ static int PushIf (int Skip, int Invert, int Cond) /* Push a new if level onto the if stack */ { /* Check for an overflow of the if stack */ - if (IfIndex >= MAX_IFS-1) { + if (PPStack->Index >= MAX_PP_IFS-1) { PPError ("Too many nested #if clauses"); return 1; } /* Push the #if condition */ - ++IfIndex; + ++PPStack->Index; if (Skip) { - IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM; return 1; } else { - IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM; return (Invert ^ Cond); } } @@ -1028,9 +2712,12 @@ static void DoError (void) { SkipWhitespace (0); if (CurC == '\0') { - PPError ("Invalid #error directive"); + PPError ("#error"); } else { - PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); + StrBuf MsgLine = AUTO_STRBUF_INITIALIZER; + TranslationPhase3 (Line, &MsgLine); + PPError ("#error: %s", SB_GetConstBuf (&MsgLine) + SB_GetIndex (&MsgLine)); + SB_Done (&MsgLine); } /* Clear the rest of line */ @@ -1042,48 +2729,53 @@ static void DoError (void) static int DoIf (int Skip) /* Process #if directive */ { - /* We're about to abuse the compiler expression parser to evaluate the - ** #if expression. Save the current tokens to come back here later. - ** NOTE: Yes, this is a hack, but it saves a complete separate expression - ** evaluation for the preprocessor. - */ - Token SavedCurTok = CurTok; - Token SavedNextTok = NextTok; + PPExpr Expr = AUTO_PPEXPR_INITIALIZER; - /* Make sure the line infos for the tokens won't get removed */ - if (SavedCurTok.LI) { - UseLineInfo (SavedCurTok.LI); + if (!Skip) { + + /* We're about to use a dedicated expression parser to evaluate the #if + ** expression. Save the current tokens to come back here later. + */ + Token SavedCurTok = CurTok; + Token SavedNextTok = NextTok; + + /* Make sure the line infos for the tokens won't get removed */ + if (SavedCurTok.LI) { + UseLineInfo (SavedCurTok.LI); + } + if (SavedNextTok.LI) { + UseLineInfo (SavedNextTok.LI); + } + + /* Macro-replace a single line with support for the "defined" operator */ + SB_Clear (MLine); + PreprocessDirective (Line, MLine, MSM_OP_DEFINED); + + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); + + /* Add two semicolons as sentinels to the line, so the following + ** expression evaluation will eat these two tokens but nothing from + ** the following line. + */ + SB_AppendStr (Line, ";;"); + SB_Terminate (Line); + + /* Load CurTok and NextTok with tokens from the new input */ + NextToken (); + NextToken (); + + /* Call the expression parser */ + ParsePPExprInLine (&Expr); + + /* Restore input source */ + MLine = InitLine (MLine); + + /* Reset the old tokens */ + CurTok = SavedCurTok; + NextTok = SavedNextTok; } - if (SavedNextTok.LI) { - UseLineInfo (SavedNextTok.LI); - } - - /* Switch into special preprocessing mode */ - Preprocessing = 1; - - /* Expand macros in this line */ - PreprocessLine (); - - /* Add two semicolons as sentinels to the line, so the following - ** expression evaluation will eat these two tokens but nothing from - ** the following line. - */ - SB_AppendStr (Line, ";;"); - SB_Terminate (Line); - - /* Load CurTok and NextTok with tokens from the new input */ - NextToken (); - NextToken (); - - /* Call the expression parser */ - ExprDesc Expr = NoCodeConstExpr (hie1); - - /* End preprocessing mode */ - Preprocessing = 0; - - /* Reset the old tokens */ - CurTok = SavedCurTok; - NextTok = SavedNextTok; /* Set the #if condition according to the expression result */ return PushIf (Skip, 1, Expr.IVal != 0); @@ -1094,14 +2786,21 @@ static int DoIf (int Skip) static int DoIfDef (int skip, int flag) /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ { - ident Ident; + int Value = 0; - SkipWhitespace (0); - if (MacName (Ident) == 0) { - return 0; - } else { - return PushIf (skip, flag, IsMacro(Ident)); + if (!skip) { + ident Ident; + + SkipWhitespace (0); + if (MacName (Ident)) { + CheckForBadIdent (Ident, IS_Get (&Standard), 0); + Value = IsMacro (Ident); + /* Check for extra tokens */ + CheckExtraTokens (flag ? "ifdef" : "ifndef"); + } } + + return PushIf (skip, flag, Value); } @@ -1111,14 +2810,15 @@ static void DoInclude (void) { char RTerm; InputType IT; - StrBuf Filename = STATIC_STRBUF_INITIALIZER; + StrBuf Filename = AUTO_STRBUF_INITIALIZER; + /* Macro-replace a single line with special support for <filename> */ + SB_Clear (MLine); + PreprocessDirective (Line, MLine, MSM_TOK_HEADER); - /* Preprocess the remainder of the line */ - PreprocessLine (); - - /* Skip blanks */ - SkipWhitespace (0); + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); /* Get the next char and check for a valid file name terminator. Setup ** the include directory spec (SYS/USR) by looking at the terminator. @@ -1136,7 +2836,7 @@ static void DoInclude (void) break; default: - PPError ("'\"' or '<' expected"); + PPError ("#include expects \"FILENAME\" or <FILENAME>"); goto Done; } NextChar (); @@ -1150,9 +2850,13 @@ static void DoInclude (void) /* Check if we got a terminator */ if (CurC == RTerm) { + /* Skip the terminator */ + NextChar (); + /* Check for extra tokens following the filename */ + CheckExtraTokens ("include"); /* Open the include file */ OpenIncludeFile (SB_GetConstBuf (&Filename), IT); - } else if (CurC == '\0') { + } else { /* No terminator found */ PPError ("#include expects \"FILENAME\" or <FILENAME>"); } @@ -1161,6 +2865,9 @@ Done: /* Free the allocated filename data */ SB_Done (&Filename); + /* Restore input source */ + MLine = InitLine (MLine); + /* Clear the remaining line so the next input will come from the new ** file (if open) */ @@ -1169,27 +2876,119 @@ Done: +static unsigned GetLineDirectiveNum (void) +/* Get a decimal digit-sequence from the input. Return 0 on errors. */ +{ + unsigned long Num = 0; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + /* The only non-decimal-numeric character allowed in the digit-sequence is + ** the digit separator '\'' as of C23, but we haven't supported it yet. + */ + SkipWhitespace (0); + while (IsDigit (CurC)) + { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + + /* Ensure the buffer is terminated with a '\0' */ + SB_Terminate (&Buf); + if (SkipWhitespace (0) != 0 || CurC == '\0') { + const char* Str = SB_GetConstBuf (&Buf); + if (Str[0] == '\0') { + PPWarning ("#line directive interprets number as decimal, not octal"); + } else { + Num = strtoul (Str, 0, 10); + if (Num > 2147483647) { + PPError ("#line directive requires an integer argument not greater than 2147483647"); + ClearLine (); + Num = 0; + } else if (Num == 0) { + PPError ("#line directive requires a positive integer argument"); + ClearLine (); + } + } + } else { + PPError ("#line directive requires a simple decimal digit sequence"); + ClearLine (); + } + + /* Done with the buffer */ + SB_Done (&Buf); + + return (unsigned)Num; +} + + + +static void DoLine (void) +/* Process #line directive */ +{ + unsigned LineNum; + + /* Macro-replace a single line with support for the "defined" operator */ + SB_Clear (MLine); + PreprocessDirective (Line, MLine, MSM_NONE); + + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); + + /* Parse and check the specified line number */ + LineNum = GetLineDirectiveNum (); + if (LineNum != 0) { + /* Parse and check the optional filename argument */ + if (SB_GetIndex (Line) < SB_GetLen (Line)) { + StrBuf Filename = AUTO_STRBUF_INITIALIZER; + if (SB_GetString (Line, &Filename)) { + SB_Terminate (&Filename); + SetCurrentFilename (SB_GetConstBuf (&Filename)); + } else { + PPError ("Invalid filename for #line directive"); + LineNum = 0; + } + SB_Done (&Filename); + } + + /* #line actually sets the line number of the next line */ + if (LineNum > 0) { + SetCurrentLineNum (LineNum - 1); + /* Check for extra tokens at the end */ + CheckExtraTokens ("line"); + } + } + + /* Restore input source */ + MLine = InitLine (MLine); +} + + + static void DoPragma (void) /* Handle a #pragma line by converting the #pragma preprocessor directive into ** the _Pragma() compiler operator. */ { - /* Skip blanks following the #pragma directive */ - SkipWhitespace (0); + StrBuf* PragmaLine = OLine; - /* Copy the remainder of the line into MLine removing comments and ws */ + PRECONDITION (PragmaLine != 0); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PragmaLine); + + /* Macro-replace a single line */ SB_Clear (MLine); - Pass1 (Line, MLine); + PreprocessDirective (Line, MLine, MSM_NONE); - /* Convert the directive into the operator */ - SB_CopyStr (Line, "_Pragma ("); + /* Convert #pragma to _Pragma () */ + SB_AppendStr (PragmaLine, "_Pragma ("); SB_Reset (MLine); - Stringize (MLine, Line); - SB_AppendChar (Line, ')'); + Stringize (MLine, PragmaLine); + SB_AppendChar (PragmaLine, ')'); - /* Initialize reading from line */ - SB_Reset (Line); - InitLine (Line); + /* End this line */ + SB_SetIndex (PragmaLine, SB_GetLen (PragmaLine)); } @@ -1201,8 +3000,11 @@ static void DoUndef (void) SkipWhitespace (0); if (MacName (Ident)) { + CheckForBadIdent (Ident, IS_Get (&Standard), 0); UndefineMacro (Ident); } + /* Check for extra tokens */ + CheckExtraTokens ("undef"); } @@ -1212,9 +3014,12 @@ static void DoWarning (void) { SkipWhitespace (0); if (CurC == '\0') { - PPError ("Invalid #warning directive"); + PPWarning ("#warning"); } else { - PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); + StrBuf MsgLine = AUTO_STRBUF_INITIALIZER; + TranslationPhase3 (Line, &MsgLine); + PPWarning ("#warning: %s", SB_GetConstBuf (&MsgLine) + SB_GetIndex (&MsgLine)); + SB_Done (&MsgLine); } /* Clear the rest of line */ @@ -1223,23 +3028,17 @@ static void DoWarning (void) -void Preprocess (void) -/* Preprocess a line */ +static int ParseDirectives (unsigned ModeFlags) +/* Handle directives. Return 1 if any whitespace or newlines are parsed. */ { - int Skip; + int PPSkip = 0; ident Directive; - /* Create the output buffer if we don't already have one */ - if (MLine == 0) { - MLine = NewStrBuf (); - } - - /* Skip white space at the beginning of the line */ - SkipWhitespace (0); + /* Skip white space at the beginning of the first line */ + int Whitespace = SkipWhitespace (0); /* Check for stuff to skip */ - Skip = 0; - while (CurC == '\0' || CurC == '#' || Skip) { + while (CurC == '\0' || CurC == '#' || PPSkip) { /* Check for preprocessor lines lines */ if (CurC == '#') { @@ -1250,30 +3049,31 @@ void Preprocess (void) continue; } if (!IsSym (Directive)) { - PPError ("Preprocessor directive expected"); + if (!PPSkip) { + PPError ("Preprocessor directive expected"); + } ClearLine (); } else { - switch (FindPPToken (Directive)) { + switch (FindPPDirectiveType (Directive)) { - case PP_DEFINE: - if (!Skip) { - DefineMacro (); + case PPD_DEFINE: + if (!PPSkip) { + DoDefine (); } break; - case PP_ELIF: - if (IfIndex >= 0) { - if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { - + case PPD_ELIF: + if (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { /* Handle as #else/#if combination */ - if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { - Skip = !Skip; + if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; } - IfStack[IfIndex] |= IFCOND_ELSE; - Skip = DoIf (Skip); + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; + PPSkip = DoIf (PPSkip); /* #elif doesn't need a terminator */ - IfStack[IfIndex] &= ~IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM; } else { PPError ("Duplicate #else/#elif"); } @@ -1282,13 +3082,16 @@ void Preprocess (void) } break; - case PP_ELSE: - if (IfIndex >= 0) { - if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { - if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { - Skip = !Skip; + case PPD_ELSE: + if (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; } - IfStack[IfIndex] |= IFCOND_ELSE; + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; + + /* Check for extra tokens */ + CheckExtraTokens ("else"); } else { PPError ("Duplicate #else"); } @@ -1297,77 +3100,84 @@ void Preprocess (void) } break; - case PP_ENDIF: - if (IfIndex >= 0) { + case PPD_ENDIF: + if (PPStack->Index >= 0) { /* Remove any clauses on top of stack that do not ** need a terminating #endif. */ - while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { - --IfIndex; + while (PPStack->Index >= 0 && + (PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) { + --PPStack->Index; } /* Stack may not be empty here or something is wrong */ - CHECK (IfIndex >= 0); + CHECK (PPStack->Index >= 0); /* Remove the clause that needs a terminator */ - Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0; + PPSkip = (PPStack->Stack[PPStack->Index--] & IFCOND_SKIP) != 0; + + /* Check for extra tokens */ + CheckExtraTokens ("endif"); } else { PPError ("Unexpected '#endif'"); } break; - case PP_ERROR: - if (!Skip) { + case PPD_ERROR: + if (!PPSkip) { DoError (); } break; - case PP_IF: - Skip = DoIf (Skip); + case PPD_IF: + PPSkip = DoIf (PPSkip); break; - case PP_IFDEF: - Skip = DoIfDef (Skip, 1); + case PPD_IFDEF: + PPSkip = DoIfDef (PPSkip, 1); break; - case PP_IFNDEF: - Skip = DoIfDef (Skip, 0); + case PPD_IFNDEF: + PPSkip = DoIfDef (PPSkip, 0); break; - case PP_INCLUDE: - if (!Skip) { + case PPD_INCLUDE: + if (!PPSkip) { DoInclude (); } break; - case PP_LINE: - /* Should do something in C99 at least, but we ignore it */ - if (!Skip) { - ClearLine (); + case PPD_LINE: + if (!PPSkip) { + DoLine (); } break; - case PP_PRAGMA: - if (!Skip) { - DoPragma (); - goto Done; + case PPD_PRAGMA: + if (!PPSkip) { + if ((ModeFlags & MSM_IN_ARG_LIST) == 0) { + DoPragma (); + return Whitespace; + } else { + PPError ("Embedded #pragma directive within macro arguments is unsupported"); + } } break; - case PP_UNDEF: - if (!Skip) { + case PPD_UNDEF: + if (!PPSkip) { DoUndef (); } break; - case PP_WARNING: + case PPD_WARNING: /* #warning is a non standard extension */ if (IS_Get (&Standard) > STD_C99) { - if (!Skip) { + if (!PPSkip) { DoWarning (); } } else { - if (!Skip) { + if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); @@ -1375,7 +3185,7 @@ void Preprocess (void) break; default: - if (!Skip) { + if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); @@ -1384,19 +3194,216 @@ void Preprocess (void) } if (NextLine () == 0) { - if (IfIndex >= 0) { - PPError ("'#endif' expected"); - } - return; + break; } - SkipWhitespace (0); + ++PendingNewLines; + Whitespace = SkipWhitespace (0) || Whitespace; } - PreprocessLine (); + return Whitespace; +} -Done: - if (Verbosity > 1 && SB_NotEmpty (Line)) { - printf ("%s:%u: %.*s\n", GetCurrentFile (), GetCurrentLine (), - (int) SB_GetLen (Line), SB_GetConstBuf (Line)); + + +void HandleSpecialMacro (Macro* M, const char* Name) +/* Handle special "magic" macros that may change */ +{ + if (strcmp (Name, "__COUNTER__") == 0) { + /* Replace __COUNTER__ with the current counter number */ + if (IS_Get (&Standard) < STD_CC65) { + PPWarning ("__COUNTER__ is a cc65 extension"); + } + SB_Printf (&M->Replacement, "%u", GetCurrentCounter ()); + } else if (strcmp (Name, "__LINE__") == 0) { + /* Replace __LINE__ with the current line number */ + SB_Printf (&M->Replacement, "%u", GetCurrentLineNum ()); + } else if (strcmp (Name, "__FILE__") == 0) { + /* Replace __FILE__ with the current filename */ + StrBuf B = AUTO_STRBUF_INITIALIZER; + SB_InitFromString (&B, GetCurrentFilename ()); + SB_Clear (&M->Replacement); + Stringize (&B, &M->Replacement); + SB_Done (&B); } } + + + +/*****************************************************************************/ +/* Preprocessing */ +/*****************************************************************************/ + + + +static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. +*/ +{ + /* Switch to the new input source */ + StrBuf* OldSource = InitLine (Source); + + /* Loop removing ws and comments */ + while (CurC != '\0') { + int HasWhiteSpace = 0; + while (1) { + /* Squeeze runs of blanks */ + if (IsSpace (CurC)) { + NextChar (); + HasWhiteSpace = 1; + } else if (CurC == '/' && NextC == '*') { + OldStyleComment (); + HasWhiteSpace = 1; + } else if (CurC == '/' && NextC == '/') { + NewStyleComment (); + HasWhiteSpace = 1; + } else { + /* No more white space */ + break; + } + } + if (HasWhiteSpace) { + SB_AppendChar (Target, ' '); + } else if (IsQuotedString ()) { + CopyQuotedString (Target); + } else { + SB_AppendChar (Target, CurC); + NextChar (); + } + } + + /* Terminate the new input line */ + SB_Terminate (Target); + + /* Switch back to the old source */ + InitLine (OldSource); +} + + + +static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFlags) +/* Preprocess a single line. Handle specified tokens and operators, remove +** whitespace and comments, then do macro replacement. +*/ +{ + MacroExp E; + + SkipWhitespace (0); + InitMacroExp (&E); + ReplaceMacros (Source, Target, &E, ModeFlags | MSM_IN_DIRECTIVE); + DoneMacroExp (&E); +} + + + +void Preprocess (void) +/* Preprocess lines count of which is affected by directives */ +{ + MacroExp E; + + SB_Clear (PLine); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Parse any directives */ + OLine = PLine; + ParseDirectives (MSM_MULTILINE); + OLine = 0; + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Add leading whitespace to prettify preprocessor output */ + if (CurC != '\0') { + AppendIndent (PLine, SB_GetIndex (Line)); + } + + /* Expand macros if any */ + InitMacroExp (&E); + ReplaceMacros (Line, PLine, &E, MSM_MULTILINE); + DoneMacroExp (&E); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Read from the new line */ + SB_Reset (PLine); + PLine = InitLine (PLine); + + if (Verbosity > 1 && SB_NotEmpty (Line)) { + printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (), + (int) SB_GetLen (Line), SB_GetConstBuf (Line)); + } + + /* Free all undefined macros */ + FreeUndefinedMacros (); +} + + + +void InitPreprocess (void) +/* Init preprocessor */ +{ + /* Create the output buffers */ + MLine = NewStrBuf (); + PLine = NewStrBuf (); +} + + + +void DonePreprocess (void) +/* Done with preprocessor */ +{ + /* Done with the output buffers */ + SB_Done (MLine); + SB_Done (PLine); +} + + + +void SetPPIfStack (PPIfStack* Stack) +/* Specify which PP #if stack to use */ +{ + PPStack = Stack; +} + + + +void ContinueLine (void) +/* Continue the current line ended with a '\\' */ +{ + ++ContinuedLines; +} + + + +void PreprocessBegin (void) +/* Initialize preprocessor with current file */ +{ + /* Reset #if depth */ + PPStack->Index = -1; + + /* Remember to update source file location in preprocess-only mode */ + FileChanged = 1; + + /* Enable diagnostics on new style comments in C89 mode */ + AllowNewComments = 0; +} + + + +void PreprocessEnd (void) +/* Preprocessor done with current file */ +{ + /* Check for missing #endif */ + while (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) { + PPError ("#endif expected"); + } + --PPStack->Index; + } + + /* Remember to update source file location in preprocess-only mode */ + FileChanged = 1; +} diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 78a91a590..e2a1b073c 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -36,21 +36,28 @@ #ifndef PREPROC_H #define PREPROC_H - +#include "macrotab.h" /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ -/* Set when the preprocessor calls NoCodeConstExpr() recursively */ -extern unsigned char Preprocessing; +/* Maximum #if depth per file */ +#define MAX_PP_IFS 256 + +/* Data struct used for per-file-directive handling */ +typedef struct PPIfStack PPIfStack; +struct PPIfStack { + unsigned char Stack[MAX_PP_IFS]; + int Index; +}; /*****************************************************************************/ -/* code */ +/* Code */ /*****************************************************************************/ @@ -58,6 +65,27 @@ extern unsigned char Preprocessing; void Preprocess (void); /* Preprocess a line */ +void SetPPIfStack (PPIfStack* Stack); +/* Specify which PP #if stack to use */ + +void ContinueLine (void); +/* Continue the current line ended with a '\\' */ + +void PreprocessBegin (void); +/* Initialize preprocessor with current file */ + +void PreprocessEnd (void); +/* Preprocessor done with current file */ + +void InitPreprocess (void); +/* Init preprocessor */ + +void DonePreprocess (void); +/* Done with preprocessor */ + +void HandleSpecialMacro (Macro* M, const char* Name); +/* Handle special "magic" macros that may change */ + /* End of preproc.h */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 70203d027..ede77cb2c 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -39,6 +39,8 @@ #include <errno.h> #include <ctype.h> #include <math.h> +#include <inttypes.h> +#include <limits.h> /* common */ #include "chartype.h" @@ -69,6 +71,7 @@ Token CurTok; /* The current token */ Token NextTok; /* The next token */ +int PPParserRunning; /* Is tokenizer used by the preprocessor */ @@ -150,6 +153,11 @@ static const struct Keyword { #define IT_ULONG 0x08 +/* Internal type for numeric constant scanning. +** Size must be explicit for cross-platform uniformity. +*/ +typedef uint32_t scan_t; + /*****************************************************************************/ /* code */ @@ -188,10 +196,12 @@ static int SkipWhite (void) { while (1) { while (CurC == '\0') { - if (NextLine () == 0) { + /* If reading next line fails or is disabled with directives, bail + ** out. + */ + if (PPParserRunning || PreprocessNextLine () == 0) { return 0; } - Preprocess (); } if (IsSpace (CurC)) { NextChar (); @@ -232,10 +242,20 @@ void SymName (char* S) +int IsWideQuoted (char First, char Second) +/* Return 1 if the two successive characters indicate a wide string literal or +** a wide char constant, otherwise return 0. +*/ +{ + return First == 'L' && IsQuote(Second); +} + + + int IsSym (char* S) /* If a symbol follows, read it and return 1, otherwise return 0 */ { - if (IsIdent (CurC)) { + if (IsIdent (CurC) && !IsWideQuoted (CurC, NextC)) { SymName (S); return 1; } else { @@ -245,6 +265,45 @@ int IsSym (char* S) +int IsPPNumber (int Cur, int Next) +/* Return 1 if the two successive characters indicate a pp-number, otherwise +** return 0. +*/ +{ + return Cur != '.' ? IsDigit (Cur) : IsDigit (Next); +} + + + +void CopyPPNumber (StrBuf* Target) +/* Copy a pp-number from the input to Target */ +{ + int Std; + + if (!IsPPNumber (CurC, NextC)) { + return; + } + + /* P-exp is only valid in C99 and later */ + Std = IS_Get (&Standard); + while (IsIdent (CurC) || IsDigit (CurC) || CurC == '.') { + SB_AppendChar (Target, CurC); + if (NextC == '+' || NextC == '-') { + if (CurC == 'e' || CurC == 'E' || + (Std >= STD_C99 && (CurC == 'p' || CurC == 'P'))) { + SB_AppendChar (Target, NextC); + NextChar (); + } else { + NextChar (); + break; + } + } + NextChar (); + } +} + + + static void UnknownChar (char C) /* Error message for unknown character */ { @@ -370,6 +429,15 @@ static void CharConst (void) { int C; + if (CurC == 'L') { + /* Wide character constant */ + NextTok.Tok = TOK_WCCONST; + NextChar (); + } else { + /* Narrow character constant */ + NextTok.Tok = TOK_CCONST; + } + /* Skip the quote */ NextChar (); @@ -384,9 +452,6 @@ static void CharConst (void) NextChar (); } - /* Setup values and attributes */ - NextTok.Tok = TOK_CCONST; - /* Translate into target charset */ NextTok.IVal = SignExtendChar (TgtTranslateChar (C)); @@ -457,75 +522,86 @@ static void StringConst (void) static void NumericConst (void) /* Parse a numeric constant */ { - unsigned Base; /* Temporary number base */ - unsigned Prefix; /* Base according to prefix */ - StrBuf S = STATIC_STRBUF_INITIALIZER; + unsigned Base; /* Temporary number base according to prefix */ + unsigned Index; + StrBuf Src = AUTO_STRBUF_INITIALIZER; int IsFloat; char C; unsigned DigitVal; - unsigned long IVal; /* Value */ + scan_t IVal; /* Scanned value. */ + int Overflow; + + /* Get the pp-number first, then parse on it */ + CopyPPNumber (&Src); + SB_Terminate (&Src); + SB_Reset (&Src); /* Check for a leading hex, octal or binary prefix and determine the ** possible integer types. */ - if (CurC == '0') { + if (SB_Peek (&Src) == '0') { /* Gobble 0 and examine next char */ - NextChar (); - if (toupper (CurC) == 'X') { - Base = Prefix = 16; - NextChar (); /* gobble "x" */ - } else if (toupper (CurC) == 'B' && IS_Get (&Standard) >= STD_CC65) { - Base = Prefix = 2; - NextChar (); /* gobble 'b' */ + SB_Skip (&Src); + if (toupper (SB_Peek (&Src)) == 'X' && + IsXDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) { + Base = 16; + SB_Skip (&Src); /* gobble "x" */ + } else if (toupper (SB_Peek (&Src)) == 'B' && + IS_Get (&Standard) >= STD_CC65 && + IsDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) { + Base = 2; + SB_Skip (&Src); /* gobble 'b' */ } else { Base = 10; /* Assume 10 for now - see below */ - Prefix = 8; /* Actual prefix says octal */ } } else { - Base = Prefix = 10; + Base = 10; } - /* Because floating point numbers don't have octal prefixes (a number - ** with a leading zero is decimal), we first have to read the number - ** before converting it, so we can determine if it's a float or an - ** integer. + /* Because floating point numbers don't have octal prefixes (a number with + ** a leading zero is decimal), we first have to read the number before + ** converting it, so we can determine if it's a float or an integer. */ - while (IsXDigit (CurC) && HexVal (CurC) < Base) { - SB_AppendChar (&S, CurC); - NextChar (); + Index = SB_GetIndex (&Src); + while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) { + SB_Skip (&Src); } - SB_Terminate (&S); /* The following character tells us if we have an integer or floating ** point constant. Note: Hexadecimal floating point constants aren't ** supported in C89. */ - IsFloat = (CurC == '.' || - (Base == 10 && toupper (CurC) == 'E') || - (Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99)); + IsFloat = (C == '.' || + (Base == 10 && toupper (C) == 'E') || + (Base == 16 && toupper (C) == 'P' && IS_Get (&Standard) >= STD_C99)); - /* If we don't have a floating point type, an octal prefix results in an - ** octal base. - */ - if (!IsFloat && Prefix == 8) { + /* An octal prefix for an integer type results in an octal base */ + if (!IsFloat && Base == 10 && SB_LookAt (&Src, 0) == '0') { Base = 8; } - /* Since we do now know the correct base, convert the remembered input - ** into a number. - */ - SB_Reset (&S); + /* Since we now know the correct base, convert the input into a number */ + SB_SetIndex (&Src, Index); IVal = 0; - while ((C = SB_Get (&S)) != '\0') { + Overflow = 0; + while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) { DigitVal = HexVal (C); if (DigitVal >= Base) { - Error ("Numeric constant contains digits beyond the radix"); + Error ("Invalid digit \"%c\" beyond radix %u constant", C, Base); + SB_Clear (&Src); + break; } - IVal = (IVal * Base) + DigitVal; + /* Test result of adding digit for overflow. */ + if (((scan_t)(IVal * Base + DigitVal) / Base) != IVal) { + Overflow = 1; + } + IVal = IVal * Base + DigitVal; + SB_Skip (&Src); + } + if (Overflow) { + Error ("Numerical constant \"%s\" too large for internal %d-bit representation", + SB_GetConstBuf (&Src), (int)(sizeof(IVal)*CHAR_BIT)); } - - /* We don't need the string buffer any longer */ - SB_Done (&S); /* Distinguish between integer and floating point constants */ if (!IsFloat) { @@ -537,27 +613,32 @@ static void NumericConst (void) ** possible to convert the data to unsigned long even if the IT_ULONG ** flag were not set, but we are not doing that. */ - if (toupper (CurC) == 'U') { + if (toupper (SB_Peek (&Src)) == 'U') { /* Unsigned type */ - NextChar (); - if (toupper (CurC) != 'L') { + SB_Skip (&Src); + if (toupper (SB_Peek (&Src)) != 'L') { Types = IT_UINT | IT_ULONG; } else { - NextChar (); + SB_Skip (&Src); Types = IT_ULONG; } - } else if (toupper (CurC) == 'L') { + } else if (toupper (SB_Peek (&Src)) == 'L') { /* Long type */ - NextChar (); - if (toupper (CurC) != 'U') { + SB_Skip (&Src); + if (toupper (SB_Peek (&Src)) != 'U') { Types = IT_LONG | IT_ULONG; WarnTypes = IT_ULONG; } else { - NextChar (); + SB_Skip (&Src); Types = IT_ULONG; } } else { - if (Prefix == 10) { + if (SB_Peek (&Src) != '\0') { + Error ("Invalid suffix \"%s\" on integer constant", + SB_GetConstBuf (&Src) + SB_GetIndex (&Src)); + } + + if (Base == 10) { /* Decimal constants are of any type but uint */ Types = IT_INT | IT_LONG | IT_ULONG; WarnTypes = IT_LONG | IT_ULONG; @@ -579,7 +660,7 @@ static void NumericConst (void) if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && (WarnTypes & IT_LONG) != 0) { - Warning ("Integer constant is long"); + Warning ("Integer constant implies signed long"); } } if (IVal > 0xFFFF) { @@ -596,7 +677,7 @@ static void NumericConst (void) ** a preceding unary op or when it is used in constant calculation. */ if (WarnTypes & IT_ULONG) { - Warning ("Integer constant is unsigned long"); + Warning ("Integer constant implies unsigned long"); } } @@ -621,16 +702,16 @@ static void NumericConst (void) Double FVal = FP_D_FromInt (IVal); /* Convert to double */ /* Check for a fractional part and read it */ - if (CurC == '.') { + if (SB_Peek (&Src) == '.') { Double Scale; /* Skip the dot */ - NextChar (); + SB_Skip (&Src); /* Read fractional digits */ Scale = FP_D_Make (1.0); - while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) { + 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); /* Add it to the float value */ @@ -638,25 +719,25 @@ static void NumericConst (void) /* Scale base */ Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); /* Skip the digit */ - NextChar (); + SB_Skip (&Src); } } /* Check for an exponent and read it */ - if ((Base == 16 && toupper (CurC) == 'F') || - (Base == 10 && toupper (CurC) == 'E')) { + if ((Base == 16 && toupper (SB_Peek (&Src)) == 'P') || + (Base == 10 && toupper (SB_Peek (&Src)) == 'E')) { unsigned Digits; unsigned Exp; /* Skip the exponent notifier */ - NextChar (); + SB_Skip (&Src); /* Read an optional sign */ - if (CurC == '-') { - NextChar (); - } else if (CurC == '+') { - NextChar (); + if (SB_Peek (&Src) == '-') { + SB_Skip (&Src); + } else if (SB_Peek (&Src) == '+') { + SB_Skip (&Src); } /* Read exponent digits. Since we support only 32 bit floats @@ -667,11 +748,11 @@ static void NumericConst (void) */ Digits = 0; Exp = 0; - while (IsDigit (CurC)) { + while (IsDigit (SB_Peek (&Src))) { if (++Digits <= 3) { - Exp = Exp * 10 + HexVal (CurC); + Exp = Exp * 10 + HexVal (SB_Peek (&Src)); } - NextChar (); + SB_Skip (&Src); } /* Check for errors: We must have exponent digits, and not more @@ -690,10 +771,14 @@ static void NumericConst (void) } /* Check for a suffix and determine the type of the constant */ - if (toupper (CurC) == 'F') { - NextChar (); + if (toupper (SB_Peek (&Src)) == 'F') { + SB_Skip (&Src); NextTok.Type = type_float; } else { + if (SB_Peek (&Src) != '\0') { + Error ("Invalid suffix \"%s\" on floating constant", + SB_GetConstBuf (&Src) + SB_GetIndex (&Src)); + } NextTok.Type = type_double; } @@ -702,6 +787,9 @@ static void NumericConst (void) NextTok.Tok = TOK_FCONST; } + + /* We don't need the string buffer any longer */ + SB_Done (&Src); } @@ -743,39 +831,38 @@ void NextToken (void) } /* Determine the next token from the lookahead */ - if (IsDigit (CurC) || (CurC == '.' && IsDigit (NextC))) { + if (IsPPNumber (CurC, NextC)) { /* A number */ NumericConst (); return; } - /* Check for wide character literals */ - if (CurC == 'L' && NextC == '\"') { - StringConst (); - return; + /* Check for wide character constants and literals */ + if (CurC == 'L') { + if (NextC == '\"') { + StringConst (); + return; + } else if (NextC == '\'') { + CharConst (); + return; + } } /* Check for keywords and identifiers */ if (IsSym (token)) { - /* Check for a keyword */ - if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { - /* Reserved word found */ - return; + if (!PPParserRunning) { + /* Check for a keyword */ + if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) { + /* Reserved word found */ + return; + } } + /* No reserved word, check for special symbols */ if (token[0] == '_' && token[1] == '_') { /* Special symbols */ - if (strcmp (token+2, "FILE__") == 0) { - NextTok.SVal = AddLiteral (GetCurrentFile()); - NextTok.Tok = TOK_SCONST; - return; - } else if (strcmp (token+2, "LINE__") == 0) { - NextTok.Tok = TOK_ICONST; - NextTok.IVal = GetCurrentLine(); - NextTok.Type = type_int; - return; - } else if (strcmp (token+2, "func__") == 0) { + if (strcmp (token+2, "func__") == 0) { /* __func__ is only defined in functions */ if (CurrentFunc) { NextTok.SVal = AddLiteral (F_GetFuncName (CurrentFunc)); @@ -1011,6 +1098,15 @@ void NextToken (void) SetTok (TOK_COMP); break; + case '#': + NextChar (); + if (CurC == '#') { + SetTok (TOK_DOUBLE_HASH); + } else { + NextTok.Tok = TOK_HASH; + } + break; + default: UnknownChar (CurC); diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index e6a362bf3..338ad6a65 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -79,6 +79,10 @@ typedef enum token_t { TOK_FASTCALL, TOK_CDECL, + /* Address sizes */ + TOK_FAR, + TOK_NEAR, + /* Tokens denoting types */ TOK_FIRST_TYPE, TOK_ENUM = TOK_FIRST_TYPE, @@ -95,94 +99,101 @@ typedef enum token_t { TOK_VOID, TOK_LAST_TYPE = TOK_VOID, - /* Control statements */ + /* Selection statements */ + TOK_IF, + TOK_ELSE, + TOK_SWITCH, + + /* Iteration statements */ + TOK_WHILE, TOK_DO, TOK_FOR, - TOK_GOTO, - TOK_IF, - TOK_RETURN, - TOK_SWITCH, - TOK_WHILE, - TOK_ASM, + /* Jump statements */ + TOK_GOTO, + TOK_CONTINUE, + TOK_BREAK, + TOK_RETURN, + + /* Labels */ TOK_CASE, TOK_DEFAULT, - TOK_BREAK, - TOK_CONTINUE, - TOK_ELSE, - TOK_ELLIPSIS, + + /* Misc. */ + TOK_ATTRIBUTE, + TOK_PRAGMA, + TOK_STATIC_ASSERT, + TOK_ASM, TOK_SIZEOF, - TOK_IDENT, - TOK_SEMI, - - /* Primary operators */ - TOK_LBRACK, + /* Punctuators */ + TOK_FIRST_PUNC, + TOK_LBRACK = TOK_FIRST_PUNC, + TOK_RBRACK, TOK_LPAREN, + TOK_RPAREN, + TOK_LCURLY, + TOK_RCURLY, TOK_DOT, TOK_PTR_REF, - - TOK_LCURLY, - TOK_RBRACK, - TOK_COMP, TOK_INC, - TOK_PLUS_ASSIGN, - TOK_PLUS, - TOK_COMMA, TOK_DEC, - TOK_MINUS_ASSIGN, - TOK_RCURLY, - TOK_MINUS, - TOK_MUL_ASSIGN, + TOK_ADDR, + TOK_AND = TOK_ADDR, /* Alias */ TOK_STAR, TOK_MUL = TOK_STAR, /* Alias */ - TOK_DIV_ASSIGN, - TOK_DIV, - TOK_BOOL_AND, - TOK_AND_ASSIGN, - TOK_AND, - TOK_NE, + TOK_PLUS, + TOK_MINUS, + TOK_COMP, TOK_BOOL_NOT, - TOK_BOOL_OR, - TOK_OR_ASSIGN, - TOK_OR, - TOK_EQ, - TOK_ASSIGN, - - /* Inequalities */ - TOK_LE, - TOK_LT, - TOK_GE, - TOK_GT, - - TOK_SHL_ASSIGN, - TOK_SHL, - TOK_SHR_ASSIGN, - TOK_SHR, - TOK_XOR_ASSIGN, - TOK_XOR, - TOK_MOD_ASSIGN, + TOK_DIV, TOK_MOD, + TOK_SHL, + TOK_SHR, + TOK_LT, + TOK_GT, + TOK_LE, + TOK_GE, + TOK_EQ, + TOK_NE, + TOK_XOR, + TOK_OR, + TOK_BOOL_AND, + TOK_BOOL_OR, TOK_QUEST, TOK_COLON, - TOK_RPAREN, - TOK_SCONST, + TOK_SEMI, + TOK_ELLIPSIS, + TOK_ASSIGN, + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_COMMA, + TOK_HASH, + TOK_HASH_HASH, + TOK_DOUBLE_HASH = TOK_HASH_HASH, /* Alias */ + TOK_LAST_PUNC = TOK_DOUBLE_HASH, + + /* Primary expressions */ TOK_ICONST, TOK_CCONST, + TOK_WCCONST, TOK_FCONST, + TOK_SCONST, TOK_WCSCONST, - - TOK_ATTRIBUTE, - TOK_STATIC_ASSERT, - TOK_FAR, - TOK_NEAR, + TOK_IDENT, TOK_A, TOK_X, TOK_Y, TOK_AX, - TOK_EAX, - - TOK_PRAGMA + TOK_EAX } token_t; @@ -210,6 +221,7 @@ struct Token { extern Token CurTok; /* The current token */ extern Token NextTok; /* The next token */ +extern int PPParserRunning; /* Is tokenizer used by the preprocessor */ @@ -219,6 +231,17 @@ extern Token NextTok; /* The next token */ +#if defined(HAVE_INLINE) +INLINE int TokIsPunc (const Token* T) +/* Return true if the token is a punctuator */ +{ + return (T->Tok >= TOK_FIRST_PUNC && T->Tok <= TOK_LAST_PUNC); +} +#else +# define TokIsPunc(T) \ + ((T)->Tok >= TOK_FIRST_PUNC && (T)->Tok <= TOK_LAST_PUNC) +#endif + #if defined(HAVE_INLINE) INLINE int TokIsStorageClass (const Token* T) /* Return true if the token is a storage class specifier */ @@ -259,9 +282,22 @@ void SymName (char* S); ** least of size MAX_IDENTLEN+1. */ +int IsWideQuoted (char First, char Second); +/* Return 1 if the two successive characters indicate a wide string literal or +** a wide char constant, otherwise return 0. +*/ + int IsSym (char* S); /* If a symbol follows, read it and return 1, otherwise return 0 */ +int IsPPNumber (int Cur, int Next); +/* Return 1 if the two successive characters indicate a pp-number, otherwise +** return 0. +*/ + +void CopyPPNumber (StrBuf* Target); +/* Copy a pp-number from the input to Target */ + void NextToken (void); /* Get next token from input stream */ diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index eb879a475..1224bfecb 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -139,43 +139,51 @@ void ShiftExpr (struct ExprDesc* Expr) /* Remove the code that pushes the rhs onto the stack. */ RemoveCode (&Mark2); - /* If the shift count is greater or equal than the bit count of - ** the operand, the behaviour is undefined according to the - ** standard. + /* If the shift count is greater than or equal to the width of the + ** promoted left operand, the behaviour is undefined according to + ** the standard. */ - if (Expr2.IVal < 0) { - - Warning ("Shift count '%ld' is negative", Expr2.IVal); - Expr2.IVal &= ExprBits - 1; - - } else if (Expr2.IVal >= (long) ExprBits) { - - Warning ("Shift count '%ld' >= width of type", Expr2.IVal); - Expr2.IVal &= ExprBits - 1; - + if (!ED_IsUneval (Expr)) { + if (Expr2.IVal < 0) { + Warning ("Negative shift count %ld treated as %u for %s", + Expr2.IVal, + (unsigned)Expr2.IVal & (ExprBits - 1), + GetBasicTypeName (ResultType)); + } else if (Expr2.IVal >= (long) ExprBits) { + Warning ("Shift count %ld >= width of %s treated as %u", + Expr2.IVal, + GetBasicTypeName (ResultType), + (unsigned)Expr2.IVal & (ExprBits - 1)); + } } - /* If the shift count is zero, nothing happens. If the left hand - ** side is a constant, the result is constant. - */ - if (Expr2.IVal == 0 || lconst) { + /* Here we simply "wrap" the shift count around the width */ + Expr2.IVal &= ExprBits - 1; - /* Set the type */ + /* Additional check for bit-fields */ + if (IsTypeBitField (Expr->Type) && + Tok == TOK_SHR && + Expr2.IVal >= (long) Expr->Type->A.B.Width) { + if (!ED_IsUneval (Expr)) { + Warning ("Right-shift count %ld >= width of bit-field", Expr2.IVal); + } + } + + /* If the left hand side is a constant, the result is constant */ + if (lconst) { + /* Set the result type */ Expr->Type = ResultType; - if (lconst) { - - /* Evaluate the result */ - switch (Tok) { - case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; - case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; - default: /* Shutup gcc */ break; - } - - /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr); + /* Evaluate the result */ + switch (Tok) { + case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; + case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; + default: /* Shutup gcc */ break; } + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr, 1); + /* Result is already got, remove the generated code */ RemoveCode (&Mark1); @@ -183,6 +191,15 @@ void ShiftExpr (struct ExprDesc* Expr) continue; } + /* If the shift count is zero, nothing happens */ + if (Expr2.IVal == 0) { + /* Result is already got, remove the pushing code */ + RemoveCode (&Mark2); + + /* Be sure to mark the value as in the primary */ + goto MakeRVal; + } + /* If we're shifting an integer or unsigned to the right, the lhs ** has a quasi-const address, and the shift count is larger than 8, ** we can load just the high byte as a char with the correct diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 5ed5ce671..d22c73dcf 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -531,7 +531,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* Bail out, no need for further processing */ goto ExitPoint; @@ -540,7 +540,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ @@ -604,7 +604,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) DoDeferred (SQP_KEEP_EAX, &Arg3.Expr); /* Emit the actual function call. This will also cleanup the stack. */ - g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize); + g_call (CF_FIXARGC, MemSet? Func_memset : Func___bzero, ParamSize); if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) { @@ -757,7 +757,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* Bail out, no need for further processing */ goto ExitPoint; @@ -766,7 +766,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ @@ -968,7 +968,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); /* We expect the closing brace */ ConsumeRParen (); @@ -1165,7 +1165,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + Expr->Type = GetFuncReturnType (Expr->Type); ExitPoint: /* We expect the closing brace */ diff --git a/src/cc65/stdnames.c b/src/cc65/stdnames.c index 687b53894..cdecd61a1 100644 --- a/src/cc65/stdnames.c +++ b/src/cc65/stdnames.c @@ -44,9 +44,9 @@ -const char Func__bzero[] = "_bzero"; /* Asm name of "_bzero" */ -const char Func_memcpy[] = "memcpy"; /* Asm name of "memcpy" */ -const char Func_memset[] = "memset"; /* Asm name of "memset" */ -const char Func_strcmp[] = "strcmp"; /* Asm name of "strcmp" */ -const char Func_strcpy[] = "strcpy"; /* Asm name of "strcpy" */ -const char Func_strlen[] = "strlen"; /* Asm name of "strlen" */ +const char Func___bzero[] = "__bzero"; /* C name of "__bzero" */ +const char Func_memcpy[] = "memcpy"; /* C name of "memcpy" */ +const char Func_memset[] = "memset"; /* C name of "memset" */ +const char Func_strcmp[] = "strcmp"; /* C name of "strcmp" */ +const char Func_strcpy[] = "strcpy"; /* C name of "strcpy" */ +const char Func_strlen[] = "strlen"; /* C name of "strlen" */ diff --git a/src/cc65/stdnames.h b/src/cc65/stdnames.h index dcb323924..e6497e4c3 100644 --- a/src/cc65/stdnames.h +++ b/src/cc65/stdnames.h @@ -44,12 +44,12 @@ -extern const char Func__bzero[]; /* Asm name of "_bzero" */ -extern const char Func_memcpy[]; /* Asm name of "memcpy" */ -extern const char Func_memset[]; /* Asm name of "memset" */ -extern const char Func_strcmp[]; /* Asm name of "strcmp" */ -extern const char Func_strcpy[]; /* Asm name of "strcpy" */ -extern const char Func_strlen[]; /* Asm name of "strlen" */ +extern const char Func___bzero[]; /* C name of "__bzero" */ +extern const char Func_memcpy[]; /* C name of "memcpy" */ +extern const char Func_memset[]; /* C name of "memset" */ +extern const char Func_strcmp[]; /* C name of "strcmp" */ +extern const char Func_strcpy[]; /* C name of "strcpy" */ +extern const char Func_strlen[]; /* C name of "strlen" */ diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 00555ffc3..0466ddf4a 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -133,7 +133,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); + SwitchData.ExprType = GetUnqualTypeCode (&SwitchExpr.Type[0]); SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index cc790c931..30ebe7dd8 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -40,6 +40,7 @@ /* cc65 */ #include "anonname.h" +#include "asmlabel.h" #include "declare.h" #include "error.h" #include "symentry.h" @@ -65,13 +66,12 @@ SymEntry* NewSymEntry (const char* Name, unsigned Flags) E->NextHash = 0; E->PrevSym = 0; E->NextSym = 0; - E->Link = 0; E->Owner = 0; E->Flags = Flags; E->Type = 0; E->Attr = 0; E->AsmName = 0; - E->V.BssName = 0; + memset (&E->V, 0, sizeof (E->V)); memcpy (E->Name, Name, Len+1); /* Return the new entry */ @@ -230,8 +230,8 @@ const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType) -void SymUseAttr (SymEntry* Sym, struct Declaration* D) -/* Use the attributes from the declaration for this symbol */ +void SymUseAttr (SymEntry* Sym, struct Declarator* D) +/* Use the attributes from the declarator for this symbol */ { /* We cannot specify attributes twice */ if ((Sym->Flags & SC_HAVEATTR) != 0) { @@ -250,7 +250,9 @@ void SymUseAttr (SymEntry* Sym, struct Declaration* D) void SymSetAsmName (SymEntry* Sym) -/* Set the assembler name for an external symbol from the name of the symbol */ +/* Set the assembler name for an external symbol from the name of the symbol. +** The symbol must have no assembler name set yet. +*/ { unsigned Len; @@ -266,7 +268,7 @@ void SymSetAsmName (SymEntry* Sym) -void CvtRegVarToAuto (SymEntry* Sym) +void SymCvtRegVarToAuto (SymEntry* Sym) /* Convert a register variable to an auto variable */ { /* Change the storage class */ @@ -278,59 +280,26 @@ void CvtRegVarToAuto (SymEntry* Sym) -SymEntry* GetSymType (const Type* T) -/* Get the symbol entry of the enum/struct/union type -** Return 0 if it is not an enum/struct/union. -*/ -{ - if ((IsClassStruct (T) || IsTypeEnum (T))) { - return T->A.S; - } - return 0; -} - - - -const char* GetSymTypeName (const Type* T) -/* Return a name string of the type or the symbol name if it is an ESU type. -** Note: This may use a static buffer that could be overwritten by other calls. -*/ -{ - static char TypeName [IDENTSIZE + 16]; - SymEntry* Sym; - - Sym = GetSymType (T); - if (Sym == 0) { - return GetBasicTypeName (T); - } - sprintf (TypeName, "%s %s", GetBasicTypeName (T), - Sym->Name[0] != '\0' ? Sym->Name : "<unknown>"); - - return TypeName; -} - - - -void ChangeSymType (SymEntry* Entry, const Type* T) +void SymChangeType (SymEntry* Sym, const Type* T) /* Change the type of the given symbol */ { - TypeFree (Entry->Type); - Entry->Type = TypeDup (T); + TypeFree (Sym->Type); + Sym->Type = TypeDup (T); } -void ChangeAsmName (SymEntry* Entry, const char* NewAsmName) +void SymChangeAsmName (SymEntry* Sym, const char* NewAsmName) /* Change the assembler name of the symbol */ { - xfree (Entry->AsmName); - Entry->AsmName = xstrdup (NewAsmName); + xfree (Sym->AsmName); + Sym->AsmName = xstrdup (NewAsmName); } -int HasAnonName (const SymEntry* Entry) +int SymHasAnonName (const SymEntry* Sym) /* Return true if the symbol entry has an anonymous name */ { - return IsAnonName (Entry->Name); + return IsAnonName (Sym->Name); } diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index bcf586510..5ebd30a75 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -105,8 +105,8 @@ struct CodeEntry; #define SC_SPADJUSTMENT 0x400000U #define SC_GOTO_IND 0x800000U /* Indirect goto */ -#define SC_ALIAS 0x01000000U /* Alias of anonymous field */ -#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious */ +#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 */ @@ -128,7 +128,6 @@ struct SymEntry { SymEntry* NextHash; /* Next entry in hash list */ SymEntry* PrevSym; /* Previous symbol in dl list */ SymEntry* NextSym; /* Next symbol double linked list */ - SymEntry* Link; /* General purpose single linked list */ struct SymTable* Owner; /* Symbol table the symbol is in */ unsigned Flags; /* Symbol flags */ Type* Type; /* Symbol type */ @@ -138,27 +137,9 @@ struct SymEntry { /* Data that differs for the different symbol types */ union { - /* Offset for locals or struct members */ + /* Offset for locals */ int Offs; - /* Data for anonymous struct or union members */ - struct { - int Offs; /* Byte offset into struct */ - unsigned ANumber; /* Numeric ID */ - SymEntry* Field; /* The real field aliased */ - } A; - - - /* Label name for static symbols */ - struct { - unsigned Label; - Collection *DefsOrRefs; - struct CodeEntry *IndJumpFrom; - } L; - - /* Value of SP adjustment needed after forward 'goto' */ - unsigned short SPAdjustment; - /* Register bank offset and offset of the saved copy on stack for ** register variables. */ @@ -167,32 +148,50 @@ struct SymEntry { int SaveOffs; } R; - /* Value for constants (including enums) */ + /* Segment name for tentantive global definitions */ + const char* BssName; + + /* Value for integer constants (including enumerators) */ long ConstVal; - /* Data for structs/unions */ - struct { - struct SymTable* SymTab; /* Member symbol table */ - unsigned Size; /* Size of the union/struct */ - unsigned ACount; /* Count of anonymous fields */ - } S; - - /* Data for enums */ - struct { - struct SymTable* SymTab; /* Member symbol table */ - const Type* Type; /* Underlying type */ - } E; - /* Data for functions */ struct { struct Segments* Seg; /* Segments for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ } F; - /* Segment name for tentantive global definitions */ - const char* BssName; + /* Label name for static symbols */ + struct { + unsigned Label; + Collection *DefsOrRefs; + struct CodeEntry *IndJumpFrom; + } L; + + /* Value of SP adjustment needed after forward 'goto' */ + unsigned short SPAdjustment; + + /* Data for anonymous struct or union members */ + struct { + int Offs; /* Byte offset into struct */ + unsigned ANumber; /* Numeric ID */ + SymEntry* Field; /* The real field aliased */ + } A; + + /* Data for structs/unions tags */ + struct { + struct SymTable* SymTab; /* Member symbol table */ + unsigned Size; /* Size of the union/struct */ + unsigned ACount; /* Count of anonymous fields */ + } S; + + /* Data for enums tags */ + struct { + struct SymTable* SymTab; /* Member symbol table */ + const Type* Type; /* Underlying type */ + } E; + } V; - char Name[1]; /* Name, dynamically allocated */ + char Name[1]; /* Name, dynamically allocated */ }; @@ -299,32 +298,24 @@ INLINE int SymHasAttr (const SymEntry* Sym, DeclAttrType A) # define SymHasAttr(Sym, A) (SymGetAttr (Sym, A) != 0) #endif -void SymUseAttr (SymEntry* Sym, struct Declaration* D); -/* Use the attributes from the declaration for this symbol */ +void SymUseAttr (SymEntry* Sym, struct Declarator* D); +/* Use the attributes from the declarator for this symbol */ void SymSetAsmName (SymEntry* Sym); -/* Set the assembler name for an external symbol from the name of the symbol */ +/* Set the assembler name for an external symbol from the name of the symbol. +** The symbol must have no assembler name set yet. +*/ -void CvtRegVarToAuto (SymEntry* Sym); +void SymCvtRegVarToAuto (SymEntry* Sym); /* Convert a register variable to an auto variable */ -SymEntry* GetSymType (const Type* T); -/* Get the symbol entry of the enum/struct/union type -** Return 0 if it is not an enum/struct/union. -*/ - -const char* GetSymTypeName (const Type* T); -/* Return a name string of the type or the symbol name if it is an ESU type. -** Note: This may use a static buffer that could be overwritten by other calls. -*/ - -void ChangeSymType (SymEntry* Entry, const Type* T); +void SymChangeType (SymEntry* Sym, const Type* T); /* Change the type of the given symbol */ -void ChangeAsmName (SymEntry* Entry, const char* NewAsmName); +void SymChangeAsmName (SymEntry* Sym, const char* NewAsmName); /* Change the assembler name of the symbol */ -int HasAnonName (const SymEntry* Entry); +int SymHasAnonName (const SymEntry* Sym); /* Return true if the symbol entry has an anonymous name */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index aa4a9a44a..a2bbf13dd 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -567,6 +567,11 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) /* Try to find the symbol in this table */ SymEntry* E = FindSymInTable (Tab, Name, Hash); + while (E != 0 && (E->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the aliased entry */ + E = E->V.A.Field; + } + /* Bail out if we found it */ if (E != 0) { return E; @@ -620,8 +625,8 @@ SymEntry FindStructField (const Type* T, const char* Name) ** value, or an empty entry struct if the field is not found. */ { - SymEntry* Entry = 0; - SymEntry Field; + SymEntry* Field = 0; + SymEntry Res; int Offs = 0; /* The given type may actually be a pointer to struct/union */ @@ -632,35 +637,35 @@ SymEntry FindStructField (const Type* T, const char* Name) /* Only structs/unions have struct/union fields... */ if (IsClassStruct (T)) { - /* Get a pointer to the struct/union type */ - const SymEntry* Struct = GetESUSymEntry (T); - CHECK (Struct != 0); + /* Get a pointer to the struct/union tag */ + const SymEntry* TagSym = GetESUTagSym (T); + CHECK (TagSym != 0); /* Now search in the struct/union symbol table. Beware: The table may ** not exist. */ - if (Struct->V.S.SymTab) { - Entry = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + if (TagSym->V.S.SymTab) { + Field = FindSymInTable (TagSym->V.S.SymTab, Name, HashStr (Name)); - if (Entry != 0) { - Offs = Entry->V.Offs; + if (Field != 0) { + Offs = Field->V.Offs; } - while (Entry != 0 && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + while (Field != 0 && (Field->Flags & SC_ALIAS) == SC_ALIAS) { /* Get the real field */ - Entry = Entry->V.A.Field; + Field = Field->V.A.Field; } } } - if (Entry != 0) { - Field = *Entry; - Field.V.Offs = Offs; + if (Field != 0) { + Res = *Field; + Res.V.Offs = Offs; } else { - memset (&Field, 0, sizeof(SymEntry)); + memset (&Res, 0, sizeof(SymEntry)); } - return Field; + return Res; } @@ -684,15 +689,15 @@ static int IsDistinctRedef (const Type* lhst, const Type* rhst, typecmpcode_t Co } -static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags) +static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) /* Check and handle redefinition of existing symbols. ** Complete array sizes and function descriptors as well. ** Return true if there *is* an error. */ { /* Get the type info of the existing symbol */ - Type* E_Type = Entry->Type; - unsigned E_SCType = Entry->Flags & SC_TYPEMASK; + Type* E_Type = Sym->Type; + unsigned E_SCType = Sym->Flags & SC_TYPEMASK; unsigned SCType = Flags & SC_TYPEMASK; /* Some symbols may be redeclared if certain requirements are met */ @@ -701,15 +706,16 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags /* Existing typedefs cannot be redeclared as anything different */ if (SCType == SC_TYPEDEF) { if (IsDistinctRedef (E_Type, T, TC_IDENTICAL, TCF_MASK_QUAL)) { - Error ("Conflicting types for typedef '%s'", Entry->Name); - Entry = 0; + Error ("Conflicting types for typedef '%s'", Sym->Name); + Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type)); + Sym = 0; } } else { - Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); - Entry = 0; + Error ("Redefinition of typedef '%s' as different kind of symbol", Sym->Name); + Sym = 0; } - } else if ((Entry->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_FUNC) == 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 @@ -720,27 +726,27 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags if (IsTypeFunc (T)) { /* Check for duplicate function definitions */ - if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { + if (SymIsDef (Sym) && (Flags & SC_DEF) == SC_DEF) { Error ("Body for function '%s' has already been defined", - Entry->Name); - Entry = 0; + Sym->Name); + Sym = 0; } else { /* New type must be compatible with the composite prototype */ if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) { - Error ("Conflicting function types for '%s'", Entry->Name); - TypeCompatibilityDiagnostic (T, E_Type, 0, "'%s' vs '%s'"); - Entry = 0; + Error ("Conflicting function types for '%s'", Sym->Name); + Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type)); + Sym = 0; } else { /* Refine the existing composite prototype with this new ** one. */ - RefineFuncDesc (Entry->Type, T); + RefineFuncDesc (Sym->Type, T); } } } else { - Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); - Entry = 0; + Error ("Redefinition of function '%s' as different kind of symbol", Sym->Name); + Sym = 0; } } else { @@ -759,8 +765,9 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || IsDistinctRedef (E_Type + 1, T + 1, TC_IDENTICAL, TCF_MASK_QUAL)) { /* Conflicting element types */ - Error ("Conflicting array types for '%s[]'", Entry->Name); - Entry = 0; + Error ("Conflicting array types for '%s[]'", Sym->Name); + Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type)); + Sym = 0; } else { /* Check if we have a size in the existing definition */ if (ESize == UNSPECIFIED) { @@ -773,24 +780,27 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags /* New type must be equivalent */ if (SCType != E_SCType) { - Error ("Redefinition of '%s' as different kind of symbol", Entry->Name); - Entry = 0; + Error ("Redefinition of '%s' as different kind of symbol", Sym->Name); + Sym = 0; } else if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) { - Error ("Conflicting types for '%s'", Entry->Name); - Entry = 0; + Error ("Conflicting types for '%s'", Sym->Name); + Note ("'%s' vs '%s'", GetFullTypeName (T), GetFullTypeName (E_Type)); + Sym = 0; } else if (E_SCType == SC_ENUMERATOR) { /* Enumerators aren't allowed to be redeclared at all, even if ** all occurences are identical. The current code logic won't ** get here, but let's just do it. */ - Error ("Redeclaration of enumerator constant '%s'", Entry->Name); - Entry = 0; + Error ("Redeclaration of enumerator constant '%s'", Sym->Name); + Sym = 0; + } else if (Flags & SC_STRUCTFIELD) { + Error ("Duplicate member '%s'", Sym->Name); } } } /* Return if there are any errors */ - return Entry == 0; + return Sym == 0; } @@ -824,38 +834,38 @@ static void AddSymEntry (SymTable* T, SymEntry* S) SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags) -/* Add an enum entry and return it */ +/* Add an enum tag entry and return it */ { SymTable* CurTagTab = TagTab; - SymEntry* Entry; + SymEntry* TagEntry; if ((Flags & SC_FICTITIOUS) == 0) { /* Do we have an entry with this name already? */ - Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); } else { /* Add a fictitious symbol in the fail-safe table */ - Entry = 0; + TagEntry = 0; CurTagTab = FailSafeTab; } - if (Entry) { + if (TagEntry) { /* We do have an entry. This may be a forward, so check it. */ - if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) { + if ((TagEntry->Flags & SC_TYPEMASK) != SC_ENUM) { /* Existing symbol is not an enum */ Error ("Symbol '%s' is already different kind", Name); - Entry = 0; + TagEntry = 0; } else if (Type != 0) { /* Define the struct size if the underlying type is given. */ - if (Entry->V.E.Type != 0) { + if (TagEntry->V.E.Type != 0) { /* Both are definitions. */ Error ("Multiple definition for 'enum %s'", Name); - Entry = 0; + TagEntry = 0; } else { - Entry->V.E.SymTab = Tab; - Entry->V.E.Type = Type; - Entry->Flags &= ~SC_DECL; - Entry->Flags |= SC_DEF; + 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 */ if (DSFlags != 0) { @@ -864,83 +874,83 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab } } - if (Entry == 0) { + if (TagEntry == 0) { /* Use the fail-safe table for fictitious symbols */ CurTagTab = FailSafeTab; } } - if (Entry == 0) { + if (TagEntry == 0) { /* Create a new entry */ - Entry = NewSymEntry (Name, SC_ENUM); + TagEntry = NewSymEntry (Name, SC_ENUM); /* Set the enum type data */ - Entry->V.E.SymTab = Tab; - Entry->V.E.Type = Type; + TagEntry->V.E.SymTab = Tab; + TagEntry->V.E.Type = Type; if (Type != 0) { - Entry->Flags |= SC_DEF; + TagEntry->Flags |= SC_DEF; } /* Remember this is the first definition of this type */ if (CurTagTab != FailSafeTab && DSFlags != 0) { - if ((Entry->Flags & SC_DEF) != 0) { + if ((TagEntry->Flags & SC_DEF) != 0) { *DSFlags |= DS_NEW_TYPE_DEF; } *DSFlags |= DS_NEW_TYPE_DECL; } /* Add it to the current table */ - AddSymEntry (CurTagTab, Entry); + AddSymEntry (CurTagTab, TagEntry); } /* Return the entry */ - return Entry; + return TagEntry; } SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags) -/* Add a struct/union entry and return it */ +/* Add a struct/union tag entry and return it */ { SymTable* CurTagTab = TagTab; - SymEntry* Entry; - unsigned Type = (Flags & SC_TYPEMASK); + SymEntry* TagEntry; + unsigned SCType = (Flags & SC_TYPEMASK); - /* Type must be struct or union */ - PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); + /* 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? */ - Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + TagEntry = FindSymInTable (CurTagTab, Name, HashStr (Name)); } else { /* Add a fictitious symbol in the fail-safe table */ - Entry = 0; + TagEntry = 0; CurTagTab = FailSafeTab; } - if (Entry) { + if (TagEntry) { /* We do have an entry. This may be a forward, so check it. */ - if ((Entry->Flags & SC_TYPEMASK) != Type) { + if ((TagEntry->Flags & SC_TYPEMASK) != SCType) { /* Existing symbol is not a struct */ Error ("Symbol '%s' is already different kind", Name); - Entry = 0; - } else if ((Entry->Flags & Flags & SC_DEF) == SC_DEF) { + TagEntry = 0; + } else if ((TagEntry->Flags & Flags & SC_DEF) == SC_DEF) { /* Both structs are definitions. */ - if (Type == SC_STRUCT) { + if (SCType == SC_STRUCT) { Error ("Multiple definition for 'struct %s'", Name); } else { Error ("Multiple definition for 'union %s'", Name); } - Entry = 0; + TagEntry = 0; } else { /* Define the struct size if it is a definition */ if ((Flags & SC_DEF) == SC_DEF) { - Entry->Flags = Flags; - Entry->V.S.SymTab = Tab; - Entry->V.S.Size = Size; + TagEntry->Flags = Flags; + TagEntry->V.S.SymTab = Tab; + TagEntry->V.S.Size = Size; /* Remember this is the first definition of this type */ if (DSFlags != 0) { @@ -949,35 +959,35 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl } } - if (Entry == 0) { + if (TagEntry == 0) { /* Use the fail-safe table for fictitious symbols */ CurTagTab = FailSafeTab; } } - if (Entry == 0) { + if (TagEntry == 0) { /* Create a new entry */ - Entry = NewSymEntry (Name, Flags); + TagEntry = NewSymEntry (Name, Flags); /* Set the struct data */ - Entry->V.S.SymTab = Tab; - Entry->V.S.Size = Size; + TagEntry->V.S.SymTab = Tab; + TagEntry->V.S.Size = Size; /* Remember this is the first definition of this type */ if (CurTagTab != FailSafeTab && DSFlags != 0) { - if ((Entry->Flags & SC_DEF) != 0) { + if ((TagEntry->Flags & SC_DEF) != 0) { *DSFlags |= DS_NEW_TYPE_DEF; } *DSFlags |= DS_NEW_TYPE_DECL; } /* Add it to the current tag table */ - AddSymEntry (CurTagTab, Entry); + AddSymEntry (CurTagTab, TagEntry); } /* Return the entry */ - return Entry; + return TagEntry; } @@ -999,7 +1009,7 @@ 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 = NewBitFieldType (T, BitOffs, BitWidth); + Entry->Type = NewBitFieldOf (T, BitOffs, BitWidth); Entry->V.Offs = Offs; if (!SignednessSpecified) { @@ -1011,7 +1021,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, ** `char -> unsigned char` adjustment that is performed with other integral types. */ CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || - IsTypeChar (Entry->Type)); + IsRankChar (Entry->Type)); Entry->Type[0].C &= ~T_MASK_SIGN; Entry->Type[0].C |= T_SIGN_UNSIGNED; Entry->Type[1].C &= ~T_MASK_SIGN; @@ -1068,7 +1078,7 @@ DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) DOR = xmalloc (sizeof (DefOrRef)); CollAppend (E->V.L.DefsOrRefs, DOR); - DOR->Line = GetCurrentLine (); + DOR->Line = GetCurrentLineNum (); DOR->LocalsBlockId = (size_t)CollLast (&CurrentFunc->LocalsBlockStack); DOR->Flags = Flags; DOR->StackPtr = StackPtr; @@ -1136,7 +1146,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) (size_t)CollAt (AIC, DOR->Depth - 1) != DOR->LocalsBlockId)) { Warning ("Goto at line %d to label %s jumps into a block with " "initialization of an object that has automatic storage duration", - GetCurrentLine (), Name); + GetCurrentLineNum (), Name); } } @@ -1279,6 +1289,11 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); + } else if ((Flags & SC_ALIAS) == SC_ALIAS) { + /* Just clear the info */ + Entry->V.A.Field = 0; + Entry->V.A.ANumber = 0; + Entry->V.A.Offs = 0; } else { Internal ("Invalid flags in AddLocalSym: %04X", Flags); } @@ -1296,13 +1311,26 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Add an external or global symbol to the symbol table and return the entry */ { - /* Start from the local symbol table */ - SymTable* Tab = SymTab; + /* Add the new declaration to the global symbol table if no errors */ + SymTable* Tab = SymTab0; + + /* Only search this name in the local and global symbol tables */ + SymEntry* Entry = 0; + SymEntry* Alias = 0; + + if (SymTab != SymTab0) { + Alias = Entry = FindLocalSym (Name); + while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the aliased entry */ + Entry = Entry->V.A.Field; + } + } + + if (Entry == 0) { + Entry = FindGlobalSym (Name); + } - /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTree (Tab, Name); if (Entry) { - /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; @@ -1312,15 +1340,14 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Name); Entry = 0; } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then - ** diagnose the conflict. It will warn and compile an extern - ** declaration if both declarations are global, otherwise give an - ** error. + /* 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. */ - if (Tab == SymTab0 && + if (SymTab == SymTab0 && (Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { - Warning ("Static declaration of '%s' follows non-static declaration", Name); + 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) { @@ -1332,8 +1359,12 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) */ if (Entry->Owner == SymTab0) { if ((Flags & SC_STORAGE) == 0) { - /* Linkage must be unchanged */ + /* 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); } @@ -1353,12 +1384,9 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Use the fail-safe table for fictitious symbols */ Tab = FailSafeTab; } - - } else if ((Flags & (SC_EXTERN | SC_FUNC)) != 0) { - /* Add the new declaration to the global symbol table instead */ - Tab = SymTab0; } - if (Entry == 0 || Entry->Owner != Tab) { + + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -1376,6 +1404,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Add the entry to the symbol table */ AddSymEntry (Tab, Entry); + + } + + /* 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; } /* Return the entry */ diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 1df61a822..b711fe606 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -169,10 +169,10 @@ unsigned short FindSPAdjustment (const char* Name); SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags); -/* Add an enum entry and return it */ +/* Add an enum tag entry and return it */ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags); -/* Add a struct/union entry and return it */ +/* Add a struct/union tag entry and return it */ SymEntry* AddBitField (const char* Name, const Type* Type, unsigned Offs, unsigned BitOffs, unsigned BitWidth, int SignednessSpecified); diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 6052f4a84..c3239652f 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -244,7 +244,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SymEntry* Sym2; FuncDesc* F1; FuncDesc* F2; - TypeCode LeftType, RightType; + TypeCode LeftRank, RightRank; long LeftCount, RightCount; @@ -262,20 +262,22 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } - /* Get the left and right types */ - LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE); - RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE); + /* Get the ranks of the left and right hands */ + LeftRank = (GetUnqualTypeCode (lhs) & T_MASK_RANK); + RightRank = (GetUnqualTypeCode (rhs) & T_MASK_RANK); /* If one side is a pointer and the other side is an array, both are ** compatible. */ - if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) { - RightType = T_TYPE_PTR; - SetResult (Result, TC_PTR_DECAY); - } - if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) { - LeftType = T_TYPE_PTR; - SetResult (Result, TC_STRICT_COMPATIBLE); + if (Result->Indirections == 0) { + if (LeftRank == T_RANK_PTR && RightRank == T_RANK_ARRAY) { + RightRank = T_RANK_PTR; + SetResult (Result, TC_PTR_DECAY); + } + if (LeftRank == T_RANK_ARRAY && RightRank == T_RANK_PTR) { + LeftRank = T_RANK_PTR; + SetResult (Result, TC_STRICT_COMPATIBLE); + } } /* Bit-fields are considered compatible if they have the same @@ -288,13 +290,13 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) lhs->A.B.Width != rhs->A.B.Width) { SetResult (Result, TC_INCOMPATIBLE); } - if (LeftType != RightType) { + if (LeftRank != RightRank) { SetResult (Result, TC_STRICT_COMPATIBLE); } } - /* If the underlying types are not identical, the types are incompatible */ - if (LeftType != RightType) { + /* If the ranks are different, the types are incompatible */ + if (LeftRank != RightRank) { SetResult (Result, TC_INCOMPATIBLE); return; } @@ -303,8 +305,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { /* Compare the tag types */ - Sym1 = IsTypeEnum (lhs) ? GetESUSymEntry (lhs) : 0; - Sym2 = IsTypeEnum (rhs) ? GetESUSymEntry (rhs) : 0; + Sym1 = IsTypeEnum (lhs) ? GetESUTagSym (lhs) : 0; + Sym2 = IsTypeEnum (rhs) ? GetESUTagSym (rhs) : 0; if (Sym1 != Sym2) { if (Sym1 == 0 || Sym2 == 0) { @@ -334,8 +336,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* 'char' is neither 'signed char' nor 'unsigned char' */ - if ((IsISOChar (lhs) && !IsISOChar (rhs)) || - (!IsISOChar (lhs) && IsISOChar (rhs))) { + if ((IsDeclTypeChar (lhs) && !IsDeclTypeChar (rhs)) || + (!IsDeclTypeChar (lhs) && IsDeclTypeChar (rhs))) { SetResult (Result, TC_SIGN_DIFF); } @@ -347,14 +349,14 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* Check for special type elements */ - switch (LeftType) { - case T_TYPE_PTR: + switch (LeftRank) { + case T_RANK_PTR: ++Result->Indirections; if (Result->Indirections == 1) { - if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + if ((GetUnqualTypeCode (lhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_LEFT; } - if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + if ((GetUnqualTypeCode (rhs + 1) & T_MASK_RANK) == T_RANK_VOID) { Result->F |= TCF_VOID_PTR_ON_RIGHT; } } else { @@ -362,7 +364,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } break; - case T_TYPE_FUNC: + case T_RANK_FUNC: /* Compare the function descriptors */ F1 = GetFuncDesc (lhs); F2 = GetFuncDesc (rhs); @@ -396,7 +398,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Keep on and compare the return type */ break; - case T_TYPE_ARRAY: + case T_RANK_ARRAY: /* Check member count */ LeftCount = GetElementCount (lhs); RightCount = GetElementCount (rhs); @@ -417,11 +419,11 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } break; - case T_TYPE_STRUCT: - case T_TYPE_UNION: + case T_RANK_STRUCT: + case T_RANK_UNION: /* Compare the tag types */ - Sym1 = GetESUSymEntry (lhs); - Sym2 = GetESUSymEntry (rhs); + Sym1 = GetESUTagSym (lhs); + Sym2 = GetESUTagSym (rhs); CHECK (Sym1 != 0 || Sym2 != 0); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index c72b2c5eb..6bdb45b5f 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -55,7 +55,7 @@ -static void DoConversion (ExprDesc* Expr, const Type* NewType) +static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) /* Emit code to convert the given expression to a new type. */ { const Type* OldType; @@ -128,6 +128,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** internally already represented by a long. */ if (NewBits <= OldBits) { + long OldVal = Expr->IVal; /* Cut the value to the new size */ Expr->IVal &= (0xFFFFFFFFUL >> (32 - NewBits)); @@ -139,6 +140,10 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) Expr->IVal |= shl_l (~0UL, NewBits); } } + + if ((OldVal != Expr->IVal) && IS_Get (&WarnConstOverflow) && !Explicit) { + Warning ("Implicit conversion of constant overflows %d-bit destination", NewBits); + } } /* Do the integer constant <-> absolute address conversion if necessary */ @@ -238,10 +243,10 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) ** void pointers, just with warnings. */ if (Result.C == TC_PTR_SIGN_DIFF) { - /* Specific warning for pointee signedness difference */ + /* Specific warning for pointer signedness difference */ if (IS_Get (&WarnPointerSign)) { TypeCompatibilityDiagnostic (NewType, Expr->Type, - 0, "Pointer conversion to '%s' from '%s' changes pointee signedness"); + 0, "Pointer conversion to '%s' from '%s' changes pointer signedness"); } } else if ((Result.C <= TC_PTR_INCOMPATIBLE || (Result.F & TCF_INCOMPATIBLE_QUAL) != 0)) { @@ -283,7 +288,7 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Both types must be complete */ if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { /* Do the actual conversion */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 0); } else { /* We should have already generated error elsewhere so that we ** could just silently fail here to avoid excess errors, but to @@ -330,7 +335,7 @@ void TypeCast (ExprDesc* Expr) ReplaceType (Expr, NewType); } else if (IsCastType (Expr->Type)) { /* Convert the value. The result has always the new type */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 1); } else { TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, "Cast to incompatible type '%s' from '%s'"); @@ -430,7 +435,7 @@ void TypeComposition (Type* lhs, const Type* rhs) } /* Check for sanity */ - CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); + CHECK (GetUnqualTypeCode (lhs) == GetUnqualTypeCode (rhs)); /* Check for special type elements */ if (IsTypeFunc (lhs)) { diff --git a/src/cl65/main.c b/src/cl65/main.c index 67e9444f4..553fb9ca6 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -1216,7 +1216,7 @@ static void OptPrintTargetPath (const char* Opt attribute ((unused)), SearchPaths* TargetPaths = NewSearchPath (); AddSubSearchPathFromEnv (TargetPaths, "CC65_HOME", "target"); -#if defined(CL65_TGT) && !defined(_WIN32) +#if defined(CL65_TGT) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (TargetPaths, CL65_TGT); #endif AddSubSearchPathFromBin (TargetPaths, "target"); diff --git a/src/common/attrib.h b/src/common/attrib.h index 07e08b2df..3cdacb9d5 100644 --- a/src/common/attrib.h +++ b/src/common/attrib.h @@ -44,14 +44,20 @@ -#if defined(__GNUC__) -# define attribute(a) __attribute__(a) +#ifdef __clang__ +# define attribute(a) __attribute__(a) +# define ATTR_UNUSED(x) __attribute__((__unused__)) x +# define ATTR_NORETURN __attribute__((analyzer_noreturn)) +#elif defined(__GNUC__) +# define attribute(a) __attribute__(a) +# define ATTR_UNUSED(x) __attribute__((__unused__)) x +# define ATTR_NORETURN __attribute__((noreturn)) #else # define attribute(a) +# define ATTR_UNUSED(x) x +# define ATTR_NORETURN #endif - - /* End of attrib.h */ #endif diff --git a/src/common/cmdline.c b/src/common/cmdline.c index 4de79a419..6b24a75fb 100644 --- a/src/common/cmdline.c +++ b/src/common/cmdline.c @@ -181,6 +181,17 @@ void InitCmdLine (int* aArgCount, char*** aArgVec, const char* aProgName) /* Use the default */ ProgName = aProgName; } + else { + /* remove .exe extension, if there is any + ** + ** Note: This creates a new string that is + ** never free()d. + ** As this is exactly only string, and it + ** lives for the whole lifetime of the tool, + ** this is not an issue. + */ + ProgName = MakeFilename (ProgName, ""); + } } /* Make a CmdLine struct */ diff --git a/src/common/target.c b/src/common/target.c index 4a851034a..ad62990bd 100644 --- a/src/common/target.c +++ b/src/common/target.c @@ -163,6 +163,7 @@ static const TargetEntry TargetMap[] = { { "geos", TGT_GEOS_CBM }, { "geos-apple", TGT_GEOS_APPLE }, { "geos-cbm", TGT_GEOS_CBM }, + { "kim1", TGT_KIM1 }, { "lunix", TGT_LUNIX }, { "lynx", TGT_LYNX }, { "module", TGT_MODULE }, @@ -219,6 +220,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = { { "c65", CPU_4510, BINFMT_BINARY, CTPET }, { "cx16", CPU_65C02, BINFMT_BINARY, CTPET }, { "sym1", CPU_6502, BINFMT_BINARY, CTNone }, + { "kim1", CPU_6502, BINFMT_BINARY, CTNone }, }; /* Target system */ diff --git a/src/common/target.h b/src/common/target.h index 7087048e2..0cec74b6e 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -87,6 +87,7 @@ typedef enum { TGT_C65, TGT_CX16, TGT_SYM1, + TGT_KIM1, TGT_COUNT /* Number of target systems */ } target_t; diff --git a/src/common/xsprintf.c b/src/common/xsprintf.c index a3fbc676b..556e4f359 100644 --- a/src/common/xsprintf.c +++ b/src/common/xsprintf.c @@ -486,6 +486,18 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap) } break; + /* support the MSVC specific I64 for long long */ + case 'I': + F = *Format++; + if (F == '6') { + F = *Format++; + if (F == '4') { + F = *Format++; + P.LengthMod = lmLongLong; + } + } + break; + case 'l': F = *Format++; if (F == 'l') { diff --git a/src/da65/attrtab.c b/src/da65/attrtab.c index a9143584a..b70e017a1 100644 --- a/src/da65/attrtab.c +++ b/src/da65/attrtab.c @@ -34,6 +34,7 @@ /* da65 */ +#include "cpu.h" #include "error.h" #include "attrtab.h" @@ -48,6 +49,12 @@ /* Attribute table */ static unsigned short AttrTab[0x10000]; +/* 65816 attribute table */ +#define MAX_LONG_ATTRS 256 +static unsigned short LongAttrVal[MAX_LONG_ATTRS]; +static unsigned LongAttrAddr[MAX_LONG_ATTRS]; +static unsigned LongAttrsUsed; + /*****************************************************************************/ @@ -59,12 +66,19 @@ static unsigned short AttrTab[0x10000]; void AddrCheck (unsigned Addr) /* Check if the given address has a valid range */ { - if (Addr >= 0x10000) { + if (Addr >= 0x10000 && CPU != CPU_65816) { Error ("Address out of range: %08X", Addr); } } +unsigned char IsLongAddr (unsigned Addr) +/* Is it 24-bit? */ +{ + return Addr >= 0x10000 && CPU == CPU_65816; +} + + attr_t GetAttr (unsigned Addr) /* Return the attribute for the given address */ @@ -72,6 +86,17 @@ attr_t GetAttr (unsigned Addr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + unsigned i; + for (i = 0; i < LongAttrsUsed; i++) { + if (LongAttrAddr[i] == Addr) { + return LongAttrVal[i]; + } + } + + return 0; + } + /* Return the attribute */ return AttrTab[Addr]; } @@ -148,6 +173,33 @@ void MarkAddr (unsigned Addr, attr_t Attr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + unsigned i; + for (i = 0; i < LongAttrsUsed; i++) { + if (LongAttrAddr[i] == Addr) { + + /* We must not have more than one style bit */ + if (Attr & atStyleMask) { + if (LongAttrVal[i] & atStyleMask) { + Error ("Duplicate style for long address %06X", Addr); + } + } + LongAttrVal[i] |= Attr; + + return; + } + } + + if (LongAttrsUsed >= MAX_LONG_ATTRS) { + Error ("Too many long addresses"); + } + LongAttrVal[LongAttrsUsed] |= Attr; + LongAttrAddr[LongAttrsUsed] = Addr; + LongAttrsUsed++; + + return; + } + /* We must not have more than one style bit */ if (Attr & atStyleMask) { if (AttrTab[Addr] & atStyleMask) { @@ -168,7 +220,7 @@ attr_t GetStyleAttr (unsigned Addr) AddrCheck (Addr); /* Return the attribute */ - return (AttrTab[Addr] & atStyleMask); + return (GetAttr (Addr) & atStyleMask); } @@ -180,5 +232,5 @@ attr_t GetLabelAttr (unsigned Addr) AddrCheck (Addr); /* Return the attribute */ - return (AttrTab[Addr] & atLabelMask); + return (GetAttr (Addr) & atLabelMask); } diff --git a/src/da65/attrtab.h b/src/da65/attrtab.h index 18515ce49..4a0ea8225 100644 --- a/src/da65/attrtab.h +++ b/src/da65/attrtab.h @@ -66,15 +66,25 @@ typedef enum attr_t { atDepLabel = 0x0040, /* Dependent label */ atUnnamedLabel = 0x0080, /* Unnamed label */ - atLabelDefined = 0x0100, /* True if we defined the label */ - - atStyleMask = 0x000F, /* Output style */ - atLabelMask = 0x00F0, /* Label information */ - /* Segment */ atSegment = 0x0100, /* Code is in a segment */ atSegmentEnd = 0x0200, /* Segment end */ atSegmentStart = 0x0400, /* Segment start */ + + /* Table unit separator */ + atTableUnit = 0x0800, + + /* 65816 addressing mode */ + atMem8 = 0x1000, /* M flag enabled, 8-bit */ + atMem16 = 0x2000, /* M flag disabled, 16-bit */ + atIdx8 = 0x4000, /* X flag enabled, 8-bit */ + atIdx16 = 0x8000, /* X flag disabled, 16-bit */ + + atStyleMask = 0x000F, /* Output style */ + atLabelMask = 0x00F0, /* Label information */ + atSegmentMask = 0x0700, /* Segment information */ + at65816Mask = 0xF000, /* 65816 information */ + } attr_t; @@ -88,6 +98,9 @@ typedef enum attr_t { void AddrCheck (unsigned Addr); /* Check if the given address has a valid range */ +unsigned char IsLongAddr (unsigned Addr); +/* Check if the given address is 24-bit */ + attr_t GetAttr (unsigned Addr); /* Return the attribute for the given address */ diff --git a/src/da65/code.c b/src/da65/code.c index 3fb6a21d3..a162e6482 100644 --- a/src/da65/code.c +++ b/src/da65/code.c @@ -194,6 +194,17 @@ unsigned long GetCodeDWord (unsigned Addr) +unsigned GetCodeLongAddr (unsigned Addr) +/* Get a word from the given address */ +{ + unsigned Lo = GetCodeByte (Addr); + unsigned Mid = GetCodeByte (Addr+1); + unsigned Hi = GetCodeByte (Addr+2); + return Lo | (Mid << 8) | (Hi << 16); +} + + + unsigned GetRemainingBytes (void) /* Return the number of remaining code bytes */ { diff --git a/src/da65/code.h b/src/da65/code.h index 50e68ebdf..aa3c6a290 100644 --- a/src/da65/code.h +++ b/src/da65/code.h @@ -72,6 +72,9 @@ unsigned GetCodeWord (unsigned Addr); unsigned long GetCodeDWord (unsigned Addr); /* Get a dword from the given address */ +unsigned GetCodeLongAddr (unsigned Addr); +/* Get a 24-bit address from the given address */ + unsigned GetRemainingBytes (void); /* Return the number of remaining code bytes */ diff --git a/src/da65/comments.c b/src/da65/comments.c index 7c671131f..f136ae3d2 100644 --- a/src/da65/comments.c +++ b/src/da65/comments.c @@ -52,6 +52,11 @@ /* Comment table */ static const char* CommentTab[0x10000]; +#define MAX_LONG_COMMENTS 256 +static const char* LongCommentVal[MAX_LONG_COMMENTS]; +static unsigned LongCommentAddr[MAX_LONG_COMMENTS]; +static unsigned LongCommentsUsed; + /*****************************************************************************/ @@ -60,17 +65,43 @@ static const char* CommentTab[0x10000]; +static unsigned FindLongIndex (unsigned Addr) +{ + unsigned i; + for (i = 0; i < LongCommentsUsed; i++) { + if (LongCommentAddr[i] == Addr) { + return i; + } + } + return -1; +} + + + void SetComment (unsigned Addr, const char* Comment) /* Set a comment for the given address */ { /* Check the given address */ AddrCheck (Addr); - /* If we do already have a comment, warn and ignore the new one */ - if (CommentTab[Addr]) { - Warning ("Duplicate comment for address $%04X", Addr); + if (IsLongAddr (Addr)) { + if (FindLongIndex (Addr)) { + Warning ("Duplicate comment for address $%06X", Addr); + } else { + if (LongCommentsUsed >= MAX_LONG_COMMENTS) { + Error("Too many long-address comments"); + } + LongCommentVal[LongCommentsUsed] = xstrdup (Comment); + LongCommentAddr[LongCommentsUsed] = Addr; + LongCommentsUsed++; + } } else { - CommentTab[Addr] = xstrdup (Comment); + /* If we do already have a comment, warn and ignore the new one */ + if (CommentTab[Addr]) { + Warning ("Duplicate comment for address $%04X", Addr); + } else { + CommentTab[Addr] = xstrdup (Comment); + } } } @@ -82,6 +113,14 @@ const char* GetComment (unsigned Addr) /* Check the given address */ AddrCheck (Addr); + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + if (i < LongCommentsUsed) { + return LongCommentVal[i]; + } + return NULL; + } + /* Return the label if any */ return CommentTab[Addr]; } diff --git a/src/da65/data.c b/src/da65/data.c index 7355e60d1..f85cd327d 100644 --- a/src/da65/data.c +++ b/src/da65/data.c @@ -70,7 +70,7 @@ static unsigned GetSpan (attr_t Style) if ((Attr & atStyleMask) != Style) { break; } - if ((Attr & (atSegmentStart | atSegmentEnd))) { + if ((Attr & (atSegmentStart | atSegmentEnd | atTableUnit))) { break; } ++Count; diff --git a/src/da65/handler.c b/src/da65/handler.c index 255b8da86..79b3192de 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -99,7 +99,9 @@ static const char* GetAbsOverride (unsigned Flags, unsigned Addr) ** string, otherwise return the empty string. */ { - if ((Flags & flAbsOverride) != 0 && Addr < 0x100) { + if ((Flags & flFarOverride) != 0 && Addr < 0x10000) { + return "f:"; + } else if ((Flags & flAbsOverride) != 0 && Addr < 0x100) { return "a:"; } else { return ""; @@ -121,8 +123,10 @@ static const char* GetAddrArg (unsigned Flags, unsigned Addr) static char Buf [32]; if (Addr < 0x100) { xsprintf (Buf, sizeof (Buf), "$%02X", Addr); - } else { + } else if (Addr < 0x10000) { xsprintf (Buf, sizeof (Buf), "$%04X", Addr); + } else { + xsprintf (Buf, sizeof (Buf), "$%06X", Addr); } return Buf; } @@ -229,6 +233,28 @@ void OH_Immediate (const OpcDesc* D) +void OH_Immediate65816M (const OpcDesc* D) +{ + if (GetAttr (PC) & atMem16) { + OneLine (D, "#$%04X", GetCodeWord (PC+1)); + } else { + OneLine (D, "#$%02X", GetCodeByte (PC+1)); + } +} + + + +void OH_Immediate65816X (const OpcDesc* D) +{ + if (GetAttr (PC) & atIdx16) { + OneLine (D, "#$%04X", GetCodeWord (PC+1)); + } else { + OneLine (D, "#$%02X", GetCodeByte (PC+1)); + } +} + + + void OH_ImmediateWord (const OpcDesc* D) { OneLine (D, "#$%04X", GetCodeWord (PC+1)); @@ -322,14 +348,28 @@ void OH_AbsoluteY (const OpcDesc* D) void OH_AbsoluteLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeLongAddr (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s%s", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteLongX (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeLongAddr (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s%s,x", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } @@ -358,7 +398,17 @@ void OH_Relative (const OpcDesc* D) void OH_RelativeLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + signed short Offs = GetCodeWord (PC+1); + + /* Calculate the target address */ + unsigned Addr = (((int) PC+3) + Offs) & 0xFFFF; + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } @@ -541,14 +591,15 @@ void OH_ImmediateAbsoluteX (const OpcDesc* D) void OH_StackRelative (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Output the line */ + OneLine (D, "$%02X,s", GetCodeByte (PC+1)); } void OH_DirectIndirectLongX (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + Error ("Not implemented %s", __FUNCTION__); } @@ -571,14 +622,28 @@ void OH_StackRelativeIndirectY4510 (const OpcDesc* D attribute ((unused))) void OH_DirectIndirectLong (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeByte (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "[%s]", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirectLongY (const OpcDesc* D attribute ((unused))) { - Error ("Not implemented"); + /* Get the operand */ + unsigned Addr = GetCodeByte (PC+1); + + /* Generate a label in pass 1 */ + GenerateLabel (D->Flags, Addr); + + /* Output the line */ + OneLine (D, "[%s],y", GetAddrArg (D->Flags, Addr)); } @@ -613,6 +678,19 @@ void OH_BlockMove (const OpcDesc* D) +void OH_BlockMove65816 (const OpcDesc* D) +{ + /* Get source operand */ + unsigned Src = GetCodeByte (PC+2); + /* Get destination operand */ + unsigned Dst = GetCodeByte (PC+1); + + /* Output the line */ + OneLine (D, "#$%02X, #$%02X", Src, Dst); +} + + + void OH_AbsoluteXIndirect (const OpcDesc* D attribute ((unused))) { /* Get the operand */ diff --git a/src/da65/handler.h b/src/da65/handler.h index eaa66e7fd..ee9b18bbc 100644 --- a/src/da65/handler.h +++ b/src/da65/handler.h @@ -57,6 +57,8 @@ void OH_Illegal (const OpcDesc* D attribute ((unused))); void OH_Accumulator (const OpcDesc*); void OH_Implicit (const OpcDesc*); void OH_Immediate (const OpcDesc*); +void OH_Immediate65816M (const OpcDesc*); +void OH_Immediate65816X (const OpcDesc*); void OH_ImmediateWord (const OpcDesc*); void OH_Direct (const OpcDesc*); void OH_DirectX (const OpcDesc*); @@ -89,6 +91,7 @@ void OH_StackRelativeIndirectY4510 (const OpcDesc*); void OH_DirectIndirectLong (const OpcDesc*); void OH_DirectIndirectLongY (const OpcDesc*); void OH_BlockMove (const OpcDesc*); +void OH_BlockMove65816 (const OpcDesc*); void OH_AbsoluteXIndirect (const OpcDesc*); /* Mitsubishi 740 */ diff --git a/src/da65/infofile.c b/src/da65/infofile.c index 6db82cb36..fbf367cc9 100644 --- a/src/da65/infofile.c +++ b/src/da65/infofile.c @@ -35,6 +35,7 @@ #include <stdio.h> #include <string.h> +#include <ctype.h> #include <limits.h> #if defined(_MSC_VER) /* Microsoft compiler */ @@ -521,11 +522,13 @@ static void RangeSection (void) /* Parse a range section */ { static const IdentTok RangeDefs[] = { - { "COMMENT", INFOTOK_COMMENT }, - { "END", INFOTOK_END }, - { "NAME", INFOTOK_NAME }, - { "START", INFOTOK_START }, - { "TYPE", INFOTOK_TYPE }, + { "COMMENT", INFOTOK_COMMENT }, + { "END", INFOTOK_END }, + { "NAME", INFOTOK_NAME }, + { "START", INFOTOK_START }, + { "TYPE", INFOTOK_TYPE }, + { "ADDRMODE", INFOTOK_ADDRMODE }, + { "UNIT", INFOTOK_UNIT }, }; static const IdentTok TypeDefs[] = { @@ -543,12 +546,14 @@ static void RangeSection (void) /* Which values did we get? */ enum { - tNone = 0x00, - tStart = 0x01, - tEnd = 0x02, - tType = 0x04, - tName = 0x08, - tComment= 0x10, + tNone = 0x00, + tStart = 0x01, + tEnd = 0x02, + tType = 0x04, + tName = 0x08, + tComment = 0x10, + tAddrMode = 0x20, + tUnit = 0x40, tNeeded = (tStart | tEnd | tType) }; unsigned Attributes = tNone; @@ -557,9 +562,11 @@ static void RangeSection (void) unsigned Start = 0; unsigned End = 0; unsigned char Type = 0; + unsigned AddrMode = 0; char* Name = 0; char* Comment = 0; unsigned MemberSize = 0; + unsigned Unit = 0; /* Skip the token */ @@ -592,9 +599,19 @@ static void RangeSection (void) case INFOTOK_END: AddAttr ("END", &Attributes, tEnd); InfoNextTok (); - InfoAssureInt (); - InfoRangeCheck (0x0000, 0xFFFF); - End = InfoIVal; + + if (InfoTok == INFOTOK_OFFSET_INTCON) { + InfoRangeCheck (0x0000, 0xFFFF); + if (!(Attributes & tStart)) + InfoError ("When using End with an offset, Start must be specified before"); + End = Start + InfoIVal - 1; + if (End > 0xFFFF) + InfoError ("Range error"); + } else { + InfoAssureInt (); + InfoRangeCheck (0x0000, 0xFFFF); + End = InfoIVal; + } InfoNextTok (); break; @@ -637,6 +654,46 @@ static void RangeSection (void) InfoNextTok (); break; + case INFOTOK_ADDRMODE: + AddAttr ("ADDRMODE", &Attributes, tAddrMode); + InfoNextTok (); + InfoAssureStr (); + if (InfoSVal[0] == '\0') { + InfoError ("AddrMode may not be empty"); + } + if (InfoSVal[1] == '\0') { + InfoError ("AddrMode must be two characters long"); + } + if (tolower(InfoSVal[0]) == 'm') { + if (InfoSVal[0] == 'm') { + AddrMode = atMem16; + } else { + AddrMode = atMem8; + } + } else { + InfoError ("AddrMode syntax: mx"); + } + if (tolower(InfoSVal[1]) == 'x') { + if (InfoSVal[1] == 'x') { + AddrMode |= atIdx16; + } else { + AddrMode |= atIdx8; + } + } else { + InfoError ("AddrMode syntax: mx"); + } + InfoNextTok (); + break; + + case INFOTOK_UNIT: + AddAttr ("UNIT", &Attributes, tUnit); + InfoNextTok (); + InfoAssureInt (); + InfoRangeCheck (0x0002, 0xFFFF); + Unit = InfoIVal; + InfoNextTok (); + break; + default: Internal ("Unexpected token: %u", InfoTok); } @@ -651,6 +708,35 @@ static void RangeSection (void) InfoError ("Required values missing from this section"); } + if (CPU == CPU_65816) { + if (Type == atCode && !(Attributes & tAddrMode)) { + InfoError ("65816 code sections require addressing mode"); + } + if (Type != atCode && (Attributes & tAddrMode)) { + InfoError ("AddrMode is only valid for code sections"); + } + } + + /* Only tables support unit sizes */ + if ((Attributes & tUnit) && + Type != atAddrTab && + Type != atByteTab && + Type != atDByteTab && + Type != atDWordTab && + Type != atRtsTab && + Type != atTextTab && + Type != atWordTab) { + InfoError ("Only table types support unit size"); + } + + /* Mark each unit separator */ + if (Attributes & tUnit) { + unsigned i; + for (i = Start; i < End; i += Unit) { + MarkAddr(i, atTableUnit); + } + } + /* Start must be less than end */ if (Start > End) { InfoError ("Start value must not be greater than end value"); @@ -662,7 +748,7 @@ static void RangeSection (void) } /* Set the range */ - MarkRange (Start, End, Type); + MarkRange (Start, End, Type | AddrMode); /* Do we have a label? */ if (Attributes & tName) { diff --git a/src/da65/labels.c b/src/da65/labels.c index 542205c11..4cae0316a 100644 --- a/src/da65/labels.c +++ b/src/da65/labels.c @@ -60,6 +60,12 @@ /* Symbol table */ static const char* SymTab[0x10000]; +/* 65816 symbol table */ +#define MAX_LONG_LABELS 256 +static const char* LongSymVal[MAX_LONG_LABELS]; +static unsigned LongSymAddr[MAX_LONG_LABELS]; +static unsigned LongLabelsUsed; + /*****************************************************************************/ @@ -74,12 +80,27 @@ static const char* MakeLabelName (unsigned Addr) */ { static char LabelBuf [32]; - xsprintf (LabelBuf, sizeof (LabelBuf), "L%04X", Addr); + xsprintf (LabelBuf, sizeof (LabelBuf), + IsLongAddr (Addr) ? "L%06X" : "L%04X", Addr); return LabelBuf; } +static unsigned FindLongIndex (unsigned Addr) +{ + unsigned i; + for (i = 0; i < LongLabelsUsed; i++) { + if (LongSymAddr[i] == Addr) { + return i; + } + } + + return -1; +} + + + static void AddLabel (unsigned Addr, attr_t Attr, const char* Name) /* Add a label */ { @@ -91,19 +112,41 @@ static void AddLabel (unsigned Addr, attr_t Attr, const char* Name) /* Allow redefinition if identical. Beware: Unnamed labels don't ** have a name (you guessed that, didn't you?). */ - if (ExistingAttr == Attr && - ((Name == 0 && SymTab[Addr] == 0) || - (Name != 0 && SymTab[Addr] != 0 && - strcmp (SymTab[Addr], Name) == 0))) { - return; + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + if (ExistingAttr == Attr && + ((Name == 0 && LongSymVal[i] == 0) || + (Name != 0 && LongSymVal[i] != 0 && + strcmp (LongSymVal[i], Name) == 0))) { + return; + } + Error ("Duplicate label for address $%06X (%s): '%s'", Addr, + LongSymVal[i] == 0 ? "<unnamed label>" : LongSymVal[i], + Name == 0 ? "<unnamed label>" : Name); + } else { + if (ExistingAttr == Attr && + ((Name == 0 && SymTab[Addr] == 0) || + (Name != 0 && SymTab[Addr] != 0 && + strcmp (SymTab[Addr], Name) == 0))) { + return; + } + Error ("Duplicate label for address $%04X (%s): '%s'", Addr, + SymTab[Addr] == 0 ? "<unnamed label>" : SymTab[Addr], + Name == 0 ? "<unnamed label>" : Name); } - Error ("Duplicate label for address $%04X (%s): '%s'", Addr, - SymTab[Addr] == 0 ? "<unnamed label>" : SymTab[Addr], - Name == 0 ? "<unnamed label>" : Name); } /* Create a new label (xstrdup will return NULL if input NULL) */ - SymTab[Addr] = xstrdup (Name); + if (IsLongAddr (Addr)) { + if (LongLabelsUsed >= MAX_LONG_LABELS) { + Error ("Too many long labels"); + } + LongSymAddr[LongLabelsUsed] = Addr; + LongSymVal[LongLabelsUsed] = xstrdup (Name); + LongLabelsUsed++; + } else { + SymTab[Addr] = xstrdup (Name); + } /* Remember the attribute */ MarkAddr (Addr, Attr); @@ -254,6 +297,10 @@ const char* GetLabelName (unsigned Addr) */ if (A == atUnnamedLabel) { return ""; + } else if (IsLongAddr (Addr)) { + /* Return the label if any */ + const unsigned i = FindLongIndex (Addr); + return i < LongLabelsUsed ? LongSymVal[i] : NULL; } else { /* Return the label if any */ return SymTab[Addr]; @@ -327,6 +374,10 @@ const char* GetLabel (unsigned Addr, unsigned RefFrom) return FwdLabels[Count-1]; } + } else if (IsLongAddr (Addr)) { + /* Return the label if any */ + const unsigned i = FindLongIndex (Addr); + return i < LongLabelsUsed ? LongSymVal[i] : NULL; } else { /* Return the label if any */ return SymTab[Addr]; @@ -371,7 +422,13 @@ static void DefOutOfRangeLabel (unsigned long Addr) case atIntLabel: case atExtLabel: - DefConst (SymTab[Addr], GetComment (Addr), Addr); + if (IsLongAddr (Addr)) { + const unsigned i = FindLongIndex (Addr); + DefConst (i < LongLabelsUsed ? LongSymVal[i] : NULL, + GetComment (Addr), Addr); + } else { + DefConst (SymTab[Addr], GetComment (Addr), Addr); + } break; case atUnnamedLabel: @@ -390,6 +447,7 @@ void DefOutOfRangeLabels (void) /* Output any labels that are out of the loaded code range */ { unsigned long Addr; + unsigned i; SeparatorLine (); @@ -412,5 +470,10 @@ void DefOutOfRangeLabels (void) DefOutOfRangeLabel (Addr++); } + /* 65816 long range */ + for (i = 0; i < LongLabelsUsed; i++) { + DefOutOfRangeLabel (LongSymAddr[i]); + } + SeparatorLine (); } diff --git a/src/da65/main.c b/src/da65/main.c index 1fc07f006..67a01dc3b 100644 --- a/src/da65/main.c +++ b/src/da65/main.c @@ -55,6 +55,7 @@ #include "data.h" #include "error.h" #include "global.h" +#include "handler.h" #include "infofile.h" #include "labels.h" #include "opctable.h" @@ -63,6 +64,8 @@ #include "segment.h" +static unsigned PrevAddrMode; + /*****************************************************************************/ /* Code */ @@ -427,8 +430,13 @@ static void OneOpcode (unsigned RemainingBytes) switch (Style) { case atDefault: - D->Handler (D); - PC += D->Size; + if (CPU == CPU_65816) { + DataByteLine (1); + ++PC; + } else { + D->Handler (D); + PC += D->Size; + } break; case atCode: @@ -436,12 +444,36 @@ static void OneOpcode (unsigned RemainingBytes) ** following insn, fall through to byte mode. */ if (D->Size <= RemainingBytes) { + if (CPU == CPU_65816) { + const unsigned AddrMode = GetAttr (PC) & at65816Mask; + if (PrevAddrMode != AddrMode) { + if ((PrevAddrMode & atMem8) != (AddrMode & atMem8) || + (PrevAddrMode & atMem16) != (AddrMode & atMem16)) { + OutputMFlag(!!(AddrMode & atMem8)); + } + if ((PrevAddrMode & atIdx8) != (AddrMode & atIdx8) || + (PrevAddrMode & atIdx16) != (AddrMode & atIdx16)) { + OutputXFlag(!!(AddrMode & atIdx8)); + } + + PrevAddrMode = AddrMode; + } + } + /* Output labels within the next insn */ for (I = 1; I < D->Size; ++I) { ForwardLabel (I); } /* Output the insn */ D->Handler (D); + if (CPU == CPU_65816 && (D->Flags & flSizeChanges)) { + if ((D->Handler == OH_Immediate65816M && + GetAttr (PC) & atMem16) || + (D->Handler == OH_Immediate65816X && + GetAttr (PC) & atIdx16)) { + PC++; + } + } PC += D->Size; break; } @@ -503,6 +535,8 @@ static void OnePass (void) { unsigned Count; + PrevAddrMode = 0; + /* Disassemble until nothing left */ while ((Count = GetRemainingBytes()) > 0) { OneOpcode (Count); diff --git a/src/da65/opc65816.c b/src/da65/opc65816.c index b7775d2e2..06272f318 100644 --- a/src/da65/opc65816.c +++ b/src/da65/opc65816.c @@ -56,13 +56,13 @@ const OpcDesc OpcTable_65816[256] = { { "asl", 2, flUseLabel, OH_Direct }, /* $06 */ { "ora", 2, flUseLabel, OH_DirectIndirectLong }, /* $07 */ { "php", 1, flNone, OH_Implicit }, /* $08 */ - { "ora", 2, flNone, OH_Immediate }, /* $09 */ + { "ora", 2, flSizeChanges, OH_Immediate65816M }, /* $09 */ { "asl", 1, flNone, OH_Accumulator }, /* $0a */ { "phd", 1, flNone, OH_Implicit }, /* $0b */ { "tsb", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0c */ { "ora", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0d */ { "asl", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0e */ - { "ora", 4, flUseLabel, OH_AbsoluteLong }, /* $0f */ + { "ora", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $0f */ { "bpl", 2, flLabel, OH_Relative }, /* $10 */ { "ora", 2, flUseLabel, OH_DirectIndirectY }, /* $11 */ { "ora", 2, flUseLabel, OH_DirectIndirect }, /* $12 */ @@ -78,23 +78,23 @@ const OpcDesc OpcTable_65816[256] = { { "trb", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $1c */ { "ora", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1d */ { "asl", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1e */ - { "ora", 4, flUseLabel, OH_AbsoluteLongX }, /* $1f */ + { "ora", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $1f */ { "jsr", 3, flLabel, OH_JsrAbsolute }, /* $20 */ { "and", 2, flUseLabel, OH_DirectXIndirect }, /* $21 */ - { "jsl", 3, flLabel, OH_AbsoluteLong }, /* $22 */ + { "jsl", 4, flLabel|flFarOverride, OH_AbsoluteLong }, /* $22 */ { "and", 2, flNone, OH_StackRelative }, /* $23 */ { "bit", 2, flUseLabel, OH_Direct }, /* $24 */ { "and", 2, flUseLabel, OH_Direct }, /* $25 */ { "rol", 2, flUseLabel, OH_Direct }, /* $26 */ { "and", 2, flUseLabel, OH_DirectIndirectLong }, /* $27 */ { "plp", 1, flNone, OH_Implicit }, /* $28 */ - { "and", 2, flNone, OH_Immediate }, /* $29 */ + { "and", 2, flSizeChanges, OH_Immediate65816M }, /* $29 */ { "rol", 1, flNone, OH_Accumulator }, /* $2a */ { "pld", 1, flNone, OH_Implicit }, /* $2b */ { "bit", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2c */ { "and", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2d */ { "rol", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2e */ - { "and", 4, flUseLabel, OH_AbsoluteLong }, /* $2f */ + { "and", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $2f */ { "bmi", 2, flLabel, OH_Relative }, /* $30 */ { "and", 2, flUseLabel, OH_DirectIndirectY }, /* $31 */ { "and", 2, flUseLabel, OH_DirectIndirect }, /* $32 */ @@ -110,28 +110,28 @@ const OpcDesc OpcTable_65816[256] = { { "bit", 3, flUseLabel, OH_AbsoluteX }, /* $3c */ { "and", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3d */ { "rol", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3e */ - { "and", 4, flUseLabel, OH_AbsoluteLongX }, /* $3f */ + { "and", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $3f */ { "rti", 1, flNone, OH_Rts }, /* $40 */ { "eor", 2, flUseLabel, OH_DirectXIndirect }, /* $41 */ { "wdm", 2, flNone, OH_Implicit }, /* $42 */ { "eor", 2, flNone, OH_StackRelative }, /* $43 */ - { "mvp", 3, flNone, OH_BlockMove }, /* $44 */ + { "mvp", 3, flNone, OH_BlockMove65816 }, /* $44 */ { "eor", 2, flUseLabel, OH_Direct }, /* $45 */ { "lsr", 2, flUseLabel, OH_Direct }, /* $46 */ { "eor", 2, flUseLabel, OH_DirectIndirectLong }, /* $47 */ { "pha", 1, flNone, OH_Implicit }, /* $48 */ - { "eor", 2, flNone, OH_Immediate }, /* $49 */ + { "eor", 2, flSizeChanges, OH_Immediate65816M }, /* $49 */ { "lsr", 1, flNone, OH_Accumulator }, /* $4a */ { "phk", 1, flNone, OH_Implicit }, /* $4b */ { "jmp", 3, flLabel, OH_JmpAbsolute }, /* $4c */ { "eor", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4d */ { "lsr", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4e */ - { "eor", 4, flUseLabel, OH_AbsoluteLong }, /* $4f */ + { "eor", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $4f */ { "bvc", 2, flLabel, OH_Relative }, /* $50 */ { "eor", 2, flUseLabel, OH_DirectIndirectY }, /* $51 */ { "eor", 2, flUseLabel, OH_DirectIndirect }, /* $52 */ { "eor", 2, flNone, OH_StackRelativeIndirectY}, /* $53 */ - { "mvn", 3, flNone, OH_BlockMove }, /* $54 */ + { "mvn", 3, flNone, OH_BlockMove65816 }, /* $54 */ { "eor", 2, flUseLabel, OH_DirectX }, /* $55 */ { "lsr", 2, flUseLabel, OH_DirectX }, /* $56 */ { "eor", 2, flUseLabel, OH_DirectIndirectLongY }, /* $57 */ @@ -139,10 +139,10 @@ const OpcDesc OpcTable_65816[256] = { { "eor", 3, flUseLabel, OH_AbsoluteY }, /* $59 */ { "phy", 1, flNone, OH_Implicit }, /* $5a */ { "tcd", 1, flNone, OH_Implicit }, /* $5b */ - { "jml", 4, flLabel, OH_AbsoluteLong }, /* $5c */ + { "jml", 4, flLabel|flFarOverride, OH_AbsoluteLong }, /* $5c */ { "eor", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5d */ { "lsr", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5e */ - { "eor", 4, flUseLabel, OH_AbsoluteLongX }, /* $5f */ + { "eor", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $5f */ { "rts", 1, flNone, OH_Rts }, /* $60 */ { "adc", 2, flUseLabel, OH_DirectXIndirect }, /* $61 */ { "per", 3, flLabel, OH_RelativeLong }, /* $62 */ @@ -152,13 +152,13 @@ const OpcDesc OpcTable_65816[256] = { { "ror", 2, flUseLabel, OH_Direct }, /* $66 */ { "adc", 2, flUseLabel, OH_DirectIndirectLong }, /* $67 */ { "pla", 1, flNone, OH_Implicit }, /* $68 */ - { "adc", 2, flNone, OH_Immediate }, /* $69 */ + { "adc", 2, flSizeChanges, OH_Immediate65816M }, /* $69 */ { "ror", 1, flNone, OH_Accumulator }, /* $6a */ { "rtl", 1, flNone, OH_Implicit }, /* $6b */ { "jmp", 3, flLabel, OH_JmpAbsoluteIndirect }, /* $6c */ { "adc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6d */ { "ror", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6e */ - { "adc", 4, flUseLabel, OH_AbsoluteLong }, /* $6f */ + { "adc", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $6f */ { "bvs", 2, flLabel, OH_Relative }, /* $70 */ { "adc", 2, flUseLabel, OH_DirectIndirectY }, /* $71 */ { "adc", 2, flUseLabel, OH_DirectIndirect }, /* $72 */ @@ -174,7 +174,7 @@ const OpcDesc OpcTable_65816[256] = { { "jmp", 3, flLabel, OH_AbsoluteXIndirect }, /* $7c */ { "adc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7d */ { "ror", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7e */ - { "adc", 4, flUseLabel, OH_AbsoluteLongX }, /* $7f */ + { "adc", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $7f */ { "bra", 2, flLabel, OH_Relative }, /* $80 */ { "sta", 2, flUseLabel, OH_DirectXIndirect }, /* $81 */ { "brl", 3, flLabel, OH_RelativeLong }, /* $82 */ @@ -184,13 +184,13 @@ const OpcDesc OpcTable_65816[256] = { { "stx", 2, flUseLabel, OH_Direct }, /* $86 */ { "sta", 2, flUseLabel, OH_DirectIndirectLong }, /* $87 */ { "dey", 1, flNone, OH_Implicit }, /* $88 */ - { "bit", 2, flNone, OH_Immediate }, /* $89 */ + { "bit", 2, flSizeChanges, OH_Immediate65816M }, /* $89 */ { "txa", 1, flNone, OH_Implicit }, /* $8a */ { "phb", 1, flNone, OH_Implicit }, /* $8b */ { "sty", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8c */ { "sta", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8d */ { "stx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8e */ - { "sta", 4, flUseLabel, OH_AbsoluteLong }, /* $8f */ + { "sta", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $8f */ { "bcc", 2, flLabel, OH_Relative }, /* $90 */ { "sta", 2, flUseLabel, OH_DirectIndirectY }, /* $91 */ { "sta", 2, flUseLabel, OH_DirectIndirect }, /* $92 */ @@ -206,23 +206,23 @@ const OpcDesc OpcTable_65816[256] = { { "stz", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $9c */ { "sta", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9d */ { "stz", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9e */ - { "sta", 4, flUseLabel, OH_AbsoluteLongX }, /* $9f */ - { "ldy", 2, flNone, OH_Immediate }, /* $a0 */ + { "sta", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $9f */ + { "ldy", 2, flSizeChanges, OH_Immediate65816X }, /* $a0 */ { "lda", 2, flUseLabel, OH_DirectXIndirect }, /* $a1 */ - { "ldx", 2, flNone, OH_Immediate }, /* $a2 */ + { "ldx", 2, flSizeChanges, OH_Immediate65816X }, /* $a2 */ { "lda", 2, flNone, OH_StackRelative }, /* $a3 */ { "ldy", 2, flUseLabel, OH_Direct }, /* $a4 */ { "lda", 2, flUseLabel, OH_Direct }, /* $a5 */ { "ldx", 2, flUseLabel, OH_Direct }, /* $a6 */ { "lda", 2, flUseLabel, OH_DirectIndirectLong }, /* $a7 */ { "tay", 1, flNone, OH_Implicit }, /* $a8 */ - { "lda", 2, flNone, OH_Immediate }, /* $a9 */ + { "lda", 2, flSizeChanges, OH_Immediate65816M }, /* $a9 */ { "tax", 1, flNone, OH_Implicit }, /* $aa */ { "plb", 1, flNone, OH_Implicit }, /* $ab */ { "ldy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ac */ { "lda", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ad */ { "ldx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ae */ - { "lda", 4, flUseLabel, OH_AbsoluteLong }, /* $af */ + { "lda", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $af */ { "bcs", 2, flLabel, OH_Relative }, /* $b0 */ { "lda", 2, flUseLabel, OH_DirectIndirectY }, /* $b1 */ { "lda", 2, flUseLabel, OH_DirectIndirect }, /* $b2 */ @@ -238,8 +238,8 @@ const OpcDesc OpcTable_65816[256] = { { "ldy", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bc */ { "lda", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bd */ { "ldx", 3, flUseLabel|flAbsOverride, OH_AbsoluteY }, /* $be */ - { "lda", 4, flUseLabel, OH_AbsoluteLongX }, /* $bf */ - { "cpy", 2, flNone, OH_Immediate }, /* $c0 */ + { "lda", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $bf */ + { "cpy", 2, flSizeChanges, OH_Immediate65816X }, /* $c0 */ { "cmp", 2, flUseLabel, OH_DirectXIndirect }, /* $c1 */ { "rep", 2, flNone, OH_Immediate }, /* $c2 */ { "cmp", 2, flNone, OH_StackRelative }, /* $c3 */ @@ -248,18 +248,18 @@ const OpcDesc OpcTable_65816[256] = { { "dec", 2, flUseLabel, OH_Direct }, /* $c6 */ { "cmp", 2, flUseLabel, OH_DirectIndirectLong }, /* $c7 */ { "iny", 1, flNone, OH_Implicit }, /* $c8 */ - { "cmp", 2, flNone, OH_Immediate }, /* $c9 */ + { "cmp", 2, flSizeChanges, OH_Immediate65816M }, /* $c9 */ { "dex", 1, flNone, OH_Implicit }, /* $ca */ { "wai", 1, flNone, OH_Implicit }, /* $cb */ { "cpy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cc */ { "cmp", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cd */ { "dec", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ce */ - { "cmp", 4, flUseLabel, OH_AbsoluteLong }, /* $cf */ + { "cmp", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $cf */ { "bne", 2, flLabel, OH_Relative }, /* $d0 */ { "cmp", 2, flUseLabel, OH_DirectIndirectY }, /* $d1 */ { "cmp", 2, flUseLabel, OH_DirectIndirect }, /* $d2 */ { "cmp", 2, flNone, OH_StackRelativeIndirectY}, /* $d3 */ - { "pei", 2, flUseLabel, OH_Direct }, /* $d4 */ + { "pei", 2, flUseLabel, OH_DirectIndirect }, /* $d4 */ { "cmp", 2, flUseLabel, OH_DirectX }, /* $d5 */ { "dec", 2, flUseLabel, OH_DirectX }, /* $d6 */ { "cmp", 2, flUseLabel, OH_DirectIndirectLongY }, /* $d7 */ @@ -270,8 +270,8 @@ const OpcDesc OpcTable_65816[256] = { { "jml", 3, flLabel, OH_AbsoluteIndirect }, /* $dc */ { "cmp", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $dd */ { "dec", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $de */ - { "cmp", 4, flUseLabel, OH_AbsoluteLongX }, /* $df */ - { "cpx", 2, flNone, OH_Immediate }, /* $e0 */ + { "cmp", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $df */ + { "cpx", 2, flSizeChanges, OH_Immediate65816X }, /* $e0 */ { "sbc", 2, flUseLabel, OH_DirectXIndirect }, /* $e1 */ { "sep", 2, flNone, OH_Immediate }, /* $e2 */ { "sbc", 2, flNone, OH_StackRelative }, /* $e3 */ @@ -280,13 +280,13 @@ const OpcDesc OpcTable_65816[256] = { { "inc", 2, flUseLabel, OH_Direct }, /* $e6 */ { "sbc", 2, flUseLabel, OH_DirectIndirectLong }, /* $e7 */ { "inx", 1, flNone, OH_Implicit }, /* $e8 */ - { "sbc", 2, flNone, OH_Immediate }, /* $e9 */ + { "sbc", 2, flSizeChanges, OH_Immediate65816M }, /* $e9 */ { "nop", 1, flNone, OH_Implicit }, /* $ea */ { "xba", 1, flNone, OH_Implicit }, /* $eb */ { "cpx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ec */ { "sbc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ed */ { "inc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ee */ - { "sbc", 4, flUseLabel, OH_AbsoluteLong }, /* $ef */ + { "sbc", 4, flUseLabel|flFarOverride, OH_AbsoluteLong }, /* $ef */ { "beq", 2, flLabel, OH_Relative }, /* $f0 */ { "sbc", 2, flUseLabel, OH_DirectIndirectY }, /* $f1 */ { "sbc", 2, flUseLabel, OH_DirectIndirect }, /* $f2 */ @@ -302,5 +302,5 @@ const OpcDesc OpcTable_65816[256] = { { "jsr", 3, flLabel, OH_AbsoluteXIndirect }, /* $fc */ { "sbc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fd */ { "inc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fe */ - { "sbc", 4, flUseLabel, OH_AbsoluteLongX }, /* $ff */ + { "sbc", 4, flUseLabel|flFarOverride, OH_AbsoluteLongX }, /* $ff */ }; diff --git a/src/da65/opcdesc.h b/src/da65/opcdesc.h index 399a0962d..6baffe377 100644 --- a/src/da65/opcdesc.h +++ b/src/da65/opcdesc.h @@ -53,7 +53,8 @@ enum { flLabel = flUseLabel|flGenLabel, /* Generate and use a label */ flIllegal = 0x10, /* Illegal instruction */ flAbsOverride = 0x20, /* Need a: override */ - flFarOverride = 0x40 /* Need f: override */ + flFarOverride = 0x40, /* Need f: override */ + flSizeChanges = 0x80 /* 65816: size may change */ }; /* Forward/typedef for struct OpcDesc */ diff --git a/src/da65/opctable.c b/src/da65/opctable.c index 255a3557f..d9068d253 100644 --- a/src/da65/opctable.c +++ b/src/da65/opctable.c @@ -74,6 +74,7 @@ void SetOpcTable (cpu_t CPU) case CPU_6502DTV: OpcTable = OpcTable_6502DTV; break; case CPU_65SC02: OpcTable = OpcTable_65SC02; break; case CPU_65C02: OpcTable = OpcTable_65C02; break; + case CPU_65816: OpcTable = OpcTable_65816; break; case CPU_HUC6280: OpcTable = OpcTable_HuC6280; break; case CPU_M740: OpcTable = OpcTable_M740; break; case CPU_4510: OpcTable = OpcTable_4510; break; diff --git a/src/da65/output.c b/src/da65/output.c index 5b0b6b79c..8e786e130 100644 --- a/src/da65/output.c +++ b/src/da65/output.c @@ -401,3 +401,23 @@ void OutputSettings (void) LineFeed (); LineFeed (); } + + + +void OutputMFlag (unsigned char enabled) +/* Output the 65816 M-flag state */ +{ + Indent (MCol); + Output (enabled ? ".a8" : ".a16"); + LineFeed (); +} + + + +void OutputXFlag (unsigned char enabled) +/* Output the 65816 X-flag state */ +{ + Indent (MCol); + Output (enabled ? ".i8" : ".i16"); + LineFeed (); +} diff --git a/src/da65/output.h b/src/da65/output.h index 13ea0cc85..bc20aace0 100644 --- a/src/da65/output.h +++ b/src/da65/output.h @@ -108,6 +108,12 @@ void LineComment (unsigned PC, unsigned Count); void OutputSettings (void); /* Output CPU and other settings */ +void OutputMFlag (unsigned char enabled); +/* Output the 65816 M-flag state */ + +void OutputXFlag (unsigned char enabled); +/* Output the 65816 X-flag state */ + /* End of output.h */ diff --git a/src/da65/scanner.c b/src/da65/scanner.c index 33fb3a826..d0301c08a 100644 --- a/src/da65/scanner.c +++ b/src/da65/scanner.c @@ -372,6 +372,14 @@ Again: return; } + /* Decimal number offset? */ + if (C == '+') { + NextChar (); + InfoIVal = GetDecimalToken (); + InfoTok = INFOTOK_OFFSET_INTCON; + return; + } + /* Other characters */ switch (C) { diff --git a/src/da65/scanner.h b/src/da65/scanner.h index d4e38177b..ce76d4a98 100644 --- a/src/da65/scanner.h +++ b/src/da65/scanner.h @@ -48,6 +48,7 @@ typedef enum token_t { INFOTOK_NONE, INFOTOK_INTCON, + INFOTOK_OFFSET_INTCON, INFOTOK_STRCON, INFOTOK_CHARCON, INFOTOK_IDENT, @@ -89,6 +90,8 @@ typedef enum token_t { INFOTOK_START, INFOTOK_END, INFOTOK_TYPE, + INFOTOK_ADDRMODE, + INFOTOK_UNIT, INFOTOK_CODE, INFOTOK_BYTETAB, diff --git a/src/dbginfo/dbginfo.c b/src/dbginfo/dbginfo.c index fdebe6910..fee41012c 100644 --- a/src/dbginfo/dbginfo.c +++ b/src/dbginfo/dbginfo.c @@ -4736,14 +4736,18 @@ static SpanInfoListEntry* FindSpanInfoByAddr (const SpanInfoList* L, cc65_addr A -static LineInfo* FindLineInfoByLine (const Collection* LineInfos, cc65_line Line) -/* Find the LineInfo for a given line number. The function returns the line -** info or NULL if none was found. +static int FindLineInfoByLine (const Collection* LineInfos, cc65_line Line, + unsigned *Index) +/* Find the LineInfo for a given line number. The function returns true if the +** name was found. In this case, Index contains the index of the first item +** that matches. If the item wasn't found, the function returns false and +** Index contains the insert position for Name. */ { /* Do a binary search */ int Lo = 0; int Hi = (int) CollCount (LineInfos) - 1; + int Found = 0; while (Lo <= Hi) { /* Mid of range */ @@ -4755,16 +4759,20 @@ static LineInfo* FindLineInfoByLine (const Collection* LineInfos, cc65_line Line /* Found? */ if (Line > CurItem->Line) { Lo = Cur + 1; - } else if (Line < CurItem->Line) { - Hi = Cur - 1; } else { - /* Found */ - return CurItem; + Hi = Cur - 1; + /* Since we may have duplicates, repeat the search until we've + ** the first item that has a match. + */ + if(Line == CurItem->Line) { + Found = 1; + } } } - /* Not found */ - return 0; + /* Pass back the index. This is also the insert position */ + *Index = Lo; + return Found; } @@ -6127,13 +6135,17 @@ const cc65_lineinfo* cc65_line_byid (cc65_dbginfo Handle, unsigned Id) const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo Handle, unsigned FileId, cc65_line Line) /* Return line information for a source file/line number combination. The -** function returns NULL if no line information was found. +** function returns NULL if no line information was found, otherwise a list +** of line infos. */ { const DbgInfo* Info; const FileInfo* F; cc65_lineinfo* D; LineInfo* L = 0; + unsigned I; + unsigned Index; + unsigned Count; /* Check the parameter */ assert (Handle != 0); @@ -6150,18 +6162,31 @@ const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo Handle, unsigned FileId, F = CollAt (&Info->FileInfoById, FileId); /* Search in the file for the given line */ - L = FindLineInfoByLine (&F->LineInfoByLine, Line); - - /* Bail out if we didn't find the line */ - if (L == 0) { + if(!FindLineInfoByLine (&F->LineInfoByLine, Line, &Index)) { + /* Not found */ return 0; } + /* Index contains the first position. Count how many lines with this number + ** we have. Skip the first one, since we have at least one. + */ + Count = 1; + + while ((unsigned) Index + Count < CollCount( &F->LineInfoByLine)) { + L = CollAt (&F->LineInfoByLine, (unsigned) Index + Count); + if (L->Line != Line) { + break; + } + ++Count; + } + /* Prepare the struct we will return to the caller */ - D = new_cc65_lineinfo (1); + D = new_cc65_lineinfo (Count); /* Copy the data */ - CopyLineInfo (D->data, L); + for (I = 0; I < Count; ++I) { + CopyLineInfo (D->data + I, CollAt (&F->LineInfoByLine, Index++)); + } /* Return the allocated struct */ return D; diff --git a/src/dbginfo/dbginfo.h b/src/dbginfo/dbginfo.h index 38d891e7c..95aae837c 100644 --- a/src/dbginfo/dbginfo.h +++ b/src/dbginfo/dbginfo.h @@ -258,7 +258,8 @@ const cc65_lineinfo* cc65_line_bynumber (cc65_dbginfo handle, unsigned source_id, cc65_line line); /* Return line information for a source file/line number combination. The -** function returns NULL if no line information was found. +** function returns NULL if no line information was found, otherwise a list +** of line infos. */ const cc65_lineinfo* cc65_line_bysource (cc65_dbginfo Handle, unsigned source_id); diff --git a/src/grc65/main.c b/src/grc65/main.c index 349b5c110..7d31bfc52 100644 --- a/src/grc65/main.c +++ b/src/grc65/main.c @@ -850,14 +850,25 @@ static char *filterInput (FILE *F, char *tbl) /* loads file into buffer filtering it out */ int a, prevchar = -1, i = 0, bracket = 0, quote = 1; - for (;;) { - a = getc(F); - if ((a == '\n') || (a == '\015')) a = ' '; - if (a == ',' && quote) a = ' '; - if (a == '\042') quote =! quote; + a = getc(F); + while (1) { + if (i >= BLOODY_BIG_BUFFER) { + AbEnd ("File too large for internal parsing buffer (%d bytes)",BLOODY_BIG_BUFFER); + } + if (((a == '\n') || (a == '\015')) || + (a == ',' && quote)) { + a = ' '; + } + if (a == '\042') { + quote =! quote; + } if (quote) { - if ((a == '{') || (a == '(')) bracket++; - if ((a == '}') || (a == ')')) bracket--; + if ((a == '{') || (a == '(')) { + bracket++; + } + if ((a == '}') || (a == ')')) { + bracket--; + } } if (a == EOF) { tbl[i] = '\0'; @@ -873,13 +884,18 @@ static char *filterInput (FILE *F, char *tbl) if (a == ';' && quote) { do { a = getc (F); - } while (a != '\n'); - fseek (F, -1, SEEK_CUR); + } while (a != '\n' && a != EOF); + /* Don't discard this newline/EOF, continue to next loop. + ** A previous implementation used fseek(F,-1,SEEK_CUR), + ** which is invalid for text mode files, and was unreliable across platforms. + */ + continue; } else { tbl[i++] = a; prevchar = a; } } + a = getc(F); } if (bracket != 0) AbEnd ("There are unclosed brackets!"); diff --git a/src/ld65/bin.c b/src/ld65/bin.c index bd822cc23..15b534f66 100644 --- a/src/ld65/bin.c +++ b/src/ld65/bin.c @@ -193,8 +193,16 @@ static void BinWriteMem (BinDesc* D, MemoryArea* M) NewAddr += M->Start; } if (DoWrite || (M->Flags & MF_FILL) != 0) { - /* Seek in "overwrite" segments */ if (S->Flags & SF_OVERWRITE) { + /* Seek in "overwrite" segments. Fill if the seek position has not been reached yet. */ + unsigned long FileLength; + unsigned long SeekTarget = NewAddr - M->Start + M->FileOffs; + fseek (D->F, 0, SEEK_END); + FileLength = ftell (D->F); + if (SeekTarget > FileLength) { + WriteMult (D->F, M->FillVal, SeekTarget - FileLength); + PrintNumVal ("SF_OVERWRITE", SeekTarget - FileLength); + } fseek (D->F, NewAddr - M->Start + M->FileOffs, SEEK_SET); } else { WriteMult (D->F, M->FillVal, NewAddr-Addr); diff --git a/src/ld65/config.c b/src/ld65/config.c index c22ced1ef..6c1f6ad4c 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -1936,6 +1936,11 @@ unsigned CfgProcess (void) GetString (M->Name)); } M->Size = GetExprVal (M->SizeExpr); + if (M->Size >= 0x80000000) { + CfgError (GetSourcePos (M->LI), + "Size of memory area '%s' is negative: %ld", + GetString (M->Name), (long)M->Size); + } /* Walk through the segments in this memory area */ for (J = 0; J < CollCount (&M->SegList); ++J) { @@ -1945,6 +1950,10 @@ unsigned CfgProcess (void) /* Remember the start address before handling this segment */ unsigned long StartAddr = Addr; + /* For computing FillLevel */ + unsigned long FillLevel; + unsigned long FillAdded = 0; + /* Take note of "overwrite" segments and make sure there are no ** other segment types following them in current memory region. */ @@ -2034,14 +2043,14 @@ unsigned CfgProcess (void) ++Overflows; if (S->Flags & SF_OFFSET) { CfgWarning (GetSourcePos (S->LI), - "Segment '%s' offset is too small in '%s' by %lu byte%c", + "Segment '%s' offset is too small in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); + Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } else { CfgWarning (GetSourcePos (S->LI), - "Segment '%s' start address is too low in '%s' by %lu byte%c", + "Segment '%s' start address is too low in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); + Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } } else { Addr = NewAddr; @@ -2081,14 +2090,19 @@ unsigned CfgProcess (void) /* Increment the fill level of the memory area; and, check for an ** overflow. */ - M->FillLevel = Addr + S->Seg->Size - M->Start; - if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { + FillLevel = Addr + S->Seg->Size - M->Start; + if (FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { ++Overflows; M->Flags |= MF_OVERFLOW; CfgWarning (GetSourcePos (M->LI), - "Segment '%s' overflows memory area '%s' by %lu byte%c", + "Segment '%s' overflows memory area '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), - M->FillLevel - M->Size, (M->FillLevel - M->Size == 1) ? ' ' : 's'); + FillLevel - M->Size, (FillLevel - M->Size == 1) ? "" : "s"); + } + if (FillLevel > M->FillLevel) { + /* Regular segments increase FillLevel. Overwrite segments may increase but not decrease FillLevel. */ + FillAdded = FillLevel - M->FillLevel; + M->FillLevel = FillLevel; } /* If requested, define symbols for the start and size of the @@ -2107,13 +2121,14 @@ unsigned CfgProcess (void) Addr += S->Seg->Size; /* If this segment will go out to the file, or its place - ** in the file will be filled, then increase the file size, - ** unless it's an OVERWRITE segment. + ** in the file will be filled, then increase the file size. + ** An OVERWRITE segment will only increase the size if it overlapped some of the fill area. */ if (S->Load == M && - ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0) && - (S->Flags & SF_OVERWRITE) == 0) { - M->F->Size += Addr - StartAddr; + ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0)) { + M->F->Size += (!(S->Flags & SF_OVERWRITE)) ? + (Addr - StartAddr) : + FillAdded; } } diff --git a/src/ld65/error.c b/src/ld65/error.c index 20dfd4537..344e4c1b3 100644 --- a/src/ld65/error.c +++ b/src/ld65/error.c @@ -46,6 +46,17 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Statistics */ +unsigned WarningCount = 0; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -66,6 +77,9 @@ void Warning (const char* Format, ...) fprintf (stderr, "%s: Warning: %s\n", ProgName, SB_GetConstBuf (&S)); SB_Done (&S); + + /* Count warnings */ + ++WarningCount; } diff --git a/src/ld65/error.h b/src/ld65/error.h index 75b8e0bc1..1285ca725 100644 --- a/src/ld65/error.h +++ b/src/ld65/error.h @@ -43,6 +43,17 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Statistics */ +extern unsigned WarningCount; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ diff --git a/src/ld65/filepath.c b/src/ld65/filepath.c index 1ceb2333e..c84fe6f46 100644 --- a/src/ld65/filepath.c +++ b/src/ld65/filepath.c @@ -88,13 +88,13 @@ void InitSearchPaths (void) AddSubSearchPathFromEnv (CfgDefaultPath, "CC65_HOME", "cfg"); /* Add some compiled-in search paths if defined at compile time. */ -#if defined(LD65_LIB) && !defined(_WIN32) +#if defined(LD65_LIB) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (LibDefaultPath, LD65_LIB); #endif -#if defined(LD65_OBJ) && !defined(_WIN32) +#if defined(LD65_OBJ) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (ObjDefaultPath, LD65_OBJ); #endif -#if defined(LD65_CFG) && !defined(_WIN32) +#if defined(LD65_CFG) && !defined(_WIN32) && !defined(_AMIGA) AddSearchPath (CfgDefaultPath, LD65_CFG); #endif diff --git a/src/ld65/global.c b/src/ld65/global.c index 8f43232fe..93dd65958 100644 --- a/src/ld65/global.c +++ b/src/ld65/global.c @@ -43,19 +43,20 @@ -const char* OutputName = "a.out"; /* Name of output file */ -unsigned OutputNameUsed = 0; /* Output name was used by %O */ +const char* OutputName = "a.out"; /* Name of output file */ +unsigned OutputNameUsed = 0; /* Output name was used by %O */ -unsigned ModuleId = 0; /* Id for o65 module */ +unsigned ModuleId = 0; /* Id for o65 module */ /* Start address */ -unsigned char HaveStartAddr = 0; /* Start address not given */ -unsigned long StartAddr = 0x200; /* Start address */ +unsigned char HaveStartAddr = 0; /* Start address not given */ +unsigned long StartAddr = 0x200; /* Start address */ -unsigned char VerboseMap = 0; /* Verbose map file */ -unsigned char AllowMultDef = 0; /* Allow multiple definitions */ -unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ +unsigned char VerboseMap = 0; /* Verbose map file */ +unsigned char AllowMultDef = 0; /* Allow multiple definitions */ +unsigned char LargeAlignment = 0; /* Don't warn about large alignments */ +unsigned char WarningsAsErrors = 0; /* Error if any warnings */ -const char* MapFileName = 0; /* Name of the map file */ -const char* LabelFileName = 0; /* Name of the label file */ -const char* DbgFileName = 0; /* Name of the debug file */ +const char* MapFileName = 0; /* Name of the map file */ +const char* LabelFileName = 0; /* Name of the label file */ +const char* DbgFileName = 0; /* Name of the debug file */ diff --git a/src/ld65/global.h b/src/ld65/global.h index a923f6de5..ad85daf0a 100644 --- a/src/ld65/global.h +++ b/src/ld65/global.h @@ -44,21 +44,22 @@ -extern const char* OutputName; /* Name of output file */ -extern unsigned OutputNameUsed; /* Output name was used by %O */ +extern const char* OutputName; /* Name of output file */ +extern unsigned OutputNameUsed; /* Output name was used by %O */ -extern unsigned ModuleId; /* Id for o65 module */ +extern unsigned ModuleId; /* Id for o65 module */ -extern unsigned char HaveStartAddr; /* True if start address was given */ -extern unsigned long StartAddr; /* Start address */ +extern unsigned char HaveStartAddr; /* True if start address was given */ +extern unsigned long StartAddr; /* Start address */ -extern unsigned char VerboseMap; /* Verbose map file */ -extern unsigned char AllowMultDef; /* Allow multiple definitions */ -extern unsigned char LargeAlignment; /* Don't warn about large alignments */ +extern unsigned char VerboseMap; /* Verbose map file */ +extern unsigned char AllowMultDef; /* Allow multiple definitions */ +extern unsigned char LargeAlignment; /* Don't warn about large alignments */ +extern unsigned char WarningsAsErrors; /* Error if any warnings */ -extern const char* MapFileName; /* Name of the map file */ -extern const char* LabelFileName; /* Name of the label file */ -extern const char* DbgFileName; /* Name of the debug file */ +extern const char* MapFileName; /* Name of the map file */ +extern const char* LabelFileName; /* Name of the label file */ +extern const char* DbgFileName; /* Name of the debug file */ diff --git a/src/ld65/main.c b/src/ld65/main.c index f2415a914..70e9c84d1 100644 --- a/src/ld65/main.c +++ b/src/ld65/main.c @@ -559,6 +559,15 @@ static void OptVersion (const char* Opt attribute ((unused)), +static void OptWarningsAsErrors (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Generate an error if any warnings occur */ +{ + WarningsAsErrors = 1; +} + + + static void OptMultDef (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* Set flag to allow multiple definitions of a global symbol */ @@ -637,6 +646,7 @@ static void ParseCommandLine(void) { "--start-group", 0, CmdlOptStartGroup }, { "--target", 1, CmdlOptTarget }, { "--version", 0, OptVersion }, + { "--warnings-as-errors", 0, OptWarningsAsErrors }, }; unsigned I; @@ -845,6 +855,10 @@ int main (int argc, char* argv []) (MemoryAreaOverflows > 1) ? 's' : ' '); } + if (WarningCount > 0 && WarningsAsErrors) { + Error("Warnings as errors"); + } + /* Create the output file */ CfgWriteTarget (); diff --git a/src/ld65/scanner.c b/src/ld65/scanner.c index 718951aa5..5f53e5765 100644 --- a/src/ld65/scanner.c +++ b/src/ld65/scanner.c @@ -95,6 +95,9 @@ void CfgWarning (const FilePos* Pos, const char* Format, ...) Warning ("%s:%u: %s", GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf)); SB_Done (&Buf); + + /* Count warnings */ + ++WarningCount; } diff --git a/src/ld65/segments.c b/src/ld65/segments.c index 10d2cda2c..255b8ccd1 100644 --- a/src/ld65/segments.c +++ b/src/ld65/segments.c @@ -230,7 +230,7 @@ Section* ReadSection (FILE* F, ObjData* O) "%lu. Last module requiring alignment was '%s'.", GetString (Name), Alignment, MAX_ALIGNMENT, GetObjFileName (O)); - } else if (Alignment >= LARGE_ALIGNMENT && !LargeAlignment) { + } else if (Alignment >= LARGE_ALIGNMENT && Alignment > S->Alignment && Alignment > Sec->Alignment && !LargeAlignment) { Warning ("Combined alignment for segment '%s' is suspiciously " "large (%lu). Last module requiring alignment was '%s'.", GetString (Name), Alignment, GetObjFileName (O)); diff --git a/src/sim65/6502.c b/src/sim65/6502.c index 6c23b0dfc..9d2c93da8 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -64,18 +64,12 @@ static CPURegs Regs; /* Cycles for the current insn */ static unsigned Cycles; -/* Total number of CPU cycles exec'd */ -static unsigned long TotalCycles; - /* NMI request active */ static unsigned HaveNMIRequest; /* IRQ request active */ static unsigned HaveIRQRequest; -/* flag to print cycles at program termination */ -int PrintCycles; - /*****************************************************************************/ /* Helper functions and macros */ @@ -3277,18 +3271,6 @@ unsigned ExecuteInsn (void) Handlers[CPU][OPC] (); } - /* Count cycles */ - TotalCycles += Cycles; - /* Return the number of clock cycles needed by this insn */ return Cycles; } - - - -unsigned long GetCycles (void) -/* Return the total number of cycles executed */ -{ - /* Return the total number of cycles */ - return TotalCycles; -} diff --git a/src/sim65/6502.h b/src/sim65/6502.h index f8e894567..39b995793 100644 --- a/src/sim65/6502.h +++ b/src/sim65/6502.h @@ -96,12 +96,6 @@ unsigned ExecuteInsn (void); ** executed instruction. */ -unsigned long GetCycles (void); -/* Return the total number of clock cycles executed */ - -extern int PrintCycles; -/* flag to print cycles at program termination */ - /* End of 6502.h */ diff --git a/src/sim65/error.c b/src/sim65/error.c index 441b07d2a..fc24ca006 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -41,6 +41,20 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* flag to print cycles at program termination */ +int PrintCycles = 0; + +/* cycles are counted by main.c */ +extern unsigned long long TotalCycles; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -99,3 +113,14 @@ void Internal (const char* Format, ...) va_end (ap); exit (SIM65_ERROR); } + + + +void SimExit (int Code) +/* Exit the simulation with an exit code */ +{ + if (PrintCycles) { + fprintf (stdout, "%llu cycles\n", TotalCycles); + } + exit (Code); +} diff --git a/src/sim65/error.h b/src/sim65/error.h index ea54fa048..6dbee974c 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -49,12 +49,17 @@ -#define SIM65_ERROR 0x7F -/* Does not use EXIT_FAILURE because it may overlap with test results. */ +#define SIM65_ERROR -1 +/* An error result for errors that are not part of the simulated test. +** Note that set simulated test can only return 8-bit errors 0-255. +*/ -#define SIM65_ERROR_TIMEOUT 0x7E +#define SIM65_ERROR_TIMEOUT -2 /* An error result for max CPU instructions exceeded. */ +extern int PrintCycles; +/* flag to print cycles at program termination */ + /*****************************************************************************/ @@ -75,6 +80,9 @@ void ErrorCode (int Code, const char* Format, ...) attribute((noreturn, format(p void Internal (const char* Format, ...) attribute((noreturn, format(printf,1,2))); /* Print an internal error message and die */ +void SimExit (int Code); +/* Exit the simulation with an exit code */ + /* End of error.h */ diff --git a/src/sim65/main.c b/src/sim65/main.c index f2daf9295..3c7cdc157 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -60,8 +60,14 @@ /* Name of program file */ const char* ProgramFile; -/* exit simulator after MaxCycles Cycles */ -unsigned long MaxCycles; +/* count of total cycles executed */ +unsigned long long TotalCycles = 0; + +/* exit simulator after MaxCycles Cccles */ +unsigned long long MaxCycles = 0; + +/* countdown from MaxCycles */ +unsigned long long RemainCycles; /* Header signature 'sim65' */ static const unsigned char HeaderSignature[] = { @@ -72,7 +78,6 @@ static const unsigned char HeaderSignature[] = { static const unsigned char HeaderVersion = 2; - /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -139,7 +144,7 @@ static void OptQuitXIns (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* quit after MaxCycles cycles */ { - MaxCycles = strtoul(Arg, NULL, 0); + MaxCycles = strtoull(Arg, NULL, 0); } static unsigned char ReadProgramFile (void) @@ -184,6 +189,7 @@ static unsigned char ReadProgramFile (void) } /* Get load address */ + Val2 = 0; /* suppress uninitialized variable warning */ if (((Val = fgetc(F)) == EOF) || ((Val2 = fgetc(F)) == EOF)) { Error ("'%s': Header missing load address", ProgramFile); @@ -236,6 +242,7 @@ int main (int argc, char* argv[]) unsigned I; unsigned char SPAddr; + unsigned int Cycles; /* Initialize the cmdline module */ InitCmdLine (&argc, &argv, "sim65"); @@ -298,18 +305,24 @@ int main (int argc, char* argv[]) MemInit (); SPAddr = ReadProgramFile (); - ParaVirtInit (I, SPAddr); Reset (); + RemainCycles = MaxCycles; while (1) { - ExecuteInsn (); - if (MaxCycles && (GetCycles () >= MaxCycles)) { - ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); + Cycles = ExecuteInsn (); + TotalCycles += Cycles; + if (MaxCycles) { + if (Cycles > RemainCycles) { + ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); + } + RemainCycles -= Cycles; } } - /* Return an apropriate exit code */ - return EXIT_SUCCESS; + /* Unreachable. sim65 program must exit through paravirtual PVExit + ** or timeout from MaxCycles producing an error. + */ + return SIM65_ERROR; } diff --git a/src/sim65/memory.c b/src/sim65/memory.c index 11f0be55a..68e7bb93b 100644 --- a/src/sim65/memory.c +++ b/src/sim65/memory.c @@ -46,7 +46,7 @@ /* THE memory */ -static unsigned char Mem[0x10000]; +unsigned char Mem[0x10000]; @@ -73,14 +73,6 @@ void MemWriteWord (unsigned Addr, unsigned Val) -unsigned char MemReadByte (unsigned Addr) -/* Read a byte from a memory location */ -{ - return Mem[Addr]; -} - - - unsigned MemReadWord (unsigned Addr) /* Read a word from a memory location */ { diff --git a/src/sim65/memory.h b/src/sim65/memory.h index 41cc800d3..cef786aaa 100644 --- a/src/sim65/memory.h +++ b/src/sim65/memory.h @@ -36,7 +36,9 @@ #ifndef MEMORY_H #define MEMORY_H +#include "inline.h" +extern unsigned char Mem[0x10000]; /*****************************************************************************/ /* Code */ @@ -50,8 +52,15 @@ void MemWriteByte (unsigned Addr, unsigned char Val); void MemWriteWord (unsigned Addr, unsigned Val); /* Write a word to a memory location */ -unsigned char MemReadByte (unsigned Addr); +#if defined(HAVE_INLINE) +INLINE unsigned char MemReadByte (unsigned Addr) /* Read a byte from a memory location */ +{ + return Mem[Addr]; +} +#else +#define MemReadByte(Addr) Mem[Addr] +#endif unsigned MemReadWord (unsigned Addr); /* Read a word from a memory location */ diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index db4120326..2e52d6e7e 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -63,6 +63,7 @@ /* sim65 */ #include "6502.h" +#include "error.h" #include "memory.h" #include "paravirt.h" @@ -123,11 +124,7 @@ static unsigned PopParam (unsigned char Incr) static void PVExit (CPURegs* Regs) { Print (stderr, 1, "PVExit ($%02X)\n", Regs->AC); - if (PrintCycles) { - Print (stdout, 0, "%lu cycles\n", GetCycles ()); - } - - exit (Regs->AC); + SimExit (Regs->AC); /* Error code in range 0-255. */ } @@ -166,7 +163,7 @@ static void PVArgs (CPURegs* Regs) static void PVOpen (CPURegs* Regs) { - char Path[1024]; + char Path[PVOPEN_PATH_SIZE]; int OFlag = O_INITIAL; int OMode = 0; unsigned RetVal, I = 0; @@ -183,9 +180,15 @@ static void PVOpen (CPURegs* Regs) } do { - Path[I] = MemReadByte (Name++); + if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) { + break; + } + ++I; + if (I >= PVOPEN_PATH_SIZE) { + Error("PVOpen path too long at address $%04X",Name); + } } - while (Path[I++]); + while (1); Print (stderr, 2, "PVOpen (\"%s\", $%04X)\n", Path, Flags); @@ -235,7 +238,15 @@ static void PVClose (CPURegs* Regs) Print (stderr, 2, "PVClose ($%04X)\n", FD); - RetVal = close (FD); + if (FD != 0xFFFF) { + RetVal = close (FD); + } else { + /* test/val/constexpr.c "abuses" close, expecting close(-1) to return -1. + ** This behaviour is not the same on all target platforms. + ** MSVC's close treats it as a fatal error instead and terminates. + */ + RetVal = 0xFFFF; + } SetAX (Regs, RetVal); } diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index bfa38e047..3badb50ea 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -47,6 +47,9 @@ #define PARAVIRT_BASE 0xFFF4 /* Lowest address used by a paravirtualization hook */ +#define PVOPEN_PATH_SIZE 1024 +/* Maximum path size supported by PVOpen */ + /*****************************************************************************/ diff --git a/src/sp65.vcxproj b/src/sp65.vcxproj index 1b7a18427..a9f0919a3 100644 --- a/src/sp65.vcxproj +++ b/src/sp65.vcxproj @@ -62,9 +62,11 @@ <ClCompile Include="sp65\geosicon.c" /> <ClCompile Include="sp65\input.c" /> <ClCompile Include="sp65\koala.c" /> + <ClCompile Include="sp65\lynxpalette.c" /> <ClCompile Include="sp65\lynxsprite.c" /> <ClCompile Include="sp65\main.c" /> <ClCompile Include="sp65\output.c" /> + <ClCompile Include="sp65\palconv.c" /> <ClCompile Include="sp65\palette.c" /> <ClCompile Include="sp65\pcx.c" /> <ClCompile Include="sp65\raw.c" /> @@ -84,8 +86,10 @@ <ClInclude Include="sp65\geosicon.h" /> <ClInclude Include="sp65\input.h" /> <ClInclude Include="sp65\koala.h" /> + <ClInclude Include="sp65\lynxpalette.h" /> <ClInclude Include="sp65\lynxsprite.h" /> <ClInclude Include="sp65\output.h" /> + <ClInclude Include="sp65\palconv.h" /> <ClInclude Include="sp65\palette.h" /> <ClInclude Include="sp65\pcx.h" /> <ClInclude Include="sp65\pixel.h" /> @@ -95,4 +99,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/src/sp65/bitmap.h b/src/sp65/bitmap.h index bf5e60559..e94ec71fd 100644 --- a/src/sp65/bitmap.h +++ b/src/sp65/bitmap.h @@ -75,6 +75,9 @@ struct Bitmap { unsigned Width; unsigned Height; + /* Bits per pixels */ + unsigned BPP; + /* Palette for indexed bitmap types, otherwise NULL */ Palette* Pal; @@ -179,6 +182,17 @@ INLINE unsigned GetBitmapColors (const Bitmap* B) # define GetBitmapColors(B) ((B)->Pal? (B)->Pal->Count : (1U << 24)) #endif +#if defined(HAVE_INLINE) +INLINE unsigned GetBitmapBPP (const Bitmap* B) +/* Get the bits per pixel of the converted sprite + */ +{ + return B->BPP; +} +#else +# define GetBitmapBPP(B) ((B)->BPP +#endif + /* End of bitmap.h */ diff --git a/src/sp65/lynxpalette.c b/src/sp65/lynxpalette.c new file mode 100644 index 000000000..b084af26e --- /dev/null +++ b/src/sp65/lynxpalette.c @@ -0,0 +1,93 @@ +/*****************************************************************************/ +/* */ +/* lynxpalette.c */ +/* */ +/* Lynx palette backend for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* 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> + +/* common */ +#include "attrib.h" +#include "print.h" + +/* sp65 */ +#include "attr.h" +#include "error.h" +#include "palette.h" +#include "lynxpalette.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + +StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A) +/* Generate binary output in Lynx palette format for the bitmap B. The output +** is stored in a string buffer (which is actually a dynamic char array) and +** returned. +** +*/ +{ + StrBuf* D; + const Palette* P = GetBitmapPalette (B); + const char* Format = GetAttrVal(A, "format"); + unsigned I; + + if (Format == 0) { + /* No format specified */ + } + D = NewStrBuf (); + for (I = 0; I < 16; ++I) { + + /* Get the color entry */ + const Color* C = P->Entries + I; + + /* Add the green component */ + SB_AppendChar (D, C->G >> 4); + } + for (I = 0; I < 16; ++I) { + + /* Get the color entry */ + const Color* C = P->Entries + I; + + /* Add the blue,red component */ + SB_AppendChar (D, (C->B & 0xF0) | (C->R >> 4)); + } + + /* Return the converted palette */ + return D; +} + diff --git a/src/sp65/lynxpalette.h b/src/sp65/lynxpalette.h new file mode 100644 index 000000000..b4e3defe3 --- /dev/null +++ b/src/sp65/lynxpalette.h @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* */ +/* lynxpalette.h */ +/* */ +/* Lynx palette format backend for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* 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 LYNXPALETTE_H +#define LYNXPALETTE_H + + + +/* common */ +#include "coll.h" +#include "strbuf.h" + +/* sp65 */ +#include "bitmap.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +StrBuf* GenLynxPalette (const Bitmap* B, const Collection* A); +/* Generate binary output in Lynx palette format for the bitmap B. The output +** is stored in a string buffer (which is actually a dynamic char array) and +** returned. +*/ + + + +/* End of lynxpalette.h */ + +#endif diff --git a/src/sp65/lynxsprite.c b/src/sp65/lynxsprite.c index 4d7669faf..6bd9b03de 100644 --- a/src/sp65/lynxsprite.c +++ b/src/sp65/lynxsprite.c @@ -88,12 +88,18 @@ static enum Mode GetMode (const Collection* A) } -static unsigned GetActionPointX (const Collection* A) +static unsigned GetActionPointX (const Bitmap* B, const Collection* A) /* Return the sprite mode from the attribute collection A */ { /* Check for a action point x attribute */ const char* ActionPointX = GetAttrVal (A, "ax"); if (ActionPointX) { + if (strcmp (ActionPointX, "mid") == 0) { + return GetBitmapWidth (B) / 2; + } + if (strcmp (ActionPointX, "max") == 0) { + return GetBitmapWidth (B) - 1; + } return atoi(ActionPointX); } else { return 0; @@ -101,12 +107,18 @@ static unsigned GetActionPointX (const Collection* A) } -static unsigned GetActionPointY (const Collection* A) +static unsigned GetActionPointY (const Bitmap* B, const Collection* A) /* Return the sprite mode from the attribute collection A */ { /* Check for a action point y attribute */ const char* ActionPointY = GetAttrVal (A, "ay"); if (ActionPointY) { + if (strcmp (ActionPointY, "mid") == 0) { + return GetBitmapHeight (B) / 2; + } + if (strcmp (ActionPointY, "max") == 0) { + return GetBitmapHeight (B) - 1; + } return atoi(ActionPointY); } else { return 0; @@ -125,6 +137,124 @@ static unsigned GetEdgeIndex (const Collection* A) } } +static unsigned GetQuadrant (const Collection* A) +/* Return the sprite mode from the attribute collection A */ +{ + /* Get index for edge color in shaped mode */ + const char* Quadrant = GetAttrVal (A, "quadrant"); + if (Quadrant) { + return atoi(Quadrant); + } else { + return 0; + } +} + +static void OptimizePenpal (const Bitmap* B, char *PenPal) +/* Create an optimal Penpal */ +{ + char usage[16]; + unsigned I, J, Val; + + memset(usage, 0, sizeof(usage)); + for (J = 0; J < GetBitmapHeight (B); J++) { + for (I = 0; I < GetBitmapWidth (B); I++) { + Val = GetPixel (B, I, J).Index; + if (Val < 16) { + usage[Val] = 1; + } + } + } + J = 0; + for (I = 0; I < 16; I++) { + if (usage[I]) { + switch (I) { + case 0: + PenPal[J] = '0'; + break; + case 1: + PenPal[J] = '1'; + break; + case 2: + PenPal[J] = '2'; + break; + case 3: + PenPal[J] = '3'; + break; + case 4: + PenPal[J] = '4'; + break; + case 5: + PenPal[J] = '5'; + break; + case 6: + PenPal[J] = '6'; + break; + case 7: + PenPal[J] = '7'; + break; + case 8: + PenPal[J] = '8'; + break; + case 9: + PenPal[J] = '9'; + break; + case 10: + PenPal[J] = 'a'; + break; + case 11: + PenPal[J] = 'b'; + break; + case 12: + PenPal[J] = 'c'; + break; + case 13: + PenPal[J] = 'd'; + break; + case 14: + PenPal[J] = 'e'; + break; + case 15: + PenPal[J] = 'f'; + break; + } + J++; + } + } + while (J < 16) { + PenPal[J] = 0; + J++; + } + /* printf("Penpal %s\n", PenPal); */ +} + +static unsigned GetPenpal (const Bitmap* B, const Collection* A, char *PenPal) +/* Return the penpal from the attribute collection A */ +{ + const char* Pen = GetAttrVal (A, "pen"); + if (Pen) { + if (strcmp (Pen, "opt") == 0) { + /* So we need to optimize the penpal and colour depth */ + OptimizePenpal (B, PenPal); + } else { + strncpy(PenPal, Pen, 17); + } + return 1; + } + return 0; +} + +static unsigned GetBPP (const Collection* A) +/* Return the sprite depth from the attribute collection A */ +{ + /* Get index for edge color in shaped mode */ + const char* BPP = GetAttrVal (A, "bpp"); + if (BPP) { + return atoi(BPP); + } else { + return 0; + } +} + static char OutBuffer[512]; /* The maximum size is 508 pixels */ static unsigned char OutIndex; @@ -140,26 +270,16 @@ static void AssembleByte(unsigned bits, char val) return; } /* handle end of line */ - if (bits == 8) { + if (bits == 7) { if (bit_counter != 8) { byte <<= bit_counter; OutBuffer[OutIndex++] = byte; if (!OutIndex) { Error ("Sprite is too large for the Lynx"); } - if (byte & 0x1) { - OutBuffer[OutIndex++] = byte; - if (!OutIndex) { - Error ("Sprite is too large for the Lynx"); - } - } - } - return; - } - /* handle end of line for literal */ - if (bits == 7) { - if (bit_counter != 8) { - byte <<= bit_counter; + } else { + /* Add pad byte */ + byte = 0; OutBuffer[OutIndex++] = byte; if (!OutIndex) { Error ("Sprite is too large for the Lynx"); @@ -189,28 +309,78 @@ static void AssembleByte(unsigned bits, char val) } while (--bits); } -static unsigned char ChoosePackagingMode(signed len, signed index, char ColorBits, char LineBuffer[512]) +static unsigned char AnalyseNextChunks(signed *newlen, signed len, char data[32], char ColorBits) { - --len; - if (!len) { - return 0; + char longest = 1; + unsigned char prev = 255; + char count = 0; + char index = 0; + char lindex = 0; + int i; + int literal_cost; + int packed_cost; + + for (i = 0; i < len; i++) { + index = index + 1; + if (data[i] == prev) { + count = count + 1; + if (count >= longest) { + longest = count; + lindex = index - count; + } + } else { + prev = data[i]; + count = 1; + } } - if (LineBuffer[index] != LineBuffer[index + 1]) { - return 0; + if (longest == 1) { + if (len > 16) { + *newlen = 16; + } else { + *newlen = len; + } + return 'L'; } - if (ColorBits > 2) { - return 1; + if ((lindex > 0) && (lindex + longest > 15)) { + /* We cannot pack the stride in this packet */ + *newlen = lindex; + return 'A'; } - if (LineBuffer[index] != LineBuffer[index + 2]) { - return 0; + /* Cost till end of area */ + literal_cost = 5 + lindex * ColorBits + longest * ColorBits; + packed_cost = 5 + lindex * ColorBits + 5 + ColorBits; + if (packed_cost < literal_cost) { + if (lindex == 0) { + /* Use packed data */ + if (longest > 16) { + *newlen = 16; + } else { + *newlen = longest; + } + return 'P'; + } + /* We had a good find, but it was not at the start of the line */ + *newlen = lindex; + return 'A'; } - if (ColorBits > 1) { - return 1; + /* There is no point in packing - use literal */ + if (len > 16) { + *newlen = 16; + } else { + *newlen = len; } - if (LineBuffer[index] != LineBuffer[index + 3]) { - return 0; + return 'L'; +} + +static unsigned char GetNextChunk(signed *newlen, signed len, char data[32], char ColorBits) +{ + char oper = 'A'; + + while (oper == 'A') { + oper = AnalyseNextChunks(newlen, len, data, ColorBits); + len = *newlen; } - return 1; + return oper; /* The packet type is now P or L and the length is in newlen */ } static void WriteOutBuffer(StrBuf *D) @@ -235,27 +405,25 @@ static void WriteOutBuffer(StrBuf *D) static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512], int len, int LastOpaquePixel) { /* -** The data starts with a byte count. It tells the number of bytes on this -** line + 1. -** Special case is a count of 1. It will change to next quadrant. -** Other special case is 0. It will end the sprite. -** -** Ordinary data packet. These are bits in a stream. -** 1=literal 0=packed -** 4 bit count (+1) -** for literal you put "count" values -** for packed you repeat the value "count" times -** Never use packed mode for one pixel -** If the last bit on a line is 1 you need to add a byte of zeroes -** A sequence 00000 ends a scan line -** -** All data is high nybble first -*/ + * The data starts with a byte count. It tells the number of bytes on this + * line + 1. + * Special case is a count of 1. It will change to next quadrant. + * Other special case is 0. It will end the sprite. + * + * Ordinary data packet. These are bits in a stream. + * 1=literal 0=packed + * 4 bit count (+1) + * for literal you put "count" values + * for packed you repeat the value "count" times + * Never use packed mode for one pixel + * If the last bit on a line is in use you need to add a byte of zeroes + * A sequence 00000 ends a scan line + * + * All data is high nybble first + */ unsigned char V = 0; signed i; signed count; - unsigned char differ[16]; - unsigned char *d_ptr; AssembleByte(0, 0); switch (M) { @@ -270,100 +438,46 @@ static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, WriteOutBuffer(D); break; case smPacked: + case smShaped: + if (M == smShaped) { + if (LastOpaquePixel > -1) { + if (LastOpaquePixel < len - 1) { + len = LastOpaquePixel + 1; + } + } else { + len = 0; + } + } i = 0; while (len) { - if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) { + signed analyselen; + analyselen = len; + if (analyselen > 32) { + analyselen = 32; + } + if (GetNextChunk(&count, analyselen, LineBuffer + i, ColorBits) == 'P') { /* Make runlength packet */ V = LineBuffer[i]; - ++i; - --len; - count = 0; - do { - ++count; - ++i; - --len; - } while (V == LineBuffer[i] && len && count != 15); - - AssembleByte(5, count); - AssembleByte(ColorBits, V); + i += count; + len -= count; + AssembleByte(5, count-1); + AssembleByte(ColorBits, V & ColorMask); } else { /* Make packed literal packet */ - d_ptr = differ; - V = LineBuffer[i++]; - *d_ptr++ = V; - --len; - count = 0; - while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) { - V = LineBuffer[i++]; - *d_ptr++ = V; - ++count; - --len; - } - - AssembleByte(5, count | 0x10); - d_ptr = differ; + AssembleByte(5, (count-1) | 0x10); do { - AssembleByte(ColorBits, *d_ptr++); - } while (--count >= 0); - + AssembleByte(ColorBits, LineBuffer[i]); + i++; + len--; + } while (--count > 0); } } - AssembleByte(8, 0); + /* Force EOL for shaped? AssembleByte(5, 0); */ + AssembleByte(7, 0); /* Write the buffer to file */ WriteOutBuffer(D); break; - - case smShaped: - if (LastOpaquePixel > -1) { - if (LastOpaquePixel < len - 1) { - len = LastOpaquePixel + 1; - } - i = 0; - while (len) { - if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) { - /* Make runlength packet */ - V = LineBuffer[i]; - ++i; - --len; - count = 0; - do { - ++count; - ++i; - --len; - } while (V == LineBuffer[i] && len && count != 15); - - AssembleByte(5, count); - AssembleByte(ColorBits, V); - - } else { - /* Make packed literal packet */ - d_ptr = differ; - V = LineBuffer[i++]; - *d_ptr++ = V; - --len; - count = 0; - while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) { - V = LineBuffer[i++]; - *d_ptr++ = V; - ++count; - --len; - } - - AssembleByte(5, count | 0x10); - d_ptr = differ; - do { - AssembleByte(ColorBits, *d_ptr++); - } while (--count >= 0); - - } - } - AssembleByte(5, 0); - AssembleByte(8, 0); - /* Write the buffer to file */ - WriteOutBuffer(D); - } - break; } } @@ -373,10 +487,10 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) ** returned. ** ** The Lynx will draw 4 quadrants: -** - Down right -** - Up right -** - Up left -** - Down left +** 0 - Down right +** 1 - Up right +** 2 - Up left +** 3 - Down left ** ** The sprite will end with a byte 0. */ @@ -388,13 +502,24 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) char ColorBits; char ColorMask; char EdgeIndex; + char Quadrant; + char quad; + char BPP; + /* The default mapping is 1:1 plus extra colours become 0 */ + char Map[17] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0}; + signed PenColors; + char PenPal[18]; + signed Val; /* Get EdgeIndex */ EdgeIndex = GetEdgeIndex (A); + /* Get Quadrant for starting the draw process */ + Quadrant = GetQuadrant (A) & 3; + /* Action point of the sprite */ - OX = GetActionPointX (A); - OY = GetActionPointY (A); + OX = GetActionPointX (B, A); + OY = GetActionPointY (B, A); if (OX >= GetBitmapWidth (B)) { Error ("Action point X cannot be larger than bitmap width"); } @@ -410,145 +535,293 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Get the sprite mode */ M = GetMode (A); - /* Now check if bitmap indexes are ok */ - if (GetBitmapColors (B) > 16) { - Error ("Too many colors for a Lynx sprite"); + /* Now check how to do the mapping */ + if (GetPenpal (B, A, &PenPal[0])) { + signed I; + + /* Modify the map by content of PenPal */ + PenColors = strlen(PenPal); + for (I = 0; I < PenColors; I++) { + switch (PenPal[I]) { + case '0': + Map[0] = I; + break; + case '1': + Map[1] = I; + break; + case '2': + Map[2] = I; + break; + case '3': + Map[3] = I; + break; + case '4': + Map[4] = I; + break; + case '5': + Map[5] = I; + break; + case '6': + Map[6] = I; + break; + case '7': + Map[7] = I; + break; + case '8': + Map[8] = I; + break; + case '9': + Map[9] = I; + break; + case 'a': + case 'A': + Map[10] = I; + break; + case 'b': + case 'B': + Map[11] = I; + break; + case 'c': + case 'C': + Map[12] = I; + break; + case 'd': + case 'D': + Map[13] = I; + break; + case 'e': + case 'E': + Map[14] = I; + break; + case 'f': + case 'F': + Map[15] = I; + break; + /* The X is reserved as transparency. This allows for shaped sprites */ + case 'x': + case 'X': + Map[16] = I; + break; + } + } + } else { + PenColors = GetBitmapColors (B); } ColorBits = 4; - ColorMask = 0x0f; - if (GetBitmapColors (B) < 9) { + if (PenColors < 9) { ColorBits = 3; - ColorMask = 0x07; } - if (GetBitmapColors (B) < 5) { + if (PenColors < 5) { ColorBits = 2; - ColorMask = 0x03; } - if (GetBitmapColors (B) < 3) { + if (PenColors < 3) { ColorBits = 1; - ColorMask = 0x01; } + BPP = GetBPP (A); + if (BPP > 0) { + ColorBits = BPP; + } + switch (ColorBits) { + case 1: + ColorMask = 0x01; + break; + case 2: + ColorMask = 0x03; + break; + case 3: + ColorMask = 0x07; + break; + default: + case 4: + ColorMask = 0x0f; + break; + } + /* B->BPP = ColorBits; */ /* Create the output buffer and resize it to the required size. */ D = NewStrBuf (); SB_Realloc (D, 63); - /* Convert the image for quadrant bottom right */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + for (quad = 0; quad < 4; quad++) { + switch ((Quadrant + quad) & 3) { + case 0: + /* Convert the image for quadrant bottom right */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + LineBuffer[i] = Map[Val] & ColorMask; - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } - ++i; - } + if ((OY == 0) && (OX == 0)) { + /* Trivial case only one quadrant */ - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } + /* Mark end of sprite */ + SB_AppendChar (D, 0); - if ((OY == 0) && (OX == 0)) { - /* Trivial case only one quadrant */ - - /* Mark end of sprite */ - SB_AppendChar (D, 0); - - /* Return the converted bitmap */ - return D; - } - - /* Next quadrant */ - SB_AppendChar (D, 1); - - /* Convert the image for quadrant top right */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ - - /* Fill the LineBuffer for easier optimisation */ - for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; + /* Return the converted bitmap */ + return D; } - ++i; - } + if ((quad == 1) && (OY == 0)) { + /* Special case only two quadrants */ - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } + /* Mark end of sprite */ + SB_AppendChar (D, 0); - if (OX == 0) { - /* Special case only two quadrants */ - - /* Mark end of sprite */ - SB_AppendChar (D, 0); - - /* Return the converted bitmap */ - return D; - } - - /* Next quadrant */ - SB_AppendChar (D, 1); - - /* Convert the image for quadrant top left */ - for (Y = OY - 1; Y >= 0; --Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ - - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; + /* Return the converted bitmap */ + return D; } - ++i; - } + break; + case 1: + /* Convert the image for quadrant top right */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); - } + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < (signed)GetBitmapWidth (B); ++X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; - /* Next quadrant */ - SB_AppendChar (D, 1); + LineBuffer[i] = Map[Val] & ColorMask; - /* Convert the image for quadrant bottom left */ - for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { - signed i = 0; - signed LastOpaquePixel = -1; - char LineBuffer[512]; /* The maximum size is 508 pixels */ + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } - /* Fill the LineBuffer for easier optimisation */ - for (X = OX - 1; X >= 0; --X) { - - /* Fetch next bit into byte buffer */ - LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; - - if (LineBuffer[i] != EdgeIndex) { - LastOpaquePixel = i; + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } - ++i; + + if ((OY == GetBitmapHeight (B) - 1) && (OX == 0)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == 0)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + case 2: + /* Convert the image for quadrant top left */ + for (Y = OY - 1; Y >= 0; --Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + + LineBuffer[i] = Map[Val] & ColorMask; + + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } + + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == GetBitmapHeight (B) - 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OY == GetBitmapHeight (B) - 1)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + case 3: + /* Convert the image for quadrant bottom left */ + for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) { + signed i = 0; + signed LastOpaquePixel = -1; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX - 1; X >= 0; --X) { + /* Fetch next bit into byte buffer */ + Val = GetPixel (B, X, Y).Index; + if (Val > 16) Val = 16; + + LineBuffer[i] = Map[Val] & ColorMask; + + if (Val != EdgeIndex) { + LastOpaquePixel = i; + } + ++i; + } + + encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); + } + if ((OY == 0) && (OX == GetBitmapWidth (B) - 1)) { + /* Trivial case only one quadrant */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + if ((quad == 1) && (OX == GetBitmapWidth (B) - 1)) { + /* Special case only two quadrants */ + + /* Mark end of sprite */ + SB_AppendChar (D, 0); + + /* Return the converted bitmap */ + return D; + } + break; + } + if (quad < 3) { + /* Next quadrant */ + SB_AppendChar (D, 1); + } else { + /* End sprite */ + SB_AppendChar (D, 0); } - - encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel); } - /* End sprite */ - SB_AppendChar (D, 0); - /* Return the converted bitmap */ return D; } diff --git a/src/sp65/lynxsprite.h b/src/sp65/lynxsprite.h index fe686ec8e..e700b4f46 100644 --- a/src/sp65/lynxsprite.h +++ b/src/sp65/lynxsprite.h @@ -54,9 +54,9 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A); -/* Generate binary output in packed Lynx sprite format for the bitmap B. The output -** is stored in a string buffer (which is actually a dynamic char array) and -** returned. +/* Generate binary output in packed Lynx sprite format for the bitmap B. +** The output is stored in a string buffer (which is actually a dynamic +** char array) and returned. */ diff --git a/src/sp65/main.c b/src/sp65/main.c index 1dda696d6..62dec0952 100644 --- a/src/sp65/main.c +++ b/src/sp65/main.c @@ -1,6 +1,6 @@ /*****************************************************************************/ /* */ -/* main.c */ +/* main.c */ /* */ /* Main program of the sp65 sprite and bitmap utility */ /* */ @@ -47,6 +47,7 @@ /* sp65 */ #include "attr.h" #include "convert.h" +#include "palconv.h" #include "error.h" #include "input.h" #include "output.h" @@ -68,10 +69,13 @@ static Bitmap* C; /* Output data from convertion */ static StrBuf* D; +/* Output data from palconv */ +static StrBuf* E; + /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ @@ -88,11 +92,11 @@ static void Usage (void) " -lc\t\t\t\tList all possible conversions\n" " -r file[,attrlist]\t\tRead an input file\n" " -v\t\t\t\tIncrease verbosity\n" + " -p tgt,file[,attrlist]\t\tWrite the palette to a file\n" " -w file[,attrlist]\t\tWrite the output to a file\n" "\n" "Long options:\n" " --convert-to fmt[,attrlist]\tConvert into target format\n" - " --dump-palette\t\tDump palette as table\n" " --help\t\t\tHelp (this text)\n" " --list-conversions\t\tList all possible conversions\n" " --pop\t\t\t\tRestore the original loaded image\n" @@ -100,6 +104,7 @@ static void Usage (void) " --slice x,y,w,h\t\tGenerate a slice from the loaded bitmap\n" " --verbose\t\t\tIncrease verbosity\n" " --version\t\t\tPrint the version number and exit\n" + " --palette tgt,file[,attrlist]\tWrite the palette to a file\n" " --write file[,attrlist]\tWrite the output to a file\n", ProgName); } @@ -137,6 +142,21 @@ static void SetOutputData (StrBuf* N) } +static void SetPalOutputData (StrBuf* N) +/* Delete the old output data and replace it by the given one. The new one +** may be NULL to clear it. +*/ +{ + /* Delete the old output data */ + if (E != 0) { + FreeStrBuf (E); + } + + /* Set the new one */ + E = N; +} + + static void OptConvertTo (const char* Opt attribute ((unused)), const char* Arg) /* Convert the bitmap into a target format */ @@ -282,15 +302,45 @@ static void OptVerbose (const char* Opt attribute ((unused)), static void OptVersion (const char* Opt attribute ((unused)), - const char* Arg attribute ((unused))) + const char* Arg attribute ((unused))) /* Print the assembler version */ { fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ()); - exit(EXIT_SUCCESS); } +static void OptPalette (const char* Opt attribute ((unused)), const char* Arg) +/* Write an output file */ +{ + static const char* const NameList[] = { + "target", "name", "format" + }; + + + /* Parse the argument */ + Collection* A = ParseAttrList (Arg, NameList, 2); + + /* We must have a bitmap ... */ + if (C == 0) { + Error ("No bitmap"); + } + + /* ... which must be indexed */ + if (!BitmapIsIndexed (C)) { + Error ("Current bitmap is not indexed"); + } + + /* Convert the palette */ + SetPalOutputData (PaletteTo (C, A)); + + /* Write the file */ + WriteOutputFile (E, A, C); + + /* Delete the attribute list */ + FreeAttrList (A); +} + static void OptWrite (const char* Opt attribute ((unused)), const char* Arg) /* Write an output file */ { @@ -381,6 +431,10 @@ int main (int argc, char* argv []) OptVerbose (Arg, 0); break; + case 'p': + OptPalette (Arg, GetArg (&I, 2)); + break; + case 'w': OptWrite (Arg, GetArg (&I, 2)); break; diff --git a/src/sp65/palconv.c b/src/sp65/palconv.c new file mode 100644 index 000000000..e92f3c22e --- /dev/null +++ b/src/sp65/palconv.c @@ -0,0 +1,104 @@ +/*****************************************************************************/ +/* */ +/* palconv.c */ +/* */ +/* Color palette conversions for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* */ +/* 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 <string.h> +#include <stdlib.h> + +/* common */ +#include "check.h" +#include "xmalloc.h" + +/* sp65 */ +#include "attr.h" +#include "error.h" +#include "palette.h" +#include "lynxpalette.h" + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + +/* Type of the entry in the palette table */ +typedef struct PaletteMapEntry PaletteMapEntry; +struct PaletteMapEntry { + const char* Format; + StrBuf* (*PaletteFunc) (const Bitmap*, const Collection*); +}; + +/* Converter table, alphabetically sorted */ +static const PaletteMapEntry PaletteMap[] = { + { "lynx-palette", GenLynxPalette }, +}; + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + +static int Compare (const void* Key, const void* MapEntry) +/* Compare function for bsearch */ +{ + return strcmp (Key, ((const PaletteMapEntry*) MapEntry)->Format); +} + + + +StrBuf* PaletteTo (const Bitmap* B, const Collection* A) +/* Convert the palette of bitmap B into some sort of other binary format. +** The output is stored in a string buffer (which is actually a dynamic +** char array) and returned. The actual output format is taken from the +** "format" attribute in the attribute collection A. +*/ +{ + const PaletteMapEntry* E; + + /* Get the format to convert to */ + const char* Format = NeedAttrVal (A, "target", "palette"); + + /* Search for the matching converter */ + E = bsearch (Format, + PaletteMap, + sizeof (PaletteMap) / sizeof (PaletteMap[0]), + sizeof (PaletteMap[0]), + Compare); + if (E == 0) { + Error ("No such target format: '%s'", Format); + } + + /* Do the conversion */ + return E->PaletteFunc (B, A); +} + diff --git a/src/sp65/palconv.h b/src/sp65/palconv.h new file mode 100644 index 000000000..693281c20 --- /dev/null +++ b/src/sp65/palconv.h @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* */ +/* palconv.h */ +/* */ +/* Color palette conversions for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2022, Karri Kaksonen */ +/* */ +/* 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 PALCONV_H +#define PALCONV_H + + + +#include <stdio.h> + +/* common */ +#include "coll.h" +#include "strbuf.h" + +/* sp65 */ +#include "bitmap.h" + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + +StrBuf* PaletteTo (const Bitmap* B, const Collection* A); +/* Convert the palette of bitmap B into some sort of other binary format. +** The output is stored in a string buffer (which is actually a dynamic char +** array) and returned. The actual output format is taken from the "target" +** attribute in the attribute collection A. +*/ + +void ListPaletteTargets (FILE* F); +/* Output a list of palette targets */ + +/* End of palette.h */ + +#endif + + + diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index d721671b3..e6d99b30b 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -153,11 +153,12 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) P->Compressed, Name); } /* We support: - ** - one plane with either 1 or 8 bits per pixel + ** - one plane with either 1, 4 or 8 bits per pixel ** - three planes with 8 bits per pixel ** - four planes with 8 bits per pixel (does this exist?) */ if (!((P->BPP == 1 && P->Planes == 1) || + (P->BPP == 4 && P->Planes == 1) || (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) { /* We could support others, but currently we don't */ Error ("Unsupported PCX format: %u planes, %u bpp in PCX file '%s'", @@ -204,11 +205,14 @@ static void DumpPCXHeader (const PCXHeader* P, const char* Name) static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) /* Read one (possibly compressed) plane from the file */ { - if (P->Compressed) { + unsigned i; + if (P->Compressed) { /* Uncompress RLE data */ - unsigned Remaining = P->Width; - while (Remaining) { + signed Remaining = P->BytesPerPlane; + signed WidthCounter = P->Width; + + while (Remaining > 0) { unsigned char C; @@ -224,21 +228,111 @@ static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) } /* Write the data to the buffer */ - if (C > Remaining) { - C = Remaining; + switch (P->BPP) { + default: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = B; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 4: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = B >> 4; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 15; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 2: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = (B >> 6) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 4) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 2) & 3; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 3; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; + case 1: + for (i = 0; i < C; i++) { + if (WidthCounter > 0) { + *L = (B >> 7) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 6) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 5) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 4) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 3) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 2) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = (B >> 1) & 1; + L += 1; + WidthCounter -= 1; + } + if (WidthCounter > 0) { + *L = B & 1; + L += 1; + WidthCounter -= 1; + } + Remaining -= 1; + } + break; } - memset (L, B, C); - - /* Bump counters */ - L += C; - Remaining -= C; - } } else { - /* Just read one line */ - ReadData (F, L, P->Width); - + if (P->BPP == 4) { + printf("Not implemented\n"); + } else { + ReadData (F, L, P->Width); + } } } diff --git a/targettest/Makefile b/targettest/Makefile index 0450bfd4e..4d989d0df 100644 --- a/targettest/Makefile +++ b/targettest/Makefile @@ -693,6 +693,17 @@ EXELIST_bbc = \ EXELIST_lunix = \ notavailable +# omitted: arg-test clock-test clock cpeek-test conio cprintf cursor deb dir-test +# em-test exec-test1 exec-test2 fileio-test ft getopt-test heaptest joy-test +# mouse-test mul-test posixio-test rename-test scanf-test seek ser-test strdup-test +# stroserror-test uname-test +EXELIST_kim1 = \ + minimal \ + div-test \ + moddiv-test \ + strnlen \ + strqtok-test + # Unlisted targets will try to build everything. # That lets us learn what they cannot build, and what settings # we need to use for programs that can be built and run. @@ -742,6 +753,7 @@ TARGETS := \ creativision \ cx16 \ gamate \ + kim1 \ lunix \ lynx \ nes \ @@ -761,6 +773,7 @@ TARGETS := \ define TARGET_recipe +@echo making targettest for: $(T) @$(MAKE) -j2 SYS:=$(T) @$(MAKE) --no-print-directory clean SYS:=$(T) @@ -808,6 +821,11 @@ testcode.d64: testcode $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) # $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) +testcode.d81: testcode + @$(C1541) -format testcode,AA d81 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) + # -------------------------------------------------------------------------- # Rule to make an Apple II disk with all testcode. Needs the AppleCommander # program, available at https://applecommander.github.io/, and a template disk diff --git a/targettest/atari/Makefile b/targettest/atari/Makefile index dd4f6078f..d5b4d9593 100644 --- a/targettest/atari/Makefile +++ b/targettest/atari/Makefile @@ -39,6 +39,7 @@ EXELIST_atari = \ multi.xex \ ostype.xex \ scrcode.com \ + sound.xex \ sys.xex ifneq ($(EXELIST_$(SYS)),) @@ -74,7 +75,8 @@ scrcode.com: scrcode.s $(CL) -t atari -C atari-asm.cfg -o scrcode.com scrcode.s sys.xex: sys.c $(CL) -t atari -o sys.xex sys.c - +sound.xex: sound.c + $(CL) -t atari -o sound.xex sound.c clean: @$(DEL) charmapping.xex 2>$(NULLDEV) @$(DEL) defdev.xex 2>$(NULLDEV) @@ -85,3 +87,4 @@ clean: @$(DEL) scrcode.o 2>$(NULLDEV) @$(DEL) scrcode.com 2>$(NULLDEV) @$(DEL) sys.xex 2>$(NULLDEV) + @$(DEL) sound.xex 2>$(NULLDEV) diff --git a/targettest/atari/sound.c b/targettest/atari/sound.c new file mode 100644 index 000000000..d1c50e1b4 --- /dev/null +++ b/targettest/atari/sound.c @@ -0,0 +1,20 @@ +/* +** Test program for _sound for atari +** +** January 6 2023 Mariano Domínguez +*/ + +#include <stdio.h> +#include <conio.h> +#include <atari.h> +#include <cc65.h> + +int main(void) +{ + int i=0; + printf("playing sound \n"); + _sound(1,121,10,15); //voice, pitch, distortion, volume + for(i=0;i<9000;i++); + _sound(1,0,0,0); //silencing, same as Atari Basic + return 0; +} diff --git a/targettest/cbm/Makefile b/targettest/cbm/Makefile index 47ae5171d..c3171b13e 100644 --- a/targettest/cbm/Makefile +++ b/targettest/cbm/Makefile @@ -2,6 +2,10 @@ # var. to build for another target system. SYS ?= c64 +# This one comes with the VICE emulator. +# See http://vice-emu.sourceforge.net/ +C1541 ?= c1541 + # Just the usual way to find out if we're # using cmd.exe to execute make rules. ifneq ($(shell echo),) @@ -32,11 +36,20 @@ endif EXELIST_c64 = \ petscii.prg \ - cbmdir-test.prg + cbmdir-test.prg \ + cbmread.prg + +EXTRALIST_c64 = \ + read-0 \ + read-1 \ + read-8 EXELIST_vic20 = \ cbmdir-test.prg +DISK_c64 = testcode.d64 +DISK_vic20 = testcode.d64 + ifneq ($(EXELIST_$(SYS)),) testcode: $(EXELIST_$(SYS)) else @@ -66,5 +79,38 @@ else $(CL) -t $(SYS) -Oris -o $@ $< endif +cbmread.prg: cbmread.c +ifeq ($(SYS),vic20) + $(CL) -t $(SYS) -C vic20-32k.cfg -Oris -o $@ $< +else + $(CL) -t $(SYS) -Oris -o $@ $< +endif + +# -------------------------------------------------------------------------- +# Rule to make a CBM disk with all testcode. Needs the c1541 program that comes +# with the VICE emulator. + +define D64_WRITE_PRG_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" "$(subst ?,$(SPACE),$(subst .prg,,$(notdir $(file))))",p >$(NULLDEV) + +endef # D64_WRITE_PRG_recipe + +define D64_WRITE_SEQ_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV) + +endef # D64_WRITE_SEQ_recipe + +disk: $(DISK_$(SYS)) + +testcode.d64: testcode + @$(C1541) -format "testcode,00" d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(EXTRALIST_$(SYS)),$(D64_WRITE_SEQ_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) + clean: @$(DEL) *.lbl petscii.prg cbmdir-test.prg 2>$(NULLDEV) + @$(DEL) $(DISK_c64) + @$(DEL) $(DISK_vic20) diff --git a/targettest/cbm/cbmread.c b/targettest/cbm/cbmread.c new file mode 100644 index 000000000..b48410e00 --- /dev/null +++ b/targettest/cbm/cbmread.c @@ -0,0 +1,82 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <cbm.h> + +int res = 0; +int tests = 0; + +#define LFN 8 + +#define MAXREADSIZE 256 + +static char data[MAXREADSIZE]; + +void test1 (char * what, char * filename, int len1, int len2, int rlen1, int rlen2) +{ + int rlen; + unsigned char err; + + printf ("1:%s (%d bytes)..." , what, rlen1); + + err = cbm_open (LFN, 8, 8, filename); + if (err != 0) { + printf ("\nError: could not open file.\n"); + goto test1exit; + } + + rlen = cbm_read (LFN, data, rlen1); + if (rlen == -1) { + printf ("\ncbm_read error (%d)\n", _oserror); + res++; + goto test1exit; + } + if (rlen != len1) { + printf ("\nError: 1st read returned %d instead of %d\n", rlen, len1); + res++; + goto test1exit; + } + + printf (" OK\n"); + + printf ("2:%s (%d bytes)..." , what, rlen2); + + rlen = cbm_read (LFN, data, rlen2); + if (rlen == -1) { + printf ("\ncbm_read error (%d)\n", _oserror); + res++; + goto test1exit; + } + if (rlen != len2) { + printf ("\nError: 2nd read returned %d instead of %d\n", rlen, len2); + res++; + goto test1exit; + } + + printf (" OK\n"); + +test1exit: + cbm_close (LFN); + tests++; +} + +int main(void) +{ + test1 ("Read empty file", "read-0", 0, 0, 0, 0); + test1 ("Read empty file", "read-0", 0, 0, 1, 1); + test1 ("Read empty file", "read-0", 0, 0, 8, 8); + + test1 ("Read 1 byte file", "read-1", 0, 0, 0, 0); + test1 ("Read 1 byte file", "read-1", 1, 0, 1, 1); + test1 ("Read 1 byte file", "read-1", 1, 0, 8, 8); + + test1 ("Read 8 byte file", "read-8", 0, 0, 0, 0); + test1 ("Read 8 byte file", "read-8", 1, 1, 1, 1); + test1 ("Read 8 byte file", "read-8", 4, 4, 4, 4); + test1 ("Read 8 byte file", "read-8", 8, 0, 8, 8); + test1 ("Read 8 byte file", "read-8", 8, 0,10, 1); + + printf ("%d/%d Tests failed.\n", res, tests); + return res; +} diff --git a/test/asm/listing/ref/032-assert-error5.ld65err-ref b/targettest/cbm/read-0 similarity index 100% rename from test/asm/listing/ref/032-assert-error5.ld65err-ref rename to targettest/cbm/read-0 diff --git a/targettest/cbm/read-1 b/targettest/cbm/read-1 new file mode 100644 index 000000000..7371f47a6 --- /dev/null +++ b/targettest/cbm/read-1 @@ -0,0 +1 @@ +B \ No newline at end of file diff --git a/targettest/cbm/read-8 b/targettest/cbm/read-8 new file mode 100644 index 000000000..42ec1bdb8 --- /dev/null +++ b/targettest/cbm/read-8 @@ -0,0 +1 @@ +Gª»ÌÝ \ No newline at end of file diff --git a/targettest/conio.c b/targettest/conio.c index 49434595b..efe82d7c6 100644 --- a/targettest/conio.c +++ b/targettest/conio.c @@ -130,9 +130,11 @@ void main(void) case CH_ENTER: clrscr(); return; +#ifdef CH_CURS_LEFT case CH_CURS_LEFT: inpos = (inpos - 1) % 8; break; +#endif case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': (void)textcolor(i - '0'); @@ -164,7 +166,9 @@ void main(void) default: cputc(i); /* fallthrough */ +#ifdef CH_CURS_RIGHT case CH_CURS_RIGHT: +#endif inpos = (inpos + 1) % 8; } #endif diff --git a/targettest/em-test.c b/targettest/em-test.c index 2da9138e1..6f887bdcc 100644 --- a/targettest/em-test.c +++ b/targettest/em-test.c @@ -96,6 +96,7 @@ static emd_test_t drivers[] = { { '8', "C64DTV himem", "dtv-himem.emd" }, { '9', "65816 extra banks", "c64-65816.emd" }, { 'k', "Kerberos", "c64-kerberos.emd" }, + { 'r', "Retro Replay RAM", "c64-rrr.emd" }, #endif #if defined(__C128__) diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile index f757c3062..89abca6b6 100644 --- a/targettest/pce/Makefile +++ b/targettest/pce/Makefile @@ -73,4 +73,7 @@ endif dd if=$< bs=8K count=${COUNT} >> $@ clean: - @$(DEL) conio.o conio.??? 2>$(NULLDEV) + @$(DEL) conio.o 2>$(NULLDEV) + @$(DEL) conio.pce 2>$(NULLDEV) + @$(DEL) conio.bin 2>$(NULLDEV) + @$(DEL) conio.map 2>$(NULLDEV) diff --git a/test/Makefile b/test/Makefile index abc70d58f..22e425c9c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,6 +22,7 @@ continue: @$(MAKE) -C val all @$(MAKE) -C ref all @$(MAKE) -C err all + @$(MAKE) -C standard all @$(MAKE) -C misc all @$(MAKE) -C todo all @@ -31,6 +32,7 @@ mostlyclean: @$(MAKE) -C val clean @$(MAKE) -C ref clean @$(MAKE) -C err clean + @$(MAKE) -C standard clean @$(MAKE) -C misc clean @$(MAKE) -C todo clean diff --git a/test/asm/Makefile b/test/asm/Makefile index 3481dae78..dea53f6b2 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -12,7 +12,7 @@ endif WORKDIR = ../testwrk/asm -SUBDIRS = cpudetect opcodes listing val err +SUBDIRS = cpudetect opcodes listing val err misc .PHONY: all continue mostlyclean clean diff --git a/test/asm/err/endmacro.s b/test/asm/err/endmacro.s new file mode 100644 index 000000000..8907675fb --- /dev/null +++ b/test/asm/err/endmacro.s @@ -0,0 +1,6 @@ +; for PR #2013 +; should produce error output: +; ... Error: '.ENDMACRO' expected for macro 'test' + +.macro test + nop .endmacro diff --git a/test/asm/listing/108-long-rts.s b/test/asm/listing/108-long-rts.s new file mode 100644 index 000000000..a8bf860cd --- /dev/null +++ b/test/asm/listing/108-long-rts.s @@ -0,0 +1,8 @@ +; test of long-rts promotion + +.p816 +.feature long_jsr_jmp_rts +.smart + +.proc farproc : far + rts ; should be $6B (RTL) and not $60 (RTS) +.endproc diff --git a/test/asm/listing/109-brk-signature.s b/test/asm/listing/109-brk-signature.s new file mode 100644 index 000000000..e9c23dd63 --- /dev/null +++ b/test/asm/listing/109-brk-signature.s @@ -0,0 +1,45 @@ +; test of optional signature byte for BRK on all 6502-derived CPUs +; and also COP on 65C816 + +.setcpu "6502" +brk ; 1 byte +brk 0 ; 2 bytes +brk $60 ; 2 bytes +brk #$60 ; 2 bytes + +.setcpu "6502X" +brk +brk $60 +brk #$60 + +.setcpu "6502DTV" +brk +brk $60 +brk #$60 + +.setcpu "65SC02" +brk +brk $60 +brk #$60 + +.setcpu "65816" +brk +brk $60 +brk #$60 +cop +cop $60 +cop #$60 +; WDM is a NOP that gives +2 PC, probably not useful to make its signature byte optional +;wdm +wdm $60 +wdm #$60 + +.setcpu "4510" +brk +brk $60 +brk #$60 + +.setcpu "HuC6280" +brk +brk $60 +brk #$60 diff --git a/test/asm/listing/200-overwrite.cfg b/test/asm/listing/200-overwrite.cfg new file mode 100644 index 000000000..b7b2fee49 --- /dev/null +++ b/test/asm/listing/200-overwrite.cfg @@ -0,0 +1,20 @@ +MEMORY +{ + A: start = 0, size = 8, file = %O, fill = yes, fillval = $33; + B: start = 8, size = 8, file = %O, fill = yes, fillval = $44; + C: start = 0, size = 8, file = %O, fill = yes, fillval = $55; + D: start = 8, size = 8, file = %O, fill = no, fillval = $66; +} +SEGMENTS +{ + A: load = A, type = ro; + B: load = B, type = ro; + C0: load = C, type = ro; + C1: load = C, type = ro, start = 5; + D: load = D, type = ro; + + AO: load = A, type = overwrite, start = 4; + BO: load = B, type = overwrite, start = 8+5; + CO: load = C, type = overwrite, start = 2; + DO: load = D, type = overwrite, start = 8+4; +} diff --git a/test/asm/listing/200-overwrite.s b/test/asm/listing/200-overwrite.s new file mode 100644 index 000000000..cd2349c6a --- /dev/null +++ b/test/asm/listing/200-overwrite.s @@ -0,0 +1,29 @@ +; verification of overwrite segment feature +; See: https://github.com/cc65/cc65/issues/1366 + +; A: full memory area which is overwritten to the end +.segment "A" +.byte 0,1,2,3,4,5,6,7 +.segment "AO" +.byte $24,$25,$26,$27 + +; B: incomplete memory area overwritten in the fill area +.segment "B" +.byte 0,1,2 +.segment "BO" +.byte $25,$26 + +; C: memory area with gap overwritten across the gap +.segment "C0" +.byte 0,1,2 +.segment "C1" +.byte 5,6,7 +.segment "CO" +.byte $22,$23,$24,$25 + +; D: incomplete memory area without fill, +; but overwrite extends past existing segments +.segment "D" +.byte 0,1,2 +.segment "DO" +.byte $24,$25 diff --git a/test/asm/listing/201-overwrite-overflow.cfg b/test/asm/listing/201-overwrite-overflow.cfg new file mode 100644 index 000000000..cada57d5a --- /dev/null +++ b/test/asm/listing/201-overwrite-overflow.cfg @@ -0,0 +1,12 @@ +MEMORY +{ + A: start = 0, size = 8, file = %O, fill = yes, fillval = $33; + B: start = 8, size = 8, file = %O, fill = yes, fillval = $44; +} +SEGMENTS +{ + A: load = A, type = ro; + B: load = B, type = ro; + AO: load = A, type = overwrite, start = 6; + BO: load = B, type = overwrite, start = 8+6; +} diff --git a/test/asm/listing/201-overwrite-overflow.s b/test/asm/listing/201-overwrite-overflow.s new file mode 100644 index 000000000..156388ab5 --- /dev/null +++ b/test/asm/listing/201-overwrite-overflow.s @@ -0,0 +1,13 @@ +; verification of overwrite segment overflow cases + +; error: overflow past end of A memory area +.segment "A" +.byte 0,1,2,3 +.segment "AO" +.byte $26,$27,$28 + +; error: overflow past end of B memory area +.segment "B" +.byte 0,1,2,3 +.segment "BO" +.byte $26,$27,$28 diff --git a/test/asm/listing/Makefile b/test/asm/listing/Makefile index d3dc21409..23aa3969c 100644 --- a/test/asm/listing/Makefile +++ b/test/asm/listing/Makefile @@ -5,25 +5,31 @@ ifneq ($(shell echo),) endif ifdef CMD_EXE + S = $(subst /,\,/) EXE = .exe MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /q /s $(subst /,\,$1) + TRUE = exit 0 + CAT = type $(subst /,\,$1) else + S = / EXE = MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 + TRUE = true + CAT = cat $1 endif ifdef QUIET # .SILENT: endif -CA65 := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) -LD65 := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) WORKDIR = ../../../testwrk/asm/listing -ISEQUAL = ../../../testwrk/isequal$(EXE) +ISEQUAL = ..$S..$S..$Stestwrk$Sisequal$(EXE) CC = gcc CFLAGS = -O2 @@ -50,14 +56,22 @@ $(WORKDIR)/$1.bin: $1.s $(ISEQUAL) # compile without generating listing ifeq ($(wildcard control/$1.err),) - $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1 + $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2) ifeq ($(wildcard control/$1.no-ld65),) - $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1 +ifeq ($(wildcard $1.cfg),) + $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) +else + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) +endif endif else - $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1 || true + $(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2> $$(@:.bin=.err2) || $(TRUE) ifeq ($(wildcard control/$1.no-ld65),) - $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1 || true +ifeq ($(wildcard $1.cfg),) + $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) +else + $(LD65) -C $$(<:.s=.cfg) -o $$@ $$(@:.bin=.o) > $$(@:.bin=.ld65-err) 2> $$(@:.bin=.ld65-err2) || $(TRUE) +endif endif endif @@ -67,18 +81,25 @@ else $(ISEQUAL) --empty $$(@:.bin=.err) endif +ifneq ($(wildcard ref/$1.err2-ref),) + $(ISEQUAL) ref/$1.err2-ref $$(@:.bin=.err2) +else + $(ISEQUAL) --empty $$(@:.bin=.err2) +endif + ifneq ($(wildcard ref/$1.bin-ref),) $(ISEQUAL) --binary ref/$1.bin-ref $$@ endif +# rem $(indfo $(CAT) $(subst /,$$S,$$$(@:.bin=.ld65-err))) + ifneq ($(wildcard ref/$1.ld65err-ref),) - @echo cat $$(@:.bin=.ld65-err) - cat $$(@:.bin=.ld65-err) - @echo - @echo + @echo $(CAT) $$(@:.bin=.ld65-err) +# FIXME: somehow this refuses to work in cmd.exe +ifndef CMD_EXE + $(call CAT,$$(@:.bin=.ld65-err)) -diff -u ref/$1.ld65err-ref $$(@:.bin=.ld65-err) - @echo - @echo +endif $(ISEQUAL) --wildcards ref/$1.ld65err-ref $$(@:.bin=.ld65-err) else ifneq ($(wildcard $(WORKDIR)/$1.ld65-err),) @@ -86,16 +107,38 @@ ifneq ($(wildcard $(WORKDIR)/$1.ld65-err),) endif endif +ifneq ($(wildcard ref/$1.ld65err2-ref),) + @echo $(CAT) $$(@:.bin=.ld65-err2) +# FIXME: somehow this refuses to work in cmd.exe +ifndef CMD_EXE + $(call CAT,$$(@:.bin=.ld65-err2)) + -diff -u ref/$1.ld65err2-ref $$(@:.bin=.ld65-err2) +endif + $(ISEQUAL) --wildcards ref/$1.ld65err2-ref $$(@:.bin=.ld65-err2) +else +ifneq ($(wildcard $(WORKDIR)/$1.ld65-err2),) + $(ISEQUAL) --empty $$(@:.bin=.ld65-err2) +endif +endif + # compile with listing file ifeq ($(wildcard control/$1.err),) - $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1 + $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2) ifeq ($(wildcard control/$1.no-ld65),) - $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1 +ifeq ($(wildcard $1.cfg),) + $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) +else + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) +endif endif else - $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1 || true + $(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2> $$(@:.bin=.list-err2) || $(TRUE) ifeq ($(wildcard control/$1.no-ld65),) - $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1 || true +ifeq ($(wildcard $1.cfg),) + $(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) +else + $(LD65) -C $$(<:.s=.cfg) -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) > $$(@:.bin=.list-ld65-err) 2> $$(@:.bin=.list-ld65-err2) || $(TRUE) +endif endif endif @@ -113,10 +156,26 @@ ifneq ($(wildcard $(WORKDIR)/$1.list-ld65-err),) endif endif +ifneq ($(wildcard ref/$1.err2-ref),) + $(ISEQUAL) ref/$1.err2-ref $$(@:.bin=.list-err2) +else + $(ISEQUAL) --empty $$(@:.bin=.list-err2) +endif + +ifneq ($(wildcard ref/$1.ld65err2-ref),) + $(ISEQUAL) --wildcards ref/$1.ld65err2-ref $$(@:.bin=.list-ld65-err2) +else +ifneq ($(wildcard $(WORKDIR)/$1.list-ld65-err2),) + $(ISEQUAL) --empty $$(@:.bin=.list-ld65-err2) +endif +endif + # check if the result bin is the same as without listing file ifeq ($(wildcard control/$1.err),) +ifeq ($(wildcard control/$1.err2),) $(ISEQUAL) $$@ $$(@:.bin=.list-bin) endif +endif ifneq ($(wildcard ref/$1.list-ref),) # we have a reference file, compare that, too diff --git a/test/asm/listing/control/201-overwrite-overflow.err b/test/asm/listing/control/201-overwrite-overflow.err new file mode 100644 index 000000000..e69de29bb diff --git a/test/asm/listing/readme.txt b/test/asm/listing/readme.txt index e43f2008a..5436b234a 100644 --- a/test/asm/listing/readme.txt +++ b/test/asm/listing/readme.txt @@ -4,24 +4,42 @@ Overall test: These testcases can be used to test different aspects of the assembler. The name of a test is everything in the form <test>.s. +If a custom linker configuration is needed, also include <test>.cfg. + The following reference files can be added: -- <test>.bin-ref: +- ref/<test>.bin-ref: This is a reference for the resulting binary. The binary as binary tested against this file. If they are not equal, the test fails. -- <test>.list-ref +- ref/<test>.list-ref This is a reference for the resulting listing output This file *must* have the first line of the listing removed, as that contains a ca65 version string, and almost always this will be changed! +- ref/<test>.err-ref + This is a reference for the resulting ca65 stdout (>) output. + +- ref/<test>.err2-ref + This is a reference for the resulting ca65 stderr (2>) output. + +- ref/<test>.ld65err-ref + This is a reference for the resutling ld65 stdout (>) output. + +- ref/<test>.ld65err2-ref + This is a reference for the resulting ld65 stderr (2>) output. + +The following control files can be added to control the tests. +These files are empty (contents ignored), and only the filename is used: + +- control/<test>.err + Test is expected to produce an error. + +- control/<test>.no-ld65 + Skip the ld65 step. + Note that the resulting .bin file is generated twice: Once with no listing file, and once with listing file. This way, one can find out if the listing file generation changes anything with the resulting binary output. - - -TODO: -- add the possibility to test for specific error output that are to be - expected diff --git a/test/asm/listing/ref/010-paramcount.err-ref b/test/asm/listing/ref/010-paramcount.err-ref index a66162eb5..baf73f50f 100644 --- a/test/asm/listing/ref/010-paramcount.err-ref +++ b/test/asm/listing/ref/010-paramcount.err-ref @@ -1,15 +1,6 @@ .paramcount = 3 .paramcount = 5 -010-paramcount.s:18: Warning: User warning: r1 is blank! -010-paramcount.s:14: Note: Macro was defined here -010-paramcount.s:8: Note: Macro was defined here .paramcount = 3 .paramcount = 5 -010-paramcount.s:19: Warning: User warning: r1 is blank! -010-paramcount.s:14: Note: Macro was defined here -010-paramcount.s:8: Note: Macro was defined here .paramcount = 1 .paramcount = 5 -010-paramcount.s:20: Warning: User warning: r1 is blank! -010-paramcount.s:14: Note: Macro was defined here -010-paramcount.s:8: Note: Macro was defined here diff --git a/test/asm/listing/ref/010-paramcount.err2-ref b/test/asm/listing/ref/010-paramcount.err2-ref new file mode 100644 index 000000000..304c9de61 --- /dev/null +++ b/test/asm/listing/ref/010-paramcount.err2-ref @@ -0,0 +1,9 @@ +010-paramcount.s:18: Warning: User warning: r1 is blank! +010-paramcount.s:14: Note: Macro was defined here +010-paramcount.s:8: Note: Macro was defined here +010-paramcount.s:19: Warning: User warning: r1 is blank! +010-paramcount.s:14: Note: Macro was defined here +010-paramcount.s:8: Note: Macro was defined here +010-paramcount.s:20: Warning: User warning: r1 is blank! +010-paramcount.s:14: Note: Macro was defined here +010-paramcount.s:8: Note: Macro was defined here diff --git a/test/asm/listing/ref/030-assert-success.err-ref b/test/asm/listing/ref/030-assert-success.err2-ref similarity index 100% rename from test/asm/listing/ref/030-assert-success.err-ref rename to test/asm/listing/ref/030-assert-success.err2-ref diff --git a/test/asm/listing/ref/030-assert-success.ld65err-ref b/test/asm/listing/ref/030-assert-success.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/030-assert-success.ld65err-ref rename to test/asm/listing/ref/030-assert-success.ld65err2-ref diff --git a/test/asm/listing/ref/031-assert-error.err-ref b/test/asm/listing/ref/031-assert-error.err2-ref similarity index 100% rename from test/asm/listing/ref/031-assert-error.err-ref rename to test/asm/listing/ref/031-assert-error.err2-ref diff --git a/test/asm/listing/ref/032-assert-error2.ld65err-ref b/test/asm/listing/ref/032-assert-error2.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error2.ld65err-ref rename to test/asm/listing/ref/032-assert-error2.ld65err2-ref diff --git a/test/asm/listing/ref/032-assert-error3.ld65err-ref b/test/asm/listing/ref/032-assert-error3.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error3.ld65err-ref rename to test/asm/listing/ref/032-assert-error3.ld65err2-ref diff --git a/test/asm/listing/ref/032-assert-error4.ld65err-ref b/test/asm/listing/ref/032-assert-error4.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error4.ld65err-ref rename to test/asm/listing/ref/032-assert-error4.ld65err2-ref diff --git a/test/asm/listing/ref/032-assert-error5.ld65err2-ref b/test/asm/listing/ref/032-assert-error5.ld65err2-ref new file mode 100644 index 000000000..e69de29bb diff --git a/test/asm/listing/ref/032-assert-error6.ld65err-ref b/test/asm/listing/ref/032-assert-error6.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error6.ld65err-ref rename to test/asm/listing/ref/032-assert-error6.ld65err2-ref diff --git a/test/asm/listing/ref/032-assert-error7.ld65err-ref b/test/asm/listing/ref/032-assert-error7.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error7.ld65err-ref rename to test/asm/listing/ref/032-assert-error7.ld65err2-ref diff --git a/test/asm/listing/ref/032-assert-error8.ld65err-ref b/test/asm/listing/ref/032-assert-error8.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/032-assert-error8.ld65err-ref rename to test/asm/listing/ref/032-assert-error8.ld65err2-ref diff --git a/test/asm/listing/ref/033-assert-ldwarning-success.ld65err-ref b/test/asm/listing/ref/033-assert-ldwarning-success.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/033-assert-ldwarning-success.ld65err-ref rename to test/asm/listing/ref/033-assert-ldwarning-success.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror1.err-ref b/test/asm/listing/ref/034-assert-lderror1.err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror1.err-ref rename to test/asm/listing/ref/034-assert-lderror1.err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror2.ld65err-ref b/test/asm/listing/ref/034-assert-lderror2.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror2.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror2.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror3.ld65err-ref b/test/asm/listing/ref/034-assert-lderror3.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror3.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror3.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror4.ld65err-ref b/test/asm/listing/ref/034-assert-lderror4.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror4.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror4.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror6.ld65err-ref b/test/asm/listing/ref/034-assert-lderror6.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror6.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror6.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror7.ld65err-ref b/test/asm/listing/ref/034-assert-lderror7.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror7.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror7.ld65err2-ref diff --git a/test/asm/listing/ref/034-assert-lderror8.ld65err-ref b/test/asm/listing/ref/034-assert-lderror8.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/034-assert-lderror8.ld65err-ref rename to test/asm/listing/ref/034-assert-lderror8.ld65err2-ref diff --git a/test/asm/listing/ref/040-align.ld65err-ref b/test/asm/listing/ref/040-align.ld65err2-ref similarity index 100% rename from test/asm/listing/ref/040-align.ld65err-ref rename to test/asm/listing/ref/040-align.ld65err2-ref diff --git a/test/asm/listing/ref/050-case-on-1.err-ref b/test/asm/listing/ref/050-case-on-1.err2-ref similarity index 100% rename from test/asm/listing/ref/050-case-on-1.err-ref rename to test/asm/listing/ref/050-case-on-1.err2-ref diff --git a/test/asm/listing/ref/050-case-on-2.err-ref b/test/asm/listing/ref/050-case-on-2.err2-ref similarity index 100% rename from test/asm/listing/ref/050-case-on-2.err-ref rename to test/asm/listing/ref/050-case-on-2.err2-ref diff --git a/test/asm/listing/ref/050-case-on-3.err-ref b/test/asm/listing/ref/050-case-on-3.err2-ref similarity index 100% rename from test/asm/listing/ref/050-case-on-3.err-ref rename to test/asm/listing/ref/050-case-on-3.err2-ref diff --git a/test/asm/listing/ref/050-case-on-4.err-ref b/test/asm/listing/ref/050-case-on-4.err2-ref similarity index 100% rename from test/asm/listing/ref/050-case-on-4.err-ref rename to test/asm/listing/ref/050-case-on-4.err2-ref diff --git a/test/asm/listing/ref/050-case-on-5.err-ref b/test/asm/listing/ref/050-case-on-5.err2-ref similarity index 100% rename from test/asm/listing/ref/050-case-on-5.err-ref rename to test/asm/listing/ref/050-case-on-5.err2-ref diff --git a/test/asm/listing/ref/108-long-rts.bin-ref b/test/asm/listing/ref/108-long-rts.bin-ref new file mode 100644 index 000000000..23fa7d31a --- /dev/null +++ b/test/asm/listing/ref/108-long-rts.bin-ref @@ -0,0 +1 @@ +k \ No newline at end of file diff --git a/test/asm/listing/ref/109-brk-signature.bin-ref b/test/asm/listing/ref/109-brk-signature.bin-ref new file mode 100644 index 000000000..0eaa09faa Binary files /dev/null and b/test/asm/listing/ref/109-brk-signature.bin-ref differ diff --git a/test/asm/listing/ref/200-overwrite.bin-ref b/test/asm/listing/ref/200-overwrite.bin-ref new file mode 100644 index 000000000..b3a6f4f63 Binary files /dev/null and b/test/asm/listing/ref/200-overwrite.bin-ref differ diff --git a/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref b/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref new file mode 100644 index 000000000..e372938ef --- /dev/null +++ b/test/asm/listing/ref/201-overwrite-overflow.ld65err2-ref @@ -0,0 +1,3 @@ +ld65: Warning: 201-overwrite-overflow.cfg:3: Segment 'AO' overflows memory area 'A' by 1 byte +ld65: Warning: 201-overwrite-overflow.cfg:4: Segment 'BO' overflows memory area 'B' by 1 byte +ld65: Error: Cannot generate most of the files due to memory area overflows diff --git a/test/asm/misc/Makefile b/test/asm/misc/Makefile new file mode 100644 index 000000000..5a9d4f3ef --- /dev/null +++ b/test/asm/misc/Makefile @@ -0,0 +1,70 @@ +# Makefile for the remaining asm tests that need special care in one way or another + +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + S = $(subst /,\,/) + NOT = - # Hack + EXE = .exe + NULLDEV = nul: + MKDIR = mkdir $(subst /,\,$1) + RMDIR = -rmdir /s /q $(subst /,\,$1) +else + S = / + NOT = ! + EXE = + NULLDEV = /dev/null + MKDIR = mkdir -p $1 + RMDIR = $(RM) -r $1 +endif + +ifdef QUIET + .SILENT: + NULLOUT = >$(NULLDEV) + NULLERR = 2>$(NULLDEV) +endif + +SIM65FLAGS = -x 200000000 + +CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) +SIM65 := $(if $(wildcard ../../../bin/sim65*),..$S..$S..$Sbin$Ssim65,sim65) + +WORKDIR = ..$S..$S..$Stestwrk$Sasm$Smisc + +.PHONY: all clean + +SOURCES := $(wildcard *.s) +TESTS = $(SOURCES:%.s=$(WORKDIR)/%.6502.prg) +TESTS += $(SOURCES:%.s=$(WORKDIR)/%.65c02.prg) + +all: $(TESTS) + +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +define PRG_template + +# sim65 ensure 64-bit wait time does not timeout +$(WORKDIR)/sim65-timein.$1.prg: sim65-timein.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timein.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +# sim65 ensure 64-bit wait time does timeout +$(WORKDIR)/sim65-timeout.$1.prg: sim65-timeout.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timeout.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(NOT) $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +endef # PRG_template + +$(eval $(call PRG_template,6502)) +$(eval $(call PRG_template,65c02)) + +clean: + @$(call RMDIR,$(WORKDIR)) diff --git a/test/asm/misc/sim65-time-wait.inc b/test/asm/misc/sim65-time-wait.inc new file mode 100644 index 000000000..bc761ac16 --- /dev/null +++ b/test/asm/misc/sim65-time-wait.inc @@ -0,0 +1,55 @@ +; Shared timer for: +; sim65-timein.s +; sim65-timeout.s + +; wait A * 100,000,000 cycles, plus small amount of overhead +wait100m: + tay + bne :+ + rts ; return quickly if A=0 +: + jsr wait50331648 ; 50331648 + jsr wait25165824 ; 75497472 + jsr wait12582912 ; 88080384 + jsr wait6291456 ; 94371840 + jsr wait3145728 ; 97517568 + jsr wait1572864 ; 99090432 + jsr wait786432 ; 99876864 + jsr wait98304 ; 99975168 + jsr wait24576 ; 99999744 + jsr wait192 ; 99999936 + jsr wait48 ; 99999984 + nop ; 99999986 + nop ; 99999988 + php ; 99999991 + plp ; 99999995 + dey ; 99999997 + bne :- ; 100000000 + rts +; Note that this branch could cross a page if poorly aligned, +; adding an additional 1 cycle per loop. +; This precision is not important for the tests used. + +wait50331648: jsr wait25165824 +wait25165824: jsr wait12582912 +wait12582912: jsr wait6291456 +wait6291456: jsr wait3145728 +wait3145728: jsr wait1572864 +wait1572864: jsr wait786432 +wait786432: jsr wait393216 +wait393216: jsr wait196608 +wait196608: jsr wait98304 +wait98304: jsr wait49152 +wait49152: jsr wait24576 +wait24576: jsr wait12288 +wait12288: jsr wait6144 +wait6144: jsr wait3072 +wait3072: jsr wait1536 +wait1536: jsr wait768 +wait768: jsr wait384 +wait384: jsr wait192 +wait192: jsr wait96 +wait96: jsr wait48 +wait48: jsr wait24 +wait24: jsr wait12 +wait12: rts diff --git a/test/asm/misc/sim65-timein.s b/test/asm/misc/sim65-timein.s new file mode 100644 index 000000000..13365f0a8 --- /dev/null +++ b/test/asm/misc/sim65-timein.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timein.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,300,000,000 cycles + lda #43 + jsr wait100m + ; This is a positive test. + ; If the timeout did not occur, returning 0 reports success. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/misc/sim65-timeout.s b/test/asm/misc/sim65-timeout.s new file mode 100644 index 000000000..6f1778dcd --- /dev/null +++ b/test/asm/misc/sim65-timeout.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timeout.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,500,000,000 cycles + lda #45 + jsr wait100m + ; This is a negative test. + ; If the timeout did not occur, returning 0 reports failure. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/readme.txt b/test/asm/readme.txt index c3198c12a..9b716e60c 100644 --- a/test/asm/readme.txt +++ b/test/asm/readme.txt @@ -1,32 +1,44 @@ Assembler Testcases =================== -Opcode Tests: -------------- +cpudetect +--------- -these go into opcodes/. Refer to opcodes/readme.txt +Tests the --cpu command line option of ca65/ld65. +Refer to cpudetect/readme.txt -CPU Detect Tests ----------------- +opcodes +------- -these go into cpudetect/. Refer to cpudetect/readme.txt +Test of assembler opcodes for each CPU. +Refer to opcodes/readme.txt -Overall tests: --------------- - -These go into listing/. Refer to listing/readme.txt - -val: ----- - -Works very much like the /val directory used to test the compiler - individual -tests are run in the simulator and should exit with an exit code of 0 when they -pass, or either -1 or a number indicating what part of the test failed on error. - err: ---- -Works very much like the /err directory used to test the compiler - individual -tests are assembled and MUST NOT assemble without error. +Used to test assembler errors. These tests MUST NOT assemble without error. + + +listing: +-------- + +This is the most versatile assembler test form, allowing control customizations, +reference tests for binary output, stdout and error text ouput, error tests, +listings, custom linker configuration, etc. as needed. +Refer to listing/readme.txt + + +val: +---- + +Runtime assembly tests using sim65 that should end with an exit code of 0 if +they pass. If they fail the exit code should be either -1, or a number +indicating what part of the test failed. + + +misc: +----- + +This is for tests that require special make steps or conditions. diff --git a/test/asm/val/Makefile b/test/asm/val/Makefile index 91dae9afd..54b1100ec 100644 --- a/test/asm/val/Makefile +++ b/test/asm/val/Makefile @@ -22,7 +22,7 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 +SIM65FLAGS = -x 4000000000 CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) diff --git a/test/asm/val/addrsize.s b/test/asm/val/addrsize.s new file mode 100644 index 000000000..932090df0 --- /dev/null +++ b/test/asm/val/addrsize.s @@ -0,0 +1,32 @@ +; test .addrsize and ensure .feature addrsize is allowed, but inactive + +.export _main + +.segment "ZEROPAGE" +zplabel: + +.segment "CODE" +abslabel: + +; exit with 0 + +_main: + lda #0 + tax + rts + + +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize + +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" + +.feature addrsize - +.assert .addrsize(zplabel) = 1, error, ".addrsize 1 expected for ZEROPAGE" +.assert .addrsize(abslabel) = 2, error, ".addrsize 2 expected for absolute" diff --git a/test/asm/val/bug1655.inc b/test/asm/val/bug1655.inc new file mode 100644 index 000000000..a3c34fff8 --- /dev/null +++ b/test/asm/val/bug1655.inc @@ -0,0 +1,5 @@ + + inx ; x = 2 + cpx #$02 + jne exiterror + diff --git a/test/asm/val/bug1655.s b/test/asm/val/bug1655.s new file mode 100644 index 000000000..383f871e4 --- /dev/null +++ b/test/asm/val/bug1655.s @@ -0,0 +1,23 @@ + .macpack longbranch + + .import _exit + .export _main + + .macpack cpu + .macro pfinc what + .out .sprintf("Including bug%s.inc", what) + .include .sprintf("bug%s.inc", what) + .endmacro + +_main: + ldx #1 ; test counter + + pfinc "1655" + + ;--------------------------------------------------------------------- + ; exit OK + ldx #0 +exiterror: + txa + ldx #0 + jmp _exit diff --git a/test/asm/val/endmacro.s b/test/asm/val/endmacro.s new file mode 100644 index 000000000..cfb8efefb --- /dev/null +++ b/test/asm/val/endmacro.s @@ -0,0 +1,30 @@ +; for PR #2013 + .import _exit + .export _main + + ; this macro is invalid, but should not cause an error (if it is never expanded) + .macro invalid + nop .endmacro + .endmacro + + .define temp_endmac .endmacro + .macro new_mac + .define startmac .macro + .define endmac .endmacro + temp_endmac + + .undefine temp_endmac + + new_mac + + startmac dex2 + dex + dex + endmac + +_main: + ldx #$02 + dex2 + ; x should be zero + txa + jmp _exit diff --git a/test/asm/val/feature.s b/test/asm/val/feature.s new file mode 100644 index 000000000..0def9d92c --- /dev/null +++ b/test/asm/val/feature.s @@ -0,0 +1,139 @@ +; a simple test of every .feature + +.export _main + +.segment "ZEROPAGE" +zplabel: + +.segment "CODE" + +; exit with 0 + +_main: + ; if any feature needs a runtime test, + ; it can be added here. + lda #0 + tax + rts + +.feature at_in_identifiers on +ident@with@at: + rts +.feature at_in_identifiers off + + +.feature bracket_as_indirect + lda [$82],y +.feature bracket_as_indirect- + + +.feature c_comments + lda zplabel /* comment */ + /* comment */ +/* multiline +** comment +*/ +.feature c_comments - + + +.feature dollar_in_identifiers +ident$with$dollar: + rts +.feature dollar_in_identifiers - + + +.feature dollar_is_pc +.assert $ = *, error, "dollar_is_pc failure" +.feature dollar_is_pc - + + +.feature force_range + lda #-1 +.feature force_range - + + +.feature labels_without_colons +labelwithoutcolon + jmp labelwithoutcolon +.feature labels_without_colons - + + +.feature leading_dot_in_identifiers +.identifierwithdot: + rts +.feature leading_dot_in_identifiers - + + +.feature long_jsr_jmp_rts +.p816 + ; long addresses require alternate instruction names JSL, JML without this feature + jsr $123456 + jmp $123456 +; smart + far + long_jsr_jmp_rts will promote rts to rtl +.smart + +.proc long_rts : far + rts ; should become RTL ($6B) instead of RTS ($60) + ; the emitted opcode is not verified by this test, + ; see test/asm/listing/108-long-rts +.endproc +.smart - +.p02 +.feature long_jsr_jmp_rts - + + +.feature loose_char_term +.byte 'a' +.byte "a" +.feature loose_char_term - + + +.feature loose_string_term +.asciiz "string" +.asciiz 'string' +.feature loose_string_term - + + +.feature missing_char_term + lda #'a +.feature missing_char_term - + + +.feature org_per_seg +.segment "RODATA" +.org $5678 +.assert * = $5678, error, "org_per_seg failed" +.segment "CODE" +.org $9ABC +.assert * = $9ABC, error, "org_per_seg failed" +.segment "RODATA" +.assert * = $5678, error, "org_per_seg failed" +.reloc +.segment "CODE" +.assert * = $9ABC, error, "org_per_seg failed" +.reloc +.feature org_per_seg - + + +.feature pc_assignment +* = $1234 +.assert * = $1234, error, "pc_assignment failed" +.reloc +.feature pc_assignment - + + +.feature string_escapes +.asciiz "quote:\"" +.feature string_escapes - + + +.feature ubiquitous_idents +.macro bit + brk +.endmacro + bit +.feature ubiquitous_idents - + + +.feature underline_in_numbers +.byte %10_10_10_10 +.feature underline_in_numbers - diff --git a/test/asm/val/ismnemonic.s b/test/asm/val/ismnemonic.s new file mode 100644 index 000000000..2d131e7a9 --- /dev/null +++ b/test/asm/val/ismnemonic.s @@ -0,0 +1,846 @@ +; Tests to ensure .ismnemonic is working correctly +; The .ismnemonic function calls FindInstruction internally, +; which is how the assembler detects all instructions +; +; Currently supported CPUs: +; "6502" +; "6502X" +; "6502DTV" +; "65SC02" +; "65C02" +; "4510" +; "huc6280" +; "65816" +; "sweet16" + +; count any errors: +ismnemonic_error .set 0 + +; macro to test an instruction +.macro test_Ismnemonic instr + .if .ismnemonic(instr) + ; do nothing + .else + ismnemonic_error .set 1 + .endif +.endmacro + +; test .feature ubiquitous_idents + + ; allow overloading mnemonics +.feature ubiquitous_idents + +.setcpu "6502" + +; make an adc macro +.macro adc +.endmacro + +; should not match +.if .ismnemonic(adc) + ismnemonic_error .set 1 +.endif + +.delmac adc + +; test all instructions: + +; there is no instruction table for "none", make sure 'adc' (common to all CPUs) and 'add' (sweet16) doesn't match +.setcpu "none" +.if .ismnemonic(adc) || .ismnemonic(add) + ismnemonic_error .set 1 +.endif + +.setcpu "6502" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "6502X" +test_Ismnemonic adc +test_Ismnemonic alr +test_Ismnemonic anc +test_Ismnemonic and +test_Ismnemonic ane +test_Ismnemonic arr +test_Ismnemonic asl +test_Ismnemonic axs +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dcp +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic isc +test_Ismnemonic jam +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic las +test_Ismnemonic lax +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rla +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rra +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sax +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sha +test_Ismnemonic shx +test_Ismnemonic shy +test_Ismnemonic slo +test_Ismnemonic sre +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tas +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "6502DTV" +test_Ismnemonic adc +test_Ismnemonic alr +test_Ismnemonic anc +test_Ismnemonic and +test_Ismnemonic ane +test_Ismnemonic arr +test_Ismnemonic asl +test_Ismnemonic axs +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic las +test_Ismnemonic lax +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic rla +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rra +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sac +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sha +test_Ismnemonic shx +test_Ismnemonic shy +test_Ismnemonic sir +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65SC02" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65C02" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic sta +test_Ismnemonic stp +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya +test_Ismnemonic wai + +.setcpu "4510" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic asr +test_Ismnemonic asw +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bsr +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cle +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic cpz +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dew +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic dez +test_Ismnemonic eom +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inw +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic inz +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lbcc +test_Ismnemonic lbcs +test_Ismnemonic lbeq +test_Ismnemonic lbmi +test_Ismnemonic lbne +test_Ismnemonic lbpl +test_Ismnemonic lbra +test_Ismnemonic lbvc +test_Ismnemonic lbvs +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic ldz +test_Ismnemonic lsr +test_Ismnemonic map +test_Ismnemonic neg +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic phd +test_Ismnemonic php +test_Ismnemonic phw +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic phz +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic plz +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic row +test_Ismnemonic rti +test_Ismnemonic rtn +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic see +test_Ismnemonic sei +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic tab +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic taz +test_Ismnemonic tba +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tsx +test_Ismnemonic tsy +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya +test_Ismnemonic tys +test_Ismnemonic tza + +.setcpu "HuC6280" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bbr0 +test_Ismnemonic bbr1 +test_Ismnemonic bbr2 +test_Ismnemonic bbr3 +test_Ismnemonic bbr4 +test_Ismnemonic bbr5 +test_Ismnemonic bbr6 +test_Ismnemonic bbr7 +test_Ismnemonic bbs0 +test_Ismnemonic bbs1 +test_Ismnemonic bbs2 +test_Ismnemonic bbs3 +test_Ismnemonic bbs4 +test_Ismnemonic bbs5 +test_Ismnemonic bbs6 +test_Ismnemonic bbs7 +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic bsr +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic cla +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic clx +test_Ismnemonic cly +test_Ismnemonic cmp +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic csh +test_Ismnemonic csl +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jmp +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pha +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rmb0 +test_Ismnemonic rmb1 +test_Ismnemonic rmb2 +test_Ismnemonic rmb3 +test_Ismnemonic rmb4 +test_Ismnemonic rmb5 +test_Ismnemonic rmb6 +test_Ismnemonic rmb7 +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rts +test_Ismnemonic sax +test_Ismnemonic say +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic set +test_Ismnemonic smb0 +test_Ismnemonic smb1 +test_Ismnemonic smb2 +test_Ismnemonic smb3 +test_Ismnemonic smb4 +test_Ismnemonic smb5 +test_Ismnemonic smb6 +test_Ismnemonic smb7 +test_Ismnemonic st0 +test_Ismnemonic st1 +test_Ismnemonic st2 +test_Ismnemonic sta +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic sxy +test_Ismnemonic tai +test_Ismnemonic tam +test_Ismnemonic tam0 +test_Ismnemonic tam1 +test_Ismnemonic tam2 +test_Ismnemonic tam3 +test_Ismnemonic tam4 +test_Ismnemonic tam5 +test_Ismnemonic tam6 +test_Ismnemonic tam7 +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tdd +test_Ismnemonic tia +test_Ismnemonic tii +test_Ismnemonic tin +test_Ismnemonic tma +test_Ismnemonic tma0 +test_Ismnemonic tma1 +test_Ismnemonic tma2 +test_Ismnemonic tma3 +test_Ismnemonic tma4 +test_Ismnemonic tma5 +test_Ismnemonic tma6 +test_Ismnemonic tma7 +test_Ismnemonic trb +test_Ismnemonic tsb +test_Ismnemonic tst +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic tya + +.setcpu "65816" +test_Ismnemonic adc +test_Ismnemonic and +test_Ismnemonic asl +test_Ismnemonic bcc +test_Ismnemonic bcs +test_Ismnemonic beq +test_Ismnemonic bit +test_Ismnemonic bmi +test_Ismnemonic bne +test_Ismnemonic bpl +test_Ismnemonic bra +test_Ismnemonic brk +test_Ismnemonic brl +test_Ismnemonic bvc +test_Ismnemonic bvs +test_Ismnemonic clc +test_Ismnemonic cld +test_Ismnemonic cli +test_Ismnemonic clv +test_Ismnemonic cmp +test_Ismnemonic cop +test_Ismnemonic cpa +test_Ismnemonic cpx +test_Ismnemonic cpy +test_Ismnemonic dea +test_Ismnemonic dec +test_Ismnemonic dex +test_Ismnemonic dey +test_Ismnemonic eor +test_Ismnemonic ina +test_Ismnemonic inc +test_Ismnemonic inx +test_Ismnemonic iny +test_Ismnemonic jml +test_Ismnemonic jmp +test_Ismnemonic jsl +test_Ismnemonic jsr +test_Ismnemonic lda +test_Ismnemonic ldx +test_Ismnemonic ldy +test_Ismnemonic lsr +test_Ismnemonic mvn +test_Ismnemonic mvp +test_Ismnemonic nop +test_Ismnemonic ora +test_Ismnemonic pea +test_Ismnemonic pei +test_Ismnemonic per +test_Ismnemonic pha +test_Ismnemonic phb +test_Ismnemonic phd +test_Ismnemonic phk +test_Ismnemonic php +test_Ismnemonic phx +test_Ismnemonic phy +test_Ismnemonic pla +test_Ismnemonic plb +test_Ismnemonic pld +test_Ismnemonic plp +test_Ismnemonic plx +test_Ismnemonic ply +test_Ismnemonic rep +test_Ismnemonic rol +test_Ismnemonic ror +test_Ismnemonic rti +test_Ismnemonic rtl +test_Ismnemonic rts +test_Ismnemonic sbc +test_Ismnemonic sec +test_Ismnemonic sed +test_Ismnemonic sei +test_Ismnemonic sep +test_Ismnemonic sta +test_Ismnemonic stp +test_Ismnemonic stx +test_Ismnemonic sty +test_Ismnemonic stz +test_Ismnemonic swa +test_Ismnemonic tad +test_Ismnemonic tas +test_Ismnemonic tax +test_Ismnemonic tay +test_Ismnemonic tcd +test_Ismnemonic tcs +test_Ismnemonic tda +test_Ismnemonic tdc +test_Ismnemonic trb +test_Ismnemonic tsa +test_Ismnemonic tsb +test_Ismnemonic tsc +test_Ismnemonic tsx +test_Ismnemonic txa +test_Ismnemonic txs +test_Ismnemonic txy +test_Ismnemonic tya +test_Ismnemonic tyx +test_Ismnemonic wai +test_Ismnemonic wdm +test_Ismnemonic xba +test_Ismnemonic xce + +.setcpu "sweet16" +test_Ismnemonic add +test_Ismnemonic bc +test_Ismnemonic bk +test_Ismnemonic bm +test_Ismnemonic bm1 +test_Ismnemonic bnc +test_Ismnemonic bnm1 +test_Ismnemonic bnz +test_Ismnemonic bp +test_Ismnemonic br +test_Ismnemonic bs +test_Ismnemonic bz +test_Ismnemonic cpr +test_Ismnemonic dcr +test_Ismnemonic inr +test_Ismnemonic ld +test_Ismnemonic ldd +test_Ismnemonic pop +test_Ismnemonic popd +test_Ismnemonic rs +test_Ismnemonic rtn +test_Ismnemonic set +test_Ismnemonic st +test_Ismnemonic std +test_Ismnemonic stp +test_Ismnemonic sub + + .setcpu "6502" + + .import _exit + .export _main + +_main: + .if ismnemonic_error + ldx #$01 + .else + ldx #$00 + .endif + txa + jmp _exit diff --git a/test/dasm/65816.cfg b/test/dasm/65816.cfg new file mode 100644 index 000000000..620439c7b --- /dev/null +++ b/test/dasm/65816.cfg @@ -0,0 +1,7 @@ +MEMORY { + ROM: start = $8000, size = $8000; +} + +SEGMENTS { + CODE: load = ROM; +} diff --git a/test/dasm/65816.info b/test/dasm/65816.info new file mode 100644 index 000000000..a6d836688 --- /dev/null +++ b/test/dasm/65816.info @@ -0,0 +1,7 @@ +GLOBAL { + startaddr $8000; +}; + +RANGE { START $8000; END $8229; ADDRMODE "MX"; TYPE Code;}; +RANGE { START $822a; END $825b; ADDRMODE "mx"; TYPE Code;}; + diff --git a/test/dasm/Makefile b/test/dasm/Makefile index 542ce7d5e..e84560ad2 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -19,6 +19,8 @@ ifdef QUIET endif CL65 := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) +CA65 := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) DA65 := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) WORKDIR = ../../testwrk/dasm @@ -28,12 +30,12 @@ ISEQUAL = ../../testwrk/isequal$(EXE) CC = gcc CFLAGS = -O2 -START = --start-addr 0x8000 +START = --start-addr 0x7000 .PHONY: all clean SOURCES := $(wildcard *.s) -BINS = $(SOURCES:%disass.s=$(WORKDIR)/%reass.bin) +BINS = $(SOURCES:%disass.s=$(WORKDIR)/%reass.bin) $(WORKDIR)/65816-reass.bin CPUS = $(SOURCES:%-disass.s=%) all: $(BINS) @@ -61,5 +63,18 @@ endef # DISASS_template $(foreach cpu,$(CPUS),$(eval $(call DISASS_template,$(cpu)))) +$(WORKDIR)/test65816.bin: test65816.s | $(WORKDIR) + $(CA65) -o $(WORKDIR)/test65816.o $< + $(LD65) -o $@ -C 65816.cfg $(WORKDIR)/test65816.o + +$(WORKDIR)/65816-reass.s: $(WORKDIR)/test65816.bin + $(DA65) --cpu 65816 -i 65816.info -o $@ $< + +$(WORKDIR)/65816-reass.bin: $(WORKDIR)/65816-reass.s $(ISEQUAL) + $(if $(QUIET),echo dasm/65816-reass.bin) + $(CA65) -o $(WORKDIR)/65816-reass.o $< + $(LD65) -o $@ -C 65816.cfg $(WORKDIR)/65816-reass.o + $(ISEQUAL) --binary $(WORKDIR)/test65816.bin $@ + clean: @$(call RMDIR,$(WORKDIR)) diff --git a/test/dasm/test65816.s b/test/dasm/test65816.s new file mode 100644 index 000000000..1b447d0fe --- /dev/null +++ b/test/dasm/test65816.s @@ -0,0 +1,286 @@ +.setcpu "65816" + +ADC ($10,X) +ADC $32,S +ADC $10 +ADC [$10] +ADC #$54 +ADC $9876 +ADC $FEDBCA +ADC ($10),Y +ADC ($10) +ADC ($32,S),Y +ADC $10,X +ADC [$10],Y +ADC $9876,Y +ADC $9876,X +ADC $FEDCBA,X +SBC ($10,X) +SBC $32,S +SBC $10 +SBC [$10] +SBC #$54 +SBC $9876 +SBC $FEDBCA +SBC ($10),Y +SBC ($10) +SBC ($32,S),Y +SBC $10,X +SBC [$10],Y +SBC $9876,Y +SBC $9876,X +SBC $FEDCBA,X +CMP ($10,X) +CMP $32,S +CMP $10 +CMP [$10] +CMP #$54 +CMP $9876 +CMP $FEDBCA +CMP ($10),Y +CMP ($10) +CMP ($32,S),Y +CMP $10,X +CMP [$10],Y +CMP $9876,Y +CMP $9876,X +CMP $FEDCBA,X +CPX #$54 +CPX $10 +CPX $9876 +CPY #$54 +CPY $10 +CPY $9876 +DEC +DEC $10 +DEC $9876 +DEC $10,X +DEC $9876,X +DEX +DEY +INC +INC $10 +INC $9876 +INC $10,X +INC $9876,X +INX +INY +AND ($10,X) +AND $32,S +AND $10 +AND [$10] +AND #$54 +AND $9876 +AND $FEDBCA +AND ($10),Y +AND ($10) +AND ($32,S),Y +AND $10,X +AND [$10],Y +AND $9876,Y +AND $9876,X +AND $FEDCBA,X +EOR ($10,X) +EOR $32,S +EOR $10 +EOR [$10] +EOR #$54 +EOR $9876 +EOR $FEDBCA +EOR ($10),Y +EOR ($10) +EOR ($32,S),Y +EOR $10,X +EOR [$10],Y +EOR $9876,Y +EOR $9876,X +EOR $FEDCBA,X +ORA ($10,X) +ORA $32,S +ORA $10 +ORA [$10] +ORA #$54 +ORA $9876 +ORA $FEDBCA +ORA ($10),Y +ORA ($10) +ORA ($32,S),Y +ORA $10,X +ORA [$10],Y +ORA $9876,Y +ORA $9876,X +ORA $FEDCBA,X +BIT $10 +BIT $9876 +BIT $10,X +BIT $9876,X +BIT #$54 +TRB $10 +TRB $9876 +TSB $10 +TSB $9876 +ASL $10 +ASL +ASL $9876 +ASL $10,X +ASL $9876,X +LSR $10 +LSR +LSR $9876 +LSR $10,X +LSR $9876,X +ROL $10 +ROL +ROL $9876 +ROL $10,X +ROL $9876,X +ROR $10 +ROR +ROR $9876 +ROR $10,X +ROR $9876,X +LABEL: +BCC LABEL +BCS LABEL +BEQ LABEL +BMI LABEL +BNE LABEL +BPL LABEL +BRA LABEL +BVC LABEL +BVS LABEL +BRL LABEL +JMP $1234 +JML $FEDCBA +JMP ($1234) +JMP ($1234,X) +JMP [$1234] +JML [$1234] ; alternative to JMP [] +JSL $123456 +JSR $1234 +JSR ($1234,X) +RTL +RTS +BRK +RTI +CLC +CLD +CLI +CLV +SEC +SED +SEI +REP #$12 +SEP #$12 +LDA ($10,X) +LDA $32,S +LDA $10 +LDA [$10] +LDA #$54 +LDA $9876 +LDA $FEDBCA +LDA ($10),Y +LDA ($10) +LDA ($32,S),Y +LDA $10,X +LDA [$10],Y +LDA $9876,Y +LDA $9876,X +LDA $FEDCBA,X +LDX #$54 +LDX $10 +LDX $9876 +LDX $10,Y +LDX $9876,Y +LDY #$54 +LDY $10 +LDY $9876 +LDY $10,X +LDY $9876,X +STA ($10,X) +STA $32,S +STA $10 +STA [$10] +STA $9876 +STA $FEDBCA +STA ($10),Y +STA ($10) +STA ($32,S),Y +STA $10,X +STA [$10],Y +STA $9876,Y +STA $9876,X +STA $FEDCBA,X +STX $10 +STX $9876 +STX $10,Y +STY $10 +STY $9876 +STY $10,X +STZ $10 +STZ $10,X +STZ $9876 +STZ $9876,X +MVN #$12,#$34 +MVP #$12,#$34 +NOP +PEA $1234 +PEI ($12) +PER LABEL +PHA +PHX +PHY +PLA +PLX +PLY +PHB +PHD +PHK +PHP +PLB +PLD +PLP +STP +WAI +TAX +TAY +TSX +TXA +TXS +TXY +TYA +TYX +TCD +TCS +TDC +TSC +XBA +XCE + +.a16 +.i16 +longs: +ADC #$5432 +SBC #$5432 +CMP #$5432 +CPX #$5432 +CPY #$5432 +AND #$5432 +EOR #$5432 +ORA #$5432 +BIT #$5432 +LDA #$5432 +LDX #$5432 +LDY #$5432 + +; test of smart and long_jsr_jmp_rts +.smart +.proc short_rts : far +RTS ; not promoted to RTL +.endproc +.feature long_jsr_jmp_rts +JSR $FEDCBA ; promoted to JSL +JMP $FEDCBA ; promoted to JML +.proc long_rts : far +RTS ; promoted to RTL +.endproc diff --git a/test/err/bug1890.c b/test/err/bug1890.c new file mode 100644 index 000000000..15d857cdb --- /dev/null +++ b/test/err/bug1890.c @@ -0,0 +1,9 @@ +/* bug #1890 - Overflow in enumerator value is not detected */ + +#include <limits.h> +enum { a = ULONG_MAX, b } c = b; + +int main(void) +{ + return 0; +} diff --git a/test/err/bug1893.c b/test/err/bug1893.c new file mode 100644 index 000000000..455256179 --- /dev/null +++ b/test/err/bug1893.c @@ -0,0 +1,8 @@ +/* bug #1893 - Compiler accepts a ternary expression where it shouldn't */ + +int main(void) +{ + int a, b, c; + a == 1? b : c = 3; + return 0; +} diff --git a/test/err/bug1895-assign1a.c b/test/err/bug1895-assign1a.c new file mode 100644 index 000000000..223964104 --- /dev/null +++ b/test/err/bug1895-assign1a.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_1_A + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign1b.c b/test/err/bug1895-assign1b.c new file mode 100644 index 000000000..cccc0a318 --- /dev/null +++ b/test/err/bug1895-assign1b.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_1_B + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign2a.c b/test/err/bug1895-assign2a.c new file mode 100644 index 000000000..512b658a9 --- /dev/null +++ b/test/err/bug1895-assign2a.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_2_A + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign2b.c b/test/err/bug1895-assign2b.c new file mode 100644 index 000000000..d07191206 --- /dev/null +++ b/test/err/bug1895-assign2b.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_2_B + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign4a.c b/test/err/bug1895-assign4a.c new file mode 100644 index 000000000..c2a6f25de --- /dev/null +++ b/test/err/bug1895-assign4a.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_4_A + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign4b.c b/test/err/bug1895-assign4b.c new file mode 100644 index 000000000..740e10b04 --- /dev/null +++ b/test/err/bug1895-assign4b.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_4_B + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign5a.c b/test/err/bug1895-assign5a.c new file mode 100644 index 000000000..fed4e07d9 --- /dev/null +++ b/test/err/bug1895-assign5a.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_5_A + +#include "bug1895-common.h" diff --git a/test/err/bug1895-assign5b.c b/test/err/bug1895-assign5b.c new file mode 100644 index 000000000..ed8498e34 --- /dev/null +++ b/test/err/bug1895-assign5b.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_1_SUB_5_B + +#include "bug1895-common.h" diff --git a/test/err/bug1895-common.h b/test/err/bug1895-common.h new file mode 100644 index 000000000..03f02e2de --- /dev/null +++ b/test/err/bug1895-common.h @@ -0,0 +1,196 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types + + Test of incompatible pointer/array types in assignment ans conditional + expressions, as well as function prototypes. + + In each source file, define a single macro and include this file to perform + a coresponding test individually. + + https://github.com/cc65/cc65/issues/1895 +*/ + +/* Test 1 suite */ +#ifdef DO_TEST_1_SUB_1_A +#define TEST_1_SUB_1_A CMP_TYPES_1 +#else +#define TEST_1_SUB_1_A BLANK +#endif + +#ifdef DO_TEST_1_SUB_1_B +#define TEST_1_SUB_1_B CMP_TYPES_1 +#else +#define TEST_1_SUB_1_B BLANK +#endif + +#ifdef DO_TEST_1_SUB_2_A +#define TEST_1_SUB_2_A CMP_TYPES_1 +#else +#define TEST_1_SUB_2_A BLANK +#endif + +#ifdef DO_TEST_1_SUB_2_B +#define TEST_1_SUB_2_B CMP_TYPES_1 +#else +#define TEST_1_SUB_2_B BLANK +#endif + +#ifdef DO_TEST_1_SUB_4_A +#define TEST_1_SUB_4_A CMP_TYPES_1 +#else +#define TEST_1_SUB_4_A BLANK +#endif + +#ifdef DO_TEST_1_SUB_4_B +#define TEST_1_SUB_4_B CMP_TYPES_1 +#else +#define TEST_1_SUB_4_B BLANK +#endif + +#ifdef DO_TEST_1_SUB_5_A +#define TEST_1_SUB_5_A CMP_TYPES_1 +#else +#define TEST_1_SUB_5_A BLANK +#endif + +#ifdef DO_TEST_1_SUB_5_B +#define TEST_1_SUB_5_B CMP_TYPES_1 +#else +#define TEST_1_SUB_5_B BLANK +#endif + +/* Test 2 suite */ +#ifdef DO_TEST_2_SUB_1 +#define TEST_2_SUB_1 CMP_TYPES_2 +#else +#define TEST_2_SUB_1 BLANK +#endif + +#ifdef DO_TEST_2_SUB_2 +#define TEST_2_SUB_2 CMP_TYPES_2 +#else +#define TEST_2_SUB_2 BLANK +#endif + +#ifdef DO_TEST_2_SUB_3 +#define TEST_2_SUB_3 CMP_TYPES_2 +#else +#define TEST_2_SUB_3 BLANK +#endif + +#ifdef DO_TEST_2_SUB_4 +#define TEST_2_SUB_4 CMP_TYPES_2 +#else +#define TEST_2_SUB_4 BLANK +#endif + +#ifdef DO_TEST_2_SUB_5 +#define TEST_2_SUB_5 CMP_TYPES_2 +#else +#define TEST_2_SUB_5 BLANK +#endif + +/* Test 3 suite */ +#ifdef DO_TEST_3_SUB_1 +#define TEST_3_SUB_1 CMP_TYPES_3 +#else +#define TEST_3_SUB_1 BLANK +#endif + +#ifdef DO_TEST_3_SUB_2 +#define TEST_3_SUB_2 CMP_TYPES_3 +#else +#define TEST_3_SUB_2 BLANK +#endif + +#ifdef DO_TEST_3_SUB_3 +#define TEST_3_SUB_3 CMP_TYPES_3 +#else +#define TEST_3_SUB_3 BLANK +#endif + +#ifdef DO_TEST_3_SUB_4 +#define TEST_3_SUB_4 CMP_TYPES_3 +#else +#define TEST_3_SUB_4 BLANK +#endif + +#ifdef DO_TEST_3_SUB_5 +#define TEST_3_SUB_5 CMP_TYPES_3 +#else +#define TEST_3_SUB_5 BLANK +#endif + +/* Implementation */ +#define CONCAT(a, b) CONCAT_impl_(a, b) +#define CONCAT_impl_(a, b) a##b +#define BLANK(...) +#define DECL_FUNCS(A, B)\ + void CONCAT(foo_,__LINE__)(A); void CONCAT(foo_,__LINE__)(B); + +/* Test with assignment */ +#define CMP_TYPES_1(A, B)\ + do {\ + A p; B q;\ +_Pragma("warn(error, on)")\ + p = q;\ +_Pragma("warn(error, off)")\ + } while (0) + +/* Test with conditional expression */ +#define CMP_TYPES_2(A, B)\ + do {\ + A p; B q;\ +_Pragma("warn(error, on)")\ + v = v ? p : q;\ +_Pragma("warn(error, off)")\ + } while (0) + +/* Test with function prototype */ +#define CMP_TYPES_3(A, B)\ + do {\ + DECL_FUNCS(A,B);\ + } while (0) + +static void *v; + +typedef int (*p1)[3]; /* pointer to array */ +typedef int **q1; /* pointer to pointer */ +typedef int (**p2)[3]; /* pointer to pointer to array */ +typedef int ***q2; /* pointer to pointer to pointer */ +typedef int p3[1][3]; /* array of array */ +typedef int *q3[1]; /* array of pointer */ +typedef int const **p4; /* pointer to pointer to const */ +typedef int **q4; /* pointer to pointer to non-const */ +typedef int (*p5)(int (*)(p3)); /* pointer to function taking pointer to function taking pointer to array */ +typedef int (*q5)(int (*)(q3)); /* pointer to function taking pointer to function taking pointer to pointer */ + +int main(void) +{ + /* Warnings */ + TEST_1_SUB_1_A(p1, q1); + TEST_1_SUB_1_B(q1, p1); + TEST_1_SUB_2_A(p2, q2); + TEST_1_SUB_2_B(q2, p2); + /* TEST_1_SUB_3_A(p3, q3); */ + /* TEST_1_SUB_3_B(q3, p3); */ + TEST_1_SUB_4_A(p4, q4); + TEST_1_SUB_4_B(q4, p4); + TEST_1_SUB_5_A(p5, q5); + TEST_1_SUB_5_B(q5, p5); + + /* GCC and clang give warnings while cc65 gives errors */ + TEST_2_SUB_1(p1, q1); + TEST_2_SUB_2(p2, q2); + TEST_2_SUB_3(p3, q3); + TEST_2_SUB_4(p4, q4); + TEST_2_SUB_5(p5, q5); + + /* Errors */ + TEST_3_SUB_1(p1, q1); + TEST_3_SUB_2(p2, q2); + TEST_3_SUB_3(p3, q3); + TEST_3_SUB_4(p4, q4); + TEST_3_SUB_5(p5, q5); + + return 0; +} diff --git a/test/err/bug1895-cond1.c b/test/err/bug1895-cond1.c new file mode 100644 index 000000000..ac6a301c9 --- /dev/null +++ b/test/err/bug1895-cond1.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_2_SUB_1 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-cond2.c b/test/err/bug1895-cond2.c new file mode 100644 index 000000000..81b8b2912 --- /dev/null +++ b/test/err/bug1895-cond2.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_2_SUB_2 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-cond3.c b/test/err/bug1895-cond3.c new file mode 100644 index 000000000..f1571acbf --- /dev/null +++ b/test/err/bug1895-cond3.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_2_SUB_3 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-cond4.c b/test/err/bug1895-cond4.c new file mode 100644 index 000000000..e7ef77964 --- /dev/null +++ b/test/err/bug1895-cond4.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_2_SUB_4 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-cond5.c b/test/err/bug1895-cond5.c new file mode 100644 index 000000000..1625c599b --- /dev/null +++ b/test/err/bug1895-cond5.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_2_SUB_5 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-prototype1.c b/test/err/bug1895-prototype1.c new file mode 100644 index 000000000..30331e757 --- /dev/null +++ b/test/err/bug1895-prototype1.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_3_SUB_1 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-prototype2.c b/test/err/bug1895-prototype2.c new file mode 100644 index 000000000..c8fe213f4 --- /dev/null +++ b/test/err/bug1895-prototype2.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_3_SUB_2 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-prototype3.c b/test/err/bug1895-prototype3.c new file mode 100644 index 000000000..f7d2b79e4 --- /dev/null +++ b/test/err/bug1895-prototype3.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_3_SUB_3 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-prototype4.c b/test/err/bug1895-prototype4.c new file mode 100644 index 000000000..3d0fe2c05 --- /dev/null +++ b/test/err/bug1895-prototype4.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_3_SUB_4 + +#include "bug1895-common.h" diff --git a/test/err/bug1895-prototype5.c b/test/err/bug1895-prototype5.c new file mode 100644 index 000000000..ba9b997c9 --- /dev/null +++ b/test/err/bug1895-prototype5.c @@ -0,0 +1,5 @@ +/* Bug #1895 - missing diagnostics on incompatible pointer/array types */ + +#define DO_TEST_3_SUB_5 + +#include "bug1895-common.h" diff --git a/test/err/huge-integer-constant.c b/test/err/huge-integer-constant.c new file mode 100644 index 000000000..1f423347c --- /dev/null +++ b/test/err/huge-integer-constant.c @@ -0,0 +1,7 @@ +/* too big for internal integer representation */ +unsigned long huge = 4294967296; + +int main(void) +{ + return 0; +} diff --git a/test/err/integer-const-overflow.c b/test/err/integer-const-overflow.c new file mode 100644 index 000000000..37cc0f01e --- /dev/null +++ b/test/err/integer-const-overflow.c @@ -0,0 +1,20 @@ +/* Integer constant overflow warnings. */ + +/* Warnings as errors. */ +#pragma warn(error,on) + +/* Warn on const overflow */ +#pragma warn(const-overflow,on) + +unsigned char a = 256; +signed char b = 128; +unsigned char c = -129; +unsigned short int d = 0x00010000; +unsigned short int e = 0x80000000; +signed short int f = 32768L; +signed short int g = -32769L; + +int main(void) +{ + return 0; +} diff --git a/test/err/struct-duplicate-member.c b/test/err/struct-duplicate-member.c new file mode 100644 index 000000000..30cd06207 --- /dev/null +++ b/test/err/struct-duplicate-member.c @@ -0,0 +1,17 @@ +/* Ensure that a duplicate member in a struct produces an error. +** https://github.com/cc65/cc65/issues/2015 +*/ + +struct bads { + int a; + int a; /* this is an error */ +}; + +union badu { + int a, a; /* also an error */ +}; + +int main(void) +{ + return 0; +} diff --git a/test/misc/Makefile b/test/misc/Makefile index e6c58c5a4..c708b160b 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -58,6 +58,12 @@ $(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." @@ -126,6 +132,14 @@ $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) +# this one requires -Werror +$(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1768.$1.$2.prg) + $(CC65) -Werror -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) @@ -149,6 +163,11 @@ $(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 +# 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) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # the rest are tests that fail currently for one reason or another $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/misc/bug1768.c b/test/misc/bug1768.c new file mode 100644 index 000000000..35cee1049 --- /dev/null +++ b/test/misc/bug1768.c @@ -0,0 +1,147 @@ +/* + Copyright 2021-2022, 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 operations in unevaluated context resulted from 'sizeof' and + short-circuited code-paths in AND, OR and conditional operations. + + See also: + https://github.com/cc65/cc65/issues/1768#issuecomment-1175221466 +*/ + +#include <stdio.h> + +static int failures; + +#define TEST(EXPR)\ + {\ + int acc = 0;\ + acc += sizeof((EXPR), 0);\ + acc += (0 && (EXPR));\ + acc += (1 || (EXPR));\ + acc += (0 ? (EXPR) : 0);\ + acc += (1 ? 0 : (EXPR));\ + if (acc == 0) {\ + printf("acc = %d\n", acc);\ + ++failures;\ + }\ + } + +/* Division by zero/modulo with zero */ +void test_1(void) +{ + int i; + int j; + TEST((i / 0) | (j % 0)) +} + +/* Division by zero/modulo with zero */ +void test_2(void) +{ + int i; + int j; + TEST((i /= 0) | (j %= 0)) +} + +/* Shift by too wide counts */ +void test_3(void) +{ + int i; + int j; + TEST((i << 32) | (j >> 32)) +} + +/* Shift by too wide counts */ +void test_4(void) +{ + int i; + int j; + TEST((i <<= 32) | (j >>= 32)) +} + +/* Shift by negative counts */ +void test_5(void) +{ + int i; + int j; + TEST((i << -1) | (j >> -1)) +} + +/* Shift by negative counts */ +void test_6(void) +{ + int i; + int j; + TEST((i <<= -1) | (j >>= -1)) +} + +/* Shift bit-fields */ +void test_7(void) +{ + struct S { + long i : 24; /* Will be promoted to 32-bit integer in calculation */ + long j : 8; /* Will be promoted to 16-bit integer in calculation */ + } s; + long k; + + s.i = 1; + printf("%u\n", sizeof(s.i << 24)); + s.i = 2; + k = s.i << 16; + if (k != 0x00020000L) { + printf("k = %ld, expected: %ld\n", k, 0x00020000L); + } + TEST(s.j >> 16) +} + +/* Shift bit-fields */ +void test_8(void) +{ + struct S { + long i : 24; /* Will be promoted to 32-bit integer in calculation */ + long j : 8; /* Will be promoted to 16-bit integer in calculation */ + } s; + long k; + + s.i = 3; + printf("%u\n", sizeof(s.i << 24)); + s.i = 4; + k = s.i <<= 16; + if (k != 0x00040000L) { + printf("k = %ld, expected: %ld\n", k, 0x00040000L); + } + TEST(s.j >>= 8) +} + +/* Do all tests */ +int main(void) +{ + test_1(); + test_2(); + test_3(); + test_4(); + test_5(); + test_6(); + test_7(); + test_8(); + + printf("Failures: %d\n", failures); + return failures; +} diff --git a/test/misc/struct-by-value.c b/test/misc/struct-by-value.c new file mode 100644 index 000000000..fc44f8729 --- /dev/null +++ b/test/misc/struct-by-value.c @@ -0,0 +1,156 @@ +/* This test ensures that compilation fails if a 3-byte struct by value +** is attempted, to avoid re-introducting a bug by accident: +** https://github.com/cc65/cc65/issues/2022 +** When 3-byte structs are re-enabled, this test will compile, +** which should trigger a "misc" test failure. +** When this happens: +** Delete this comment from the top. +** Replace test/val/struct-by-value.c with this one. +** See: +** https://github.com/cc65/cc65/issues/2086 +*/ + +/* Test of passing and returning structs by value. + Structs of 1, 2, 3, 4 bytes are supported. + Note that structs of 3 bytes had a past issue: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +struct s3 return3() { return c3; } +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +} + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +} + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3); + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + fail += pass3(c3); + fail += pass4(c4); + + return fail; +} diff --git a/test/readme.txt b/test/readme.txt index dd87ea9df..d3f17148e 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -20,6 +20,28 @@ compiler is working as expected (when the tests behave as described): library. /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 + be tweaked to produce the same output as on the host in the cases where + it would be different. + + The Makefile also handles some special cases (add the tests to the + respective list in the makefile): + + - Sometimes we want to check the warnings produced by the compiler. In + that case use the CUSTOMSOURCES list. Whatever output the compiler writes + to stderr will be compared against the matching .cref file. There is an + example in custom-reference.c/.cref + + - Sometimes we want to check what kind of output the compiler produces + for a file that does not compile. In that case use the ERRORSOURCES list. + There is an example in custom-reference-error.c/.cref + + Warning: please understand that comparing the compiler output against + a reference produces a moving target, ie the tests may break randomly + at any time when the compiler output changes for whatever reason. So + only ever use this as a last resort when something can not be tested by + other means. /err - contains tests that MUST NOT compile @@ -46,7 +68,11 @@ compiler is working as expected (when the tests behave as described): which will require additional changes to the makefile(s). -To run the tests use "make" in this (top) directory, the makefile should exit +These tests only require a subset of the platform libraries. In the (top) +directory above this one, "make libtest" can be used to build only those +libraries needed for testing, instead of "make lib". + +To run the tests use "make" in this (test) directory, the makefile should exit with no error. When a test failed you can use "make continue" to run further tests. diff --git a/test/ref/Makefile b/test/ref/Makefile index d9c9817ee..abd3e9bc0 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -11,12 +11,14 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) + COPY = copy $(subst /,\,$1) $(subst /,\,$2) else S = / EXE = NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 + COPY = cp $1 $2 endif ifdef QUIET @@ -42,24 +44,48 @@ CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow .PHONY: all clean -SOURCES := $(wildcard *.c) +# list of sources that produces warnings that we want to check. a .cref file +# containing the exact output is required. +CUSTOMSOURCES = \ + custom-reference.c + +# list of sources that produce a compiler error. a .cref files containing the +# exact error output is required +ERRORSOURCES = \ + custom-reference-error.c \ + bug1889-missing-identifier.c + +SOURCES := $(filter-out $(CUSTOMSOURCES) $(ERRORSOURCES),$(wildcard *.c)) + REFS = $(SOURCES:%.c=$(WORKDIR)/%.ref) +CUSTOMREFS = $(CUSTOMSOURCES:%.c=$(WORKDIR)/%.cref) $(ERRORSOURCES:%.c=$(WORKDIR)/%.cref) + TESTS = $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).6502.prg)) TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02.prg)) -all: $(REFS) $(TESTS) +CUSTOMTESTS = $(foreach option,$(OPTIONS),$(CUSTOMSOURCES:%.c=$(WORKDIR)/%.$(option).6502.custom.prg)) +CUSTOMTESTS += $(foreach option,$(OPTIONS),$(CUSTOMSOURCES:%.c=$(WORKDIR)/%.$(option).65c02.custom.prg)) + +ERRORTESTS = $(foreach option,$(OPTIONS),$(ERRORSOURCES:%.c=$(WORKDIR)/%.$(option).6502.error.prg)) +ERRORTESTS += $(foreach option,$(OPTIONS),$(ERRORSOURCES:%.c=$(WORKDIR)/%.$(option).65c02.error.prg)) + +all: $(CUSTOMREFS) $(REFS) $(TESTS) $(CUSTOMTESTS) $(ERRORTESTS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) +$(ISEQUAL): ../isequal.c | $(WORKDIR) + $(CC) $(CFLAGS) -o $@ $< + +$(WORKDIR)/%.cref: %.cref | $(WORKDIR) + $(if $(QUIET),echo ref/$*.cref) + $(call COPY,$*.cref,$@) + $(WORKDIR)/%.ref: %.c | $(WORKDIR) $(if $(QUIET),echo ref/$*.host) $(CC) $(CFLAGS) -o $(WORKDIR)/$*.host $< $(NULLERR) $(WORKDIR)$S$*.host > $@ -$(ISEQUAL): ../isequal.c | $(WORKDIR) - $(CC) $(CFLAGS) -o $@ $< - # "yaccdbg.c" includes "yacc.c". # yaccdbg's built files must depend on both of them. # @@ -78,8 +104,43 @@ $(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(ISEQUAL) endef # PRG_template +# extra template for the case when compilation works, but we still want to +# compare the warning output with our custom reference +define PRG_custom_template + +$(WORKDIR)/%.$1.$2.custom.prg: %.c $(WORKDIR)/%.ref %.c $(WORKDIR)/%.cref $(ISEQUAL) + $(if $(QUIET),echo cref/$$*.$1.$2.custom.prg) + -$(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.custom.prg=.s) $$< 2> $(WORKDIR)/$$*.$1.$2.cout + $(CA65) -t sim$2 -o $$(@:.custom.prg=.o) $$(@:.custom.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.custom.prg=.o) sim$2.lib $(NULLERR) + $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out + $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.cout $(WORKDIR)/$$*.cref + $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref + +endef # PRG_error_template + +# extra template for the case when compilation fails, but we still want to +# compare the error output with our custom reference +define PRG_error_template + +$(WORKDIR)/%.$1.$2.error.prg: %.c $(WORKDIR)/%.cref $(ISEQUAL) + $(if $(QUIET),echo cref/$$*.$1.$2.error.prg) + -$(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.error.prg=.s) $$< 2> $(WORKDIR)/$$*.$1.$2.cout +# $(CA65) -t sim$2 -o $$(@:.error.prg=.o) $$(@:.error.prg=.s) $(NULLERR) +# $(LD65) -t sim$2 -o $$@ $$(@:.error.prg=.o) sim$2.lib $(NULLERR) +# $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out + $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.cout $(WORKDIR)/$$*.cref + +endef # PRG_error_template + $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),6502))) $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) +$(foreach option,$(OPTIONS),$(eval $(call PRG_custom_template,$(option),6502))) +$(foreach option,$(OPTIONS),$(eval $(call PRG_custom_template,$(option),65c02))) + +$(foreach option,$(OPTIONS),$(eval $(call PRG_error_template,$(option),6502))) +$(foreach option,$(OPTIONS),$(eval $(call PRG_error_template,$(option),65c02))) + clean: @$(call RMDIR,$(WORKDIR)) diff --git a/test/ref/bug1889-missing-identifier.c b/test/ref/bug1889-missing-identifier.c new file mode 100644 index 000000000..d9cf4aa52 --- /dev/null +++ b/test/ref/bug1889-missing-identifier.c @@ -0,0 +1,9 @@ +/* bug 1889 - endless errors due to failure in recovery from missing identifier */ + +int enum { a } x; +inline enum { b }; + +int main(void) +{ + return 0; +} diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref new file mode 100644 index 000000000..cd3f76849 --- /dev/null +++ b/test/ref/bug1889-missing-identifier.cref @@ -0,0 +1,3 @@ +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 diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c new file mode 100644 index 000000000..c86a8b9e9 --- /dev/null +++ b/test/ref/custom-reference-error.c @@ -0,0 +1,21 @@ + +/* + this is an example (not actually a regression test) that shows how to + make a check that compares the compiler (error-) output with a provided + reference. + + to produce a reference file, first make sure your program "works" as intended, + then "make" in this directory once and copy the produced compiler output to + the reference: + + $ cp ../../testwrk/ref/custom-reference-error.g.6502.out custom-reference-error.cref + + and then "make" again to confirm +*/ + +int main(int argc, char* argv[]) +{ + printf("%02x", 0x42); + n = 0; /* produce an error */ + /* another error */ +} diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref new file mode 100644 index 000000000..fa584f307 --- /dev/null +++ b/test/ref/custom-reference-error.cref @@ -0,0 +1,5 @@ +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 diff --git a/test/ref/custom-reference.c b/test/ref/custom-reference.c new file mode 100644 index 000000000..5d9c356df --- /dev/null +++ b/test/ref/custom-reference.c @@ -0,0 +1,24 @@ + +/* + this is an example (not actually a regression test) that shows how to + make a check that compares the compiler (error-) output with a provided + reference. + + to produce a reference file, first make sure your program "works" as intended, + then "make" in this directory once and copy the produced compiler output to + the reference: + + $ cp ../../testwrk/ref/custom-reference.g.6502.out custom-reference.cref + + and then "make" again to confirm +*/ + +#include <stdint.h> +#include <stdio.h> + +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 new file mode 100644 index 000000000..4dba6009b --- /dev/null +++ b/test/ref/custom-reference.cref @@ -0,0 +1,2 @@ +custom-reference.c:24: Warning: Parameter 'argc' is never used +custom-reference.c:24: Warning: Parameter 'argv' is never used diff --git a/test/standard/Makefile b/test/standard/Makefile index 054623b79..bf513c84e 100644 --- a/test/standard/Makefile +++ b/test/standard/Makefile @@ -22,7 +22,7 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 -c +SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) diff --git a/test/todo/Makefile b/test/todo/Makefile index 17561f8f4..062b899ce 100644 --- a/test/todo/Makefile +++ b/test/todo/Makefile @@ -31,7 +31,7 @@ CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) -WORKDIR = ../../testwrk/val +WORKDIR = ../../testwrk/todo OPTIONS = g O Os Osi Osir Osr Oi Oir Or @@ -49,7 +49,7 @@ $(WORKDIR): define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) - $(if $(QUIET),echo val/$$*.$1.$2.prg) + $(if $(QUIET),echo todo/$$*.$1.$2.prg) $(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.prg=.s) $$< $(NULLERR) $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) diff --git a/test/todo/inline-asm-1489.c b/test/todo/inline-asm-1489.c new file mode 100644 index 000000000..57699ab0a --- /dev/null +++ b/test/todo/inline-asm-1489.c @@ -0,0 +1,109 @@ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#define testasm1(C) (__AX__ = (C), \ + asm("and #$3f"),\ + __AX__) + +#define testasm2(C) (__A__ = (C), \ + asm("and #$3f"),\ + __A__) + +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 }; + + +uint8_t ref1[32] = { 0x10, 0x01, 0x22, 0x03, 0x34, 0x35, 0x26, 0x07, 0, 0 }; +uint8_t ref2[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0, 0 }; + +uint8_t dest[32]; +int res = 0; + +void dotest1a(uint8_t *s, uint8_t *d) +{ + printf("dotest1a\n"); + while (*s != 0) { + *d = (testasm1(*s)); +// printf("%04x:%02x\n",d,*d); + d++; + s++; + } +} + +void dotest1b(uint8_t *s, uint8_t *d) +{ + printf("dotest1b\n"); + while (*s != 0) { + *d = (testasm2(*s)); + d++; + s++; + } +} + +void dotest2a (void) +{ + char *p = &src2[0]; + uint16_t scaddr=&dest[0]; //output to line 11 on the screen + + printf("dotest2a\n"); + while (*p != 0) { + (*(unsigned char *)(scaddr++)=(testasm1(*p))); + p++; + } +} + +void dotest2b (void) +{ + char *p = &src2[0]; + uint16_t scaddr=&dest[0]; //output to line 11 on the screen + + printf("dotest2b\n"); + while (*p != 0) { + (*(unsigned char *)(scaddr++)=(testasm2(*p))); + p++; + } +} + +int docmp(uint8_t *s, uint8_t *r) +{ + int res = 0; + while (*s != 0) { +// printf("is %02x\n", *s); + if (*r != *s) { + printf("is %02x expected %02x\n", *s, *r); + res++; + } + r++; + s++; + } + return res; +} + +int main(void) +{ + memset(dest, 0, 10); + memset(dest, 0x11, 8); + dotest1a(src, dest); + res = docmp(dest, ref1); + + memset(dest, 0, 10); + memset(dest, 0x22, 8); + dotest2a(); + res += docmp(&dest[0], &ref2[0]); + + memset(dest, 0, 10); + memset(dest, 0x33, 8); + dotest1b(&src[0], &dest[0]); + res = docmp(&dest[0], &ref1[0]); + + memset(dest, 0, 10); + memset(dest, 0x44, 8); + dotest2b(); + res += docmp(&dest[0], &ref2[0]); + + printf("res: %02x\n", res); + return res; +} diff --git a/test/todo/sprintf-test.c b/test/todo/sprintf-test.c index bd5de44b4..ea50f418a 100644 --- a/test/todo/sprintf-test.c +++ b/test/todo/sprintf-test.c @@ -564,9 +564,9 @@ int main (void) /* Output the result */ if (Failures) { - printf ("%u tests, %u failures\n", Tests, Failures); + printf ("sprintf-test: %u tests, %u failures\n", Tests, Failures); } else { - printf ("%u tests: Ok\n", Tests); + printf ("sprintf-test: %u tests: Ok\n", Tests); } /* Wait for a key so we can read the result */ diff --git a/test/val/Makefile b/test/val/Makefile index acac670cb..56d8e5ff9 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -9,11 +9,13 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) + CATRES = else S = / NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 + CATRES = || (cat $(WORKDIR)/$$@.out && false) endif ifdef QUIET @@ -22,7 +24,7 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 5000000000 -c +SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) @@ -51,7 +53,7 @@ $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) $(CC65) -t sim$2 $$(CC65FLAGS) --add-source -$1 -o $$(@:.prg=.s) $$< $(NULLERR) $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) - $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$@.out + $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$@.out $(CATRES) endef # PRG_template diff --git a/test/val/add4.c b/test/val/add4.c index f02a7fb9c..2371aea69 100644 --- a/test/val/add4.c +++ b/test/val/add4.c @@ -22,7 +22,6 @@ long long0 = 0; long long1 = 0; unsigned long ulong0 = 0; unsigned long ulong1 = 0; -#define NULL 0 char *cP0=NULL; char *cP1=NULL; int *iP0=NULL; diff --git a/test/val/assign-use1.c b/test/val/assign-shift-use1.c similarity index 100% rename from test/val/assign-use1.c rename to test/val/assign-shift-use1.c diff --git a/test/val/fields.c b/test/val/bitfield-1.c similarity index 100% rename from test/val/fields.c rename to test/val/bitfield-1.c diff --git a/test/val/char-bitfield.c b/test/val/bitfield-char-1.c similarity index 100% rename from test/val/char-bitfield.c rename to test/val/bitfield-char-1.c diff --git a/test/val/call1.c b/test/val/boolean-call1.c similarity index 100% rename from test/val/call1.c rename to test/val/boolean-call1.c diff --git a/test/val/bug1047.c b/test/val/bug1047-bitfield-char.c similarity index 100% rename from test/val/bug1047.c rename to test/val/bug1047-bitfield-char.c diff --git a/test/val/bug1094.c b/test/val/bug1094-nested-init.c similarity index 100% rename from test/val/bug1094.c rename to test/val/bug1094-nested-init.c diff --git a/test/val/bug1095.c b/test/val/bug1095-bitfield-signed.c similarity index 100% rename from test/val/bug1095.c rename to test/val/bug1095-bitfield-signed.c diff --git a/test/val/bug1139.c b/test/val/bug1139-bitfield-in-if.c similarity index 100% rename from test/val/bug1139.c rename to test/val/bug1139-bitfield-in-if.c diff --git a/test/val/bug1178.c b/test/val/bug1178-struct-copy.c similarity index 100% rename from test/val/bug1178.c rename to test/val/bug1178-struct-copy.c diff --git a/test/val/bug1181.c b/test/val/bug1181-struct-field-null-cmp.c similarity index 100% rename from test/val/bug1181.c rename to test/val/bug1181-struct-field-null-cmp.c diff --git a/test/val/bug1252.c b/test/val/bug1252.c new file mode 100644 index 000000000..ab4fd21fc --- /dev/null +++ b/test/val/bug1252.c @@ -0,0 +1,45 @@ + +// bug #1252 - inline asm: cc65 chokes on label ref if space/tab follows + +#include <stdlib.h> + +int main(void) +{ +// first test the recommended way to use labels in inline assembly: + +// this works +c_label: + asm("inx\n" + "bne %g\n", + c_label); + +// this does not work +c_label2: + asm("inx\n" + "bne %g \n", + c_label2); + +// now the following is from the original bug report. note that using labels +// this way only works by chance - the name of the label may clash with +// generated labels + +// this works + asm("label1: inx\n" + "bne label1\n"); + +// this does not work + asm("label2: inx\n" + "bne label2 \n"); + +// a variant of the above using local labels. + +// this works + asm("@label1: inx\n" + "bne @label1\n"); + +// this does not work + asm("@label2: inx\n" + "bne @label2 \n"); + + return EXIT_SUCCESS; +} diff --git a/test/val/bug1267.c b/test/val/bug1267-bitfield-typedef-signedness.c similarity index 100% rename from test/val/bug1267.c rename to test/val/bug1267-bitfield-typedef-signedness.c diff --git a/test/val/bug1332.c b/test/val/bug1332-bitfield.c similarity index 100% rename from test/val/bug1332.c rename to test/val/bug1332-bitfield.c diff --git a/test/misc/bug1357.c b/test/val/bug1357-pp.c similarity index 100% rename from test/misc/bug1357.c rename to test/val/bug1357-pp.c diff --git a/test/val/bug1373.c b/test/val/bug1373.c new file mode 100644 index 000000000..f13f375c1 --- /dev/null +++ b/test/val/bug1373.c @@ -0,0 +1,54 @@ + +/* #1373 - #pragma charmap works in unexpected ways */ + +#include <stdio.h> +#include <string.h> + +char res0[10]; +char res1[10]; +char res2[10]; +char res3[10]; +char res4[10]; + +int err = 0; + +#pragma charmap(0x61, 0x44) +#define STRING_A "abABa" + +extern char mappedA[10]; + +#pragma charmap(0x61, 0x61) +char notmappedA[10] = "abABa"; + +void test(void); + +#pragma charmap(0x61, 0x42) +int main(void) +{ + char mappedB[10] = STRING_A; + sprintf(res0, "abABa"); /* expected: BbABB */ + +#pragma charmap(0x61, 0x61) + sprintf(res1, mappedA); /* expected: CbABC */ + sprintf(res2, STRING_A); /* expected: abABa */ + sprintf(res3, mappedB); /* expected: BbABB */ + +#pragma charmap(0x61, 0x43) + sprintf(res4, notmappedA); /* expected: abABa */ + + test(); + + return err; +} + +char mappedA[10] = "abABa"; + +#pragma charmap(0x61, 0x61) +void test(void) +{ + puts(res0); if (strcmp(res0, "BbABB") != 0) { puts("expected: BbABB"); err++; } + puts(res1); if (strcmp(res1, "CbABC") != 0) { puts("expected: CbABC"); err++; } + puts(res2); if (strcmp(res2, "abABa") != 0) { puts("expected: abABa"); err++; } + puts(res3); if (strcmp(res3, "BbABB") != 0) { puts("expected: BbABB"); err++; } + puts(res4); if (strcmp(res4, "abABa") != 0) { puts("expected: abABa"); err++; } +} diff --git a/test/val/bug1408.c b/test/val/bug1408.c index 8ecc1be68..137899315 100644 --- a/test/val/bug1408.c +++ b/test/val/bug1408.c @@ -1,41 +1,41 @@ -/* Bug #1408: Signed char type comparisons with unsigned numeric constants */ - -#include <stdio.h> - -static int failures = 0; -static signed char x = -1; - -int main(void) -{ - if (!(x > -2u)) { - printf("x > -2u should be true\n"); - ++failures; - } - if (!(x > 0u)) { - printf("x > 0u should be true\n"); - ++failures; - } - if (!(x > 255u)) { - printf("x > 255u should be true\n"); - ++failures; - } - - if (!(-2u < x)) { - printf("-2u < x should be true\n"); - ++failures; - } - if (!(0u < x)) { - printf("0u < x should be true\n"); - ++failures; - } - if (!(255u < x)) { - printf("255u < x should be true\n"); - ++failures; - } - - if (failures != 0) { - printf("Failures: %d\n", failures); - } - - return failures; -} +/* Bug #1408: Signed char type comparisons with unsigned numeric constants */ + +#include <stdio.h> + +static int failures = 0; +static signed char x = -1; + +int main(void) +{ + if (!(x > -2u)) { + printf("x > -2u should be true\n"); + ++failures; + } + if (!(x > 0u)) { + printf("x > 0u should be true\n"); + ++failures; + } + if (!(x > 255u)) { + printf("x > 255u should be true\n"); + ++failures; + } + + if (!(-2u < x)) { + printf("-2u < x should be true\n"); + ++failures; + } + if (!(0u < x)) { + printf("0u < x should be true\n"); + ++failures; + } + if (!(255u < x)) { + printf("255u < x should be true\n"); + ++failures; + } + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + + return failures; +} diff --git a/test/val/bug1451.c b/test/val/bug1451-struct-ptr-to-local.c similarity index 94% rename from test/val/bug1451.c rename to test/val/bug1451-struct-ptr-to-local.c index f9cca2561..2148e034d 100644 --- a/test/val/bug1451.c +++ b/test/val/bug1451-struct-ptr-to-local.c @@ -1,39 +1,39 @@ -/* Bug #1451 - local struct field access via the address of the struct */ - -#include <stdio.h> - -typedef struct { - int a; - int b; -} S; - -int failures = 0; - -int main(void) -{ - S a = {2, 5}; - S b = {1, 4}; - S m[1] = {{6, 3}}; - S *p = &a; - - (&a)->a += b.a; - p->b += b.b; - m->a += b.a; - - if ((&a)->a != 3) { - ++failures; - printf("Expected 3, got %d\n", (&a)->a); - } - - if (p->b != 9) { - ++failures; - printf("Expected 9, got %d\n", p->b); - } - - if (m->a != 7) { - ++failures; - printf("Expected 7, got %d\n", m->a); - } - - return failures; -} +/* Bug #1451 - local struct field access via the address of the struct */ + +#include <stdio.h> + +typedef struct { + int a; + int b; +} S; + +int failures = 0; + +int main(void) +{ + S a = {2, 5}; + S b = {1, 4}; + S m[1] = {{6, 3}}; + S *p = &a; + + (&a)->a += b.a; + p->b += b.b; + m->a += b.a; + + if ((&a)->a != 3) { + ++failures; + printf("Expected 3, got %d\n", (&a)->a); + } + + if (p->b != 9) { + ++failures; + printf("Expected 9, got %d\n", p->b); + } + + if (m->a != 7) { + ++failures; + printf("Expected 7, got %d\n", m->a); + } + + return failures; +} diff --git a/test/val/bug1462.c b/test/val/bug1462-biefield-assign-1.c similarity index 100% rename from test/val/bug1462.c rename to test/val/bug1462-biefield-assign-1.c diff --git a/test/val/bug1462-2.c b/test/val/bug1462-biefield-assign-2.c similarity index 100% rename from test/val/bug1462-2.c rename to test/val/bug1462-biefield-assign-2.c diff --git a/test/val/bug1462-3.c b/test/val/bug1462-biefield-assign-3.c similarity index 100% rename from test/val/bug1462-3.c rename to test/val/bug1462-biefield-assign-3.c diff --git a/test/val/bug1462-4.c b/test/val/bug1462-biefield-assign-4.c similarity index 95% rename from test/val/bug1462-4.c rename to test/val/bug1462-biefield-assign-4.c index f811ddbd6..e607dbd25 100644 --- a/test/val/bug1462-4.c +++ b/test/val/bug1462-biefield-assign-4.c @@ -1,6 +1,6 @@ /* issue #1462 - Bit-fields are still broken */ -/* More tests on "op= expression result value" that a naive fix might fail with */ +/* When (un-)signedness involves with integral promotion */ #include <stdio.h> #include <limits.h> diff --git a/test/val/bug1573.c b/test/val/bug1573.c new file mode 100644 index 000000000..47172c398 --- /dev/null +++ b/test/val/bug1573.c @@ -0,0 +1,81 @@ +/* Tests for predefined macros __LINE__ and __FILE__ as well as #line control */ + +#include <stdio.h> +#include <string.h> + +static int failures = 0; + +#if !defined __LINE__ +#error __LINE__ is not predefined! +#endif + +#if !defined __FILE__ +#error __FILE__ is not predefined! +#endif + +#define CONCAT(a,b) CONCAT_impl_(a,b) +#define CONCAT_impl_(a,b) a##b +#define MKSTR(a) MKSTR_impl_(a) +#define MKSTR_impl_(a) #a +char CONCAT(ident,__LINE__)[0+__LINE__]; +char CONCAT(ident,__LINE__)[0+__LINE__]; + +#define GET_FILE() __FILE__ +#define THIS_FILENAME_1 "bug1573.c" +#define THIS_FILENAME_2 "<<bar>>" +#define INC_FILENAME_1 "bug1573.h" +#define INC_FILENAME_2 "<<foo>>" + +#line __LINE__ THIS_FILENAME_1 /* Note: #line sets the line number of the NEXT line */ +void foo(void) +{ + if (strcmp (GET_FILE(), THIS_FILENAME_1) != 0) { + printf("Expected: %s, got: %s\n", THIS_FILENAME_1, GET_FILE()); + ++failures; + } +} + +#line __LINE__ THIS_FILENAME_2 /* Note: #line sets the line number of the NEXT line */ +#include INC_FILENAME_1 +long line2 = __LINE__; + +int main(void) +{ + if (strcmp (filename1, INC_FILENAME_1) != 0) { + printf("Expected filename1: %s, got: %s\n", INC_FILENAME_1, filename1); + ++failures; + } + + if (strcmp (filename2, INC_FILENAME_2) != 0) { + printf("Expected filename2: %s, got: %s\n", INC_FILENAME_2, filename2); + ++failures; + } + + foo(); + +#line 65535 + if (strcmp (GET_FILE(), THIS_FILENAME_2) != 0) { + printf("Expected: %s, got: %s\n", THIS_FILENAME_2, GET_FILE()); + ++failures; + } + + if (line1 != 5L) { + printf("Expected line1: %ld, got: %ld\n", 5L, line1); + ++failures; + } + + if (line2 != 38L) { + printf("Expected line2: %ld, got: %ld\n", 38L, line2); + ++failures; + } + + if (strcmp (ans1, ans2) != 0 || strcmp (ans1, "42") != 0) { + ++failures; + printf("Expected: 42, ans1: %s, ans2: %s\n", ans1, ans2); + } + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + return failures; +} diff --git a/test/val/bug1573.h b/test/val/bug1573.h new file mode 100644 index 000000000..4e64b5ca2 --- /dev/null +++ b/test/val/bug1573.h @@ -0,0 +1,14 @@ +/* Tests for predefined macros __LINE__ and __FILE__ as well as #line control */ + +#line __LINE__ INC_FILENAME_1 +#define GET_LINE() __LINE__ +char filename1[] = GET_FILE(); +long line1 = GET_LINE(); + +#line 42 +const char ans1[] = MKSTR(GET_LINE()); + +#line 40 INC_FILENAME_2 +char filename2[] = GET_FILE(); +const char ans2[] = \ +MKSTR(GET_LINE()); diff --git a/test/val/bug1643.c b/test/val/bug1643.c new file mode 100644 index 000000000..c6237b7fb --- /dev/null +++ b/test/val/bug1643.c @@ -0,0 +1,12 @@ +/* bug #1643, macro expansion in #include */ + +#define MKSTR(a) MKSTR_IMPL(a) +#define MKSTR_IMPL(a) #a +#define BUG1643_H bug1643.h + +#include MKSTR(BUG1643_H) + +int main(void) +{ + return BUG1643_RESULT; +} diff --git a/test/val/bug1643.h b/test/val/bug1643.h new file mode 100644 index 000000000..068263436 --- /dev/null +++ b/test/val/bug1643.h @@ -0,0 +1,13 @@ +/* bug #1643, macro expansion in #include */ + +#define STDIO_H <stdio.h> +#include STDIO_H + +#ifdef string +#undef string +#endif + +#define string 0!%^&*/_= +#include <string.h> + +#define BUG1643_RESULT 0 diff --git a/test/val/bug1690.c b/test/val/bug1690.c index 499dc6b35..78c0cda41 100644 --- a/test/val/bug1690.c +++ b/test/val/bug1690.c @@ -1,30 +1,30 @@ -/* OptCmp1 messed up with labels */ - -#include <stdio.h> - -static int failures = 0; -static unsigned int z = 0xFF23; - -int main(void) -{ - register unsigned int x = 0x200; - register unsigned int y = 0; - - do { - ++y; - } while (--x); - if (y != 0x200) { - printf("y should be 0x200, not 0x%X.\n", y); - ++failures;; - } - - if ((z -= 0x23)) { - /* Passed -- non-zero z looks like non-zero. */ - } else { - /* Failed -- only the low byte of z was tested. */ - printf("Test thinks non-zero z is zero.\n"); - ++failures; - } - - return failures; -} +/* OptCmp1 messed up with labels */ + +#include <stdio.h> + +static int failures = 0; +static unsigned int z = 0xFF23; + +int main(void) +{ + register unsigned int x = 0x200; + register unsigned int y = 0; + + do { + ++y; + } while (--x); + if (y != 0x200) { + printf("y should be 0x200, not 0x%X.\n", y); + ++failures;; + } + + if ((z -= 0x23)) { + /* Passed -- non-zero z looks like non-zero. */ + } else { + /* Failed -- only the low byte of z was tested. */ + printf("Test thinks non-zero z is zero.\n"); + ++failures; + } + + return failures; +} diff --git a/test/val/bug1822-pptest.c b/test/val/bug1822-pptest.c new file mode 100644 index 000000000..133d69f6b --- /dev/null +++ b/test/val/bug1822-pptest.c @@ -0,0 +1,25 @@ +/* Bug #1822 - Redefined macros failed to be all undefined with a single #undef */ + +#undef F +#undef F + +#define F 1 +#define F 1 + +#undef F +#if defined F +#error #undef F fails! +#endif + +#define F 0 + +#include <stdio.h> + +int main(void) +{ + if (F != 0) + { + printf("failed: F = %d\n", F); + } + return F; +} diff --git a/test/val/bug1838.c b/test/val/bug1838.c new file mode 100644 index 000000000..38becf5e9 --- /dev/null +++ b/test/val/bug1838.c @@ -0,0 +1,35 @@ +/* Bug 1838 - function parameters declared as function types rather than function pointers */ + +#include <stdio.h> + +static int failures = 0; + +typedef int fn_t(int); + +int main(void) +{ + void foo(fn_t*); + fn_t bar; + + foo(bar); + return failures; +} + +void foo(int func(int)) +{ + int n = func(42); + + if (n != 12) { + printf("n = %d, expected: 12\n", n); + ++failures; + } +} + +int bar(int a) +{ + if (a != 42) { + printf("a = %d, expected: 42\n", a); + ++failures; + } + return 12; +} diff --git a/test/val/bug1847-struct-field-access.c b/test/val/bug1847-struct-field-access.c new file mode 100644 index 000000000..f7e19e40b --- /dev/null +++ b/test/val/bug1847-struct-field-access.c @@ -0,0 +1,46 @@ +/* Bug #1847 - struct field access */ + +#include <stdio.h> + +struct TestStruct { + char a; + char b; + char c; +}; + +struct TestStruct s0[2] = { {0xFF, 0, 0xFF}, {0, 0x42, 0xFF} }; +struct TestStruct* s0Ptr = s0; + +#define TEST_READ_SUB(X, E) \ + if ((X) != (E)) { \ + printf(#X ": 0x%X, expected: 0x%X\n", (X), (E)); \ + ++failures; \ + } + +#define TEST_READ(S, I, F, E) \ + TEST_READ_SUB(S[I].F, E) \ + TEST_READ_SUB((&S[I])->F, E) \ + TEST_READ_SUB((&S[I])[0].F, E) \ + TEST_READ_SUB(S##Ptr[I].F, E) \ + TEST_READ_SUB((&S##Ptr[I])->F, E) \ + TEST_READ_SUB((&(S##Ptr[I]))[0].F, E) \ + TEST_READ_SUB((&(*S##Ptr))[I].F, E) \ + TEST_READ_SUB((&(*S##Ptr)+I)->F, E) \ + TEST_READ_SUB((S##Ptr+I)->F, E) \ + TEST_READ_SUB((S##Ptr+I)[0].F, E) + +static unsigned failures = 0; + +int main(void) { + struct TestStruct s1[2] = { {0xFF, 0, 0xFF}, {0, 42, 0xFF} }; + struct TestStruct* s1Ptr = s1; + + TEST_READ(s0, 1, b, 0x42) + TEST_READ(s1, 1, b, 42) + + if (failures > 0) { + printf("Failures: %u\n", failures); + } + + return failures; +} diff --git a/test/val/bug1853-inline-asm.c b/test/val/bug1853-inline-asm.c new file mode 100644 index 000000000..9813566dc --- /dev/null +++ b/test/val/bug1853-inline-asm.c @@ -0,0 +1,21 @@ + +/* #1853 - Regression on inline assembly expression evaluation */ + +int main(void) +{ +/* +compiles with e.g. Git 2f4e2a3 to the expected + + lda 1 + lda 1 + 1 + rts + +However, with the current HEAD, it compiles to + + lda 1 + lda +*/ + __asm__("lda 1"); + __asm__("lda 1 + 1"); + return 0; +} diff --git a/test/val/bug1891.c b/test/val/bug1891.c new file mode 100644 index 000000000..0373ba46d --- /dev/null +++ b/test/val/bug1891.c @@ -0,0 +1,19 @@ +/* bug #1891 - backslash/newline sequence in string constants is treated wrong */ + +#include <stdio.h> +#include <string.h> + +const char* a = "hello \ +world"; +const char* b = \ +"hello world"; + +int main(void) +{ + if (strcmp(a, b) != 0) { + printf("a:\n%s\n", a); + printf("b:\n%s\n", b); + return 1; + } + return 0; +} diff --git a/test/val/bug1933.c b/test/val/bug1933.c new file mode 100644 index 000000000..4715f38fe --- /dev/null +++ b/test/val/bug1933.c @@ -0,0 +1,9 @@ + +/* bug #1933 - wrong printf specifier breaks data lines */ + +unsigned char info_signature[3] = {3, 21, 63 | 0x80}; + +int main(void) +{ + return 0; +} diff --git a/test/val/bug1936.c b/test/val/bug1936.c new file mode 100644 index 000000000..e97231797 --- /dev/null +++ b/test/val/bug1936.c @@ -0,0 +1,88 @@ + +/* bug #1936 - Compiler produces broken Assembly (129 operand for bne) */ + +#include <stdint.h> + +static uint8_t item_counter; + + +static uint8_t freeze; +static uint8_t powerUp; + +static uint8_t wall_appeared; +static uint8_t freeze_locked; +static uint8_t zombie_locked; + + +struct ItemStruct +{ + uint8_t _active; + void(*_effect)(void); +} ; +typedef struct ItemStruct Item; + +static Item freezeItem; +static Item powerUpItem; +static Item wallItem; +static Item zombieItem; + + +static Item extraPointsItem[1]; + + +uint8_t find_inactive(Item* itemArray) +{ +} + + +void drop_item(register Item *item, uint8_t max_counter) +{ +} + + +void handle_item_drop(void) +{ + { + if(item_counter==1) + { + if(!powerUpItem._active) + { + drop_item(&powerUpItem,35); + } + } + else if((!freeze_locked)&&(!freeze)) + { + if(!freezeItem._active) + { + drop_item(&freezeItem,45); + } + } + else if(!wall_appeared&&(powerUp>=9)) + { + if(!wallItem._active) + { + drop_item(&wallItem,35); + } + } + else if(!zombie_locked && !zombieItem._active) + { + drop_item(&zombieItem,50); + } + else + { + uint8_t index; + + index = find_inactive(extraPointsItem); + if(index!=1) // REMARK: compilation does not fail with 0 + { + drop_item(&extraPointsItem[index],90); + } + } + } +} + +int main(void) +{ + return 0; +} + diff --git a/test/val/bug1941-shift-by-zero.c b/test/val/bug1941-shift-by-zero.c new file mode 100644 index 000000000..eaeba814f --- /dev/null +++ b/test/val/bug1941-shift-by-zero.c @@ -0,0 +1,28 @@ +/* Bug 1941 - Bitwise shift char types by 0 count results in out-of-range access */ + +#include <stdio.h> +#include <stdint.h> + +uint8_t foo = 42U; /* "Low byte" */ +uint8_t goo = 1U; /* "High byte" - you need it to reproduce the issue */ +int16_t bar = 256; /* ...or just do it with this */ + +_Static_assert (sizeof (foo >> 0) == sizeof (int), "Shift result should be int-promoted"); +_Static_assert (sizeof ((int8_t)bar << 0) == sizeof (int), "Shift result should be int-promoted"); + +unsigned failures; + +int main(void) +{ + if (foo >> 0 != foo) { + ++failures; + printf("foo failed\n"); + } + + if ((int8_t)bar << 0 != (int8_t)bar) { + ++failures; + printf("bar failed\n"); + } + + return failures; +} diff --git a/test/val/bug1989.c b/test/val/bug1989.c new file mode 100644 index 000000000..b3e3d4c8d --- /dev/null +++ b/test/val/bug1989.c @@ -0,0 +1,40 @@ + +/* bug #1989 - OptStackOps Opt_a_toscmpbool bypassed a comparison, discovered in 544a49c */ + +#include <stdlib.h> + +unsigned char i,r,j; + +void fail() // for the r=0 case, the == comparison was getting jumped over by OptStackOps +{ + if ((i & 0x1f) == (r ? 0 : 16)) j -=8; +} + +void pass() +{ + if ((i & 0x1f) == (unsigned char)(r ? 0 : 16)) j -= 8; +} + +void test(unsigned char ti, unsigned char tr, unsigned char tj) +{ + unsigned char rj; + i = ti; + r = tr; + j = tj; + pass(); + rj = j; + i = ti; + r = tr; + j = tj; + fail(); + if (j != rj) exit(1); +} + +int main(void) +{ + test( 1,0,33); + test( 0,0,33); + test( 1,1,33); + test(16,1,33); + return 0; +} diff --git a/test/val/bug2060.c b/test/val/bug2060.c new file mode 100644 index 000000000..59b4774a4 --- /dev/null +++ b/test/val/bug2060.c @@ -0,0 +1,56 @@ +/* Test of bug: https://github.com/cc65/cc65/issues/2060 */ + +#include <stdio.h> + +#define W 320 + +unsigned long test1(unsigned char* p, unsigned long n) +{ + (void)p; + return n; +} + +unsigned long test0(unsigned char* p, int x, int y, unsigned char b) +{ + (void)b; + return test1(p, (long)y * W + x); +} + +#define TEST(ta,tb) \ + expect = (long)tb * W + ta; \ + result = test0(p,ta,tb,0x56); \ + printf("%4d * %3d + %4d = %08lx",tb,W,ta,result); \ + if (expect != result) { printf(" expected: %08lx\n",expect); ++fail; } \ + else printf("\n"); + +int main(void) +{ + unsigned char* p = (unsigned char*)0x1234; + unsigned long expect, result; + int fail = 0; + + TEST(1,3); + TEST(50,60); + TEST(99,88); + TEST(128,102); + TEST(129,102); + TEST(320,102); + /* Bug 2060 indicated failure when y > 102. + Because: (y * 320) > 32767 + The promotion of x from int to long had an incorrect high word, + because it was done before loading x into AX, rather than after. + */ + TEST(0,103); + TEST(150,170); + TEST(300,180); + /* x < 0 also fails because its high word sign extend is incorrect. */ + TEST(-100,50); + TEST(-49,99); + TEST(-300,-180); + /* This passed despite the bug, because y * 320 coincidentally had the + same high word. + */ + TEST(-1,-1); + + return fail; +} diff --git a/test/val/bug2079-struct-assign.c b/test/val/bug2079-struct-assign.c new file mode 100644 index 000000000..b8a41fe8b --- /dev/null +++ b/test/val/bug2079-struct-assign.c @@ -0,0 +1,66 @@ + +/* test struct assignment, of structs with a length of 3, which happen to be + a special case eg when passing/returning structs + related to bugs #2022, #2079 */ + +#include <stdio.h> +#include <stdlib.h> + +int failures = 0; + +struct foo { char a; char b; char c; }; +struct foo foo, bar; +void f3(void) +{ + foo.a = 6; + foo.b = 6; + foo.c = 6; + bar.a = 1; + bar.b = 2; + bar.c = 3; + foo = bar; + printf("%d %d %d, %d %d %d (1,2,3 1,2,3)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 1) || (foo.b != 2) || (foo.c != 3) || + (bar.a != 1) || (bar.b != 2) || (bar.c != 3)) { + failures++; + } + foo.a = 3; + foo.b = 2; + foo.c = 1; + printf("%d %d %d, %d %d %d (3,2,1 1,2,3)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 3) || (foo.b != 2) || (foo.c != 1) || + (bar.a != 1) || (bar.b != 2) || (bar.c != 3)) { + failures++; + } + bar.a = 5; + bar.b = 6; + bar.c = 7; + printf("%d %d %d, %d %d %d (3,2,1 5,6,7)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 3) || (foo.b != 2) || (foo.c != 1) || + (bar.a != 5) || (bar.b != 6) || (bar.c != 7)) { + failures++; + } + bar = foo; + foo.a = 6; + foo.b = 6; + foo.c = 6; + printf("%d %d %d, %d %d %d (6,6,6 3,2,1)\n", + foo.a, foo.b, foo.c, + bar.a, bar.b, bar.c); + if ((foo.a != 6) || (foo.b != 6) || (foo.c != 6) || + (bar.a != 3) || (bar.b != 2) || (bar.c != 1)) { + failures++; + } +} + +int main(void) +{ + f3(); + return failures; +} diff --git a/test/misc/bug760.c b/test/val/bug760.c similarity index 100% rename from test/misc/bug760.c rename to test/val/bug760.c diff --git a/test/val/bug895.c b/test/val/bug895.c index c4892d7b1..3c0331a6d 100644 --- a/test/val/bug895.c +++ b/test/val/bug895.c @@ -21,7 +21,7 @@ unsigned int uia, uib; unsigned long ula, ulb; #define OPTCMP8TEST_SINGLE(num,cmpop,asmprefix,vara,varb,b0,b1,a0,a1,typename,name) \ - typename name ## _ ## num ## (void) { \ + typename name ## _ ## num(void) { \ varb = b0; \ asm( asmprefix ); \ vara = a0; \ @@ -30,7 +30,7 @@ unsigned long ula, ulb; } #define OPTCMP8TEST_VERIFY(num,b,desc,printterm,name) \ - ASSERT_AreEqual(name ## _ ## num ##(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); + ASSERT_AreEqual(name ## _ ## num(),b,printterm,"Incorrect optimization of const comparison (" #name "_" #num ": " desc ")."); /* Generates a set of comparison tests for one type and set of test values. ** name = a name for this test (no spaces) diff --git a/test/val/bzero.c b/test/val/bzero.c new file mode 100644 index 000000000..4e7ed6ac1 --- /dev/null +++ b/test/val/bzero.c @@ -0,0 +1,51 @@ + +// test if memset and bzero work as expected after optimizations + +#include <string.h> + +char s1[10] = { 1,2,3,4,5,6,7,8,9,10 }; +char r1[10] = { 0,0,0,0,0,6,7,8,9,10 }; + +char s2[10] = { 1,2,3,4,5,6,7,8,9,10 }; +char r2[10] = { 0,0,0,0,0,0,7,8,9,10 }; + +char s3[10] = { 1,2,3,4,5,6,7,8,9,10 }; +char r3[10] = { 0,0,0,0,0,0,0,8,9,10 }; + +char *p1, *p2, *p3; + +int res = 0; + +int main(void) +{ + /* regular bzero */ + bzero(s1, 5); + p1 = __AX__; /* this works because bzero jumps into memset */ + /* this gets converted to __bzero */ + p2 = memset(s2, 0, 6); + /* call internal __bzero (we should not do this in real code) */ + p3 = __bzero(s3, 7); + + /* check the results */ + if (memcmp(s1, r1, 10) != 0) { + res++; + } + if (memcmp(s2, r2, 10) != 0) { + res++; + } + if (memcmp(s3, r3, 10) != 0) { + res++; + } + + if (p1 != s1) { + res++; + } + if (p2 != s2) { + res++; + } + if (p3 != s3) { + res++; + } + + return res; +} diff --git a/test/val/common.h b/test/val/common.h index dada61a14..61da6c325 100644 --- a/test/val/common.h +++ b/test/val/common.h @@ -20,3 +20,4 @@ #define SIZEOF_LONG_32BIT #define UNSIGNED_CHARS #define UNSIGNED_BITFIELDS +#define INTEGER_CONSTANT_MAX_32BIT diff --git a/test/val/bool3.c b/test/val/compare-bool3.c similarity index 100% rename from test/val/bool3.c rename to test/val/compare-bool3.c diff --git a/test/val/const-side-effect.c b/test/val/const-side-effect.c new file mode 100644 index 000000000..cebc6f099 --- /dev/null +++ b/test/val/const-side-effect.c @@ -0,0 +1,160 @@ +/* Check code generation for constant operands with side-effects */ + +#include <stdio.h> + +static int failures = 0; + +#define TEST(X, Y, L) \ + if (x != X || y != Y) { \ + printf("Failed: " L "\nExpected: x = " #X ", y = " #Y ", got: x = %d, y = %d\n\n", x, y); \ + ++failures; \ + } + +#define TEST_LINE_UNARY(OP, RH, ID) \ + "x = " #OP "(set(&y, " #ID "), " #RH ")" + +#define TEST_UNARY(OP, RH, RS, ID) \ + x = -!(RS), y = -!(RS); \ + x = OP (set(&y, ID), RH); \ + TEST(RS, ID, TEST_LINE_UNARY(OP, RH, ID)) + +#define TEST_LINE_RHS_EFFECT(LH, OP, RH, ID) \ + "x = " #LH " " #OP " (set(&y, " #ID "), " #RH ")" + +#define TEST_LINE_LHS_EFFECT(LH, OP, RH, ID) \ + "y = (set(&x, " #ID "), " #LH ") " #OP " " #RH + +#define TEST_BINARY(LH, OP, RH, RS, ID) \ + x = -!(RS), y = -!(RS); \ + x = LH OP (set(&y, ID), RH); \ + TEST(RS, ID, TEST_LINE_RHS_EFFECT(LH, OP, RH, ID)) \ + y = -!(RS), x = -!(RS); \ + y = (set(&x, ID), LH) OP RH; \ + TEST(ID, RS, TEST_LINE_LHS_EFFECT(LH, OP, RH, ID)) \ + y = -!(RS); \ + x = (set(&x, LH), x) OP (set(&y, ID), RH); \ + TEST(RS, ID, TEST_LINE_RHS_EFFECT((set(&x, LH), x), OP, RH, ID)) \ + x = -!(RS); \ + y = (set(&x, ID), LH) OP (set(&y, RH), y); \ + TEST(ID, RS, TEST_LINE_LHS_EFFECT(LH, OP, (set(&y, RH), y), ID)) + +#define TEST_LINE_RHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID) \ + "x = (" #LT ")" #LH " " #OP " (" #RT ")(set(&y, " #ID "), " #RH ")" + +#define TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID) \ + "y = (" #LT ")(set(&x, " #ID "), " #LH ") " #OP " (" #RT ")" #RH + +#define TEST_BINARY_WITH_CAST(LT, LH, OP, RT, RH, RS, ID) \ + x = -!(RS), y = -!(RS); \ + x = (LT)LH OP (RT)(set(&y, ID), RH); \ + TEST(RS, ID, TEST_LINE_RHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID)) \ + y = -!(RS), x = -!(RS); \ + y = (LT)(set(&x, ID), LH) OP (RT)RH; \ + TEST(ID, RS, TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, RH, ID)) \ + y = -!(RS); \ + x = (LT)(set(&x, LH), x) OP (RT)(set(&y, ID), RH); \ + TEST(RS, ID, TEST_LINE_RHS_EFFECT_WITH_CAST(LT, (set(&x, LH), x), OP, RT, RH, ID)) \ + x = -!(RS); \ + y = (LT)(set(&x, ID), LH) OP (RT)(set(&y, RH), y); \ + TEST(ID, RS, TEST_LINE_LHS_EFFECT_WITH_CAST(LT, LH, OP, RT, (set(&y, RH), y), ID)) + +void set(int *p, int q) +{ + *p = q; +} + +int twice(int a) +{ + return a * 2; +} + +int (*twicep)(int) = twice; + +void test_unary(void) +{ + int x, y; + + TEST_UNARY(+, 42, 42, 1); + TEST_UNARY(-, -42, 42, 2); + TEST_UNARY(~, ~42, 42, 3); + TEST_UNARY(!, 42, 0, 4); +} + +void test_binary_arithmetic(void) +{ + int x, y; + + TEST_BINARY(41, +, 1, 42, 1) + TEST_BINARY(42, +, 0, 42, 1) + + TEST_BINARY(43, -, 1, 42, 2) + TEST_BINARY(42, -, 0, 42, 2) + + TEST_BINARY(6, *, 7, 42, 3) + TEST_BINARY(42, *, 1, 42, 3) + TEST_BINARY(-42, *, -1, 42, 3) + + TEST_BINARY(126, /, 3, 42, 4) + TEST_BINARY(42, /, 1, 42, 4) + TEST_BINARY(-42, /, -1, 42, 4) + + TEST_BINARY(85, %, 43, 42, 5) + TEST_BINARY(10794, %, 256, 42, 5) + + TEST_BINARY(84, >>, 1, 42, 6) + TEST_BINARY(42, >>, 0, 42, 6) + TEST_BINARY(10752, >>, 8, 42, 6) + TEST_BINARY(21504, >>, 9, 42, 6) + + TEST_BINARY(21, <<, 1, 42, 7) + TEST_BINARY(42, <<, 0, 42, 7) + TEST_BINARY(42, <<, 8, 10752, 7) + + TEST_BINARY(59, &, 238, 42, 8) + TEST_BINARY(42, &, 0, 0, 8) + TEST_BINARY(42, &, -1, 42, 8) + + TEST_BINARY(34, |, 10, 42, 9) + TEST_BINARY(42, |, 0, 42, 9) + TEST_BINARY(34, |, -1, -1, 9) + + TEST_BINARY(59, ^, 17, 42, 10) + TEST_BINARY(42, ^, 0, 42, 10) + TEST_BINARY(~42, ^, -1, 42, 10) +} + +void test_binary_comparison(void) +{ + int x, y; + + TEST_BINARY(42, ==, 42, 1, 11) + + TEST_BINARY(42, !=, 43, 1, 12) + TEST_BINARY_WITH_CAST(signed char, 42, !=, long, 65536L, 1, 12) + TEST_BINARY_WITH_CAST(long, 65536L, !=, signed char, 42, 1, 12) + + TEST_BINARY(42, >, 41, 1, 13) + TEST_BINARY_WITH_CAST(int, 0, >, unsigned, 42, 0, 13) + + TEST_BINARY(42, <, 43, 1, 14) + TEST_BINARY_WITH_CAST(unsigned, 42, <, int, 0, 0, 14) + + TEST_BINARY(42, >=, 0, 1, 15) + TEST_BINARY_WITH_CAST(unsigned, 42, >=, int, 0, 1, 15) + + TEST_BINARY(42, <=, 43, 1, 16) + TEST_BINARY_WITH_CAST(int, 0, <=, unsigned, 42, 1, 16) +} + +int main(void) +{ + test_unary(); + test_binary_arithmetic(); + test_binary_comparison(); + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + + return failures; +} diff --git a/test/val/counter.c b/test/val/counter.c new file mode 100644 index 000000000..1867b1a66 --- /dev/null +++ b/test/val/counter.c @@ -0,0 +1,60 @@ +/* Tests for predefined macro __COUNTER__ */ + +#include <stdio.h> + +static int failures = 0; + +#if __COUNTER__ /* 0 */ +# error __COUNTER__ should begin at 0! +#elif __COUNTER__ == 1 /* 1 */ +# define CONCAT(a,b) CONCAT_impl_(a,b) +# define CONCAT_impl_(a,b) a##b +#endif + +#line 42 "What is the answer?" +int CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}, CONCAT(ident,__COUNTER__)[0+__LINE__] = {__LINE__}; /* 2,3 */ + +#if __COUNTER__ == 4 ? 1 || __COUNTER__ : 0 && __COUNTER__ /* 4,5,6 */ +_Static_assert(__COUNTER__ == 7, "__COUNTER__ should be 7 here!"); /* 7 */ +# define GET_COUNTER() __COUNTER__ +# define GET_LINE() __LINE__ +# warning __COUNTER__ in #warning is just output as text and will never increase! +#else +# if __COUNTER__ + __COUNTER__ + __COUNTER__ /* Skipped as a whole and not incrementing */ +# endif +# error __COUNTER__ is skipped along with the whole #error line and will never increase anyways! */ +#endif + +#include "counter.h" +#include "counter.h" + +_Static_assert(GET_COUNTER() == 10, "__COUNTER__ should be 10 here!"); /* 10 */ + +int main(void) +{ + if (ident2[0] != 42) { + printf("Expected ident2[0]: %s, got: %s\n", 42, ident2[0]); + ++failures; + } + + if (ident3[0] != 42) { + printf("Expected ident3[0]: %s, got: %s\n", 42, ident3[0]); + ++failures; + } + + if (ident8 != 8) { + printf("Expected ident8: %s, got: %s\n", 8, ident8); + ++failures; + } + + if (ident9 != 9) { + printf("Expected ident9: %s, got: %s\n", 9, ident9); + ++failures; + } + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + + return failures; +} diff --git a/test/val/counter.h b/test/val/counter.h new file mode 100644 index 000000000..b97cbf54d --- /dev/null +++ b/test/val/counter.h @@ -0,0 +1,4 @@ +/* Tests for predefined macro __COUNTER__ */ + +#line GET_COUNTER() /* 1st: 8; 2nd: 9 */ +int CONCAT(ident,GET_LINE()) = GET_LINE(); diff --git a/test/val/cq241.c b/test/val/cq241.c index 611b5a376..a6d6c5324 100644 --- a/test/val/cq241.c +++ b/test/val/cq241.c @@ -4,6 +4,14 @@ !!LICENCE!! own, freely distributeable for non-profit. read CPYRIGHT.LCC */ +/* INTEGER_CONSTANT_MAX_32BIT +** This suppresses constants longer than 32-bit, which are now an error: +** https://github.com/cc65/cc65/pull/2084 +** Because cc65's internal representation is implicitly/explicitly +** 32-bit in many places, values larger than this aren't representable, +** but also can't be checked for overflow once accepted. +*/ + #include "common.h" struct defs { @@ -62,7 +70,12 @@ long pow2(long n) { return s; } - long d[39], o[39], x[39]; +#ifndef INTEGER_CONSTANT_MAX_32BIT +#define CTCOUNT 39 +#else +#define CTCOUNT 36 +#endif + long d[CTCOUNT], o[CTCOUNT], x[CTCOUNT]; #ifndef NO_OLD_FUNC_DECL s241(pd0) @@ -212,13 +225,15 @@ int s241(struct defs *pd0) { d[33] = 1073741823; o[33] = 07777777777; x[33] = 0x3fffffff; d[34] = 1073741824; o[34] = 010000000000; x[34] = 0x40000000; d[35] = 4294967295; o[35] = 037777777777; x[35] = 0xffffffff; +#if CTCOUNT > 36 d[36] = 4294967296; o[36] = 040000000000; x[36] = 0x100000000; d[37] = 68719476735; o[37] = 0777777777777; x[37] = 0xfffffffff; d[38] = 68719476736; o[38] = 01000000000000; x[38] = 0x1000000000; +#endif /* WHEW! */ - for (j=0; j<39; j++){ + for (j=0; j<CTCOUNT; j++){ if ( g[j] != d[j] || d[j] != o[j] || o[j] != x[j]) { diff --git a/test/val/decl-extern-shadow.c b/test/val/decl-extern-shadow.c new file mode 100644 index 000000000..6df3c9d50 --- /dev/null +++ b/test/val/decl-extern-shadow.c @@ -0,0 +1,31 @@ +/* Test for shadowing and linkage of file-scope "static" and block-scope "extern" declarations */ + +static int g(int x); /* Generated functions with internal linkage are not always kept in cc65 */ + +int main(void) +{ + char f = 'f'; /* Shadows global "int f(void)" (if any) */ + char c = 'c'; /* Shadows global "int c" (if any) */ + { + void* f = 0; /* Shadows local "char f" */ + void* c = 0; /* Shadows local "char c" */ + { + int f(void); /* Shadows local "char f" and "void* f" */ + extern int g(int); /* Shadows global "int g(int x)" */ + extern int c; /* Shadows local "char c" and "void* c" */ + return f() ^ g(c); /* Link to global "int g(int x)" */ + } + } +} + +int c = 42; + +int f(void) +{ + return 42; +} + +int g(int x) +{ + return x; +} diff --git a/test/val/decl-mixed-specifiers.c b/test/val/decl-mixed-specifiers.c new file mode 100644 index 000000000..a0fb1596b --- /dev/null +++ b/test/val/decl-mixed-specifiers.c @@ -0,0 +1,19 @@ +/* bug 1888 - cc65 fails with storage class specifiers after type specifiers */ + +#include <stdio.h> + +int const typedef volatile x_type, * const volatile y_type; + +int static failures = 0; + +int extern main(void); + +int main(void) +{ + volatile static x_type const x = 42, * const volatile y[] = { 1 ? &x : (y_type)0 }; + if (**y != 42) { + ++failures; + printf("y = %d, Expected: 42\n", **y); + } + return failures; +} diff --git a/test/val/static-1.c b/test/val/decl-static-extern.c similarity index 53% rename from test/val/static-1.c rename to test/val/decl-static-extern.c index 6918e0033..2be4d336f 100644 --- a/test/val/static-1.c +++ b/test/val/decl-static-extern.c @@ -7,13 +7,12 @@ /* see: https://github.com/cc65/cc65/issues/191 + https://github.com/cc65/cc65/issues/2111 */ -#pragma warn(error, on) - static int n = 0; -extern int n; /* should not give an error */ -static int n; /* should not give an error */ +extern int n; /* extern is ignored, gives a warning but keeps previous static definiton */ +static int n; /* no error or warning, the previous static is still in effect */ int main(void) { diff --git a/test/val/static-fwd-decl.c b/test/val/decl-static-fwd.c similarity index 100% rename from test/val/static-fwd-decl.c rename to test/val/decl-static-fwd.c diff --git a/test/val/nestfor.c b/test/val/for-nested.c similarity index 100% rename from test/val/nestfor.c rename to test/val/for-nested.c diff --git a/test/val/mult1.c b/test/val/mult1.c index 6d491a427..95141d76d 100644 --- a/test/val/mult1.c +++ b/test/val/mult1.c @@ -26,15 +26,17 @@ void done() void m1(void) { - c1 = c1*5; /* char = char * lit */ + c1 = c1*5; /* char = char * lit */ + c2 = c1*c3; /* char = char * char */ - c2 = c1 *c3; /* char = char * char */ - - uc1= uc1*5; /* uchar = uchar * lit * - uc2=uc1*uc3; /* uchar = uchar * uchar */ + uc1 = uc1*3; /* uchar = uchar * lit */ + uc2 = uc1*uc3; /* uchar = uchar * uchar */ if(c2 != 25) failures++; + + if(uc2 != 36) + failures++; } void m2(unsigned char uc) @@ -96,6 +98,9 @@ int main(void) c1 = 1; c3 = 5; + uc1 = 2; + uc3 = 6; + m1(); uc1 = 0x10; @@ -107,7 +112,7 @@ int main(void) ui3 = ui1*ui2; /* uint = uint * unit */ - /*m3(TESTLIT);*/ + m3(TESTLIT); success = failures; done(); diff --git a/test/val/opsize.c b/test/val/opsize.c index 20c7f0511..8ec49e8a8 100644 --- a/test/val/opsize.c +++ b/test/val/opsize.c @@ -1,33 +1,33 @@ - -/* Test for result types of certain unary operations */ - -#include <stdio.h> - -signed char x; -struct S { - unsigned char a : 3; - unsigned int b : 3; -} s; - -int main(void) -{ - _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type"); - _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type"); - _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type"); - _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type"); - _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type"); - - _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type"); - _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type"); - _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type"); - - _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type"); - _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type"); - _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type"); - - _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type"); - _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type"); - _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type"); - - return 0; -} + +/* Test for result types of certain unary operations */ + +#include <stdio.h> + +signed char x; +struct S { + unsigned char a : 3; + unsigned int b : 3; +} s; + +int main(void) +{ + _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type"); + _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type"); + _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type"); + _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type"); + _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type"); + + _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type"); + _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type"); + _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type"); + + _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type"); + _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type"); + _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type"); + + _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type"); + _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type"); + _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type"); + + return 0; +} diff --git a/test/val/ppshift.c b/test/val/ppshift.c new file mode 100644 index 000000000..466b15926 --- /dev/null +++ b/test/val/ppshift.c @@ -0,0 +1,120 @@ +/* + Test of bitwise-shift in preprocessor expressions. + + Note: Keep in mind that integer constants are always 32-bit in PP for cc65. +*/ + +/* Signed lhs */ +#if 1 << 16 != 0x00010000 +#error 1 << 16 != 0x00010000 +#endif + +#if 0x00010000 << -16 != 1 +#error 0x00010000 << -16 != 1 +#endif + +#if 0x10000 >> 16 != 1 +#error 0x10000 >> 16 != 1 +#endif + +#if 1 >> -16 != 0x10000 +#error 1 >> -16 != 0x10000 +#endif + +#if 1 << 32 != 0 +#error 1 << 32 != 0 +#endif + +#if 1 << -32 != 0 +#error 1 << -32 != 0 +#endif + +#if 1 >> 32 != 0 +#error 1 >> 32 != 0 +#endif + +#if 1 >> -32 != 0 +#error 1 >> -32 != 0 +#endif + +#if -1 << 32 != 0 +#error -1 << 32 != 0 +#endif + +#if -1 << -32 != -1 +#error -1 << -32 != -1 +#endif + +#if -1 >> 32 != -1 +#error -1 >> 32 != -1 +#endif + +#if -1 >> -32 != 0 +#error -1 >> -32 != 0 +#endif + +/* NOTE: 2147483648 is an UNSIGNED integer! */ +#if -1 << 2147483648 != 0 +#error -1 << 2147483648 != 0 +#endif + +/* NOTE: -2147483648 is also an UNSIGNED integer! */ +#if -1 << -2147483648 != 0 +#error -1 << -2147483648 != 0 +#endif + +#if -1 << (-2147483647 - 1) != -1 +#error -1 << (-2147483647 - 1) != -1 +#endif + +/* NOTE: 2147483648 is an UNSIGNED integer! */ +#if -1 >> 2147483648 != -1 +#error -1 >> 2147483648 != -1 +#endif + +/* NOTE: -2147483648 is also an UNSIGNED integer! */ +#if -1 >> -2147483648 != -1 +#error -1 >> -2147483648 != 0 +#endif + +#if -1 >> (-2147483647 - 1) != 0 +#error -1 >> (-2147483647 - 1) != 0 +#endif + +/* Unsigned lhs */ +#if 1U << 16 != 0x00010000 +#error 1U << 16 != 0x00010000 +#endif + +#if 0x80000000U << -16 != 0x8000 +#error 0x80000000U << -16 != 0x8000 +#endif + +#if 0x80000000U >> 16 != 0x8000 +#error 0x80000000U >> 16 != 0x8000 +#endif + +#if 1U >> -16 != 0x10000 +#error 1U >> -16 != 0x10000 +#endif + +#if -1U << 32 != 0 +#error -1U << 32 != 0 +#endif + +#if -1U << -32 != 0 +#error -1U << -32 != 0 +#endif + +#if -1U >> 32 != 0 +#error -1U >> 32 != 0 +#endif + +#if -1U >> -32 != 0 +#error -1U >> -32 != 0 +#endif + +int main(void) +{ + return 0; +} diff --git a/test/val/pr1800.c b/test/val/pr1800.c new file mode 100644 index 000000000..cc8a60eea --- /dev/null +++ b/test/val/pr1800.c @@ -0,0 +1,73 @@ + +/* Compiler outputs errors for valid code #1788 */ +/* (rest tested later below) */ +# /* Comment */ + +/* Unexpected "Error: Illegal indirection" under #if 0 #1769 */ +#if 0 +#if x ** 1 +#endif +#endif + +/* Unexpected "Error: Division by zero" #1768 */ +#if 1 || (8 / 0) +#endif + +/* Preprocessor fixes #1800 */ + +#/* +comment*/define __ATARI__ 1 +# /*comment*/ + +#/* +*/define /**/M(/**/x, /* +*/y)/* +*/(x+/* +*/y) + +#if M(-1, 1) != 0 +#error M(x,y) error! +#endif + +#warning/**//**/asd/**/) fgh +#warning + +#if 'z' - 'a' != 25 +#if x ** 1 +#endif +#endif + +#if -1 < 0xF0000000 * 0 +/* Note: this doesn't fail with cc65 as 0xF0000000 is an unsigned integer for cc65 */ +#error "Fails with most non-cc65 compilers" +#endif + +#if !+-0, (1U >> -31), x != (2L << -1) +#warning "/**/no problem" +#if 65536L * 32768L > -1U && 1 % 0 +#error "error2" +#endif +#if -1 > 0U ? y != y : 1/0 +#error "??" +#if d<(sasa +#endif +#endif +#endif + +#if !defined(__ATARI__) +#error "__ATARI__" is undefined?! +#endif + +#if !__ATARI__ +#error "__ATARI__" == 0?! +#endif + +#ifndef __ATARI__ +#error __ATARI__ is undefined?! +#endif + +int main(void) +{ + /* nope */ + return 0; +} diff --git a/test/val/pr1833.c b/test/val/pr1833.c new file mode 100644 index 000000000..177069eb4 --- /dev/null +++ b/test/val/pr1833.c @@ -0,0 +1,13 @@ +/* Test for PR #1833 fixes */ + +#define char 1 + +#if char && !int && L'A' - L'B' == 'A' - 'B' && L'A' == 'A' +#else +#error +#endif + +int main(void) +{ + return 0; +} diff --git a/test/val/staticassert.c b/test/val/staticassert.c index e43eeec8d..3338f7a4a 100644 --- a/test/val/staticassert.c +++ b/test/val/staticassert.c @@ -65,6 +65,13 @@ struct S { int b; }; +/* _Static_assert can also appear in unions. */ +union U { + int a; + _Static_assert (1, "1 should still be true."); + int b; +}; + int main (void) { diff --git a/test/val/anon-struct1.c b/test/val/struct-anon1.c similarity index 100% rename from test/val/anon-struct1.c rename to test/val/struct-anon1.c diff --git a/test/val/anon-struct2.c b/test/val/struct-anon2.c similarity index 100% rename from test/val/anon-struct2.c rename to test/val/struct-anon2.c diff --git a/test/val/struct-by-value.c b/test/val/struct-by-value.c new file mode 100644 index 000000000..0e846c117 --- /dev/null +++ b/test/val/struct-by-value.c @@ -0,0 +1,144 @@ +/* Test of passing and returning structs by value. + Structs of 1, 2 and 4 bytes are supported. + Note that structs of 3 bytes are disabled, see: + https://github.com/cc65/cc65/issues/2022 +*/ + +int fail = 0; + +struct s1 { char a; }; +struct s2 { char a, b; }; +struct s3 { char a, b, c; }; +struct s4 { char a, b, c, d; }; + +const struct s1 c1 = { 1 }; +const struct s2 c2 = { 2, 3 }; +const struct s3 c3 = { 4, 5, 6 }; +const struct s4 c4 = { 7, 8, 9, 10 }; + +struct s1 return1() { return c1; } +struct s2 return2() { return c2; } +/*struct s3 return3() { return c3; }*/ +struct s4 return4() { return c4; } + +int compare1(struct s1 a, struct s1 b) +{ + if (a.a != b.a) return 1; + return 0; +} + +int compare2(struct s2 a, struct s2 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + return 0; +} + +/*int compare3(struct s3 a, struct s3 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + return 0; +}*/ + +int compare4(struct s4 a, struct s4 b) +{ + if (a.a != b.a) return 1; + if (a.b != b.b) return 1; + if (a.c != b.c) return 1; + if (a.d != b.d) return 1; + return 0; +} + +int pass1(struct s1 p1) +{ + struct s1 a1; + a1 = p1; + if (a1.a != c1.a) return 1; + return 0; +} + +int pass2(struct s2 p2) +{ + struct s2 a2; + a2 = p2; + if (a2.a != c2.a) return 1; + if (a2.b != c2.b) return 1; + return 0; +} + +/*int pass3(struct s3 p3) +{ + struct s3 a3; + a3 = p3; + if (a3.a != c3.a) return 1; + if (a3.b != c3.b) return 1; + if (a3.c != c3.c) return 1; + return 0; +}*/ + +int pass4(struct s4 p4) +{ + struct s4 a4; + a4 = p4; + if (a4.a != c4.a) return 1; + if (a4.b != c4.b) return 1; + if (a4.c != c4.c) return 1; + if (a4.d != c4.d) return 1; + return 0; +} + +void reset(char* gg) +{ + char i; + for (i=0;i<5;++i) gg[i] = 128+i; +} + +int test(char* gg, char start) +{ + char i; + for (i=start;i<5;++i) + if (gg[i] != 128+i) return 1; + return 0; +} + +int main() +{ + /* Used to check #2022 bug condition of extra bytes being overwritten. */ + union + { + char gg[5]; + struct s1 g1; + struct s2 g2; + struct s3 g3; + struct s4 g4; + } guard; + + reset(guard.gg); + guard.g1 = return1(); + fail += compare1(guard.g1,c1); + fail += test(guard.gg,1); + + reset(guard.gg); + guard.g2 = return2(); + fail += compare2(guard.g2,c2); + fail += test(guard.gg,2); + + /*reset(guard.gg); + guard.g3 = return3(); + fail += compare3(guard.g3,c3); + fail += test(guard.gg,3);*/ + + reset(guard.gg); + guard.g4 = return4(); + fail += compare4(guard.g4,c4); + fail += test(guard.gg,4); + + fail += pass1(c1); + fail += pass2(c2); + /*fail += pass3(c3);*/ + fail += pass4(c4); + + return fail; +} diff --git a/test/val/return-struct.c b/test/val/struct-returned.c similarity index 100% rename from test/val/return-struct.c rename to test/val/struct-returned.c diff --git a/test/val/casttochar.c b/test/val/type-cast-to-char.c similarity index 100% rename from test/val/casttochar.c rename to test/val/type-cast-to-char.c diff --git a/test/val/char-promote.c b/test/val/type-char-promote.c similarity index 100% rename from test/val/char-promote.c rename to test/val/type-char-promote.c diff --git a/test/val/uneval.c b/test/val/uneval.c index 50e00973a..fe42cc592 100644 --- a/test/val/uneval.c +++ b/test/val/uneval.c @@ -1,46 +1,46 @@ -/* - Copyright 2021, 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 deferred operations in unevaluated context resulted from 'sizeof' and - short-circuited code-paths in AND, OR and tenary operations. - - https://github.com/cc65/cc65/issues/1406 -*/ - -#include <stdio.h> - -int main(void) -{ - int i = 0; - int j = 0; - - sizeof(i++ | j--); - 0 && (i++ | j--); - 1 || (i++ | j--); - 0 ? i++ | j-- : 0; - 1 ? 0 : i++ | j--; - - if (i != 0 || j != 0) { - printf("i = %d, j = %d\n", i, j); - printf("Failures: %d\n", i - j); - } - return i - j; -} +/* + Copyright 2021, 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 deferred operations in unevaluated context resulted from 'sizeof' and + short-circuited code-paths in AND, OR and tenary operations. + + https://github.com/cc65/cc65/issues/1406 +*/ + +#include <stdio.h> + +int main(void) +{ + int i = 0; + int j = 0; + + sizeof(i++ | j--); + 0 && (i++ | j--); + 1 || (i++ | j--); + 0 ? i++ | j-- : 0; + 1 ? 0 : i++ | j--; + + if (i != 0 || j != 0) { + printf("i = %d, j = %d\n", i, j); + printf("Failures: %d\n", i - j); + } + return i - j; +} diff --git a/util/atari/Makefile b/util/atari/Makefile index db4226f69..e53c837aa 100644 --- a/util/atari/Makefile +++ b/util/atari/Makefile @@ -5,6 +5,16 @@ ifdef CROSS_COMPILE $(info CC: $(CC)) endif +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + DEL = -del /f +else + DEL = $(RM) +endif + CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS) .PHONY: mostlyclean clean @@ -15,6 +25,6 @@ ataricvt: ataricvt.c $(CC) $(CFLAGS) -o ataricvt ataricvt.c mostlyclean clean: - $(RM) ataricvt + $(DEL) ataricvt install zip: diff --git a/util/gamate/Makefile b/util/gamate/Makefile index db2a1f059..54fa74191 100644 --- a/util/gamate/Makefile +++ b/util/gamate/Makefile @@ -5,6 +5,16 @@ ifdef CROSS_COMPILE $(info CC: $(CC)) endif +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + DEL = -del /f +else + DEL = $(RM) +endif + CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS) .PHONY: mostlyclean clean @@ -15,6 +25,6 @@ gamate-fixcart: gamate-fixcart.c $(CC) $(CFLAGS) -o gamate-fixcart gamate-fixcart.c mostlyclean clean: - $(RM) gamate-fixcart + $(DEL) gamate-fixcart install zip: diff --git a/util/parse-bsnes-log.awk b/util/parse-bsnes-log.awk new file mode 100755 index 000000000..d6a014165 --- /dev/null +++ b/util/parse-bsnes-log.awk @@ -0,0 +1,61 @@ +#!/usr/bin/awk -nf +# +# Parse the bsnes-plus cpu log for the 65816 address mode ranges. +# Since the log doesn't show the op size, we have to be careful +# and add +3 to the range end - this may need manual cleanup. +# +# Grep the input for only one bank's content beforehand. +# +# (C) Lauri Kasanen, under the cc65 license +# + +{ + addr = $1 + + IGNORECASE = 1 + if ($9 ~ /^nv/) { + val = $9 + } else if ($10 ~ /^nv/) { + val = $10 + } else { + val = $11 + } + IGNORECASE = 0 + + val = substr(val, 3, 2) + if (val == "1B") # emulation mode + val = "MX" + + addrs["0x" substr(addr, 3)] = val +} + +END { + PROCINFO["sorted_in"] = "@ind_num_asc" + + start = -1 + prevval = "" + + for (addr in addrs) { + cur = addrs[addr] + if (start == -1) { # first range + start = addr + } else { + if (prevval != cur || addr - prevaddr > 4) { # start a new range + #print "diff " addr - prevaddr ", addr " addr ", pa " prevaddr + if (prevaddr + 3 >= addr + 0) + fmt = sprintf("%04x", addr - 1) + else + fmt = sprintf("%04x", prevaddr + 3) + print "RANGE { START $" substr(start, 3) "; END $" fmt "; ADDRMODE \"" prevval "\"; TYPE Code;};" + start = addr + } + } + #print "\t" addr " " addrs[addr] + + prevval = cur + prevaddr = addr + } + + fmt = sprintf("%04x", prevaddr + 3) + print "RANGE { START $" substr(start, 3) "; END $" fmt "; ADDRMODE \"" prevval "\"; TYPE Code;};" +} diff --git a/util/zlib/Makefile b/util/zlib/Makefile index 3770e1f3c..f276ddaf2 100644 --- a/util/zlib/Makefile +++ b/util/zlib/Makefile @@ -5,21 +5,33 @@ ifdef CROSS_COMPILE $(info CC: $(CC)) endif +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + DEL = -del /f +else + DEL = $(RM) +endif + CFLAGS += -O3 -Wall -Wextra -Wno-char-subscripts $(USER_CFLAGS) .PHONY: mostlyclean clean -zlib: warning +zlib: +#zlib: warning #zlib: deflater warning: - @echo "deflater needs zlib installed, use 'make deflater' to build" + @echo "util/zlib/deflater is no longer built by default" + @echo "use 'make deflater' to build if you need it" + @echo "note that you need zlib installed first" deflater: deflater.c $(CC) $(CFLAGS) -o deflater deflater.c -lz mostlyclean clean: - $(RM) deflater - -install zip: + $(DEL) deflater +install zip: diff --git a/util/zlib/readme.txt b/util/zlib/readme.txt new file mode 100644 index 000000000..90e15871c --- /dev/null +++ b/util/zlib/readme.txt @@ -0,0 +1,2 @@ +Deflater program in this directory is not built by default +Use 'make deflater' to build. Note that you need zlib installed first