diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml new file mode 100644 index 000000000..53f79fef4 --- /dev/null +++ b/.github/workflows/build-on-pull-request.yml @@ -0,0 +1,56 @@ +name: Build Pull Request +on: [pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_linux: + name: Build and Test (Linux) + runs-on: ubuntu-latest + + steps: + - shell: bash + run: git config --global core.autocrlf input + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Install Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass + + - name: Build + id: build + shell: bash + run: | + make -j2 bin USER_CFLAGS=-Werror + make -j2 lib QUIET=1 + make test QUIET=1 + make -j2 samples + make -C src clean + make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + make -C samples clean + make -j2 doc zip + + build_windows: + name: Build (Windows) + runs-on: windows-latest + + steps: + - shell: bash + run: git config --global core.autocrlf input + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Build app (debug) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + + - name: Build app (release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml new file mode 100644 index 000000000..aefc7f2f5 --- /dev/null +++ b/.github/workflows/snapshot-on-push-master.yml @@ -0,0 +1,69 @@ +name: Snapshot Build +on: + push: + branches: + master +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_windows: + name: Build (Windows) + runs-on: windows-latest + + steps: + - shell: bash + run: git config --global core.autocrlf input + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.1 + + - name: Build app (debug) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug + + - name: Build app (release) + run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release + + build_linux: + name: Build, Test and Snaphot (Linux) + runs-on: ubuntu-latest + needs: build_windows + + steps: + - shell: bash + run: git config --global core.autocrlf input + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Install Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass + + - name: Build + id: build + shell: bash + run: | + make -j2 bin USER_CFLAGS=-Werror + make -j2 lib QUIET=1 + make test QUIET=1 + make -j2 samples + make -C src clean + make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + make -C samples clean + make -j2 doc zip + + - name: Upload Snapshot Zip + uses: actions/upload-artifact@v2 + with: + name: cc65-snapshot-win32.zip + path: cc65.zip + + # TODO: Update docs at https://github.com/cc65/doc + # TODO: Publish snapshot zip at https://github.com/cc65/cc65.github.io diff --git a/.travis.yml b/.travis.yml index c84eb0cc2..4b0919a10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,26 @@ -language: - - c -install: - - sudo apt-get update - - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass -script: - - make bin USER_CFLAGS=-Werror - - make lib QUIET=1 - - make -C test QUIET=1 - - make -C src clean - - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- - - make doc zip -after_success: - - make -f Makefile.travis +language: c + +jobs: + include: + + - os: linux + name: Linux + install: + - sudo apt-get update + - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass + script: + - make -j2 bin USER_CFLAGS=-Werror + - make -j2 lib QUIET=1 + - make test QUIET=1 + - make -j2 samples + - make -C src clean + - make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + - make -C samples clean + - make -j2 doc zip + after_success: + - make -f Makefile.travis + + - os: windows + name: Windows + script: + - src/msbuild.cmd src\\cc65.sln diff --git a/Makefile b/Makefile index 206f853fc..3331f6fb9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples +.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples test .SUFFIXES: @@ -20,6 +20,9 @@ doc html info: samples: @$(MAKE) -C samples --no-print-directory $@ +test: + @$(MAKE) -C test --no-print-directory $@ + %65: @$(MAKE) -C src --no-print-directory $@ diff --git a/README.md b/README.md index 3d78fda50..33c7d2830 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [Wiki](https://github.com/cc65/wiki/wiki) -[![Build Status](https://api.travis-ci.org/cc65/cc65.svg?branch=master)](https://travis-ci.org/cc65/cc65/builds) +[![Snapshot Build](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml/badge.svg?branch=master)](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml) cc65 is a complete cross development package for 65(C)02 systems, including a powerful macro assembler, a C compiler, linker, librarian and several @@ -35,6 +35,8 @@ including - the Oric Telestrat. - the Lynx console. - the Ohio Scientific Challenger 1P. +- the Commander X16. +- the Synertek Systems Sym-1. The libraries are fairly portable, so creating a version for other 6502s shouldn't be too much work. diff --git a/asminc/apple2.inc b/asminc/apple2.inc index 5ebf73164..528c463a1 100644 --- a/asminc/apple2.inc +++ b/asminc/apple2.inc @@ -15,6 +15,7 @@ PROMPT := $33 ; Used by GETLN RNDL := $4E ; Random counter low RNDH := $4F ; Random counter high HIMEM := $73 ; Highest available memory address+1 +CURLIN := $75 ; Current line number being executed ;----------------------------------------------------------------------------- ; Vectors diff --git a/asminc/ascii_charmap.inc b/asminc/ascii_charmap.inc new file mode 100644 index 000000000..ecbf2640f --- /dev/null +++ b/asminc/ascii_charmap.inc @@ -0,0 +1,287 @@ +;/*****************************************************************************/ +;/* */ +;/* ascii_charmap.inc */ +;/* */ +;/* No translations, encodings are stored as they were typed in the host. */ +;/* */ +;/* */ +;/* 2019-09-07, Greg King */ +;/* */ +;/* 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 must not be removed or altered from any source */ +;/* distribution. */ +;/* */ +;/*****************************************************************************/ + +;/* ASCII */ +.charmap $00, $00 +.charmap $01, $01 +.charmap $02, $02 +.charmap $03, $03 +.charmap $04, $04 +.charmap $05, $05 +.charmap $06, $06 +.charmap $07, $07 +.charmap $08, $08 +.charmap $09, $09 +.charmap $0A, $0A +.charmap $0B, $0B +.charmap $0C, $0C +.charmap $0D, $0D +.charmap $0E, $0E +.charmap $0F, $0F +.charmap $10, $10 +.charmap $11, $11 +.charmap $12, $12 +.charmap $13, $13 +.charmap $14, $14 +.charmap $15, $15 +.charmap $16, $16 +.charmap $17, $17 +.charmap $18, $18 +.charmap $19, $19 +.charmap $1A, $1A +.charmap $1B, $1B +.charmap $1C, $1C +.charmap $1D, $1D +.charmap $1E, $1E +.charmap $1F, $1F +.charmap $20, $20 +.charmap $21, $21 +.charmap $22, $22 +.charmap $23, $23 +.charmap $24, $24 +.charmap $25, $25 +.charmap $26, $26 +.charmap $27, $27 +.charmap $28, $28 +.charmap $29, $29 +.charmap $2A, $2A +.charmap $2B, $2B +.charmap $2C, $2C +.charmap $2D, $2D +.charmap $2E, $2E +.charmap $2F, $2F +.charmap $30, $30 +.charmap $31, $31 +.charmap $32, $32 +.charmap $33, $33 +.charmap $34, $34 +.charmap $35, $35 +.charmap $36, $36 +.charmap $37, $37 +.charmap $38, $38 +.charmap $39, $39 +.charmap $3A, $3A +.charmap $3B, $3B +.charmap $3C, $3C +.charmap $3D, $3D +.charmap $3E, $3E +.charmap $3F, $3F +.charmap $40, $40 +.charmap $41, $41 +.charmap $42, $42 +.charmap $43, $43 +.charmap $44, $44 +.charmap $45, $45 +.charmap $46, $46 +.charmap $47, $47 +.charmap $48, $48 +.charmap $49, $49 +.charmap $4A, $4A +.charmap $4B, $4B +.charmap $4C, $4C +.charmap $4D, $4D +.charmap $4E, $4E +.charmap $4F, $4F +.charmap $50, $50 +.charmap $51, $51 +.charmap $52, $52 +.charmap $53, $53 +.charmap $54, $54 +.charmap $55, $55 +.charmap $56, $56 +.charmap $57, $57 +.charmap $58, $58 +.charmap $59, $59 +.charmap $5A, $5A +.charmap $5B, $5B +.charmap $5C, $5C +.charmap $5D, $5D +.charmap $5E, $5E +.charmap $5F, $5F +.charmap $60, $60 +.charmap $61, $61 +.charmap $62, $62 +.charmap $63, $63 +.charmap $64, $64 +.charmap $65, $65 +.charmap $66, $66 +.charmap $67, $67 +.charmap $68, $68 +.charmap $69, $69 +.charmap $6A, $6A +.charmap $6B, $6B +.charmap $6C, $6C +.charmap $6D, $6D +.charmap $6E, $6E +.charmap $6F, $6F +.charmap $70, $70 +.charmap $71, $71 +.charmap $72, $72 +.charmap $73, $73 +.charmap $74, $74 +.charmap $75, $75 +.charmap $76, $76 +.charmap $77, $77 +.charmap $78, $78 +.charmap $79, $79 +.charmap $7A, $7A +.charmap $7B, $7B +.charmap $7C, $7C +.charmap $7D, $7D +.charmap $7E, $7E +.charmap $7F, $7F + +;/* beyond ASCII */ +.charmap $80, $80 +.charmap $81, $81 +.charmap $82, $82 +.charmap $83, $83 +.charmap $84, $84 +.charmap $85, $85 +.charmap $86, $86 +.charmap $87, $87 +.charmap $88, $88 +.charmap $89, $89 +.charmap $8A, $8A +.charmap $8B, $8B +.charmap $8C, $8C +.charmap $8D, $8D +.charmap $8E, $8E +.charmap $8F, $8F +.charmap $90, $90 +.charmap $91, $91 +.charmap $92, $92 +.charmap $93, $93 +.charmap $94, $94 +.charmap $95, $95 +.charmap $96, $96 +.charmap $97, $97 +.charmap $98, $98 +.charmap $99, $99 +.charmap $9A, $9A +.charmap $9B, $9B +.charmap $9C, $9C +.charmap $9D, $9D +.charmap $9E, $9E +.charmap $9F, $9F +.charmap $A0, $A0 +.charmap $A1, $A1 +.charmap $A2, $A2 +.charmap $A3, $A3 +.charmap $A4, $A4 +.charmap $A5, $A5 +.charmap $A6, $A6 +.charmap $A7, $A7 +.charmap $A8, $A8 +.charmap $A9, $A9 +.charmap $AA, $AA +.charmap $AB, $AB +.charmap $AC, $AC +.charmap $AD, $AD +.charmap $AE, $AE +.charmap $AF, $AF +.charmap $B0, $B0 +.charmap $B1, $B1 +.charmap $B2, $B2 +.charmap $B3, $B3 +.charmap $B4, $B4 +.charmap $B5, $B5 +.charmap $B6, $B6 +.charmap $B7, $B7 +.charmap $B8, $B8 +.charmap $B9, $B9 +.charmap $BA, $BA +.charmap $BB, $BB +.charmap $BC, $BC +.charmap $BD, $BD +.charmap $BE, $BE +.charmap $BF, $BF +.charmap $C0, $C0 +.charmap $C1, $C1 +.charmap $C2, $C2 +.charmap $C3, $C3 +.charmap $C4, $C4 +.charmap $C5, $C5 +.charmap $C6, $C6 +.charmap $C7, $C7 +.charmap $C8, $C8 +.charmap $C9, $C9 +.charmap $CA, $CA +.charmap $CB, $CB +.charmap $CC, $CC +.charmap $CD, $CD +.charmap $CE, $CE +.charmap $CF, $CF +.charmap $D0, $D0 +.charmap $D1, $D1 +.charmap $D2, $D2 +.charmap $D3, $D3 +.charmap $D4, $D4 +.charmap $D5, $D5 +.charmap $D6, $D6 +.charmap $D7, $D7 +.charmap $D8, $D8 +.charmap $D9, $D9 +.charmap $DA, $DA +.charmap $DB, $DB +.charmap $DC, $DC +.charmap $DD, $DD +.charmap $DE, $DE +.charmap $DF, $DF +.charmap $E0, $E0 +.charmap $E1, $E1 +.charmap $E2, $E2 +.charmap $E3, $E3 +.charmap $E4, $E4 +.charmap $E5, $E5 +.charmap $E6, $E6 +.charmap $E7, $E7 +.charmap $E8, $E8 +.charmap $E9, $E9 +.charmap $EA, $EA +.charmap $EB, $EB +.charmap $EC, $EC +.charmap $ED, $ED +.charmap $EE, $EE +.charmap $EF, $EF +.charmap $F0, $F0 +.charmap $F1, $F1 +.charmap $F2, $F2 +.charmap $F3, $F3 +.charmap $F4, $F4 +.charmap $F5, $F5 +.charmap $F6, $F6 +.charmap $F7, $F7 +.charmap $F8, $F8 +.charmap $F9, $F9 +.charmap $FA, $FA +.charmap $FB, $FB +.charmap $FC, $FC +.charmap $FD, $FD +.charmap $FE, $FE +.charmap $FF, $FF diff --git a/asminc/atari.inc b/asminc/atari.inc index 7c46b3252..c2a210ea0 100644 --- a/asminc/atari.inc +++ b/asminc/atari.inc @@ -924,7 +924,7 @@ KEY_QUESTIONMARK = KEY_SLASH | KEY_SHIFT KEY_CLEAR = KEY_LESSTHAN | KEY_SHIFT KEY_INSERT = KEY_GREATERTHAN | KEY_SHIFT -KEY_UP = KEY_UNDERLINE | KEY_CTRL +KEY_UP = KEY_DASH | KEY_CTRL KEY_DOWN = KEY_EQUALS | KEY_CTRL KEY_LEFT = KEY_PLUS | KEY_CTRL KEY_RIGHT = KEY_ASTERISK | KEY_CTRL diff --git a/asminc/atari_atascii_charmap.inc b/asminc/atari_atascii_charmap.inc new file mode 100644 index 000000000..7c6b5e542 --- /dev/null +++ b/asminc/atari_atascii_charmap.inc @@ -0,0 +1,301 @@ +;/*****************************************************************************/ +;/* */ +;/* atari_atascii_charmap.inc */ +;/* */ +;/* Atari system standard string mapping ISO-8859-1 -> AtASCII */ +;/* */ +;/* */ +;/* */ +;/* C 2016 Christian Krueger */ +;/* */ +;/* */ +;/* 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. */ +;/* */ +;/*****************************************************************************/ + +.charmap $00, $00 +.charmap $01, $01 +.charmap $02, $02 +.charmap $03, $03 +.charmap $04, $04 +.charmap $05, $05 +.charmap $06, $06 +.charmap $07, $FD +.charmap $08, $08 +.charmap $09, $7F +.charmap $0A, $9B +.charmap $0B, $0B +.charmap $0C, $7D +.charmap $0D, $0D +.charmap $0E, $0E +.charmap $0F, $0F + +.charmap $10, $10 +.charmap $11, $11 +.charmap $12, $12 +.charmap $13, $13 +.charmap $14, $14 +.charmap $15, $15 +.charmap $16, $16 +.charmap $17, $17 +.charmap $18, $18 +.charmap $19, $19 +.charmap $1A, $1A +.charmap $1B, $1B +.charmap $1C, $1C +.charmap $1D, $1D +.charmap $1E, $1E +.charmap $1F, $1F + +.charmap $20, $20 +.charmap $21, $21 +.charmap $22, $22 +.charmap $23, $23 +.charmap $24, $24 +.charmap $25, $25 +.charmap $26, $26 +.charmap $27, $27 +.charmap $28, $28 +.charmap $29, $29 +.charmap $2A, $2A +.charmap $2B, $2B +.charmap $2C, $2C +.charmap $2D, $2D +.charmap $2E, $2E +.charmap $2F, $2F + +.charmap $30, $30 +.charmap $31, $31 +.charmap $32, $32 +.charmap $33, $33 +.charmap $34, $34 +.charmap $35, $35 +.charmap $36, $36 +.charmap $37, $37 +.charmap $38, $38 +.charmap $39, $39 +.charmap $3A, $3A +.charmap $3B, $3B +.charmap $3C, $3C +.charmap $3D, $3D +.charmap $3E, $3E +.charmap $3F, $3F + +.charmap $40, $40 +.charmap $41, $41 +.charmap $42, $42 +.charmap $43, $43 +.charmap $44, $44 +.charmap $45, $45 +.charmap $46, $46 +.charmap $47, $47 +.charmap $48, $48 +.charmap $49, $49 +.charmap $4A, $4A +.charmap $4B, $4B +.charmap $4C, $4C +.charmap $4D, $4D +.charmap $4E, $4E +.charmap $4F, $4F + +.charmap $50, $50 +.charmap $51, $51 +.charmap $52, $52 +.charmap $53, $53 +.charmap $54, $54 +.charmap $55, $55 +.charmap $56, $56 +.charmap $57, $57 +.charmap $58, $58 +.charmap $59, $59 +.charmap $5A, $5A +.charmap $5B, $5B +.charmap $5C, $5C +.charmap $5D, $5D +.charmap $5E, $5E +.charmap $5F, $5F + +.charmap $60, $60 +.charmap $61, $61 +.charmap $62, $62 +.charmap $63, $63 +.charmap $64, $64 +.charmap $65, $65 +.charmap $66, $66 +.charmap $67, $67 +.charmap $68, $68 +.charmap $69, $69 +.charmap $6A, $6A +.charmap $6B, $6B +.charmap $6C, $6C +.charmap $6D, $6D +.charmap $6E, $6E +.charmap $6F, $6F + +.charmap $70, $70 +.charmap $71, $71 +.charmap $72, $72 +.charmap $73, $73 +.charmap $74, $74 +.charmap $75, $75 +.charmap $76, $76 +.charmap $77, $77 +.charmap $78, $78 +.charmap $79, $79 +.charmap $7A, $7A +.charmap $7B, $7B +.charmap $7C, $7C +.charmap $7D, $7D +.charmap $7E, $7E +.charmap $7F, $7F + +.charmap $80, $80 +.charmap $81, $81 +.charmap $82, $82 +.charmap $83, $83 +.charmap $84, $84 +.charmap $85, $85 +.charmap $86, $86 +.charmap $87, $87 +.charmap $88, $88 +.charmap $89, $89 +.charmap $8A, $8A +.charmap $8B, $8B +.charmap $8C, $8C +.charmap $8D, $8D +.charmap $8E, $8E +.charmap $8F, $8F + +.charmap $90, $90 +.charmap $91, $91 +.charmap $92, $92 +.charmap $93, $93 +.charmap $94, $94 +.charmap $95, $95 +.charmap $96, $96 +.charmap $97, $97 +.charmap $98, $98 +.charmap $99, $99 +.charmap $9A, $9A +.charmap $9B, $9B +.charmap $9C, $9C +.charmap $9D, $9D +.charmap $9E, $9E +.charmap $9F, $9F + +.charmap $A0, $A0 +.charmap $A1, $A1 +.charmap $A2, $A2 +.charmap $A3, $A3 +.charmap $A4, $A4 +.charmap $A5, $A5 +.charmap $A6, $A6 +.charmap $A7, $A7 +.charmap $A8, $A8 +.charmap $A9, $A9 +.charmap $AA, $AA +.charmap $AB, $AB +.charmap $AC, $AC +.charmap $AD, $AD +.charmap $AE, $AE +.charmap $AF, $AF + +.charmap $B0, $B0 +.charmap $B1, $B1 +.charmap $B2, $B2 +.charmap $B3, $B3 +.charmap $B4, $B4 +.charmap $B5, $B5 +.charmap $B6, $B6 +.charmap $B7, $B7 +.charmap $B8, $B8 +.charmap $B9, $B9 +.charmap $BA, $BA +.charmap $BB, $BB +.charmap $BC, $BC +.charmap $BD, $BD +.charmap $BE, $BE +.charmap $BF, $BF + +.charmap $C0, $C0 +.charmap $C1, $C1 +.charmap $C2, $C2 +.charmap $C3, $C3 +.charmap $C4, $C4 +.charmap $C5, $C5 +.charmap $C6, $C6 +.charmap $C7, $C7 +.charmap $C8, $C8 +.charmap $C9, $C9 +.charmap $CA, $CA +.charmap $CB, $CB +.charmap $CC, $CC +.charmap $CD, $CD +.charmap $CE, $CE +.charmap $CF, $CF + +.charmap $D0, $D0 +.charmap $D1, $D1 +.charmap $D2, $D2 +.charmap $D3, $D3 +.charmap $D4, $D4 +.charmap $D5, $D5 +.charmap $D6, $D6 +.charmap $D7, $D7 +.charmap $D8, $D8 +.charmap $D9, $D9 +.charmap $DA, $DA +.charmap $DB, $DB +.charmap $DC, $DC +.charmap $DD, $DD +.charmap $DE, $DE +.charmap $DF, $DF + +.charmap $E0, $E0 +.charmap $E1, $E1 +.charmap $E2, $E2 +.charmap $E3, $E3 +.charmap $E4, $E4 +.charmap $E5, $E5 +.charmap $E6, $E6 +.charmap $E7, $E7 +.charmap $E8, $E8 +.charmap $E9, $E9 +.charmap $EA, $EA +.charmap $EB, $EB +.charmap $EC, $EC +.charmap $ED, $ED +.charmap $EE, $EE +.charmap $EF, $EF + +.charmap $F0, $F0 +.charmap $F1, $F1 +.charmap $F2, $F2 +.charmap $F3, $F3 +.charmap $F4, $F4 +.charmap $F5, $F5 +.charmap $F6, $F6 +.charmap $F7, $F7 +.charmap $F8, $F8 +.charmap $F9, $F9 +.charmap $FA, $FA +.charmap $FB, $FB +.charmap $FC, $FC +.charmap $FD, $FD +.charmap $FE, $FE +.charmap $FF, $FF diff --git a/asminc/atari_screen_charmap.inc b/asminc/atari_screen_charmap.inc new file mode 100644 index 000000000..193ea0685 --- /dev/null +++ b/asminc/atari_screen_charmap.inc @@ -0,0 +1,303 @@ +;/*****************************************************************************/ +;/* */ +;/* atari_screen_charmap.inc */ +;/* */ +;/* Atari system internal string mapping ISO-8859-1 -> Internal/Screen-Code */ +;/* */ +;/* */ +;/* */ +;/* C 2016 Christian Krueger */ +;/* */ +;/* */ +;/* 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. */ +;/* */ +;/*****************************************************************************/ + +.charmap $00, $40 +.charmap $01, $41 +.charmap $02, $42 +.charmap $03, $43 +.charmap $04, $44 +.charmap $05, $45 +.charmap $06, $46 +.charmap $07, $FD +.charmap $08, $48 +.charmap $09, $7F +.charmap $0A, $DB +.charmap $0B, $4B +.charmap $0C, $7D +.charmap $0D, $4D +.charmap $0E, $4E +.charmap $0F, $4F + +.charmap $10, $50 +.charmap $11, $51 +.charmap $12, $52 +.charmap $13, $53 +.charmap $14, $54 +.charmap $15, $55 +.charmap $16, $56 +.charmap $17, $57 +.charmap $18, $58 +.charmap $19, $59 +.charmap $1A, $5A +.charmap $1B, $5B +.charmap $1C, $5C +.charmap $1D, $5D +.charmap $1E, $5E +.charmap $1F, $5F + +.charmap $20, $00 + +.charmap $21, $01 +.charmap $22, $02 +.charmap $23, $03 +.charmap $24, $04 +.charmap $25, $05 +.charmap $26, $06 +.charmap $27, $07 +.charmap $28, $08 +.charmap $29, $09 +.charmap $2A, $0A +.charmap $2B, $0B +.charmap $2C, $0C +.charmap $2D, $0D +.charmap $2E, $0E +.charmap $2F, $0F + +.charmap $30, $10 +.charmap $31, $11 +.charmap $32, $12 +.charmap $33, $13 +.charmap $34, $14 +.charmap $35, $15 +.charmap $36, $16 +.charmap $37, $17 +.charmap $38, $18 +.charmap $39, $19 +.charmap $3A, $1A +.charmap $3B, $1B +.charmap $3C, $1C +.charmap $3D, $1D +.charmap $3E, $1E +.charmap $3F, $1F + +.charmap $40, $20 +.charmap $41, $21 +.charmap $42, $22 +.charmap $43, $23 +.charmap $44, $24 +.charmap $45, $25 +.charmap $46, $26 +.charmap $47, $27 +.charmap $48, $28 +.charmap $49, $29 +.charmap $4A, $2A +.charmap $4B, $2B +.charmap $4C, $2C +.charmap $4D, $2D +.charmap $4E, $2E +.charmap $4F, $2F + +.charmap $50, $30 +.charmap $51, $31 +.charmap $52, $32 +.charmap $53, $33 +.charmap $54, $34 +.charmap $55, $35 +.charmap $56, $36 +.charmap $57, $37 +.charmap $58, $38 +.charmap $59, $39 +.charmap $5A, $3A +.charmap $5B, $3B +.charmap $5C, $3C +.charmap $5D, $3D +.charmap $5E, $3E +.charmap $5F, $3F + +.charmap $60, $60 +.charmap $61, $61 +.charmap $62, $62 +.charmap $63, $63 +.charmap $64, $64 +.charmap $65, $65 +.charmap $66, $66 +.charmap $67, $67 +.charmap $68, $68 +.charmap $69, $69 +.charmap $6A, $6A +.charmap $6B, $6B +.charmap $6C, $6C +.charmap $6D, $6D +.charmap $6E, $6E +.charmap $6F, $6F + +.charmap $70, $70 +.charmap $71, $71 +.charmap $72, $72 +.charmap $73, $73 +.charmap $74, $74 +.charmap $75, $75 +.charmap $76, $76 +.charmap $77, $77 +.charmap $78, $78 +.charmap $79, $79 +.charmap $7A, $7A +.charmap $7B, $7B +.charmap $7C, $7C +.charmap $7D, $7D +.charmap $7E, $7E +.charmap $7F, $7F + +.charmap $80, $C0 +.charmap $81, $C1 +.charmap $82, $C2 +.charmap $83, $C3 +.charmap $84, $C4 +.charmap $85, $C5 +.charmap $86, $C6 +.charmap $87, $C7 +.charmap $88, $C8 +.charmap $89, $C9 +.charmap $8A, $CA +.charmap $8B, $CB +.charmap $8C, $CC +.charmap $8D, $CD +.charmap $8E, $CE +.charmap $8F, $CF + +.charmap $90, $D0 +.charmap $91, $D1 +.charmap $92, $D2 +.charmap $93, $D3 +.charmap $94, $D4 +.charmap $95, $D5 +.charmap $96, $D6 +.charmap $97, $D7 +.charmap $98, $D8 +.charmap $99, $D9 +.charmap $9A, $DA +.charmap $9B, $DB +.charmap $9C, $DC +.charmap $9D, $DD +.charmap $9E, $DE +.charmap $9F, $DF + +.charmap $A0, $80 +.charmap $A1, $81 +.charmap $A2, $82 +.charmap $A3, $83 +.charmap $A4, $84 +.charmap $A5, $85 +.charmap $A6, $86 +.charmap $A7, $87 +.charmap $A8, $88 +.charmap $A9, $89 +.charmap $AA, $8A +.charmap $AB, $8B +.charmap $AC, $8C +.charmap $AD, $8D +.charmap $AE, $8E +.charmap $AF, $8F + +.charmap $B0, $90 +.charmap $B1, $91 +.charmap $B2, $92 +.charmap $B3, $93 +.charmap $B4, $94 +.charmap $B5, $95 +.charmap $B6, $96 +.charmap $B7, $97 +.charmap $B8, $98 +.charmap $B9, $99 +.charmap $BA, $9A +.charmap $BB, $9B +.charmap $BC, $9C +.charmap $BD, $9D +.charmap $BE, $9E +.charmap $BF, $9F + +.charmap $C0, $A0 +.charmap $C1, $A1 +.charmap $C2, $A2 +.charmap $C3, $A3 +.charmap $C4, $A4 +.charmap $C5, $A5 +.charmap $C6, $A6 +.charmap $C7, $A7 +.charmap $C8, $A8 +.charmap $C9, $A9 +.charmap $CA, $AA +.charmap $CB, $AB +.charmap $CC, $AC +.charmap $CD, $AD +.charmap $CE, $AE +.charmap $CF, $AF + +.charmap $D0, $B0 +.charmap $D1, $B1 +.charmap $D2, $B2 +.charmap $D3, $B3 +.charmap $D4, $B4 +.charmap $D5, $B5 +.charmap $D6, $B6 +.charmap $D7, $B7 +.charmap $D8, $B8 +.charmap $D9, $B9 +.charmap $DA, $BA +.charmap $DB, $BB +.charmap $DC, $BC +.charmap $DD, $BD +.charmap $DE, $BE +.charmap $DF, $BF + +.charmap $E0, $E0 +.charmap $E1, $E1 +.charmap $E2, $E2 +.charmap $E3, $E3 +.charmap $E4, $E4 +.charmap $E5, $E5 +.charmap $E6, $E6 +.charmap $E7, $E7 +.charmap $E8, $E8 +.charmap $E9, $E9 +.charmap $EA, $EA +.charmap $EB, $EB +.charmap $EC, $EC +.charmap $ED, $ED +.charmap $EE, $EE +.charmap $EF, $EF + +.charmap $F0, $F0 +.charmap $F1, $F1 +.charmap $F2, $F2 +.charmap $F3, $F3 +.charmap $F4, $F4 +.charmap $F5, $F5 +.charmap $F6, $F6 +.charmap $F7, $F7 +.charmap $F8, $F8 +.charmap $F9, $F9 +.charmap $FA, $FA +.charmap $FB, $FB +.charmap $FC, $FC +.charmap $FD, $FD +.charmap $FE, $FE +.charmap $FF, $FF + diff --git a/asminc/c128.inc b/asminc/c128.inc index 2852631f3..749b4168c 100644 --- a/asminc/c128.inc +++ b/asminc/c128.inc @@ -7,6 +7,7 @@ ; Zero page, Commodore stuff TXTPTR := $3D ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address diff --git a/asminc/c64.inc b/asminc/c64.inc index 1d10f673d..d131c7860 100644 --- a/asminc/c64.inc +++ b/asminc/c64.inc @@ -9,6 +9,7 @@ VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $7A ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60 HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address @@ -76,6 +77,8 @@ VIC_SPR_EXP_Y := $D017 VIC_SPR_EXP_X := $D01D VIC_SPR_MCOLOR := $D01C VIC_SPR_BG_PRIO := $D01B +VIC_SPR_COLL := $D01E +VIC_SPR_BG_COLL := $D01F VIC_SPR_MCOLOR0 := $D025 VIC_SPR_MCOLOR1 := $D026 diff --git a/asminc/cbm_kernal.inc b/asminc/cbm_kernal.inc index 981687206..5f41c6267 100644 --- a/asminc/cbm_kernal.inc +++ b/asminc/cbm_kernal.inc @@ -46,6 +46,7 @@ GRAPH_SET_FONT := $FF3B GRAPH_GET_CHAR_SIZE := $FF3E GRAPH_PUT_CHAR := $FF41 + MULTI_ACPTR := $FF44 RESTORE_BASIC := $FF47 CLOCK_SET_DATE_TIME := $FF4D CLOCK_GET_DATE_TIME := $FF50 diff --git a/asminc/cbm_petscii_charmap.inc b/asminc/cbm_petscii_charmap.inc new file mode 100644 index 000000000..525bffb01 --- /dev/null +++ b/asminc/cbm_petscii_charmap.inc @@ -0,0 +1,291 @@ +;/*****************************************************************************/ +;/* */ +;/* cbm_petscii_charmap.inc */ +;/* */ +;/* CBM system standard string mapping ISO-8859-1 -> PetSCII */ +;/* */ +;/* */ +;/* 2019-03-10, Greg King */ +;/* */ +;/* 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 must not be removed or altered from any source */ +;/* distribution. */ +;/* */ +;/*****************************************************************************/ + +.charmap $00, $00 +.charmap $01, $01 +.charmap $02, $02 +.charmap $03, $03 +.charmap $04, $04 +.charmap $05, $05 +.charmap $06, $06 +.charmap $07, $07 +.charmap $08, $14 +.charmap $09, $09 +.charmap $0A, $0D +.charmap $0B, $11 +.charmap $0C, $93 +.charmap $0D, $0A +.charmap $0E, $0E +.charmap $0F, $0F +.charmap $10, $10 +.charmap $11, $0B +.charmap $12, $12 +.charmap $13, $13 +.charmap $14, $08 +.charmap $15, $15 +.charmap $16, $16 +.charmap $17, $17 +.charmap $18, $18 +.charmap $19, $19 +.charmap $1A, $1A +.charmap $1B, $1B +.charmap $1C, $1C +.charmap $1D, $1D +.charmap $1E, $1E +.charmap $1F, $1F + +.charmap $20, $20 +.charmap $21, $21 +.charmap $22, $22 +.charmap $23, $23 +.charmap $24, $24 +.charmap $25, $25 +.charmap $26, $26 +.charmap $27, $27 +.charmap $28, $28 +.charmap $29, $29 +.charmap $2A, $2A +.charmap $2B, $2B +.charmap $2C, $2C +.charmap $2D, $2D +.charmap $2E, $2E +.charmap $2F, $2F +.charmap $30, $30 +.charmap $31, $31 +.charmap $32, $32 +.charmap $33, $33 +.charmap $34, $34 +.charmap $35, $35 +.charmap $36, $36 +.charmap $37, $37 +.charmap $38, $38 +.charmap $39, $39 +.charmap $3A, $3A +.charmap $3B, $3B +.charmap $3C, $3C +.charmap $3D, $3D +.charmap $3E, $3E +.charmap $3F, $3F + +.charmap $40, $40 +.charmap $41, $C1 +.charmap $42, $C2 +.charmap $43, $C3 +.charmap $44, $C4 +.charmap $45, $C5 +.charmap $46, $C6 +.charmap $47, $C7 +.charmap $48, $C8 +.charmap $49, $C9 +.charmap $4A, $CA +.charmap $4B, $CB +.charmap $4C, $CC +.charmap $4D, $CD +.charmap $4E, $CE +.charmap $4F, $CF +.charmap $50, $D0 +.charmap $51, $D1 +.charmap $52, $D2 +.charmap $53, $D3 +.charmap $54, $D4 +.charmap $55, $D5 +.charmap $56, $D6 +.charmap $57, $D7 +.charmap $58, $D8 +.charmap $59, $D9 +.charmap $5A, $DA +.charmap $5B, $5B +.charmap $5C, $BF +.charmap $5D, $5D +.charmap $5E, $5E +.charmap $5F, $A4 + +.charmap $60, $AD +.charmap $61, $41 +.charmap $62, $42 +.charmap $63, $43 +.charmap $64, $44 +.charmap $65, $45 +.charmap $66, $46 +.charmap $67, $47 +.charmap $68, $48 +.charmap $69, $49 +.charmap $6A, $4A +.charmap $6B, $4B +.charmap $6C, $4C +.charmap $6D, $4D +.charmap $6E, $4E +.charmap $6F, $4F +.charmap $70, $50 +.charmap $71, $51 +.charmap $72, $52 +.charmap $73, $53 +.charmap $74, $54 +.charmap $75, $55 +.charmap $76, $56 +.charmap $77, $57 +.charmap $78, $58 +.charmap $79, $59 +.charmap $7A, $5A +.charmap $7B, $B3 +.charmap $7C, $DD +.charmap $7D, $AB +.charmap $7E, $B1 +.charmap $7F, $DF + +.charmap $80, $80 +.charmap $81, $81 +.charmap $82, $82 +.charmap $83, $83 +.charmap $84, $84 +.charmap $85, $85 +.charmap $86, $86 +.charmap $87, $87 +.charmap $88, $88 +.charmap $89, $89 +.charmap $8A, $8A +.charmap $8B, $8B +.charmap $8C, $8C +.charmap $8D, $8D +.charmap $8E, $8E +.charmap $8F, $8F +.charmap $90, $90 +.charmap $91, $91 +.charmap $92, $92 +.charmap $93, $0C +.charmap $94, $94 +.charmap $95, $95 +.charmap $96, $96 +.charmap $97, $97 +.charmap $98, $98 +.charmap $99, $99 +.charmap $9A, $9A +.charmap $9B, $9B +.charmap $9C, $9C +.charmap $9D, $9D +.charmap $9E, $9E +.charmap $9F, $9F + +.charmap $A0, $A0 +.charmap $A1, $A1 +.charmap $A2, $A2 +.charmap $A3, $A3 +.charmap $A4, $A4 +.charmap $A5, $A5 +.charmap $A6, $A6 +.charmap $A7, $A7 +.charmap $A8, $A8 +.charmap $A9, $A9 +.charmap $AA, $AA +.charmap $AB, $AB +.charmap $AC, $AC +.charmap $AD, $AD +.charmap $AE, $AE +.charmap $AF, $AF +.charmap $B0, $B0 +.charmap $B1, $B1 +.charmap $B2, $B2 +.charmap $B3, $B3 +.charmap $B4, $B4 +.charmap $B5, $B5 +.charmap $B6, $B6 +.charmap $B7, $B7 +.charmap $B8, $B8 +.charmap $B9, $B9 +.charmap $BA, $BA +.charmap $BB, $BB +.charmap $BC, $BC +.charmap $BD, $BD +.charmap $BE, $BE +.charmap $BF, $BF + +.charmap $C0, $60 +.charmap $C1, $61 +.charmap $C2, $62 +.charmap $C3, $63 +.charmap $C4, $64 +.charmap $C5, $65 +.charmap $C6, $66 +.charmap $C7, $67 +.charmap $C8, $68 +.charmap $C9, $69 +.charmap $CA, $6A +.charmap $CB, $6B +.charmap $CC, $6C +.charmap $CD, $6D +.charmap $CE, $6E +.charmap $CF, $6F +.charmap $D0, $70 +.charmap $D1, $71 +.charmap $D2, $72 +.charmap $D3, $73 +.charmap $D4, $74 +.charmap $D5, $75 +.charmap $D6, $76 +.charmap $D7, $77 +.charmap $D8, $78 +.charmap $D9, $79 +.charmap $DA, $7A +.charmap $DB, $7B +.charmap $DC, $7C +.charmap $DD, $7D +.charmap $DE, $7E +.charmap $DF, $7F + +.charmap $E0, $E0 +.charmap $E1, $E1 +.charmap $E2, $E2 +.charmap $E3, $E3 +.charmap $E4, $E4 +.charmap $E5, $E5 +.charmap $E6, $E6 +.charmap $E7, $E7 +.charmap $E8, $E8 +.charmap $E9, $E9 +.charmap $EA, $EA +.charmap $EB, $EB +.charmap $EC, $EC +.charmap $ED, $ED +.charmap $EE, $EE +.charmap $EF, $EF +.charmap $F0, $F0 +.charmap $F1, $F1 +.charmap $F2, $F2 +.charmap $F3, $F3 +.charmap $F4, $F4 +.charmap $F5, $F5 +.charmap $F6, $F6 +.charmap $F7, $F7 +.charmap $F8, $F8 +.charmap $F9, $F9 +.charmap $FA, $FA +.charmap $FB, $FB +.charmap $FC, $FC +.charmap $FD, $FD +.charmap $FE, $FE +.charmap $FF, $FF diff --git a/asminc/cbm_screen_charmap.inc b/asminc/cbm_screen_charmap.inc new file mode 100644 index 000000000..12d02553d --- /dev/null +++ b/asminc/cbm_screen_charmap.inc @@ -0,0 +1,305 @@ +;/*****************************************************************************/ +;/* */ +;/* cbm_screen_charmap.inc */ +;/* */ +;/* c Copyright 2019, Gerhard W. Gruber (sparhawk@gmx.at) */ +;/* */ +;/* When using CBM mode, this include converts character literals */ +;/* from ASCII to screen-code mapping, so you can write directly */ +;/* to the screen memory. */ +;/* */ +;/* If this include is used, no additional macros are needed. */ +;/* */ +;/*****************************************************************************/ + +; Char $00 -> c + 128 +.charmap $00, $80 + +; Char $01 ... $1A -> c + 128 + 64 control alphabet +.charmap $01, $C1 +.charmap $02, $C2 +.charmap $03, $C3 +.charmap $04, $C4 +.charmap $05, $C5 +.charmap $06, $C6 +.charmap $07, $C7 +.charmap $08, $C8 +.charmap $09, $C9 +.charmap $0A, $CA +.charmap $0B, $CB +.charmap $0C, $CC +.charmap $0D, $CD +.charmap $0E, $CE +.charmap $0F, $CF +.charmap $10, $D0 +.charmap $11, $D1 +.charmap $12, $D2 +.charmap $13, $D3 +.charmap $14, $D4 +.charmap $15, $D5 +.charmap $16, $D6 +.charmap $17, $D7 +.charmap $18, $D8 +.charmap $19, $D9 +.charmap $1A, $DA + +; Char $1B ... $1F -> c + 128 +.charmap $1B, $9B +.charmap $1C, $9C +.charmap $1D, $9D +.charmap $1E, $9E +.charmap $1F, $9F + +; Char $20 ... $3F -> c +.charmap $20, $20 +.charmap $21, $21 +.charmap $22, $22 +.charmap $23, $23 +.charmap $24, $24 +.charmap $25, $25 +.charmap $26, $26 +.charmap $27, $27 +.charmap $28, $28 +.charmap $29, $29 +.charmap $2A, $2A +.charmap $2B, $2B +.charmap $2C, $2C +.charmap $2D, $2D +.charmap $2E, $2E +.charmap $2F, $2F +.charmap $30, $30 +.charmap $31, $31 +.charmap $32, $32 +.charmap $33, $33 +.charmap $34, $34 +.charmap $35, $35 +.charmap $36, $36 +.charmap $37, $37 +.charmap $38, $38 +.charmap $39, $39 +.charmap $3A, $3A +.charmap $3B, $3B +.charmap $3C, $3C +.charmap $3D, $3D +.charmap $3E, $3E +.charmap $3F, $3F + +; Char $40 -> c - 64 +.charmap $40, $00 + +; Char $41 ... $5A -> c upper-case alphabet +.charmap $41, $41 +.charmap $42, $42 +.charmap $43, $43 +.charmap $44, $44 +.charmap $45, $45 +.charmap $46, $46 +.charmap $47, $47 +.charmap $48, $48 +.charmap $49, $49 +.charmap $4A, $4A +.charmap $4B, $4B +.charmap $4C, $4C +.charmap $4D, $4D +.charmap $4E, $4E +.charmap $4F, $4F +.charmap $50, $50 +.charmap $51, $51 +.charmap $52, $52 +.charmap $53, $53 +.charmap $54, $54 +.charmap $55, $55 +.charmap $56, $56 +.charmap $57, $57 +.charmap $58, $58 +.charmap $59, $59 +.charmap $5A, $5A + +; Char $5B ... $5F -> c - 64 +.charmap $5B, $1B +.charmap $5C, $1C +.charmap $5D, $1D +.charmap $5E, $1E +.charmap $5F, $1F + +; Char $60 -> c - 32 +.charmap $60, $40 + +; Char $61 ... $7A -> c - 32 - 64 lower-case alphabet +.charmap $61, $01 +.charmap $62, $02 +.charmap $63, $03 +.charmap $64, $04 +.charmap $65, $05 +.charmap $66, $06 +.charmap $67, $07 +.charmap $68, $08 +.charmap $69, $09 +.charmap $6A, $0A +.charmap $6B, $0B +.charmap $6C, $0C +.charmap $6D, $0D +.charmap $6E, $0E +.charmap $6F, $0F +.charmap $70, $10 +.charmap $71, $11 +.charmap $72, $12 +.charmap $73, $13 +.charmap $74, $14 +.charmap $75, $15 +.charmap $76, $16 +.charmap $77, $17 +.charmap $78, $18 +.charmap $79, $19 +.charmap $7A, $1A + +; Char $7B ... $7F -> c - 32 +.charmap $7B, $5B +.charmap $7C, $5C +.charmap $7D, $5D +.charmap $7E, $5E +.charmap $7F, $5F + +; Char $80 -> c + 64 +.charmap $80, $C0 + +; Char $81 ... $9A -> c control alphabet +.charmap $81, $81 +.charmap $82, $82 +.charmap $83, $83 +.charmap $84, $84 +.charmap $85, $85 +.charmap $86, $86 +.charmap $87, $87 +.charmap $88, $88 +.charmap $89, $89 +.charmap $8A, $8A +.charmap $8B, $8B +.charmap $8C, $8C +.charmap $8D, $8D +.charmap $8E, $8E +.charmap $8F, $8F +.charmap $90, $90 +.charmap $91, $91 +.charmap $92, $92 +.charmap $93, $93 +.charmap $94, $94 +.charmap $95, $95 +.charmap $96, $96 +.charmap $97, $97 +.charmap $98, $98 +.charmap $99, $99 +.charmap $9A, $9A + +; Char $9B ... $9F -> c + 64 +.charmap $9B, $DB +.charmap $9C, $DC +.charmap $9D, $DD +.charmap $9E, $DE +.charmap $9F, $DF + +; Char $A0 ... $BF -> c - 64 +.charmap $A0, $60 +.charmap $A1, $61 +.charmap $A2, $62 +.charmap $A3, $63 +.charmap $A4, $64 +.charmap $A5, $65 +.charmap $A6, $66 +.charmap $A7, $67 +.charmap $A8, $68 +.charmap $A9, $69 +.charmap $AA, $6A +.charmap $AB, $6B +.charmap $AC, $6C +.charmap $AD, $6D +.charmap $AE, $6E +.charmap $AF, $6F +.charmap $B0, $70 +.charmap $B1, $71 +.charmap $B2, $72 +.charmap $B3, $73 +.charmap $B4, $74 +.charmap $B5, $75 +.charmap $B6, $76 +.charmap $B7, $77 +.charmap $B8, $78 +.charmap $B9, $79 +.charmap $BA, $7A +.charmap $BB, $7B +.charmap $BC, $7C +.charmap $BD, $7D +.charmap $BE, $7E +.charmap $BF, $7F + +; Char $C0 ... $DF -> c - 128 +.charmap $C0, $40 + +; Char $C1 ... $DA -> c - 128 - 64 lower-case alphabet +.charmap $C1, $01 +.charmap $C2, $02 +.charmap $C3, $03 +.charmap $C4, $04 +.charmap $C5, $05 +.charmap $C6, $06 +.charmap $C7, $07 +.charmap $C8, $08 +.charmap $C9, $09 +.charmap $CA, $0A +.charmap $CB, $0B +.charmap $CC, $0C +.charmap $CD, $0D +.charmap $CE, $0E +.charmap $CF, $0F +.charmap $D0, $10 +.charmap $D1, $11 +.charmap $D2, $12 +.charmap $D3, $13 +.charmap $D4, $14 +.charmap $D5, $15 +.charmap $D6, $16 +.charmap $D7, $17 +.charmap $D8, $18 +.charmap $D9, $19 +.charmap $DA, $1A + +; Char $DB ... $DF -> c - 128 +.charmap $DB, $5B +.charmap $DC, $5C +.charmap $DD, $5D +.charmap $DE, $5E +.charmap $DF, $5F + +; Char $E0 ... $FF -> c - 128 +.charmap $E0, $60 +.charmap $E1, $61 +.charmap $E2, $62 +.charmap $E3, $63 +.charmap $E4, $64 +.charmap $E5, $65 +.charmap $E6, $66 +.charmap $E7, $67 +.charmap $E8, $68 +.charmap $E9, $69 +.charmap $EA, $6A +.charmap $EB, $6B +.charmap $EC, $6C +.charmap $ED, $6D +.charmap $EE, $6E +.charmap $EF, $6F +.charmap $F0, $70 +.charmap $F1, $71 +.charmap $F2, $72 +.charmap $F3, $73 +.charmap $F4, $74 +.charmap $F5, $75 +.charmap $F6, $76 +.charmap $F7, $77 +.charmap $F8, $78 +.charmap $F9, $79 +.charmap $FA, $7A +.charmap $FB, $7B +.charmap $FC, $7C +.charmap $FD, $7D +.charmap $FE, $7E +.charmap $FF, $7F diff --git a/asminc/cpu.mac b/asminc/cpu.mac index 6b9cb9947..31170fbed 100644 --- a/asminc/cpu.mac +++ b/asminc/cpu.mac @@ -2,18 +2,20 @@ CPU_ISET_NONE = $0001 CPU_ISET_6502 = $0002 CPU_ISET_6502X = $0004 -CPU_ISET_65SC02 = $0008 -CPU_ISET_65C02 = $0010 -CPU_ISET_65816 = $0020 -CPU_ISET_SWEET16 = $0040 -CPU_ISET_HUC6280 = $0080 -;CPU_ISET_M740 = $0100 not actually implemented -CPU_ISET_4510 = $0200 +CPU_ISET_6502DTV = $0008 +CPU_ISET_65SC02 = $0010 +CPU_ISET_65C02 = $0020 +CPU_ISET_65816 = $0040 +CPU_ISET_SWEET16 = $0080 +CPU_ISET_HUC6280 = $0100 +;CPU_ISET_M740 = $0200 not actually implemented +CPU_ISET_4510 = $0400 ; CPU capabilities CPU_NONE = CPU_ISET_NONE CPU_6502 = CPU_ISET_6502 CPU_6502X = CPU_ISET_6502|CPU_ISET_6502X +CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502X|CPU_ISET_6502DTV CPU_65SC02 = CPU_ISET_6502|CPU_ISET_65SC02 CPU_65C02 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65C02 CPU_65816 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65816 diff --git a/asminc/creativision.inc b/asminc/creativision.inc index 49d55a342..a0259ecce 100644 --- a/asminc/creativision.inc +++ b/asminc/creativision.inc @@ -5,21 +5,21 @@ ;** Screen SCREEN_ROWS = 24 SCREEN_COLS = 32 -SCREEN_PTR = $3A -CURSOR_X = $3C -CURSOR_Y = $3D +SCREEN_PTR := $3A +CURSOR_X := $3C +CURSOR_Y := $3D ;** VDP -VDP_DATA_R = $2000 -VDP_STATUS_R = $2001 -VDP_DATA_W = $3000 -VDP_CONTROL_W = $3001 +VDP_DATA_R := $2000 +VDP_STATUS_R := $2001 +VDP_DATA_W := $3000 +VDP_CONTROL_W := $3001 ;** PIA -PIA0_DATA = $1000 -PIA0_STATUS = $1001 -PIA1_DATA = $1002 -PIA1_STATUS = $1003 +PIA0_DATA := $1000 +PIA0_STATUS := $1001 +PIA1_DATA := $1002 +PIA1_STATUS := $1003 ;** General CH_VLINE = 33 @@ -30,11 +30,11 @@ CH_LLCORNER = 37 CH_LRCORNER = 38 ;** I/O (Zero-page variables) -ZP_KEYBOARD = $10 -ZP_JOY0_DIR = $11 -ZP_JOY1_DIR = $13 -ZP_JOY0_BUTTONS = $16 -ZP_JOY1_BUTTONS = $17 +ZP_KEYBOARD := $10 +ZP_JOY0_DIR := $11 +ZP_JOY1_DIR := $13 +ZP_JOY0_BUTTONS := $16 +ZP_JOY1_BUTTONS := $17 ;** Joystick direction values (ZP_JOY0_DIR/ZP_JOY1_DIR) JOY_N = $49 @@ -54,8 +54,13 @@ JOY_WNW = $4C JOY_NW = $4B JOY_NNW = $4A -;** BIOS -BIOS_IRQ1_ADDR = $FF3F -BIOS_IRQ2_ADDR = $FF52 -BIOS_NMI_RESET_ADDR = $F808 -BIOS_WRITE_VDP_REG = $FE1F +;** BIOS routines +BIOS_NMI_RESET_ADDR := $F808 +BIOS_PLAY_TUNE1 := $FBD6 +BIOS_PLAY_SONG := $FBED +BIOS_PLAY_TUNE2 := $FCE6 +BIOS_WRITE_VDP_REG := $FE1F +BIOS_QUIET_PSG := $FE54 +BIOS_POKE_PSG := $FE77 +BIOS_IRQ1_ADDR := $FF3F +BIOS_IRQ2_ADDR := $FF52 diff --git a/asminc/cx16.inc b/asminc/cx16.inc index 8891c1761..4d9ce89db 100644 --- a/asminc/cx16.inc +++ b/asminc/cx16.inc @@ -1,5 +1,5 @@ ; -; CX16 r37 definitions +; CX16 r38 definitions ; ; --------------------------------------------------------------------------- @@ -439,7 +439,7 @@ NMIVec := $0318 .scope PSG ; Programmable Sound Generator .struct PITCH .word - VOL .byte ; Left, right channels; volume + VOL .byte ; Right, left sides; volume WAVEFORM .byte ; Wave shape, pulse width .endstruct LEFT = %01 << 6 @@ -544,9 +544,11 @@ NMIVec := $0318 VERALOG .byte ; Boolean: log VERA activity KEYBOARDLOG .byte ; Boolean: log keyboard data ECHO .byte ; Type of echo that's enabled - SAVEXIT .byte ; Boolean: save on exit + SAVEXIT .byte ; Boolean: save machine state on exit GIFREC .byte ; Method of recording GIF movie - .org $9FBD + .res 2 + CYCLECOUNT .dword ; Running count of CPU cycles (Read-Only) + .res 1 KEYMAP .byte ; Current keyboard layout number (Read-Only) DETECT .byte 2 ; If is "16" string, then running on emulator (RO) .endstruct diff --git a/asminc/em-kernel.inc b/asminc/em-kernel.inc index e7cdf9a70..889ffba98 100644 --- a/asminc/em-kernel.inc +++ b/asminc/em-kernel.inc @@ -7,7 +7,7 @@ ;/* */ ;/* */ ;/* (C) 2002-2003 Ullrich von Bassewitz */ -;/* Römerstrasse 52 */ +;/* Roemerstrasse 52 */ ;/* D-70794 Filderstadt */ ;/* EMail: uz@cc65.org */ ;/* */ diff --git a/asminc/joy-kernel.inc b/asminc/joy-kernel.inc index ef729fe3c..791934197 100644 --- a/asminc/joy-kernel.inc +++ b/asminc/joy-kernel.inc @@ -7,7 +7,7 @@ ;/* */ ;/* */ ;/* (C) 2002-2006, Ullrich von Bassewitz */ -;/* Römerstraße 52 */ +;/* Roemerstrasse 52 */ ;/* D-70794 Filderstadt */ ;/* EMail: uz@cc65.org */ ;/* */ diff --git a/asminc/opcodes.inc b/asminc/opcodes.inc index aa7a65f00..e6b7e73df 100644 --- a/asminc/opcodes.inc +++ b/asminc/opcodes.inc @@ -505,4 +505,12 @@ OPC_ISC_aby = $FB OPC_NOP_abx = $FC OPC_ISC_abx = $FF +.if (.cpu .bitand ::CPU_ISET_6502DTV) + +OPC_BRA = $12 +OPC_SAC_imm = $32 +OPC_SIR_imm = $42 + +.endif + .endif diff --git a/asminc/plus4.inc b/asminc/plus4.inc index 774722e93..6c6017a78 100644 --- a/asminc/plus4.inc +++ b/asminc/plus4.inc @@ -10,6 +10,7 @@ TMPPTR := $22 ; Temporary ptr used by BASIC VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $3B ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A3 ; 60HZ clock FNAM_LEN := $AB ; Length of filename LFN := $AC ; Logical file number diff --git a/asminc/ser-kernel.inc b/asminc/ser-kernel.inc index 546587515..79ace64e9 100644 --- a/asminc/ser-kernel.inc +++ b/asminc/ser-kernel.inc @@ -7,7 +7,7 @@ ;* * ;* * ;*(C) 2003-2006, Ullrich von Bassewitz * -;* Römerstrasse 52 * +;* Roemerstrasse 52 * ;* D-70794 Filderstadt * ;*EMail: uz@cc65.org * ;* * diff --git a/asminc/stdio.inc b/asminc/stdio.inc index 3b22c47f6..426389de3 100644 --- a/asminc/stdio.inc +++ b/asminc/stdio.inc @@ -7,7 +7,7 @@ ;* */ ;* */ ;* (C) 2003-2005, Ullrich von Bassewitz */ -;* Römerstrasse 52 */ +;* Roemerstrasse 52 */ ;* D-70794 Filderstadt */ ;* EMail: uz@cc65.org */ ;* */ @@ -44,7 +44,9 @@ EOF = -1 .if .defined(__APPLE2__) FILENAME_MAX = 64+1 .elseif .defined(__ATARI__) -FILENAME_MAX = 12+1 +FILENAME_MAX = 63+1 +.elseif .defined(__CBM__) +FILENAME_MAX = 255 .elseif .defined(__LUNIX__) FILENAME_MAX = 80+1 .elseif .defined(__TELESTRAT__) diff --git a/asminc/sym1.inc b/asminc/sym1.inc new file mode 100644 index 000000000..b6a6f17fe --- /dev/null +++ b/asminc/sym1.inc @@ -0,0 +1,186 @@ +; --------------------------------------------------------------------------- +; +; SYM-1 definitions +; +; --------------------------------------------------------------------------- + + +RAMSTART := $0200 ; Entry point + + +; --------------------------------------------------------------------------- +; Monitor Functions +; --------------------------------------------------------------------------- +WARM := $8003 ; Monitor entry +SVNMI := $809B ; Save NMI entry +INBYTE := $81D9 ; Get two HEX characters and pack +ASCNIB := $8275 ; Test for carriage-return +INCCMP := $82B2 ; Increment pointer +CHKSAD := $82DD ; Compute checksum +OUTPC := $82EE ; Display program counter +OUTBYT := $82FA ; Print byte as two ASCII characters +OUTS2 := $8319 ; Print pointer +INSTAT := $8386 ; Determine if key is pressed +GETKEY := $88AF ; Get key (disregarding monitor login) +SCAND := $8906 ; Flash LED display (once) +KEYQ := $8923 ; Test for keypress +BEEP := $8972 ; Make a beep +CONFIG := $89A5 ; Configure I/O +OUTDSP := $89C1 ; Output to on-board LED display +INCHR := $8A1B ; Input character and convert to uppercase +OUTCHR := $8A47 ; Output character +INTCHR := $8A58 ; Input character without case conversion +DLYF := $8AE6 ; Delay 1 bit time +DLYH := $8AE9 ; Delay 1/2 bit time +RESET := $8B4A ; Hard reset +ACCESS := $8B86 ; Unlock lowest 4K memory +NACCES := $8B9C ; Lock lowest 4K memory +L8C78 := $8C78 ; Link to tape +DUMPT := $8E87 ; Dump memory to tape +LOADT := $8C78 ; Load memory from tape +TAPEMODE := $00FD ; Top bit on for high-speed + + +; --------------------------------------------------------------------------- +; System Memory +; --------------------------------------------------------------------------- +DISBUF := $A640 ; On-Board Display Buffer +DISBUF0 := $A640 ; Left-Most digit +DISBUF1 := $A641 ; Second digit +DISBUF2 := $A642 ; Third +DISBUF3 := $A643 ; Fourth +DISBUF4 := $A644 ; Fifth +DISBUF5 := $A645 ; Sixth and right-most digit +DISBUF6 := $A646 ; Not-used / right of display (shift buffer) +RDIG := $A645 ; Right-most digit (same as DISBUF5) +P3L := $A64A ; Parameter 3 (low-byte) +P3H := $A64B ; (high-byte) +P2L := $A64C ; Parameter 2 +P2H := $A64D ; +P1L := $A64E ; Parameter 1 +P1H := $A64F +PARNR := $A649 ; Number of Parameters Entered +PADBIT := $A650 ; Pad Bits for Carriage Return +SDBYT := $A651 ; Baud Rate for RS232 (01-4800,06-2400,10-1200,24-600,4C-300,D5-110) +ERCNT := $A652 ; Error Count (Max FF) +TECHO := $A653 ; Terminal Echo (bit-7=ECHO/NO, 6=CTL-O TOGGLE) +TOUTFL := $A654 ; Output Flags (bit-7=CRT IN, 6=TTY IN, 5=TTY OUT, 4=CRT OUT) +KSHFL := $A655 ; Keyboard Shift Flag +TV := $A656 ; Trace Velocity (0=Single Step) +LSTCOM := $A657 ; Last Monitor Command +MAXRC := $A658 ; Maximum Record Length for Memory Dump + + +; --------------------------------------------------------------------------- +; Register Followers +; --------------------------------------------------------------------------- +PCLR := $A659 ; Program Counter (low-byte) +PCHR := $A65A ; (high-byte) +SR := $A65B ; Stack Pointer +FR := $A65C ; Status Register Flags +AR := $A65D ; A Register +XR := $A65E ; X Register +YR := $A65F ; Y Register + + +; --------------------------------------------------------------------------- +; I/O Vectors (3 bytes each) +; --------------------------------------------------------------------------- +INVEC := $A660 ; Input Character +OUTVEC := $A663 ; Output Character +INSVEC := $A666 ; Input Status +URSVEC := $A669 ; Unrecognized Syntax +URCVEC := $A66C ; Unrecognized Command / Error +SCNVEC := $A66F ; Scan On-board Display + + +; --------------------------------------------------------------------------- +; Trace and Interrupt Vectors (2 bytes each) +; --------------------------------------------------------------------------- +EXEVEC := $A672 ; Exec and Alternate InVec +TRCVEC := $A674 ; Trace +UBRKVC := $A676 ; User Break after Monitor +UIRQVC := $A678 ; User non-break IRQ after Monitor +NMIVEC := $A67A ; Non-Maskable Interrupt +RSTVEC := $A67C ; Reset +IRQVEC := $A67E ; Interrupt Request + + +; --------------------------------------------------------------------------- +; I/O Registers +; --------------------------------------------------------------------------- +; +; 6532 (U27) +; +PADA := $A400 ; Keyboard / Display +P3DA := $A402 ; Serial I/O +DDPADA := $A401 ; Data-Direction Register for PADA +DDP3DA := $A403 ; Data-Direction Register for P3DA +WEDRTA := $A404 ; Write-Edge Detect Read Timer A +WEDRFA := $A405 ; Write-Edge Detect Read-Int Flags A +WEDRTB := $A406 ; Write-Edge Detect Read Timer B +WEDRFB := $A407 ; Write-Edge Detect Read-Int Flags B +TIM0001 := $A41C ; Timer / 1 +TIM0008 := $A41D ; Timer / 8 +TIM0064 := $A41E ; Timer / 64 +TIM1024 := $A41F ; Timer / 1024 +; +; 6522 (U25) +; +OR1A := $A001 ; Input / Output Register for 1A +DDR1A := $A003 ; Data-Direction Register for 1A +OR1B := $A000 ; Input / Output Register for 1B +DDR1B := $A002 ; Data-Direction Register for 1B +TIC1L := $A004 ; +TIC1H := $A005 ; +TIL1L := $A006 ; +TIL1H := $A007 ; +T2L1L := $A008 ; +T2C1L := $A008 ; +T2C1H := $A009 ; +SR1 := $A00A ; +ACR1 := $A00B ; +PCR1 := $A00C ; +IFR1 := $A00D ; +IER1 := $A00E ; +DR1A := $A00F ; +; +; 6522 (U28) +; +OR2A := $A801 ; Input / Output Register for 2A +DDR2A := $A803 ; Data-Direction Register for 2A +OR2B := $A800 ; Input / Output Register for 2B +DDR2B := $A802 ; Data-Direction Register for 2B +TIC2L := $A804 ; +TIC2H := $A805 ; +TIL2L := $A806 ; +TIL2H := $A807 ; +T2L2L := $A808 ; +T2C2L := $A808 ; +T2C2H := $A809 ; +SR2 := $A80A ; +ACR2 := $A80B ; +PCR2 := $A80C ; +IFR2 := $A80D ; +IER2 := $A80E ; +DR2A := $A80F ; +; +; 6522 (U29) +; +OR3A := $AC01 ; Write-Protect RAM, Debug On/Off, I/O-3A +DDR3A := $AC03 ; Data-Direction Register for 3A +OR3B := $AC00 ; Input / Output Register for 3B +DDR3B := $AC02 ; Data-Direction Register for 3B +TIC3L := $AC04 ; +TIC3H := $AC05 ; +TIL3L := $AC06 ; +TIL3H := $AC07 ; +T2L3L := $AC08 ; +T2C3L := $AC08 ; +T2C3H := $AC09 ; +SR3 := $AC0A ; +ACR3 := $AC0B ; +PCR3 := $AC0C ; +IFR3 := $AC0D ; +IER3 := $AC0E ; +DR3A := $AC0F ; diff --git a/asminc/telestrat.inc b/asminc/telestrat.inc index 2036574a4..7d4c1e31d 100644 --- a/asminc/telestrat.inc +++ b/asminc/telestrat.inc @@ -48,33 +48,33 @@ TR7 := $13 DEFAFF := $14 -IRQSVA := $21 ; Used to save A when a BRK call occurs -IRQSVX := $22 ; Used to save X when a BRK call occurs -IRQSVY := $23 ; Used to save Y when a BRK call occurs -IRQSVP := $24 ; Used to save P when a BRK call occurs +IRQSVA := $21 ; Used to save A when a BRK call occurs +IRQSVX := $22 ; Used to save X when a BRK call occurs +IRQSVY := $23 ; Used to save Y when a BRK call occurs +IRQSVP := $24 ; Used to save P when a BRK call occurs ADSCR := $26 -SCRNB := $28 ; Id of the current window +SCRNB := $28 ; Id of the current window -ADKBD := $2A ; Address ASCII conversion table +ADKBD := $2A ; Address ASCII conversion table -PTR_READ_DEST := $2C ; Used for XFREAD and XWRITE only in TELEMON 3.x +PTR_READ_DEST := $2C ; Used for XFREAD and XWRITE only in TELEMON 3.x -ADCLK := $40 ; Address for clock display +ADCLK := $40 ; Address for clock display TIMEUS := $42 -TIMEUD := $44 +TIMEUD := $44 ; Counter clock (1/10 of a second) HRSX := $46 HRSY := $47 -XLPRBI := $48 ; Printer flag (b7) +XLPRBI := $48 ; Printer flag (b7) HRSX40 := $49 HRSX6 := $4A -ADHRS := $4B ; Hires screen address (word) +ADHRS := $4B ; Hires screen address (word) HRS1 := $4D HRS2 := $4F @@ -277,6 +277,7 @@ XRECLK = $3C ; Reset clock XCLCL = $3D ; Close clock XWRCLK = $3E ; Displays clock in the adress in A & Y registers +; Sound primitives XSONPS = $40 ; Send data to PSG register (14 values) XOUPS = $42 ; Send Oups sound into PSG XPLAY = $43 ; Play a sound @@ -284,10 +285,25 @@ XSOUND = $44 XMUSIC = $45 XZAP = $46 ; Send Zap sound to PSG XSHOOT = $47 + +; Path Management +XGETCWD = $48 ; Get current CWD +XPUTCWD = $49 ; Chdir + +; File management XMKDIR = $4B ; Create a folder. Only available in TELEMON 3.x (bank 7 of Orix) + +XHCHRS = $4C ; Hard copy hires + +; File management XRM = $4D ; Remove a folder or a file. Only available in TELEMON 3.x (bank 7 of Orix) + XFWR = $4E ; Put a char on the first screen. Only available in TELEMON 3.x (bank 7 of Orix) -XGOKBD = $52 + +; Keyboard primitives +XALLKB = $50 ; Read Keyboard, and populate KBDCOL +XKBDAS = $51 ; Ascii conversion +XGOKBD = $52 ; Swap keyboard type (Qwerty, French ...) ; Buffer management XECRBU = $54 ; Write A or AY in the buffer @@ -299,8 +315,27 @@ XDEFBU = $59 ; Reset all value of the buffer XBUSY = $5A ; Test if the buffer is empty XMALLOC = $5B ; Only in TELEMON 3.x (bank 7 of Orix) + +; RS232 primitives +XSDUMP = $5C ; RS232 input dump +XCONSO = $5D ; Swap screen into RS232 terminal +XSLOAD = $5E ; Read a file from RS232 +XSSAVE = $5F ; Write a file to RS232 + +; Minitel primitives +XMLOAD = $60 ; Read a file from Minitel +XMSAVE = $61 ; Write a file to Minitel + XFREE = $62 ; Only in TELEMON 3.x (bank 7 of Orix) + +; Next Minitel primitives +XWCXFI = $63 ; Wait connection +XLIGNE = $64 ; +XDECON = $65 ; Minitel disconnection +XMOUT = $66 ; Send a byte to minitel (from A) + XSOUT = $67 ; Send accumulator value (A) to RS232, available in TELEMON 2.4 & 3.x : if RS232 buffer is full, the Oric Telestrat freezes + XHRSSE = $8C ; Set hires position cursor XDRAWA = $8D ; Draw a line absolute XDRAWR = $8E ; Draw a line (relative) @@ -324,7 +359,7 @@ PWD_PTR = $00 ; --------------------------------------------------------------------------- ; -BUFTRV := $100 +BUFTRV := $100 ; --------------------------------------------------------------------------- @@ -337,7 +372,7 @@ FLGTEL := $20D KOROM := $20E ; Used to compute the size of all rom bank. The result is store here. The value is in KB KORAM := $20F ; Used to compute the size of all ram bank. The result is store here. The value is in KB ; Time management -TIMED := $210 +TIMED := $210 ; Clock (1/10 of seconds) TIMES := $211 TIMEM := $212 TIMEH := $213 diff --git a/asminc/utsname.inc b/asminc/utsname.inc index 6e2289244..2c7052ce1 100644 --- a/asminc/utsname.inc +++ b/asminc/utsname.inc @@ -7,7 +7,7 @@ ;/* */ ;/* */ ;/* (C) 2003 Ullrich von Bassewitz */ -;/* Römerstrasse 52 */ +;/* Roemerstrasse 52 */ ;/* D-70794 Filderstadt */ ;/* EMail: uz@cc65.org */ ;/* */ diff --git a/asminc/vic20.inc b/asminc/vic20.inc index b82874f56..7184ab6ee 100644 --- a/asminc/vic20.inc +++ b/asminc/vic20.inc @@ -1,14 +1,14 @@ ; -; Vic20 generic definitions. Stolen mostly from c64.inc - Steve Schmidtke +; VIC-20 generic definitions. Stolen mostly from c64.inc -- Steve Schmidtke ; - ; --------------------------------------------------------------------------- ; Zero page, Commodore stuff VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $7A ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address @@ -35,6 +35,8 @@ KBDREPEAT := $28a KBDREPEATRATE := $28b KBDREPEATDELAY := $28c +RSSTAT := $297 ; RS-232 device driver status + ; --------------------------------------------------------------------------- ; Screen size @@ -88,7 +90,7 @@ VIA1_T1LH := VIA1+$7 ; Timer 1 latch, high byte VIA1_T2CL := VIA1+$8 ; Timer 2, low byte VIA1_T2CH := VIA1+$9 ; Timer 2, high byte VIA1_SR := VIA1+$A ; Shift register -VIA1_CR := VIA1+$B ; Auxiliary control register +VIA1_ACR := VIA1+$B ; Auxiliary control register VIA1_PCR := VIA1+$C ; Peripheral control register VIA1_IFR := VIA1+$D ; Interrupt flag register VIA1_IER := VIA1+$E ; Interrupt enable register @@ -110,7 +112,7 @@ VIA2_T1LH := VIA2+$7 ; Timer 1 latch, high byte VIA2_T2CL := VIA2+$8 ; Timer 2, low byte VIA2_T2CH := VIA2+$9 ; Timer 2, high byte VIA2_SR := VIA2+$A ; Shift register -VIA2_CR := VIA2+$B ; Auxiliary control register +VIA2_ACR := VIA2+$B ; Auxiliary control register VIA2_PCR := VIA2+$C ; Peripheral control register VIA2_IFR := VIA2+$D ; Interrupt flag register VIA2_IER := VIA2+$E ; Interrupt enable register diff --git a/cfg/apple2-hgr.cfg b/cfg/apple2-hgr.cfg index 3ccf7b6f3..cfe577e00 100644 --- a/cfg/apple2-hgr.cfg +++ b/cfg/apple2-hgr.cfg @@ -23,8 +23,8 @@ SEGMENTS { EXEHDR: load = HEADER, type = ro, optional = yes; STARTUP: load = MAIN, type = ro; LOWCODE: load = MAIN, type = ro, optional = yes; - HGR: load = MAIN, type = rw, optional = yes, start = $2000; - CODE: load = MAIN, type = ro start = $4000; + HGR: load = MAIN, type = rw, start = $2000; + CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; INIT: load = MAIN, type = rw; diff --git a/cfg/apple2enh-hgr.cfg b/cfg/apple2enh-hgr.cfg index 3ccf7b6f3..cfe577e00 100644 --- a/cfg/apple2enh-hgr.cfg +++ b/cfg/apple2enh-hgr.cfg @@ -23,8 +23,8 @@ SEGMENTS { EXEHDR: load = HEADER, type = ro, optional = yes; STARTUP: load = MAIN, type = ro; LOWCODE: load = MAIN, type = ro, optional = yes; - HGR: load = MAIN, type = rw, optional = yes, start = $2000; - CODE: load = MAIN, type = ro start = $4000; + HGR: load = MAIN, type = rw, start = $2000; + CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; INIT: load = MAIN, type = rw; diff --git a/cfg/atarixl-largehimem.cfg b/cfg/atarixl-largehimem.cfg index 56d2af15b..38fb68db9 100644 --- a/cfg/atarixl-largehimem.cfg +++ b/cfg/atarixl-largehimem.cfg @@ -8,39 +8,40 @@ FEATURES { } SYMBOLS { __EXEHDR__: type = import; - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __AUTOSTART__: type = import; # force inclusion of autostart "trailer" - __STACKSIZE__: type = weak, value = $0800; # 2k stack + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __AUTOSTART__: type = import; # force inclusion of autostart "trailer" + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAINHDR: file = %O, start = $0000, size = $0004; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # address of relocated character generator - CHARGEN: file = "", define = yes, start = $D800, size = $0400; + CHARGEN: file = "", define = yes, start = $D800, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $DC00, size = $FFFA - $DC00; + HIDDEN_RAM: file = "", define = yes, start = $D800 + __CHARGENSIZE__, size = $FFFA - $D800 - __CHARGENSIZE__; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -53,7 +54,7 @@ SEGMENTS { SYSCHKTRL: load = SYSCHKTRL, type = ro, optional = yes; SRPREPHDR: load = SRPREPHDR, type = ro; - LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and RAM, not zero initialized + LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and MAIN, not zero initialized SRPREP: load = SRPREPCHNK, type = rw, define = yes; SHADOW_RAM: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes; SHADOW_RAM2: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes; diff --git a/cfg/atarixl-overlay.cfg b/cfg/atarixl-overlay.cfg index 923436497..339228ea0 100644 --- a/cfg/atarixl-overlay.cfg +++ b/cfg/atarixl-overlay.cfg @@ -3,44 +3,45 @@ FEATURES { } SYMBOLS { __EXEHDR__: type = import; - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __AUTOSTART__: type = import; # force inclusion of autostart "trailer" - __STACKSIZE__: type = weak, value = $0800; # 2k stack - __OVERLAYSIZE__: type = weak, value = $1000; # 4k overlay + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __AUTOSTART__: type = import; # force inclusion of autostart "trailer" + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; + __OVERLAYSIZE__: type = weak, value = $1000; # 4k overlay __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = $7C20 - %S - __OVERLAYSIZE__ - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = $7C20 - %S - __OVERLAYSIZE__ - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; + MAINHDR: file = %O, start = $0000, size = $0004; MAIN: file = %O, define = yes, start = %S + __OVERLAYSIZE__ + - __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __OVERLAYSIZE__ - __LOWBSS_SIZE__; + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __OVERLAYSIZE__ - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; # overlays OVL1: file = "%O.1", start = %S, size = __OVERLAYSIZE__; @@ -64,7 +65,7 @@ SEGMENTS { SYSCHKTRL: load = SYSCHKTRL, type = ro, optional = yes; SRPREPHDR: load = SRPREPHDR, type = ro; - LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and RAM, not zero initialized + LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and MAIN, not zero initialized SRPREP: load = SRPREPCHNK, type = rw, define = yes; SHADOW_RAM: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes; SHADOW_RAM2: load = SRPREPCHNK, run = HIDDEN_RAM2, type = rw, define = yes, optional = yes; diff --git a/cfg/atarixl-xex.cfg b/cfg/atarixl-xex.cfg index 0b1fe9ca1..1b76855d0 100644 --- a/cfg/atarixl-xex.cfg +++ b/cfg/atarixl-xex.cfg @@ -4,35 +4,36 @@ FEATURES { STARTADDRESS: default = $2400; } SYMBOLS { - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __STACKSIZE__: type = weak, value = $0800; # 2k stack + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __STACKSIZE__: type = weak, value = $0800; # 2k stack __STARTADDRESS__: type = export, value = %S; + __CHARGENSIZE__: type = weak, value = $0400; __SYSCHKHDR__: type = export, value = 0; # Disable system check header __SYSCHKTRL__: type = export, value = 0; # Disable system check trailer } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # "system check" load chunk - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; # "shadow RAM preparation" load chunk - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned # "main program" load chunk - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; # UNUSED - hide - UNUSED: file = "", start = $0, size = $10; + UNUSED: file = "", start = $0, size = $10; } FILES { %O: format = atari; @@ -47,7 +48,7 @@ SEGMENTS { EXTZP: load = ZP, type = zp, optional = yes; SYSCHK: load = SYSCHKCHNK, type = rw, define = yes, optional = yes; - LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and RAM, not zero initialized + LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and MAIN, not zero initialized SRPREP: load = SRPREPCHNK, type = rw, define = yes; SHADOW_RAM: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes; SHADOW_RAM2: load = SRPREPCHNK, run = HIDDEN_RAM2, type = rw, define = yes, optional = yes; diff --git a/cfg/atarixl.cfg b/cfg/atarixl.cfg index 197daace6..cece23555 100644 --- a/cfg/atarixl.cfg +++ b/cfg/atarixl.cfg @@ -6,39 +6,40 @@ SYMBOLS { __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk __AUTOSTART__: type = import; # force inclusion of autostart "trailer" __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAINHDR: file = %O, start = $0000, size = $0004; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -51,7 +52,7 @@ SEGMENTS { SYSCHKTRL: load = SYSCHKTRL, type = ro, optional = yes; SRPREPHDR: load = SRPREPHDR, type = ro; - LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and RAM, not zero initialized + LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and MAIN, not zero initialized SRPREP: load = SRPREPCHNK, type = rw, define = yes; SHADOW_RAM: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes; SHADOW_RAM2: load = SRPREPCHNK, run = HIDDEN_RAM2, type = rw, define = yes, optional = yes; diff --git a/cfg/c128-asm.cfg b/cfg/c128-asm.cfg new file mode 100644 index 000000000..0da296c71 --- /dev/null +++ b/cfg/c128-asm.cfg @@ -0,0 +1,20 @@ +FEATURES { + STARTADDRESS: default = $1c01; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $00FE, define = yes; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $D000 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = rw; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/c64-overlay.cfg b/cfg/c64-overlay.cfg index e88bffe00..0f42434ad 100644 --- a/cfg/c64-overlay.cfg +++ b/cfg/c64-overlay.cfg @@ -14,7 +14,7 @@ MEMORY { ZP: file = "", define = yes, start = $0002, size = $001A; LOADADDR: file = %O, start = %S - 2, size = $0002; HEADER: file = %O, define = yes, start = %S, size = $000D; - MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __OVERLAYSTART__ - __HEADER_LAST__; BSS: file = "", start = __ONCE_RUN__, size = __OVERLAYSTART__ - __STACKSIZE__ - __ONCE_RUN__; OVL1ADDR: file = "%O.1", start = __OVERLAYSTART__ - 2, size = $0002; OVL1: file = "%O.1", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; diff --git a/cfg/cx16-asm.cfg b/cfg/cx16-asm.cfg index 3c24bd56f..92c9d96f7 100644 --- a/cfg/cx16-asm.cfg +++ b/cfg/cx16-asm.cfg @@ -1,25 +1,32 @@ +# Assembly configuration for R38 + FEATURES { STARTADDRESS: default = $0801; } SYMBOLS { __LOADADDR__: type = import; +# Putting "-u __EXEHDR__" on cl65's command line will add a BASIC RUN stub to your program. +# __EXEHDR__: type = import; __HIMEM__: type = weak, value = $9F00; } MEMORY { ZP: file = "", start = $0022, size = $0080 - $0022, define = yes; + ZP2: file = "", start = $00A9, size = $0100 - $00A9; LOADADDR: file = %O, start = %S - 2, size = $0002; MAIN: file = %O, start = %S, size = __HIMEM__ - %S; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; - EXTZP: load = ZP, type = zp, optional = yes; + EXTZP: load = ZP2, type = zp, optional = yes; # OK if BASIC functions not used LOADADDR: load = LOADADDR, type = ro; EXEHDR: load = MAIN, type = ro, optional = yes; - LOWCODE: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + ONCE: load = MAIN, type = ro, optional = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - BSS: load = MAIN, type = bss, define = yes; + INIT: load = MAIN, type = bss, optional = yes; + BSS: load = MAIN, type = bss, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg index 1f998f188..d3c2c02ae 100644 --- a/cfg/cx16-bank.cfg +++ b/cfg/cx16-bank.cfg @@ -16,84 +16,80 @@ MEMORY { HEADER: file = %O, define = yes, start = %S, size = $000D; MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__; BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __ONCE_RUN__ - __STACKSIZE__; -# BRAM00ADDR: file = "%O.00", start = __BANKRAMSTART__ - 2, size = $0002; -# BRAM00: file = "%O.00", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; BRAM01ADDR: file = "%O.01", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM01: file = "%O.01", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM01: file = "%O.01", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $01; BRAM02ADDR: file = "%O.02", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM02: file = "%O.02", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM02: file = "%O.02", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $02; BRAM03ADDR: file = "%O.03", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM03: file = "%O.03", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM03: file = "%O.03", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $03; BRAM04ADDR: file = "%O.04", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM04: file = "%O.04", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM04: file = "%O.04", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $04; BRAM05ADDR: file = "%O.05", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM05: file = "%O.05", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM05: file = "%O.05", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $05; BRAM06ADDR: file = "%O.06", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM06: file = "%O.06", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM06: file = "%O.06", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $06; BRAM07ADDR: file = "%O.07", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM07: file = "%O.07", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM07: file = "%O.07", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $07; BRAM08ADDR: file = "%O.08", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM08: file = "%O.08", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM08: file = "%O.08", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $08; BRAM09ADDR: file = "%O.09", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM09: file = "%O.09", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0AADDR: file = "%O.0a", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0A: file = "%O.0a", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0BADDR: file = "%O.0b", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0B: file = "%O.0b", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0CADDR: file = "%O.0c", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0C: file = "%O.0c", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0DADDR: file = "%O.0d", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0D: file = "%O.0d", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0EADDR: file = "%O.0e", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0E: file = "%O.0e", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; - BRAM0FADDR: file = "%O.0f", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0F: file = "%O.0f", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM09: file = "%O.09", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $09; + BRAM0AADDR: file = "%O.0A", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0A: file = "%O.0A", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0A; + BRAM0BADDR: file = "%O.0B", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0B: file = "%O.0B", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0B; + BRAM0CADDR: file = "%O.0C", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0C: file = "%O.0C", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0C; + BRAM0DADDR: file = "%O.0D", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0D: file = "%O.0D", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0D; + BRAM0EADDR: file = "%O.0E", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0E: file = "%O.0E", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0E; + BRAM0FADDR: file = "%O.0F", start = __BANKRAMSTART__ - 2, size = $0002; + BRAM0F: file = "%O.0F", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0F; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; - EXTZP: load = ZP, type = zp, optional = yes; + EXTZP: load = ZP, type = zp, optional = yes; LOADADDR: load = LOADADDR, type = ro; EXEHDR: load = HEADER, type = ro; - STARTUP: load = MAIN, type = ro; - LOWCODE: load = MAIN, type = ro, optional = yes; + STARTUP: load = MAIN, type = ro, optional = yes; + LOWCODE: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; - ONCE: load = MAIN, type = ro, define = yes; - BSS: load = BSS, type = bss, define = yes; -# BRAM00ADDR: load = BRAM00ADDR, type = ro, optional = yes; -# BANKRAM00: load = BRAM00, type = rw, define = yes, optional = yes; - BRAM01ADDR: load = BRAM01ADDR, type = ro, optional = yes; - BANKRAM01: load = BRAM01, type = rw, define = yes, optional = yes; - BRAM02ADDR: load = BRAM02ADDR, type = ro, optional = yes; - BANKRAM02: load = BRAM02, type = rw, define = yes, optional = yes; - BRAM03ADDR: load = BRAM03ADDR, type = ro, optional = yes; - BANKRAM03: load = BRAM03, type = rw, define = yes, optional = yes; - BRAM04ADDR: load = BRAM04ADDR, type = ro, optional = yes; - BANKRAM04: load = BRAM04, type = rw, define = yes, optional = yes; - BRAM05ADDR: load = BRAM05ADDR, type = ro, optional = yes; - BANKRAM05: load = BRAM05, type = rw, define = yes, optional = yes; - BRAM06ADDR: load = BRAM06ADDR, type = ro, optional = yes; - BANKRAM06: load = BRAM06, type = rw, define = yes, optional = yes; - BRAM07ADDR: load = BRAM07ADDR, type = ro, optional = yes; - BANKRAM07: load = BRAM07, type = rw, define = yes, optional = yes; - BRAM08ADDR: load = BRAM08ADDR, type = ro, optional = yes; - BANKRAM08: load = BRAM08, type = rw, define = yes, optional = yes; - BRAM09ADDR: load = BRAM09ADDR, type = ro, optional = yes; - BANKRAM09: load = BRAM09, type = rw, define = yes, optional = yes; - BRAM0AADDR: load = BRAM0AADDR, type = ro, optional = yes; - BANKRAM0A: load = BRAM0A, type = rw, define = yes, optional = yes; - BRAM0BADDR: load = BRAM0BADDR, type = ro, optional = yes; - BANKRAM0B: load = BRAM0B, type = rw, define = yes, optional = yes; - BRAM0CADDR: load = BRAM0CADDR, type = ro, optional = yes; - BANKRAM0C: load = BRAM0C, type = rw, define = yes, optional = yes; - BRAM0DADDR: load = BRAM0DADDR, type = ro, optional = yes; - BANKRAM0D: load = BRAM0D, type = rw, define = yes, optional = yes; - BRAM0EADDR: load = BRAM0EADDR, type = ro, optional = yes; - BANKRAM0E: load = BRAM0E, type = rw, define = yes, optional = yes; - BRAM0FADDR: load = BRAM0FADDR, type = ro, optional = yes; - BANKRAM0F: load = BRAM0F, type = rw, define = yes, optional = yes; + INIT: load = MAIN, type = rw, optional = yes; + ONCE: load = MAIN, type = ro, define = yes; + BSS: load = BSS, type = bss, define = yes; + BRAM01ADDR: load = BRAM01ADDR, type = ro, optional = yes; + BANKRAM01: load = BRAM01, type = rw, optional = yes, define = yes; + BRAM02ADDR: load = BRAM02ADDR, type = ro, optional = yes; + BANKRAM02: load = BRAM02, type = rw, optional = yes, define = yes; + BRAM03ADDR: load = BRAM03ADDR, type = ro, optional = yes; + BANKRAM03: load = BRAM03, type = rw, optional = yes, define = yes; + BRAM04ADDR: load = BRAM04ADDR, type = ro, optional = yes; + BANKRAM04: load = BRAM04, type = rw, optional = yes, define = yes; + BRAM05ADDR: load = BRAM05ADDR, type = ro, optional = yes; + BANKRAM05: load = BRAM05, type = rw, optional = yes, define = yes; + BRAM06ADDR: load = BRAM06ADDR, type = ro, optional = yes; + BANKRAM06: load = BRAM06, type = rw, optional = yes, define = yes; + BRAM07ADDR: load = BRAM07ADDR, type = ro, optional = yes; + BANKRAM07: load = BRAM07, type = rw, optional = yes, define = yes; + BRAM08ADDR: load = BRAM08ADDR, type = ro, optional = yes; + BANKRAM08: load = BRAM08, type = rw, optional = yes, define = yes; + BRAM09ADDR: load = BRAM09ADDR, type = ro, optional = yes; + BANKRAM09: load = BRAM09, type = rw, optional = yes, define = yes; + BRAM0AADDR: load = BRAM0AADDR, type = ro, optional = yes; + BANKRAM0A: load = BRAM0A, type = rw, optional = yes, define = yes; + BRAM0BADDR: load = BRAM0BADDR, type = ro, optional = yes; + BANKRAM0B: load = BRAM0B, type = rw, optional = yes, define = yes; + BRAM0CADDR: load = BRAM0CADDR, type = ro, optional = yes; + BANKRAM0C: load = BRAM0C, type = rw, optional = yes, define = yes; + BRAM0DADDR: load = BRAM0DADDR, type = ro, optional = yes; + BANKRAM0D: load = BRAM0D, type = rw, optional = yes, define = yes; + BRAM0EADDR: load = BRAM0EADDR, type = ro, optional = yes; + BANKRAM0E: load = BRAM0E, type = rw, optional = yes, define = yes; + BRAM0FADDR: load = BRAM0FADDR, type = ro, optional = yes; + BANKRAM0F: load = BRAM0F, type = rw, optional = yes, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/cx16.cfg b/cfg/cx16.cfg index 72fc2fe91..4b6025fb6 100644 --- a/cfg/cx16.cfg +++ b/cfg/cx16.cfg @@ -16,17 +16,17 @@ MEMORY { } SEGMENTS { ZEROPAGE: load = ZP, type = zp; - EXTZP: load = ZP, type = zp, optional = yes; + EXTZP: load = ZP, type = zp, optional = yes; LOADADDR: load = LOADADDR, type = ro; EXEHDR: load = HEADER, type = ro; - STARTUP: load = MAIN, type = ro; - LOWCODE: load = MAIN, type = ro, optional = yes; + STARTUP: load = MAIN, type = ro, optional = yes; + LOWCODE: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; - INIT: load = MAIN, type = rw; - ONCE: load = MAIN, type = ro, define = yes; - BSS: load = BSS, type = bss, define = yes; + INIT: load = MAIN, type = rw, optional = yes; + ONCE: load = MAIN, type = ro, define = yes; + BSS: load = BSS, type = bss, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/cfg/pet-overlay.cfg b/cfg/pet-overlay.cfg new file mode 100644 index 000000000..afd351a42 --- /dev/null +++ b/cfg/pet-overlay.cfg @@ -0,0 +1,82 @@ +FEATURES { + STARTADDRESS: default = $0401; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __OVERLAYADDR__: type = import; + __STACKSIZE__: type = weak, value = $0800; # 2K stack + __OVERLAYSIZE__: type = weak, value = $0800; # 2K overlay + __HIMEM__: type = weak, value = $8000; + __OVERLAYSTART__: type = export, value = __HIMEM__ - __STACKSIZE__ - __OVERLAYSIZE__; +} +MEMORY { + ZP: file = "", define = yes, start = $0055, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $000D; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __OVERLAYSTART__ - __HEADER_LAST__; + OVL1ADDR: file = "%O.1", start = __OVERLAYSTART__ - 2, size = $0002; + OVL1: file = "%O.1", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL2ADDR: file = "%O.2", start = __OVERLAYSTART__ - 2, size = $0002; + OVL2: file = "%O.2", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL3ADDR: file = "%O.3", start = __OVERLAYSTART__ - 2, size = $0002; + OVL3: file = "%O.3", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL4ADDR: file = "%O.4", start = __OVERLAYSTART__ - 2, size = $0002; + OVL4: file = "%O.4", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL5ADDR: file = "%O.5", start = __OVERLAYSTART__ - 2, size = $0002; + OVL5: file = "%O.5", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL6ADDR: file = "%O.6", start = __OVERLAYSTART__ - 2, size = $0002; + OVL6: file = "%O.6", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL7ADDR: file = "%O.7", start = __OVERLAYSTART__ - 2, size = $0002; + OVL7: file = "%O.7", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL8ADDR: file = "%O.8", start = __OVERLAYSTART__ - 2, size = $0002; + OVL8: file = "%O.8", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL9ADDR: file = "%O.9", start = __OVERLAYSTART__ - 2, size = $0002; + OVL9: file = "%O.9", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + ONCE: load = MAIN, type = ro, optional = yes; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; + BSS: load = MAIN, type = bss, define = yes; + OVL1ADDR: load = OVL1ADDR, type = ro; + OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; + OVL2ADDR: load = OVL2ADDR, type = ro; + OVERLAY2: load = OVL2, type = ro, define = yes, optional = yes; + OVL3ADDR: load = OVL3ADDR, type = ro; + OVERLAY3: load = OVL3, type = ro, define = yes, optional = yes; + OVL4ADDR: load = OVL4ADDR, type = ro; + OVERLAY4: load = OVL4, type = ro, define = yes, optional = yes; + OVL5ADDR: load = OVL5ADDR, type = ro; + OVERLAY5: load = OVL5, type = ro, define = yes, optional = yes; + OVL6ADDR: load = OVL6ADDR, type = ro; + OVERLAY6: load = OVL6, type = ro, define = yes, optional = yes; + OVL7ADDR: load = OVL7ADDR, type = ro; + OVERLAY7: load = OVL7, type = ro, define = yes, optional = yes; + OVL8ADDR: load = OVL8ADDR, type = ro; + OVERLAY8: load = OVL8, type = ro, define = yes, optional = yes; + OVL9ADDR: load = OVL9ADDR, type = ro; + OVERLAY9: load = OVL9, type = ro, define = yes, optional = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/cfg/supervision-128k.cfg b/cfg/supervision-128k.cfg index a03ab0e1b..c90367aa1 100644 --- a/cfg/supervision-128k.cfg +++ b/cfg/supervision-128k.cfg @@ -1,4 +1,4 @@ -# supervision 1284kbyte cartridge with bankswitching +# supervision 128kbyte cartridge with bankswitching # for assembler # ld65 config file @@ -30,8 +30,23 @@ SEGMENTS { BANK5: load = BANKROM5, type = ro; BANK6: load = BANKROM6, type = ro; BANK7: load = BANKROM7, type = ro; - ZEROPAGE: load = RAM, type = bss, define = yes; - DATA: load = RAM, type = bss, define = yes, offset = $0200; + ZEROPAGE: load = RAM, type = zp, define = yes; + DATA: load = RAM, type = rw, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTOR: load = ROM, type = ro, offset = $3FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; } diff --git a/cfg/supervision-16k.cfg b/cfg/supervision-16k.cfg index e0b54be23..dc6130b33 100644 --- a/cfg/supervision-16k.cfg +++ b/cfg/supervision-16k.cfg @@ -20,8 +20,8 @@ SEGMENTS { CODE: load = ROM, type = ro, define = yes; RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; - FFF0: load = ROM, type = ro, offset = $3FF0; - VECTOR: load = ROM, type = ro, offset = $3FFA; + FFF0: load = ROM, type = ro, start = $FFF0; + VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { diff --git a/cfg/supervision-64k.cfg b/cfg/supervision-64k.cfg index 9d5f15e45..d252c5d25 100644 --- a/cfg/supervision-64k.cfg +++ b/cfg/supervision-64k.cfg @@ -23,8 +23,23 @@ SEGMENTS { BANK1: load = BANKROM1, type = ro; BANK2: load = BANKROM2, type = ro; BANK3: load = BANKROM3, type = ro; - ZEROPAGE: load = RAM, type = bss, define = yes; - DATA: load = RAM, type = bss, define = yes, offset = $0200; + ZEROPAGE: load = RAM, type = zp, define = yes; + DATA: load = RAM, type = rw, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTOR: load = ROM, type = ro, offset = $3FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; } diff --git a/cfg/supervision.cfg b/cfg/supervision.cfg index d9f189f2b..1177660b8 100644 --- a/cfg/supervision.cfg +++ b/cfg/supervision.cfg @@ -19,8 +19,8 @@ SEGMENTS { CODE: load = ROM, type = ro, define = yes; RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; - FFF0: load = ROM, type = ro, offset = $7FF0; - VECTOR: load = ROM, type = ro, offset = $7FFA; + FFF0: load = ROM, type = ro, start = $FFF0; + VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { diff --git a/cfg/sym1-32k.cfg b/cfg/sym1-32k.cfg new file mode 100644 index 000000000..ad0d760f3 --- /dev/null +++ b/cfg/sym1-32k.cfg @@ -0,0 +1,46 @@ +# sym1-32k.cfg (32k) +# +# for Sym-1 with 32kb RAM +# +# ld65 --config sym1-32k.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 = $0200; # 512 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00F7; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $8000 - %S - __STACKSIZE__; + MONROM: file = "", define = yes, start = $8000, size = $1000; + EXT: file = "", define = yes, start = $9000, size = $1000; + IO: file = "", define = yes, start = $A000, size = $1000; + RAE1: file = "", define = yes, start = $B000, size = $1000; + BASROM: file = "", define = yes, start = $C000, size = $1000; + RAE2: 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/sym1-4k.cfg b/cfg/sym1-4k.cfg new file mode 100644 index 000000000..32d3cbb3a --- /dev/null +++ b/cfg/sym1-4k.cfg @@ -0,0 +1,46 @@ +# sym1-4k.cfg (4k) +# +# for Sym-1 with 4kb RAM +# +# ld65 --config sym1-4k.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 = $00F7; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $1000 - %S - __STACKSIZE__; + MONROM: file = "", define = yes, start = $8000, size = $1000; + EXT: file = "", define = yes, start = $9000, size = $1000; + IO: file = "", define = yes, start = $A000, size = $1000; + RAE1: file = "", define = yes, start = $B000, size = $1000; + BASROM: file = "", define = yes, start = $C000, size = $1000; + RAE2: 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/sym1.cfg b/cfg/sym1.cfg new file mode 100644 index 000000000..32d3cbb3a --- /dev/null +++ b/cfg/sym1.cfg @@ -0,0 +1,46 @@ +# sym1-4k.cfg (4k) +# +# for Sym-1 with 4kb RAM +# +# ld65 --config sym1-4k.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 = $00F7; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $1000 - %S - __STACKSIZE__; + MONROM: file = "", define = yes, start = $8000, size = $1000; + EXT: file = "", define = yes, start = $9000, size = $1000; + IO: file = "", define = yes, start = $A000, size = $1000; + RAE1: file = "", define = yes, start = $B000, size = $1000; + BASROM: file = "", define = yes, start = $C000, size = $1000; + RAE2: 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/vic20-asm.cfg b/cfg/vic20-asm.cfg new file mode 100644 index 000000000..7ab70888c --- /dev/null +++ b/cfg/vic20-asm.cfg @@ -0,0 +1,19 @@ +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A, define = yes; + LOADADDR: file = %O, start = $1001, size = $0002; + MAIN: file = %O, start = %S, size = $0DF3 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/vic20-tgi.cfg b/cfg/vic20-tgi.cfg new file mode 100644 index 000000000..bc7845615 --- /dev/null +++ b/cfg/vic20-tgi.cfg @@ -0,0 +1,50 @@ +# Memory configuration which supports the "vic20-hi.tgi" driver. +# Memory configuration for a VIC-20 with, at least, 8K expansion RAM. +FEATURES { + STARTADDRESS: default = $1201; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __TGIHDR__: type = import; + __STACKSIZE__: type = weak, value = $0200; # 512-byte stack + __HIMEM__: type = weak, value = $4000; +} +MEMORY { + ZP: file = "", define = yes, start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $003E; + MAIN: file = %O, define = yes, start = $2000, size = __HIMEM__ - __MAIN_START__ - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; +# The start-up code needs EXEHDR, TGI1HDR, TGI2HDR, +# and STARTUP to be next to each other, in that order. + EXEHDR: load = HEADER, type = ro; + TGI1HDR: load = HEADER, type = ro; + TGI2HDR: load = MAIN, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + ONCE: load = MAIN, type = ro, optional = yes, define = yes; + INIT: load = MAIN, type = bss, optional = yes; + BSS: load = MAIN, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/cfg/vic20.cfg b/cfg/vic20.cfg index ceaee3a87..8efeae229 100644 --- a/cfg/vic20.cfg +++ b/cfg/vic20.cfg @@ -1,7 +1,7 @@ SYMBOLS { __LOADADDR__: type = import; __EXEHDR__: type = import; - __STACKSIZE__: type = weak, value = $0400; # 1k stack + __STACKSIZE__: type = weak, value = $0100; } MEMORY { ZP: file = "", define = yes, start = $0002, size = $001A; diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 91c02c7ad..bd01b68dc 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. @@ -575,15 +575,13 @@ url="ca65.html" name="assembler manual">. Explanation of File Types - ProDOS associates a file type and an auxiliary type with each file. + ProDOS 8 associates a file type and an auxiliary type with each file. These type specifications are separate from the file's name, unlike Windows which uses the file name's suffix (a.k.a. extension) to specify the file type. For example, . The header file Example @@ -624,8 +620,8 @@ url="ca65.html" name="assembler manual">. carriage return instead of a line-feed (Linux/BSD/MacOS) or carriage return, line-feed pair (Windows). - The "sequential" text file terminology is in contrast to a - "random-access" text file which would + The 'sequential' text file terminology is in contrast to a + 'random-access' text file which would have a fixed-length, non-zero record length, so that the file position of any individual record can be calculated. diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 2d4381353..56fc05e31 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. @@ -580,15 +580,13 @@ url="ca65.html" name="assembler manual">. Explanation of File Types - ProDOS associates a file type and an auxiliary type with each file. + ProDOS 8 associates a file type and an auxiliary type with each file. These type specifications are separate from the file's name, unlike Windows which uses the file name's suffix (a.k.a. extension) to specify the file type. For example, . The header file Example @@ -629,8 +625,8 @@ url="ca65.html" name="assembler manual">. carriage return instead of a line-feed (Linux/BSD/MacOS) or carriage return, line-feed pair (Windows). - The "sequential" text file terminology is in contrast to a - "random-access" text file which would + The 'sequential' text file terminology is in contrast to a + 'random-access' text file which would have a fixed-length, non-zero record length, so that the file position of any individual record can be calculated. diff --git a/doc/atari.sgml b/doc/atari.sgml index eecd1b803..903895d17 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -332,6 +332,7 @@ See the for declaration and u _scroll _setcolor _setcolor_low +waitvsync @@ -674,9 +675,9 @@ The default callbacks definition (RS232 device drivers

-Currently there is one RS232 driver. It uses the R: device (therefore -an R: driver needs to be installed) and was tested with the 850 -interface module. +Currently there is one RS232 driver. It supports up to 9600 baud, requires hardware flow control +(RTS/CTS) and uses the R: device (therefore an R: driver needs to be installed). It was tested +with the 850 interface module. @@ -1169,7 +1170,7 @@ When using cl65, you can leave it out with this command line: cl65 -Wl -D__SYSTEM_CHECK__=1 -The value you assign to Binary format

-The standard binary output format generated by the linker for the Atari 5200 target +The binary output format generated by the linker for the Atari 5200 target is a cartridge image. It is of course possible to change this behaviour by using a modified startup file and linker config. @@ -73,11 +73,22 @@ Special locations: Programs containing Atari 5200 specific code may use the +... + OS.sdmctl = 0x00; // screen off + OS.color4 = 14; // white frame + tics = OS.rtclok[1] // get ticks +... + + Atari 5200 specific functions

-TBD. +waitvsync @@ -208,10 +219,58 @@ you cannot use any of the following functions (and a few others): Other hints

+CAR format

+ AtariROMMaker ( ) can be used to create a Changing the splash screen

+ +The 5200 ROM displays a splash screen at startup with the name of the +game and the copyright year. The year information has a 'Year-2000' +problem, the first two digits are fixed in the ROM and are always "19". + +Changing the game name

+ +The runtime library provides a default game name which is "cc65 +compiled". To change that, one has to link a file which puts data into +the " +.export __CART_NAME__: absolute = 1 +.macpack atari +.segment "CARTNAME" + scrcode " cc" + .byte '6' + 32, '5' + 32 ; use playfield 1 + scrcode " compiled" + + +'Changing the copyright year / changing the cartridge type

+ +The century is hard-coded to 1900 by the ROM. + +There are two digits which can be changed. For example "92" will give +"1992" on the screen. + +The default used by the runtime library is + + +.export __CART_YEAR__: absolute = 1 +.segment "CARTYEAR" + .byte '9' + 32,'8' + 32 ; "98", using playfield 1 + + +If the second byte of the year in the License

diff --git a/doc/atmos.sgml b/doc/atmos.sgml index 3fd61abcf..cef7770e4 100644 --- a/doc/atmos.sgml +++ b/doc/atmos.sgml @@ -176,10 +176,11 @@ No mouse drivers are currently available for the Atmos. Driver for the Telestrat integrated serial controller and the Atmos with a - serial add-on. - Note that, because of the peculiarities of the 6551 chip, together with the - use of the NMI, transmits are not interrupt driven; and, the transceiver - blocks if the receiver asserts flow control because of a full buffer. + serial add-on. Supports up to 19200 baud, requires hardware flow control + (RTS/CTS) and does interrupt driven receives. Note that, because of the + peculiarities of the 6551 chip, together with the use of the NMI, transmits + are not interrupt driven; and, the transceiver blocks if the receiver + asserts flow control because of a full buffer.

diff --git a/doc/c128.sgml b/doc/c128.sgml index 8c62b6ad1..60306814c 100644 --- a/doc/c128.sgml +++ b/doc/c128.sgml @@ -324,9 +324,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/c64.sgml b/doc/c64.sgml index 7e00f3b93..de37ed4b7 100644 --- a/doc/c64.sgml +++ b/doc/c64.sgml @@ -410,9 +410,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 2c4593d21..8ae8eabd9 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -151,7 +151,7 @@ Here is a description of all the command line options: Set the default for the CPU type. The option takes a parameter, which may be one of - 6502, 6502X, 65SC02, 65C02, 65816, sweet16, HuC6280, 4510 + 6502, 6502X, 6502DTV, 65SC02, 65C02, 65816, sweet16, HuC6280, 4510

Available operators, sorted by precedence
@@ -1351,15 +1353,15 @@ writable. Pseudo functions

-Pseudo functions expect their arguments in parenthesis, and they have a result, -either a string or an expression. +Pseudo functions expect their arguments in parentheses, and they have a result, +either a string or an expression value. .ADDRSIZE

- The .BANK

The or similar functions to address just part of it. - Please note that .BLANK

- Builtin function. The function evaluates its argument in braces and yields + Builtin function. The function evaluates its argument in parentheses and yields "false" if the argument is non blank (there is an argument), and "true" if there is no argument. The token list that makes up the function argument may optionally be enclosed in curly braces. This allows the inclusion of @@ -1477,7 +1479,7 @@ either a string or an expression. .CONST

- Builtin function. The function evaluates its argument in braces and + Builtin function. The function evaluates its argument in parentheses and yields "true" if the argument is a constant expression (that is, an expression that yields a constant value at assembly time) and "false" otherwise. As an example, the .IFCONST statement may be replaced by @@ -1487,6 +1489,41 @@ either a string or an expression. +.DEF, .DEFINED

+ + Builtin function. The function expects an identifier as argument in parentheses. + The argument is evaluated, and the function yields "true" if the identifier + is a symbol that already is defined somewhere in the source file up to the + current position. Otherwise, the function yields false. As an example, the + statement may be replaced by + + + .if .defined(a) + + + +.DEFINEDMACRO

+ + Builtin function. The function expects an identifier as argument in parentheses. + The argument is evaluated, and the function yields "true" if the identifier + already has been defined as the name of a macro. Otherwise, the function yields + false. Example: + + + .macro add foo + clc + adc foo + .endmacro + + .if .definedmacro(add) + add #$01 + .else + clc + adc #$01 + .endif + + + .HIBYTE

The function returns the high byte (that is, bits 8-15) of its argument. @@ -1523,6 +1560,23 @@ either a string or an expression. +.ISMNEM, .ISMNEMONIC

+ + Builtin function. The function expects an identifier as argument in parentheses. + The argument is evaluated, and the function yields "true" if the identifier + is defined as an instruction mnemonic that is recognized by the assembler. + Example: + + + .if .not .ismnemonic(ina) + .macro ina + clc + adc #$01 + .endmacro + .endif + + + .LEFT

Builtin function. Extracts the left part of a given token list. @@ -1717,7 +1771,7 @@ either a string or an expression. .REF, .REFERENCED

- Builtin function. The function expects an identifier as argument in braces. + Builtin function. The function expects an identifier as argument in parentheses. The argument is evaluated, and the function yields "true" if the identifier is a symbol that has already been referenced somewhere in the source file up to the current position. Otherwise the function yields false. As an example, @@ -1824,24 +1878,6 @@ either a string or an expression. -.STRAT

- - Builtin function. The function accepts a string and an index as - arguments and returns the value of the character at the given position - as an integer value. The index is zero based. - - Example: - - - .macro M Arg - ; Check if the argument string starts with '#' - .if (.strat (Arg, 0) = '#') - ... - .endif - .endmacro - - - .SPRINTF

Builtin function. It expects a format string as first argument. The number @@ -1861,9 +1897,27 @@ either a string or an expression. +.STRAT

+ + Builtin function. The function accepts a string and an index as + arguments and returns the value of the character at the given position + as an integer value. The index is zero based. + + Example: + + + .macro M Arg + ; Check if the argument string starts with '#' + .if (.strat (Arg, 0) = '#') + ... + .endif + .endmacro + + + .STRING

- Builtin function. The function accepts an argument in braces and converts + Builtin function. The function accepts an argument in parentheses and converts this argument into a string constant. The argument may be an identifier, or a constant numeric value. @@ -1882,7 +1936,7 @@ either a string or an expression. .STRLEN

- Builtin function. The function accepts a string argument in braces and + Builtin function. The function accepts a string argument in parentheses and evaluates to the length of the string. Example: @@ -1899,7 +1953,7 @@ either a string or an expression. .TCOUNT

- Builtin function. The function accepts a token list in braces. The function + Builtin function. The function accepts a token list in parentheses. The function result is the number of tokens given as argument. The token list may optionally be enclosed into curly braces which are not considered part of the list and not counted. Enclosement in curly braces allows the inclusion @@ -2082,7 +2136,11 @@ Here's a list of all control commands and a description, what they do: This will put the string "Hello world" followed by a binary zero into the current segment. There may be more strings separated by commas, but - the binary zero is only appended once (after the last one). + the binary zero is only appended once (after the last one). Strings will + be translated using the current character mapping definition. + +See: ,, + .ASSERT

@@ -2178,7 +2236,8 @@ Here's a list of all control commands and a description, what they do: .BYT, .BYTE

Define byte sized data. Must be followed by a sequence of (byte ranged) - expressions or strings. + expressions or strings. Strings will be translated using the current + character mapping definition. Example: @@ -2187,6 +2246,9 @@ Here's a list of all control commands and a description, what they do: .byt "world", $0D, $00 +See: , + + .CASE

@@ -2205,8 +2267,10 @@ Here's a list of all control commands and a description, what they do: .CHARMAP

- Apply a custom mapping for characters. The command is followed by two - numbers. The first one is the index of the source character (range 0..255); + Apply a custom mapping for characters for the commands and . The command + is followed by two numbers. The first one is the index of the source character + (range 0..255); the second one is the mapping (range 0..255). The mapping applies to all character and string constants @@ -2354,7 +2418,7 @@ Here's a list of all control commands and a description, what they do: Start a define style macro definition. The command is followed by an identifier (the macro name) and optionally by a list of formal arguments - in braces. + in parentheses. Please note that . -.DEF, .DEFINED

- - Builtin function. The function expects an identifier as argument in braces. - The argument is evaluated, and the function yields "true" if the identifier - is a symbol that is already defined somewhere in the source file up to the - current position. Otherwise the function yields false. As an example, the - statement may be replaced by - - - .if .defined(a) - - - -.DEFINEDMACRO

- - Builtin function. The function expects an identifier as argument in braces. - The argument is evaluated, and the function yields "true" if the identifier - has already been defined as the name of a macro. Otherwise the function yields - false. Example: - - - .macro add foo - clc - adc foo - .endmacro - - .if .definedmacro(add) - add #$01 - .else - clc - adc #$01 - .endif - - - .DESTRUCTOR

Export a symbol and mark it as a module destructor. This may be used @@ -3153,6 +3182,12 @@ Here's a list of all control commands and a description, what they do: (see command). +.IFPDTV

+ + Conditional assembly: Check if the assembler is currently in 6502DTV mode + (see command). + + .IFPSC02

Conditional assembly: Check if the assembler is currently in 65SC02 mode @@ -3178,7 +3213,8 @@ Here's a list of all control commands and a description, what they do: .endif - See also: + See also: , and + .IMPORT

@@ -3276,23 +3312,6 @@ Here's a list of all control commands and a description, what they do: the feature in more detail. -.ISMNEM, .ISMNEMONIC

- - Builtin function. The function expects an identifier as argument in braces. - The argument is evaluated, and the function yields "true" if the identifier - is defined as an instruction mnemonic that is recognized by the assembler. - Example: - - - .if .not .ismnemonic(ina) - .macro ina - clc - adc #$01 - .endmacro - .endif - - - .LINECONT

Switch on or off line continuations using the backslash character @@ -3348,6 +3367,22 @@ Here's a list of all control commands and a description, what they do: +.LITERAL

+ + Define byte sized data. Must be followed by a sequence of (byte ranged) + expressions or strings. Strings will disregard the current character + mapping definition and will be interpreted literally. + + Example: + + + .literal "Hello " + .literal "world", $0D, $00 + + +See: , + + .LOBYTES

Define byte sized data by extracting only the low byte (that is, bits 0-7) from @@ -3585,6 +3620,29 @@ Here's a list of all control commands and a description, what they do: +.PDTV

+ + Enable the 6502DTV instruction set. This is a superset of the 6502 + instruction set. + + See: + + +.POPCHARMAP

+ + Pop the last character mapping from the stack, and activate it. + + This command will switch back to the character mapping that was last pushed onto the + character mapping stack using the + command, and remove this entry from the stack. + + The assembler will print an error message if the mappting stack is empty when + this command is issued. + + See: , + + .POPCPU

Pop the last CPU setting from the stack, and activate it. @@ -3659,6 +3717,22 @@ Here's a list of all control commands and a description, what they do: +.PUSHCHARMAP

+ + Push the currently active character mapping onto a stack. The stack has a size of 16 + entries. + + to switch to another character mapping and to restore the old + characther mapping later, without knowledge of the current mapping. + + The assembler will print an error message if the character mapping stack is already full, + when this command is issued. + + See: , + + .PUSHCPU

Push the currently active CPU onto a stack. The stack has a size of 8 @@ -3691,6 +3765,46 @@ Here's a list of all control commands and a description, what they do: See: +.REFERTO, .REFTO

+ + Mark a symbol as referenced. + + It is useful in combination with the + command. A subroutine with two entry points can be created. When the first + entry point is called, it sets some default value as an argument, and falls + through into the second entry point. .REFERTO helps to ensure that + the second part is included into binary when only the first entry point is + actually used from the code. + + Example: + + + .ifref NegateValue ; If this subroutine is used + NegateValue: ; Define it + lda #0 + sec + sbc Value + .ifref ResetValue ; If the ResetValue is also used + jmp SetValue ; Jump over it + .else + .refto SetValue ; Ensure that SetValue will be included + .endif + .endif + + .ifref ResetValue ; If this subroutine is used + ResetValue: ; Define it + lda #0 ; Set a default value + .refto SetValue ; Ensure that SetValue will be included + .endif + + .ifref SetValue ; If this or previous subroutine is used + SetValue: + sta Value + rts + .endif + + + .RELOC

Switch back to relocatable mode. See the command line option, - namely: 6502, 6502X, 65SC02, 65C02, 65816, 4510 and HuC6280. + namely: 6502, 6502X, 6502DTV, 65SC02, 65C02, 65816, 4510 and HuC6280. See: , , + , , , , @@ -4325,8 +4440,8 @@ different: For this macro type, the number of actual parameters must match exactly the number of formal parameters. - To make this possible, formal parameters are enclosed in braces when - defining the macro. If there are no parameters, the empty braces may + To make this possible, formal parameters are enclosed in parentheses when + defining the macro. If there are no parameters, the empty parentheses may be omitted. Since style macros may not @@ -4376,8 +4491,8 @@ Macros with parameters may also be useful: Note that, while formal parameters have to be placed in parentheses, the actual argument used when invoking the macro should not be. -The invoked arguments are separated by commas only, if parentheses are -used by accident they will become part of the replaced token. +The invoked arguments are separated by commas only; if parentheses are +used by accident, they will become part of the replaced token. If you wish to have an expression follow the macro invocation, the last parameter can be enclosed in curly braces {} to indicate the end of that @@ -4586,6 +4701,7 @@ each supported CPU a constant similar to CPU_SWEET16 CPU_HUC6280 CPU_4510 + CPU_6502DTV is defined. These constants may be used to determine the exact type of the @@ -4600,6 +4716,7 @@ another constant is defined: CPU_ISET_SWEET16 CPU_ISET_HUC6280 CPU_ISET_4510 + CPU_ISET_6502DTV The value read from the / pseudo variable may @@ -4669,6 +4786,7 @@ compiler, depending on the target system selected: diff --git a/doc/cbm510.sgml b/doc/cbm510.sgml index c208f3ead..86bed7607 100644 --- a/doc/cbm510.sgml +++ b/doc/cbm510.sgml @@ -231,10 +231,10 @@ The default drivers, Driver for the 6551 ACIA chip built into the Commodore 510. Supports up to - 19200 BPS, hardware flow control (RTS/CTS), and interrupt-driven receives. - Note that, because of the peculiarities of the 6551 chip, transmits are not - interrupt driven; and, the transceiver blocks if the receiver asserts flow - control because of a full buffer. + 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that, because of the peculiarities of the 6551 chip, transmits + are not interrupt driven; and, the transceiver blocks if the receiver asserts + flow control because of a full buffer.

diff --git a/doc/cbm610.sgml b/doc/cbm610.sgml index 37a445dd1..d86950abc 100644 --- a/doc/cbm610.sgml +++ b/doc/cbm610.sgml @@ -212,10 +212,10 @@ No mouse drivers are currently available for the Commodore 610. Driver for the 6551 ACIA chip built into the Commodore 610. Supports up to - 19200 BPS, hardware flow control (RTS/CTS), and interrupt-driven receives. - Note that, because of the peculiarities of the 6551 chip, transmits are not - interrupt driven; and, the transceiver blocks if the receiver asserts flow - control because of a full buffer. + 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that, because of the peculiarities of the 6551 chip, transmits + are not interrupt driven; and, the transceiver blocks if the receiver asserts + flow control because of a full buffer.

diff --git a/doc/cc65.sgml b/doc/cc65.sgml index dba0a0288..821e76586 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -6,8 +6,9 @@ -cc65 is a C compiler for 6502 targets. It supports several 6502 based home -computers like the Commodore and Atari machines, but it is easily retargetable. +cc65 is a C compiler for 6502 targets. It supports several 6502-based home +computers such as the Commodore and Atari machines, but it easily is +retargetable. @@ -446,10 +447,10 @@ Here is a description of all the command line options: Use static storage for local variables instead of storage on the stack. Since the stack is emulated in software, this gives shorter and usually - faster code, but the code is no longer reentrant. The difference between - void f (void) @@ -545,13 +546,25 @@ Here is a description of all the command line options: Treat all warnings as errors. Warn about statements that don't have an effect. + + Warn if a pointer assignment changes the signedness of the target + of a pointer value, and the new signedness wasn't cast explicitly. + + Warn if a pointer assignment changes the type of the target + of a pointer value, and the new type wasn't cast explicitly. Warn about a / that changes a character's code number from/to 0x00. + + Warn about no return statement in function returning non-void. Warn when passing structs by value. Warn about #pragmas that aren't recognized by cc65. + + Warn about unreachable code in cases of comparing constants, etc. + + Warn about unused functions. Warn about unused labels. @@ -706,12 +719,13 @@ This cc65 version has some extensions to the ISO C standard. places.

- There are two pseudo variables named There are three pseudo variables named @@ -792,6 +806,26 @@ This cc65 version has some extensions to the ISO C standard. size zero, even if it is initialized.

+ cc65 supports + /* C11 version with message. */ + _Static_assert (sizeof (int) == 2, "Expected 2-bytes ints."); + + /* C2X version without message. */ + _Static_assert (sizeof (int) == 2); + + + cc65 supports bit-fields of any integral type that is int-sized or + smaller, and enumerated types with those types as their underlying + type. (Only Computed gotos, a GCC extension, has limited support. With it you can use fast jump tables from C. You can take the address of a label with a double ampersand, putting them in a static const array of type void *. @@ -988,10 +1022,14 @@ The compiler defines several macros at startup: This macro is defined if the target is the Supervision (-t supervision). + __SYM1__ + + This macro is defined if the target is the Sym-1 (-t sym1). + __TELESTRAT__ This macro is defined if the target is the Telestrat (-t telestrat). - + __TIME__ This macro expands to the time of translation of the preprocessing @@ -1025,25 +1063,39 @@ parameter with the #pragma bss-name ([push,] <name>)

+#pragma bss-name ([push, ]<name>[ ,<addrsize>])

- This pragma changes the name used for the BSS segment (the BSS segment - is used to store uninitialized data). The argument is a string enclosed - in double quotes. + This pragma changes the name used for the BSS segment (the BSS segment is + used to store variables with static storage duration and no explicit + initializers). The + "zp", "zeropage", "direct" + "abs", "absolute", "near", "default" + "far" + "long", "dword" + - Beware: The startup code will zero only the default BSS segment. If you - use another BSS segment, you have to do that yourself, otherwise - uninitialized variables do not have the value zero. + Note: The default linker configuration file maps only the standard segments. + If you use other segments, you must create a new linker configuration file. - The - #pragma bss-name ("MyBSS") + #pragma bss-name ("MyBSS") + #pragma bss-name (push, "MyBSS") + #pragma bss-name ("MyBSS", "zp") @@ -1099,21 +1151,33 @@ parameter with the #pragma code-name ([push,] <name>)

+#pragma code-name ([push, ]<name>[ ,<addrsize>])

- This pragma changes the name used for the CODE segment (the CODE segment - is used to store executable code). The argument is a string enclosed in - double quotes. + This pragma changes the name used for the CODE segment (the CODE segment is + used to store executable code). The + "zp", "zeropage", "direct" + "abs", "absolute", "near", "default" + "far" + "long", "dword" + - The - #pragma code-name ("MyCODE") + #pragma code-name ("MyCODE") + #pragma code-name (push, "MyCODE") + #pragma code-name (push, "MyCODE", "far") @@ -1127,21 +1191,33 @@ parameter with the #pragma data-name ([push,] <name>)

+#pragma data-name ([push, ]<name>[ ,<addrsize>])

- This pragma changes the name used for the DATA segment (the DATA segment - is used to store initialized data). The argument is a string enclosed in - double quotes. + This pragma changes the name used for the DATA segment (the DATA segment is + used to store initialized data). The + "zp", "zeropage", "direct" + "abs", "absolute", "near", "default" + "far" + "long", "dword" + - The - #pragma data-name ("MyDATA") + #pragma data-name ("MyDATA") + #pragma data-name (push, "MyDATA") + #pragma data-name ("MyDATA", "zeropage") @@ -1203,21 +1279,32 @@ parameter with the #pragma rodata-name ([push,] <name>)

+#pragma rodata-name ([push, ]<name>[ ,<addrsize>])

- This pragma changes the name used for the RODATA segment (the RODATA - segment is used to store readonly data). The argument is a string - enclosed in double quotes. + This pragma changes the name used for the RODATA segment (the RODATA segment + is used to store read-only data). The + "zp", "zeropage", "direct" + "abs", "absolute", "near", "default" + "far" + "long", "dword" + - The - #pragma rodata-name ("MyRODATA") + #pragma rodata-name ("MyRODATA") + #pragma rodata-name (push, "MyRODATA") @@ -1307,7 +1394,12 @@ parameter with the function will be used + to determine the number from the bank attribute defined in the linker config, + see . Note that + this currently implies that only the least significant 8 bits of the bank attribute + can be used. The address of a wrapped function is passed in Overview

-The Commander X16 is a modern small computer with firmware that is based on -the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips -(WDC65C22 VIA) that are like the ones in the VIC-20. +The Commander X16 is a modern small computer with firmware that is based partly +on the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips +(WDC65C22 VIA) that are like the ones in the VIC-20. It supports file storage +on Secure Digital cards. It allows two joysticks and a mouse. It has three +sound devices. Its VGA screen has twice the range of the C64 (similar to the +C128's 80-column screen), with 256 colors. This file contains an overview of the CX16 run-time system as it comes with the cc65 C compiler. It describes the memory layout, CX16-specific header files, @@ -108,7 +111,7 @@ cl65 -o file.prg -t cx16 -C cx16-asm.cfg source.s To generate code that loads to $A000: -cl65 -o file.prg -Wl -S,$A000 -t cX16 -C cX16-asm.cfg source.s +cl65 -o file.prg -Wl -S,$A000 -t cx16 -C cx16-asm.cfg source.s It also is possible to add a small BASIC header to the program, that uses SYS @@ -139,13 +142,20 @@ The functions listed below are special for the CX16. See the for declarations and usage. -get_ostype -set_tv -videomode -vpeek -vpoke +get_ostype() +set_tv() +videomode() +vpeek() +vpoke() +CBM-specific functions

@@ -154,32 +164,32 @@ machines. See the for declarations and usage. -cbm_close -cbm_closedir -cbm_k_basin -cbm_k_bsout -cbm_k_chkin -cbm_k_ckout -cbm_k_close -cbm_k_clrch -cbm_k_getin -cbm_k_load -cbm_k_open -cbm_k_readst -cbm_k_save -cbm_k_second -cbm_k_setlfs -cbm_k_setnam -cbm_k_tksa -cbm_load -cbm_open -cbm_opendir -cbm_read -cbm_readdir -cbm_save -cbm_write -get_tv -waitvsync +cbm_close() +cbm_closedir() +cbm_k_basin() +cbm_k_bsout() +cbm_k_chkin() +cbm_k_ckout() +cbm_k_close() +cbm_k_clrch() +cbm_k_getin() +cbm_k_load() +cbm_k_open() +cbm_k_readst() +cbm_k_save() +cbm_k_second() +cbm_k_setlfs() +cbm_k_setnam() +cbm_k_tksa() +cbm_load() +cbm_open() +cbm_opendir() +cbm_read() +cbm_readdir() +cbm_save() +cbm_write() +get_tv() +waitvsync() @@ -208,12 +218,22 @@ structures, accessing the struct fields will access the chip registers. Loadable drivers

-The names in the parentheses denote the symbols to be used for static linking of the drivers. +The names in the parentheses denote the symbols to be used for static linking +of the drivers. The names fit into the 8.3 character limit of the SD-Card's +FAT32 file-system. Graphics drivers

-No graphics drivers are available currently for the CX16. +The default drivers, + + This driver features a resolution of 320 across and 200 down with 256 colors, + and a slightly adjustable palette (the order of the colors can be changed in + a way that's compatible with some of the other color drivers). +

Extended memory drivers

@@ -224,10 +244,10 @@ No extended memory drivers are available currently for the CX16. Joystick drivers

The default drivers, - + Supports up to two NES (and SNES) controllers connected to the joystick ports of the CX16. It reads the four directions, and the Mouse drivers

The default drivers, - + Supports a standard 3-button mouse connected to the PS/2 mouse port of the Commander X16. - Currently (r35), this driver doesn't support

diff --git a/doc/da65.sgml b/doc/da65.sgml index 466b41984..b46ee9dd3 100644 --- a/doc/da65.sgml +++ b/doc/da65.sgml @@ -112,15 +112,17 @@ Here is a description of all the command line options: 6502 6502x + 6502dtv 65sc02 65c02 huc6280 4510 - 6502x is for the NMOS 6502 with unofficial opcodes. huc6280 is the CPU of - the PC engine. 4510 is the CPU of the Commodore C65. Support for the 65816 - currently is not available. + 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.

+ + + + + + + + +

@@ -1684,6 +1694,44 @@ used in presence of a prototype. +bios_playsound

+ + + +/ + +BASIC has a fixed tempo of 18. +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + +#include +void main (void) +{ + static const unsigned char notes[] = { + 0x77, 0x4F, 0x37, + 0x4B, 0x05, 0xBB, + 0x4F, 0x27, 0x83, + 0x93, 0x9B, 0x93, + 0x17, 0x4F, 0x96, // played backwards + 0xAB, 0x17, 0x4F, // three-note chords + 0x0E // tempo + }; + bios_playsound (notes, sizeof notes); +} + + + + + bordercolor

@@ -1778,7 +1826,7 @@ be used in presence of a prototype. The function is specific to the C128. The function will not return to the caller. - @@ -2813,6 +2861,8 @@ location of the cursor in the display screen RAM. That number can be passed to done to make it obvious that peeking doesn't move the cursor in any way. Your program must place the cursor where it wants to peek before it calls any of those functions. +On the cx16 (Commander X16) target, this function returns two values: the +background color, in the high nybble, and the text color, in the low nybble. / - / - -The linker is needed to create +The is needed to create relocatable o65 modules for use with this function. The function is available only as a fastcall function; so, it may be used only in the presence of a prototype. @@ -5767,6 +5817,78 @@ be used in presence of a prototype. +psg_delay

+ + + +/ + +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + + + + +psg_outb

+ + + +/ + +The function is only available as fastcall function, so it may only be +used in presence of a prototype. + +, + + +#include +void main (void) +{ + psg_outb (0x80); // Latch frequency + psg_outb (0x07); // Frequency byte 2 + psg_outb (0x90); // Channel 0 full volume + psg_delay (100); + psg_silence (); +} + + + + + +psg_silence

+ + + +/ +, + + + + + qsort

@@ -6196,7 +6318,7 @@ while (ser_get(&ch) == SER_ERR_NO_DATA) / - Gamate System specific information for cc65 <author> -<url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen"> +<url url="mailto:groepaz@gmx.net" name="Groepaz"> <abstract> An overview over the Gamate runtime system as it is implemented for the @@ -117,14 +117,7 @@ following functions (and a few others): <sect>Other hints<p> <itemize> -<item>The Gamate is emulated by MESS (<url url="http://www.mess.org/">), -run like this: <tt>mess gamate -debug -window -skip_gameinfo -cart test.bin</tt> -</itemize> - -some resources on the Gamate: - -<itemize> -<item><url url="http://en.wikipedia.org/wiki/Gamate"> +<item>some resources on the Gamate: <url url="http://en.wikipedia.org/wiki/Gamate"> </itemize> <sect>License<p> diff --git a/doc/geos.sgml b/doc/geos.sgml index ec9d66b1a..f7943f8cd 100644 --- a/doc/geos.sgml +++ b/doc/geos.sgml @@ -205,7 +205,7 @@ see them together in the filling box in GeoPaint. <sect2>GraphicsString <p> -<tt/void GraphicsString (char *myGString)/ +<tt/void GraphicsString (const void *myGString)/ <p> One of the more powerfull routines of GEOS. This function calls other graphic functions depending on the given command string. See the structures chapter for a more detailed description. @@ -722,9 +722,9 @@ disk. Otherwise they will be lost. Operating area is the <tt/curDirHead/. This function returns the number of free blocks on the current disk. It is counted using data in <tt/curDirHead/ so you must initialize the disk before calling it. -<sect3>ChkDskGEOS +<sect3>ChkDkGEOS <p> -<tt/char ChkDskGEOS (void)/ +<tt/char ChkDkGEOS (void)/ <p> This functions checks <tt/curDirHead/ for the GEOS Format identifier. It returns either true or false, and also sets <tt/isGEOS/ properly. You must initialize the disk before using this. @@ -858,20 +858,21 @@ The functions described here are common for SEQ and VLIR structures. <p> <tt/struct filehandle *GetNxtDirEntry (void)/ <p> -These two functions are best suited for scanning the whole directory for particular files. Note that -the returned filehandles describe all file slots in the directory - even those with deleted files. -The return value can be obtained by casting both sides to <tt/unsigned/ - as in the <tt/SetNextFree/ -function or read directly after a call to those two functions from <tt/r5/. The current sector number -is in <tt/r1/ and the sector data itself is in <tt/diskBlkBuf/. +Those two functions are best suited for scanning the whole directory for particular files. Note that +the returned filehandles describe all file slots in the directory -- even those with deleted files. +The return value is <tt/NULL/ if there are no more slots, or if there was a disk error. The +<tt/_oserror/ variable is non-zero if it was a disk error (see <tt>geos/gdisk.h</tt>). The current +directory track and sector numbers are in <tt/r1L/ and <tt/r1H/. The sector data itself is in +<tt/diskBlkBuf/. <sect3>FindFile <p> <tt/char FindFile (char *fName)/ <p> -This function scans the whole directory for the given filename. It returns either 0 (success) or 5 -(FILE_NOT_FOUND, defined in <tt/gdisk.h/) or any other fatal disk read error. After a successful -<tt/FindFile/ you will have <tt/struct filehandle/ at <tt/dirEntryBuf/ filled with the file's data and -other registers set as described in <tt/GetNxtDirEntry/. +This function scans the whole directory for the given filename. It returns either 0 (success), 5 +(FILE_NOT_FOUND, defined in <tt>geos/gdisk.h</tt>), or any other fatal disk read error. After a successful +<tt/FindFile()/, you will have <tt/struct filehandle/ at <tt/dirEntryBuf/ filled with the file's data, and +other registers set as described in <tt/GetNxtDirEntry()/. <sect3>FindFTypes <p> @@ -987,7 +988,7 @@ a particular file. <p> In GEOS there can be only one file opened at a time. Upon opening a VLIR file some information about it is copied into memory. You can retrieve the records table at <tt/fileTrScTab/ (table of -128 <tt/struct tr_se/) and from <tt/VLIRInfo/ (<tt/struct VLIR_info/. +128 <tt/struct tr_se/) and from <tt/VLIRInfo/ (<tt/struct VLIR_info/). E.g. the size of whole VLIR file can be retrieved by reading <tt/VLIRInfo.fileSize/. <sect3>OpenRecordFile diff --git a/doc/index.sgml b/doc/index.sgml index 01325529d..3bb085bf6 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -172,6 +172,9 @@ <tag><htmlurl url="supervision.html" name="supervision.html"></tag> Topics specific to the Watara Supervision Console. + <tag><htmlurl url="sym1.html" name="sym1.html"></tag> + Topics specific to the Synertek Systems Sym-1. + <tag><htmlurl url="telestrat.html" name="telestrat.html"></tag> Topics specific to the Oric Telestrat. diff --git a/doc/intro.sgml b/doc/intro.sgml index b2b141d10..0617b0ab3 100644 --- a/doc/intro.sgml +++ b/doc/intro.sgml @@ -6,6 +6,7 @@ <url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline> <url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline> +<url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:stephan.muehlstrasser@web.de" name="Stephan Mühlstrasser"> <abstract> @@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/<sys>/: Start the desired version of the emulator (CBM610 programs run on the CBM II [<tt/xcbm2/] emulator). -In the Windows versions of VICE, choose <bf>File>Autoboot disk/tape -image...</bf>, choose your executable, and click <bf/OK/. - -In the Unix versions, hold down the mouse's first button. Move the pointer to -<bf>Smart-attach disk/tape...</bf>, and release the button. Choose your -executable, and click <bf/Autostart/. +Choose <bf>File>Autostart disk/tape image...</bf>, choose your executable, +and click <bf/OK/. The file has a 14-byte header which corresponds to a PRG-format BASIC program, consisting of a single line, similar to this: @@ -499,6 +496,29 @@ The output will appear on a separate line, and you will be returned to a BASIC prompt. +<sect1>Gamate<p> + +Before you can run the cartridge image produced by the linker, the binary has to +be patched using the <bf/gamate-fixcart/ tool that is included in the cc65 +package in the util/gamata directory. + +<tscreen><verb> +gamate-fixcart <image.bin> +</verb></tscreen> + +<sect2>MESS<p> +Available at <url +url="https://www.mamedev.org">: + +MESS (Multiple Emulator Super System) is a multi system emulator that emulates +various cc65 targets. It once started as a MAME fork, but was marged into MAME +again at some point. + +<tscreen><verb> +mess gamate -debug -window -skip_gameinfo -cart <image.bin> +</verb></tscreen> + + <sect1>GEOS<p> Available at <it/Click Here Software's/ <url url="http://cbmfiles.com/geos/index.html" name="GEOS download section">: @@ -535,17 +555,8 @@ feature on. </quote> <quote> -VICE even has different ways that depend on which operating system is running -the emulator. -<itemize> -<item>In Windows, you must click on <bf/Options/ (in an always visible menu). - Then, you must click on <bf/True drive emulation/. -<item>In Unix, you must <em/hold down/ the second button on your mouse. Move - the pointer down to <bf/Drive settings/. Then, move the pointer over to - <bf/Enable true drive emulation/. (If there is a check-mark in front of - those words, that feature already is turned on -- then, move the pointer - off of that menu.) Release the mouse button. -</itemize> +In VICE, got to <bf/Settings/ -> <bf/Settings/, then <bf/Peripherial devices/ -> +<bf/Drive/. Then, you must enable the <bf/True drive emulation/ checkbox. </quote> Find the <bf/CONVERT/ program on the boot disk [tap the 6-key; then, you @@ -572,6 +583,29 @@ directory notePad. Look at the eight file-positions on each page until you see The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished reading it. +Alternatively you can use the <bf/c1541/ program that comes with VICE to write the +file to a disk image directly in GEOS format, so it can be used in GEOS directly +without having to use the <bf/CONVERT/ program. + +<tscreen><verb> +c1541 -attach geos.d64 -geoswrite hello1 +</verb></tscreen> + + +<sect1>Nintendo Entertainment System<p> + +<sect2>Mednafen (NES)<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module nes <image.bin> +</verb></tscreen> + <sect1>Ohio Scientific Challenger 1P<p> The <tt/osic1p/ runtime library returns to the boot prompt when the main() @@ -694,6 +728,32 @@ Press <RETURN>. After hitting the RETURN key, you should see the boot prompt again. +<sect1>PC Engine/TurboGrafx 16<p> + +For the cartridge image produced by the linker to work in emulators and on real +hardware, its content must be rearranged so the first 8k block becomes the last +8k block in the image. + +For example, for a 32k image this can be done using <bf/dd/ as follows: + +<tscreen><verb> +dd if=infile.bin bs=8K skip=3 > outfile.pce +dd if=infile.bin bs=8K count=3 >> outfile.pce +</verb></tscreen> + +<sect2>Mednafen<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module pce <image.pce> +</verb></tscreen> + + <sect1>Contributions wanted<p> We need your help! Recommended emulators and instructions for other targets diff --git a/doc/ld65.sgml b/doc/ld65.sgml index d8e296996..56d77ca63 100644 --- a/doc/ld65.sgml +++ b/doc/ld65.sgml @@ -760,7 +760,7 @@ There's a library subroutine called <tt/copydata/ (in a module named look at it's inner workings before using it! -<sect1>Other MEMORY area attributes<p> +<sect1>Other MEMORY area attributes<label id="MEMORY"><p> There are some other attributes not covered above. Before starting the reference section, I will discuss the remaining things here. @@ -822,7 +822,6 @@ that has a segment reference (for example a symbol). The result of this function is the value of the bank attribute for the run memory area of the segment. - <sect1>Other SEGMENT attributes<p> Segments may be aligned to some memory boundary. Specify "<tt/align = num/" to @@ -921,9 +920,8 @@ name="6502 binary relocation format specification">). It is defined like this: } </verb></tscreen> -The other format available is the Atari (xex) segmented file format, this is -the standard format used by Atari DOS 2.0 and upward file managers in the Atari -8-bit computers, and it is defined like this: +The other format available is the Atari segmented file format (xex), this is +the standard format used by Atari DOS 2.0 and upwards, and it is defined like this: <tscreen><verb> FILES { @@ -932,7 +930,8 @@ the standard format used by Atari DOS 2.0 and upward file managers in the Atari </verb></tscreen> In the Atari segmented file format, the linker will write each <tt/MEMORY/ area -as a new segment, including a header with the start and end address. +as including a header with the start and end address. If two memory areas are +contiguous, the headers will be joined if possible. The necessary o65 or Atari attributes are defined in a special section labeled <ref id="FORMAT" name="FORMAT">. @@ -1009,7 +1008,8 @@ The <tt/CONDES/ feature has several attributes: <tag><tt>segment</tt></tag> This attribute tells the linker into which segment the table should be - placed. If the segment does not exist, it is created. + placed. If the segment does not exist in any object file, it is created + in the final object code. <tag><tt>type</tt></tag> @@ -1058,7 +1058,7 @@ The <tt/CONDES/ feature has several attributes: Without specifying the <tt/CONDES/ feature, the linker will not create any tables, even if there are <tt/condes/ entries in the object files. -For more information see the <tt/.CONDES/ command in the <url +For more information, see the <tt/.CONDES/ command in the <url url="ca65.html" name="ca65 manual">. @@ -1139,6 +1139,19 @@ The builtin config files do contain segments that have a special meaning for the compiler and the libraries that come with it. If you replace the builtin config files, you will need the following information. +<sect1>INIT<p> + +The INIT segment is some kind of 'bss' segment since it contains +uninitialized data. Unlike <tt>.bss</tt> itself, its contents aren't +initialized to zero at program startup . It's mostly used by +constructors in the startup code. An example for the use of the INIT +segment is saving/restoring the zero page area used by cc65. + +<sect1>LOWCODE<p> + +For the LOWCODE segment, it is guaranteed that it won't be banked out, so it +is reachable at any time by interrupt handlers or similar. + <sect1>ONCE<p> The ONCE segment is used for initialization code run only once before @@ -1146,11 +1159,6 @@ execution reaches main() - provided that the program runs in RAM. You may for example add the ONCE segment to the heap in really memory constrained systems. -<sect1>LOWCODE<p> - -For the LOWCODE segment, it is guaranteed that it won't be banked out, so it -is reachable at any time by interrupt handlers or similar. - <sect1>STARTUP<p> This segment contains the startup code which initializes the C software stack diff --git a/doc/library.sgml b/doc/library.sgml index 303809c59..bcd5db86e 100644 --- a/doc/library.sgml +++ b/doc/library.sgml @@ -59,6 +59,9 @@ Functions that are <em/not/ available: Functions not available on all supported systems: <itemize> + <item><tt>clock</tt>: Support depends on the capabilities of the target + machine. + <p> <item><tt>fopen/fread/fwrite/fclose/fputs/fgets/fscanf</tt>: The functions are built on open/read/write/close. Those latter functions are not available on all systems. diff --git a/doc/pce.sgml b/doc/pce.sgml index 42a1ca9d3..47eeabcfd 100644 --- a/doc/pce.sgml +++ b/doc/pce.sgml @@ -2,7 +2,7 @@ <article> <title>PC-Engine (TurboGrafx 16) System-specific information for cc65 -<author><url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen">,<newline> +<author><url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King"> <abstract> @@ -206,11 +206,6 @@ following functions (and a few others): <sect>Other hints<p> -<itemize> -<item><url url="https://mednafen.github.io/" name= "Mednafen"> is a good -emulator to use for the PC-Engine. -</itemize> - Some useful resources on PCE coding: <itemize> diff --git a/doc/pet.sgml b/doc/pet.sgml index eb13d9fb9..6bedf6a0f 100644 --- a/doc/pet.sgml +++ b/doc/pet.sgml @@ -105,6 +105,7 @@ declaration and usage. <item>cbm_save <item>cbm_write <item>get_tv +<item>waitvsync </itemize> diff --git a/doc/plus4.sgml b/doc/plus4.sgml index 645de5161..79a2597d0 100644 --- a/doc/plus4.sgml +++ b/doc/plus4.sgml @@ -195,10 +195,10 @@ No mouse drivers are currently available for the Plus/4. <tag><tt/plus4-stdser.ser (plus4_stdser_ser)/</tag> Driver for the 6551 ACIA chip built into the Plus/4. Supports up to 19200 - baud, hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not - interrupt driven, and the transceiver blocks if the receiver asserts flow - control because of a full buffer. + baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that because of the peculiarities of the 6551 chip transmits + are not interrupt driven, and the transceiver blocks if the receiver asserts + flow control because of a full buffer. You need an adapter to use the builtin port, since the output levels available at the user port don't follow the RS232 standard. diff --git a/doc/sym1.sgml b/doc/sym1.sgml new file mode 100644 index 000000000..60eb1c020 --- /dev/null +++ b/doc/sym1.sgml @@ -0,0 +1,132 @@ +<!doctype linuxdoc system> + +<article> +<title>Synertek Systems Sym-1 specific information for cc65 +<author><url url="mailto:wayne@parhamdata.com" name="Wayne Parham"> + +<abstract> +An overview over the Sym-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 Sym-1 runtime system as it comes with the cc65 C compiler. It describes the memory layout, Sym-1 specific header files, available drivers, and any pitfalls specific to the platform. + +Please note that Sym-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 Sym-1 target is a raw binary BIN file, which is essentially a memory image. You can convert this to a HEX file using BIN2HEX, which is a popular open-source conversion utility program. A HEX file has ASCII representations of the hexadecimal byte values of the machine-language program. So the HEX file can be transferred to the Sym-1 using the RS-232 terminal port, just as if the machine-code was entered by hand. Enter 'm 200' in the monitor and start the HEX file transfer. + +<p> + +Included with this distribution is a 4k configuration file and a 32k config file. The Sym-1 on-board memory is limited to 4 kbytes but system memory can be increased to 32 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 Sym-1 monitor ROM. cc65 generated programs compiled and linked using 4k config run in the memory range of $200 - $0FFF. The 32k config expands this range to $7FFF. The starting memory location and entry point for running the program is $200, so when the program is transferred to the Sym-1, it is executed by typing 'g 200'. The system returns control back to the monitor ROM when the program terminates, providing the '.' prompt. + +Special locations: + +<descrip> + <tag/Text screen/ + Conio support is not currently available for the Sym-1. But stdio console functions are available. + + <tag/Stack/ + The C runtime stack is located at $0FFF on 4KB Syms, or at $7FFFfor 32KB 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 Sym-1 code may use the <tt/sym1.h/ header file. See the header file for more information. + +<sect1>Hardware access<p> + +The pseudo variables declared in the <tt/sym1.inc/ include file allow access to hardware located in the address space. See the include file for more information. + +<sect>Loadable drivers<p> + +<sect1>Graphics drivers<p> + +No graphics drivers are currently available for the Sym-1. + +<sect1>Extended memory drivers<p> + +No extended memory drivers are currently available for the Sym-1. + +<sect1>Joystick drivers<p> + +No joystick driver is currently available for the Sym-1. + +<sect1>Mouse drivers<p> + +No mouse drivers are currently available for the Sym-1. + +<sect1>RS232 device drivers<p> + +No communication port drivers are currently available for the Sym-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 Sym-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>sym1.h<p> +This header exposes Sym-specific I/O functions that are useful for reading and writing its ports and front panel. See the <tt/sym1.h/ include file for a list of the functions available. + +<sect2>Limited memory applications<p> + +As stated earlier, there are config files for 4KB and 32KB systems. If you have 32KB RAM, then you will probably want to use the sym1-32k configuration, but if not - if you are using the sym1-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. + +<sect3>Sample programs<p> + +All the samples will run on the "stock" 4KB Sym-1, except for symIO and symNotepad, which require 32KB. These sample programs can be found in the targettest/sym1 directory: + +<itemize> +<item>symHello prints "Hello World!" and then inputs characters, which are echoed on the screen. It also makes a "beep" sound.</item> +<item>symTiny does the same as symHello, but does it with puts() rather than printf() to show the difference in compiled binary size.</item> +<item>symDisplay allows entry of a message, which is then displayed by scrolling it across the front panel display.</item> +<item>symIO allows access to the Sym-1 digital I/O ports.</item> +<item>symNotepad is a simple text entry/retrieval program that uses tape storage.</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/tgi.sgml b/doc/tgi.sgml index b1497ca70..29acd8ce6 100644 --- a/doc/tgi.sgml +++ b/doc/tgi.sgml @@ -555,7 +555,7 @@ tgi_init(); //Set up the default palette and clear the screen. <descrip> <tag/Function/Install an already loaded driver and return an error code. <tag/Header/<tt/<ref id="tgi.h" name="tgi.h">/ -<tag/Declaration/<tt/unsigned char __fastcall__ tgi_install (void* driver);/ +<tag/Declaration/<tt/unsigned char __fastcall__ tgi_install (const void* driver);/ <tag/Description/The function installs a driver that was already loaded into memory (or linked statically to the program). It returns an error code (<tt/TGI_ERR_OK/ in case of success). diff --git a/doc/vic20.sgml b/doc/vic20.sgml index 4fcd0079c..0a6e85d4a 100644 --- a/doc/vic20.sgml +++ b/doc/vic20.sgml @@ -75,7 +75,6 @@ common to all CBM platforms. There are currently no special VIC20 functions. - <sect1>CBM-specific functions<p> Some functions are available for all (or at least most) of the Commodore @@ -143,7 +142,33 @@ The names in the parentheses denote the symbols to be used for static linking of <sect1>Graphics drivers<p> -No graphics drivers are currently available for the VIC20. +<descrip> + + <tag><tt/vic20-hi.tgi (vic20_hi_tgi)/</tag> + This driver features a resolution of 160×192 with two colors. The + background can be chosen from a sixteen-color palette. The foreground can + be chosen from an eight-color palette. + + The driver will use memory from addresses $1000 to $1FFF as a graphics + buffer. Therefore, the VIC-20 must have, at least, 8K of expansion RAM. + + Programs that use this driver must be linked by the <tt/vic20-tgi.cfg/ + configuration file. It will link a special header into the program. + That header will do the housekeeping that's needed by TGI. + + An example command line: + <tscreen><verb> + cl65 -D DYN_DRV=0 -t vic20 -C vic20-tgi.cfg samples/mandelbrot.c + </verb></tscreen> + + When the program starts, it will move itself up in RAM, to make room for the + buffer. When the program finishes, it will reset the BASIC interpreter. + That means that graphics pictures won't be preserved between the executions + of programs. Also, the graphics buffer shares RAM with the text screen. If + a picture must be saved, then a program must put it somewhere else (such as + a disk file) before returning to the text mode. + +</descrip> <sect1>Extended memory drivers<p> diff --git a/include/_atari5200os.h b/include/_atari5200os.h new file mode 100644 index 000000000..db0f7f0c9 --- /dev/null +++ b/include/_atari5200os.h @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* */ +/* _atari5200os.h */ +/* */ +/* Internal include file, do not use directly */ +/* */ +/* */ +/* 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 __ATARI5200OS_H +#define __ATARI5200OS_H + + +struct __os { + + /*Page zero*/ + unsigned char pokmsk; // = $00 System mask for POKEY IRQ enable + unsigned char rtclok[2]; // = $01,$02 Real time clock + unsigned char critic; // = $03 Critical section flag + unsigned char atract; // = $04 Attract mode counter + + union { + struct { + unsigned char sdlstl; // = $05 Save display list LO + unsigned char sdlsth; // = $06 Save display list HI + }; + void* sdlst; // = $05,$06 Display list shadow + }; + + unsigned char sdmctl; // = $07 DMACTL shadow + unsigned char pcolr0; // = $08 PM color 0 + unsigned char pcolr1; // = $09 PM color 1 + unsigned char pcolr2; // = $0A PM color 2 + unsigned char pcolr3; // = $0B PM color 3 + unsigned char color0; // = $0C PF color 0 + unsigned char color1; // = $0D PF color 1 + 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 + + /*Stack*/ + unsigned char stack[0x100]; // = $100-$1FF Stack + + /*Page 2 OS variables*/ + void (*vinter)(void); // = $200 Immediate IRQ vector + void (*vvblki)(void); // = $202 Immediate VBI vector + void (*vvblkd)(void); // = $204 Deferred VBI vector + void (*vdslst)(void); // = $206 DLI vector + void (*vkeybd)(void); // = $208 Keyboard IRQ vector + void (*vkeypd)(void); // = $20A Keyboard continuation vector + void (*vbrkky)(void); // = $20C Break key interrupt vector + void (*vbreak)(void); // = $20E BRK instruction interrupt vector + void (*vserin)(void); // = $210 Serial input ready vector + void (*vseror)(void); // = $212 Serial output data needed vector + void (*vseroc)(void); // = $214 Serial output completed vector + void (*vtimr1)(void); // = $216 POKEY timer 1 IRQ vector + void (*vtimr2)(void); // = $218 POKEY timer 2 IRQ vector + void (*vtimr4)(void); // = $21A POKEY timer 4 IRQ vector + +}; + +#endif diff --git a/include/_ted.h b/include/_ted.h index 68b59d706..c2cd0a04e 100644 --- a/include/_ted.h +++ b/include/_ted.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/apple2.h b/include/apple2.h index dbed3364d..57d7086ce 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -207,6 +207,8 @@ void rebootafterexit (void); #define _textcolor(color) COLOR_WHITE #define _bgcolor(color) COLOR_BLACK #define _bordercolor(color) COLOR_BLACK +#define _cpeekcolor() COLOR_WHITE +#define _cpeekrevers() 0 diff --git a/include/assert.h b/include/assert.h index 504964dc2..a4dcd5d7f 100644 --- a/include/assert.h +++ b/include/assert.h @@ -46,10 +46,14 @@ extern void __fastcall__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif - - -/* End of assert.h */ +/* TODO: Guard with #if __CC65_STD__ >= __CC65_STD_C11__ if there +** is a C11 mode. +*/ +#if __CC65_STD__ > __CC65_STD_C99__ +# define static_assert _Static_assert #endif +/* End of assert.h */ +#endif diff --git a/include/atari.h b/include/atari.h index 582e23be6..86c7b9706 100644 --- a/include/atari.h +++ b/include/atari.h @@ -6,10 +6,11 @@ /* */ /* */ /* */ -/* (C) 2000-2019 Mark Keates <markk@dendrite.co.uk> */ +/* (C) 2000-2021 Mark Keates <markk@dendrite.co.uk> */ /* Freddy Offenga <taf_offenga@yahoo.com> */ /* Christian Groessler <chris@groessler.org> */ /* Bill Kendrick <nbs@sonic.net> */ +/* et al. */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -209,7 +210,7 @@ #define KEY_CLEAR (KEY_LESSTHAN | KEY_SHIFT) #define KEY_INSERT (KEY_GREATERTHAN | KEY_SHIFT) -#define KEY_UP (KEY_UNDERLINE | KEY_CTRL) +#define KEY_UP (KEY_DASH | KEY_CTRL) #define KEY_DOWN (KEY_EQUALS | KEY_CTRL) #define KEY_LEFT (KEY_PLUS | KEY_CTRL) #define KEY_RIGHT (KEY_ASTERISK | KEY_CTRL) @@ -219,7 +220,7 @@ /* Color register functions */ /*****************************************************************************/ -extern void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminace); +extern void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminance); extern void __fastcall__ _setcolor_low (unsigned char color_reg, unsigned char color_value); extern unsigned char __fastcall__ _getcolor (unsigned char color_reg); @@ -227,6 +228,7 @@ extern unsigned char __fastcall__ _getcolor (unsigned char color_reg); /* Other screen functions */ /*****************************************************************************/ +extern void waitvsync (void); /* wait for start of next frame */ extern int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */ extern void __fastcall__ _scroll (signed char numlines); /* numlines > 0 scrolls up */ diff --git a/include/atari5200.h b/include/atari5200.h index d6c2561b2..ff176c15b 100644 --- a/include/atari5200.h +++ b/include/atari5200.h @@ -35,7 +35,7 @@ -/* Check for errors */ +/* check for errors */ #if !defined(__ATARI5200__) # error This module may only be used when compiling for the Atari 5200! #endif @@ -46,14 +46,14 @@ /* the addresses of the static drivers */ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ -/* Masks for joy_read */ +/* masks for joy_read */ #define JOY_UP_MASK 0x01 #define JOY_DOWN_MASK 0x02 #define JOY_LEFT_MASK 0x04 #define JOY_RIGHT_MASK 0x08 #define JOY_BTN_1_MASK 0x10 -/* Character codes */ +/* character codes */ #define CH_ULCORNER 0x0B /* '+' sign */ #define CH_URCORNER 0x0B #define CH_LLCORNER 0x0B @@ -65,7 +65,11 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ #define AT_NTSC 0 #define AT_PAL 1 -/* Define hardware */ +/* Define variables used by the OS*/ +#include <_atari5200os.h> +#define OS (*(struct __os*)0x0000) + +/* define hardware */ #include <_gtia.h> #define GTIA_READ (*(struct __gtia_read*)0xC000) #define GTIA_WRITE (*(struct __gtia_write*)0xC000) @@ -89,5 +93,8 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ */ #define _bordercolor(color) 0 -/* End of atari5200.h */ +/* wait for start of next frame */ +extern void waitvsync (void); + +/* end of atari5200.h */ #endif diff --git a/include/cbm.h b/include/cbm.h index ac3615c38..cceb76b1b 100644 --- a/include/cbm.h +++ b/include/cbm.h @@ -166,7 +166,7 @@ unsigned char get_tv (void); unsigned char __fastcall__ kbrepeat (unsigned char mode); /* Changes which keys have automatic repeat. */ -#if !defined(__CBM610__) && !defined(__PET__) +#if !defined(__CBM610__) void waitvsync (void); /* Wait for the start of the next video field. */ #endif @@ -191,6 +191,8 @@ unsigned char cbm_k_acptr (void); unsigned char cbm_k_basin (void); void __fastcall__ cbm_k_bsout (unsigned char C); unsigned char __fastcall__ cbm_k_chkin (unsigned char FN); +unsigned char cbm_k_chrin (void); +void __fastcall__ cbm_k_chrout (unsigned char C); void __fastcall__ cbm_k_ciout (unsigned char C); unsigned char __fastcall__ cbm_k_ckout (unsigned char FN); void cbm_k_clall (void); @@ -295,7 +297,15 @@ unsigned char __fastcall__ cbm_readdir (unsigned char lfn, /* Reads one directory line into cbm_dirent structure. ** Returns 0 if reading directory-line was successful. ** Returns non-zero if reading directory failed, or no more file-names to read. -** Returns 2 on last line. Then, l_dirent->size = the number of "blocks free." +** Returns 2 on last line. Then, l_dirent->size = the number of "blocks free", +** "blocks used", or "mb free". Return codes: +** 0 = read file-name +** 1 = couldn't read directory +** 2 = read "blocks free", "blocks used", or "mb free" +** 3 = couldn't find start of file-name +** 4 = couldn't find end of file-name +** 5 = couldn't read file-type +** 6 = premature end of file */ void __fastcall__ cbm_closedir (unsigned char lfn); diff --git a/include/cbm264.h b/include/cbm264.h index 5e8a242a7..a51ee79c5 100644 --- a/include/cbm264.h +++ b/include/cbm264.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/cbm_screen_charmap.h b/include/cbm_screen_charmap.h index 76e0d7d7b..5bf4c3f90 100644 --- a/include/cbm_screen_charmap.h +++ b/include/cbm_screen_charmap.h @@ -8,7 +8,7 @@ /* from ASCII to screen-code mapping, so you can write directly */ /* to the screen memory. */ /* */ -/* If this include is used, no additional macroes are needed. */ +/* If this include is used, no additional macros are needed. */ /* */ /*****************************************************************************/ diff --git a/include/conio.h b/include/conio.h index 72421af86..bac20e3c5 100644 --- a/include/conio.h +++ b/include/conio.h @@ -201,16 +201,19 @@ void __fastcall__ cputhex16 (unsigned val); */ #ifdef _textcolor -# define textcolor(x) _textcolor(x) +# define textcolor(color) _textcolor(color) #endif #ifdef _bgcolor -# define bgcolor(x) _bgcolor(x) +# define bgcolor(color) _bgcolor(color) #endif #ifdef _bordercolor -# define bordercolor(x) _bordercolor(x) +# define bordercolor(color) _bordercolor(color) #endif #ifdef _cpeekcolor -# define cpeekcolor(x) _cpeekcolor(x) +# define cpeekcolor() _cpeekcolor() +#endif +#ifdef _cpeekrevers +# define cpeekrevers() _cpeekrevers() #endif diff --git a/include/creativision.h b/include/creativision.h index 40b9ee03a..6910ee0cf 100644 --- a/include/creativision.h +++ b/include/creativision.h @@ -70,9 +70,9 @@ #define COLOR_WHITE 15 /* Protos */ -void __fastcall__ psg_outb(unsigned char b); -void __fastcall__ psg_delay(unsigned char b); -void psg_silence(void); -void __fastcall__ bios_playsound(void *a, unsigned char b); +void __fastcall__ psg_outb (unsigned char b); +void __fastcall__ psg_delay (unsigned char b); +void psg_silence (void); +void __fastcall__ bios_playsound (const void *a, unsigned char b); #endif /* #ifndef _CVISION_H */ diff --git a/include/cx16.h b/include/cx16.h index 253fa2482..179021e84 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -3,7 +3,7 @@ /* cx16.h */ /* */ /* CX16 system-specific definitions */ -/* For prerelease 37 */ +/* For prerelease 38 */ /* */ /* */ /* This software is provided "as-is", without any expressed or implied */ @@ -111,6 +111,25 @@ #define COLOR_LIGHTBLUE 0x0E #define COLOR_GRAY3 0x0F +/* TGI color defines */ +#define TGI_COLOR_BLACK COLOR_BLACK +#define TGI_COLOR_WHITE COLOR_WHITE +#define TGI_COLOR_RED COLOR_RED +#define TGI_COLOR_CYAN COLOR_CYAN +#define TGI_COLOR_VIOLET COLOR_VIOLET +#define TGI_COLOR_PURPLE COLOR_PURPLE +#define TGI_COLOR_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_BROWN COLOR_BROWN +#define TGI_COLOR_LIGHTRED COLOR_LIGHTRED +#define TGI_COLOR_GRAY1 COLOR_GRAY1 +#define TGI_COLOR_GRAY2 COLOR_GRAY2 +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_GRAY3 COLOR_GRAY3 + /* NES controller masks for joy_read() */ #define JOY_BTN_1_MASK 0x80 @@ -259,29 +278,35 @@ struct __vera { #define VIA1 (*(volatile struct __6522 *)0x9F60) #define VIA2 (*(volatile struct __6522 *)0x9F70) +#define RAM_BANK (VIA1.pra) +#define ROM_BANK (VIA1.prb) + /* A structure with the x16emu's settings registers */ struct __emul { unsigned char debug; /* Boolean: debugging enabled */ unsigned char vera_action; /* Boolean: displaying VERA activity */ unsigned char keyboard; /* Boolean: displaying typed keys */ - unsigned char echo; /* How Kernal output should be echoed to host */ - unsigned char save_on_exit; /* Boolean: save SD card when quitting */ + unsigned char echo; /* How to send Kernal output to host */ + unsigned char save_on_exit; /* Boolean: save machine state on exit */ unsigned char gif_method; /* How GIF movie is being recorded */ - unsigned char unused[0xD - 0x6]; - unsigned char keymap; /* Keyboard layout number */ - const char detect[2]; /* "16" if running on x16emu */ + unsigned char const unused1[2]; + unsigned long const cycle_count; /* Running total of CPU cycles (8 MHz.) */ + unsigned char const unused2[1]; + unsigned char const keymap; /* Keyboard layout number */ + char const detect[2]; /* "16" if running on x16emu */ }; -#define EMULATOR (*(volatile struct __emul)0x9FB0) +#define EMULATOR (*(volatile struct __emul *)0x9FB0) /* An array window into the half Mebibyte or two Mebibytes of banked RAM */ -#define BANK_RAM ((unsigned char[0x2000])0xA000) +#define BANK_RAM ((unsigned char *)0xA000) /* The addresses of the static drivers */ -extern void cx16_std_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void cx16_std_mou[]; /* Referred to by mouse_static_stddrv[] */ +extern void cx16_std_joy[]; /* Referenced by joy_static_stddrv[] */ +extern void cx16_std_mou[]; /* Referenced by mouse_static_stddrv[] */ +extern void cx320p1_tgi[]; /* Referenced by tgi_static_stddrv[] */ diff --git a/include/em.h b/include/em.h index b151800ae..47bd23222 100644 --- a/include/em.h +++ b/include/em.h @@ -82,7 +82,7 @@ unsigned char __fastcall__ em_load_driver (const char* driver); unsigned char em_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ em_install (void* driver); +unsigned char __fastcall__ em_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char em_uninstall (void); diff --git a/include/fcntl.h b/include/fcntl.h index 0d2398315..a1121159d 100644 --- a/include/fcntl.h +++ b/include/fcntl.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/gamate.h b/include/gamate.h index 0af21623d..8b9790e39 100644 --- a/include/gamate.h +++ b/include/gamate.h @@ -170,6 +170,8 @@ /* No support for dynamically loadable drivers */ #define DYN_DRV 0 +#define JOY_DATA 0x4400 /* hw register to read the pad bits from */ + /* Masks for joy_read */ #define JOY_UP_MASK 0x01 #define JOY_DOWN_MASK 0x02 diff --git a/include/geos/ggraph.h b/include/geos/ggraph.h index ec9fb0fa1..b550d6ca1 100644 --- a/include/geos/ggraph.h +++ b/include/geos/ggraph.h @@ -43,7 +43,7 @@ void __fastcall__ BitOtherClip(void *proc1, void *proc2, char skipl, char skipr, unsigned skiptop, struct iconpic *myIcon); -void __fastcall__ GraphicsString(char *myGfxString); +void __fastcall__ GraphicsString(const void *myGfxString); #ifdef __GEOS_CBM__ void SetNewMode(void); diff --git a/include/geos/gstruct.h b/include/geos/gstruct.h index 9e5f0ddda..6d9cb1f14 100644 --- a/include/geos/gstruct.h +++ b/include/geos/gstruct.h @@ -126,8 +126,8 @@ struct VLIR_info { /* VLIR information */ }; struct process { /* process info, declare table of that type */ - unsigned pointer; /* (like: struct process proctab[2]=... */ - unsigned jiffies; /* last entry HAVE TO BE {0,0} */ + unsigned pointer; /* (like: struct process proctab[2]= ... */ + unsigned jiffies; /* last entry MUST BE {0,0} */ }; struct iconpic { /* icon/encoded bitmap description */ @@ -135,7 +135,7 @@ struct iconpic { /* icon/encoded bitmap description */ char x; /* position in cards (*8 pixels) */ char y; char width; /* in cards */ - char heigth; /* in lines (pixels) */ + char height; /* in lines (pixels) */ }; struct icondef { /* icon definition for DoIcons */ @@ -143,7 +143,7 @@ struct icondef { /* icon definition for DoIcons */ char x; /* position in cards (*8 pixels) */ char y; char width; /* of icon (in cards) */ - char heigth; /* of icon in lines (pixels) */ + char height; /* of icon in lines (pixels) */ unsigned proc_ptr; /* pointer to function handling that icon */ }; diff --git a/include/joystick.h b/include/joystick.h index b6771c381..963c9ba95 100644 --- a/include/joystick.h +++ b/include/joystick.h @@ -89,7 +89,7 @@ unsigned char __fastcall__ joy_load_driver (const char* driver); unsigned char joy_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ joy_install (void* driver); +unsigned char __fastcall__ joy_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char joy_uninstall (void); diff --git a/include/limits.h b/include/limits.h index 23474c78c..531c6bef2 100644 --- a/include/limits.h +++ b/include/limits.h @@ -63,6 +63,20 @@ #define ULONG_MAX 4294967295UL +/* These defines that are platform dependent */ +#if defined(__APPLE2__) +# define PATH_MAX (64+1) +#elif defined(__ATARI__) +# define PATH_MAX (63+1) +#elif defined(__CBM__) +# define PATH_MAX (255) /* should be 256+1, see libsrc/common/_cmd.s why it's not */ +#elif defined(__LUNIX__) +# define PATH_MAX (80+1) +#elif defined(__TELESTRAT__) +# define PATH_MAX (50+1) +#else +# define PATH_MAX (16+1) +#endif /* End of limits.h */ diff --git a/include/locale.h b/include/locale.h index 4134dd5db..3f23e01d2 100644 --- a/include/locale.h +++ b/include/locale.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2005 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/mouse/mouse-kernel.h b/include/mouse/mouse-kernel.h index f024b0926..e2d2ced10 100644 --- a/include/mouse/mouse-kernel.h +++ b/include/mouse/mouse-kernel.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003-2006, Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/peekpoke.h b/include/peekpoke.h index 4c1156ec3..268dbfb98 100644 --- a/include/peekpoke.h +++ b/include/peekpoke.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/plus4.h b/include/plus4.h index c8aaf2eaf..325ba7d89 100644 --- a/include/plus4.h +++ b/include/plus4.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2006, Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/serial.h b/include/serial.h index 990f66521..58943d507 100644 --- a/include/serial.h +++ b/include/serial.h @@ -136,7 +136,7 @@ unsigned char __fastcall__ ser_load_driver (const char* driver); unsigned char ser_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ ser_install (void* driver); +unsigned char __fastcall__ ser_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char ser_uninstall (void); diff --git a/include/signal.h b/include/signal.h index 5cb63fcae..0d5f6ad09 100644 --- a/include/signal.h +++ b/include/signal.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2002-2005, Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/stdarg.h b/include/stdarg.h index adf73483c..f6a4fe934 100644 --- a/include/stdarg.h +++ b/include/stdarg.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/stdio.h b/include/stdio.h index 73dc05bdb..916affe71 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -40,6 +40,7 @@ #include <stddef.h> #include <stdarg.h> +#include <limits.h> @@ -64,18 +65,7 @@ extern FILE* stderr; #define SEEK_SET 2 #define TMP_MAX 256 -/* Standard defines that are platform dependent */ -#if defined(__APPLE2__) -# define FILENAME_MAX (64+1) -#elif defined(__ATARI__) -# define FILENAME_MAX (12+1) -#elif defined(__LUNIX__) -# define FILENAME_MAX (80+1) -#elif defined(__TELESTRAT__) -# define FILENAME_MAX (50+1) -#else -# define FILENAME_MAX (16+1) -#endif +#define FILENAME_MAX PATH_MAX #define L_tmpnam FILENAME_MAX diff --git a/include/sym1.h b/include/sym1.h new file mode 100644 index 000000000..e0eb81ecf --- /dev/null +++ b/include/sym1.h @@ -0,0 +1,165 @@ +/*****************************************************************************/ +/* */ +/* sym1.h */ +/* */ +/* Sym-1 system-specific definitions */ +/* */ +/* */ +/* */ +/* (C) 2020 Wayne Parham */ +/* EMail: wayne@parhamdata.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 _SYM1_H +#define _SYM1_H + + + +/* Check for errors */ +#if !defined(__SYM1__) +# error This module may only be used when compiling for the Sym-1! +#endif + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Display character definitions */ +#define DISP_1 0x06 // '1' +#define DISP_2 0x5B // '2' +#define DISP_3 0x4F // '3' +#define DISP_4 0x66 // '4' +#define DISP_5 0x6D // '5' +#define DISP_6 0x7C // '6' +#define DISP_7 0x07 // '7' +#define DISP_8 0x7F // '8' +#define DISP_9 0x67 // '9' +#define DISP_0 0x3F // '0' +#define DISP_A 0x77 // 'A' +#define DISP_b 0x7C // 'b' +#define DISP_C 0x39 // 'C' +#define DISP_c 0x58 // 'c' +#define DISP_d 0x5E // 'd' +#define DISP_E 0x79 // 'E' +#define DISP_e 0x7B // 'e' +#define DISP_F 0x71 // 'F' +#define DISP_G 0x7D // 'G' +#define DISP_g 0x6F // 'g' +#define DISP_H 0x76 // 'H' +#define DISP_h 0x74 // 'h' +#define DISP_I 0x06 // 'I' +#define DISP_i 0x04 // 'i' +#define DISP_J 0x1E // 'J' +#define DISP_K 0x74 // 'K' +#define DISP_L 0x38 // 'L' +#define DISP_M_1 0x33 // 'M' +#define DISP_M_2 0x27 // 2nd half +#define DISP_n 0x54 // 'n' +#define DISP_O 0x3F // 'O' +#define DISP_o 0x5C // 'o' +#define DISP_P 0x73 // 'P' +#define DISP_q 0x67 // 'q' +#define DISP_r 0x50 // 'r' +#define DISP_S 0x6D // 'S' +#define DISP_t 0x46 // 't' +#define DISP_U 0x3E // 'U' +#define DISP_u 0x1C // 'u' +#define DISP_V_1 0x64 // 'V' +#define DISP_V_2 0x52 // 2nd half +#define DISP_W_1 0x3C // 'W' +#define DISP_W_2 0x1E // 2nd half +#define DISP_Y 0x6E // 'Y' +#define DISP_Z 0x5B // 'Z' +#define DISP_SPACE 0x00 // ' ' +#define DISP_PERIOD 0x80 // '.' +#define DISP_HYPHEN 0x40 // '-' +#define DISP_APOSTR 0x20 // ''' +#define DISP_EQUAL 0x41 // '=' +#define DISP_3_BAR 0x49 // '=' +#define DISP_BOTTOM 0x08 // '_' +#define DISP_TOP 0x01 // Top segment +#define DISP_LEFT 0x30 // '|' Left side, both segments +#define DISP_RIGHT 0x06 // '|' Right side, both segments +#define DISP_DEGREE 0x63 // 'o' An 'o' character in the upper segments +#define DISP_HAT 0x23 // 'n' An 'n' character in the upper segments +#define DISP_FORK 0x62 // 'u' A 'u' character in the upper segments +#define DISP_SLASH 0x51 // '/' +#define DISP_BACKSLASH 0x34 // '\' +#define DISP_TOP_RIGHT 0x02 // Top right segment +#define DISP_TOP_LEFT 0x20 // Top left segment +#define DISP_LOW_RIGHT 0x04 // Lower right segment +#define DISP_LOW_LEFT 0x10 // Lower left segment + + +/*****************************************************************************/ +/* Hardware */ +/*****************************************************************************/ + + + +#include <_6522.h> +#define VIA1 (*(struct __6522*)0xA000) // U25 +#define VIA2 (*(struct __6522*)0xA800) // U28 +#define VIA3 (*(struct __6522*)0xAC00) // U29 + + +struct _display { + unsigned char d0; // left-most seven-segment display + unsigned char d1; // second seven-segment display + unsigned char d2; // third seven-segment display + unsigned char d3; // fouth seven-segment display + unsigned char d4; // fifth seven-segment display + unsigned char d5; // sixth seven-segment display + unsigned char d6; // buffer byte to the right +}; +#define DISPLAY (*(struct _display*)0xA640) + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void beep (void); +/* Beep sound. */ + +void fdisp (void); +/* Flash display */ + +int __fastcall__ loadt (unsigned char); +/* Read from tape */ + +int __fastcall__ dumpt (unsigned char, const void*, const void*); +/* Write to tape */ + + + +/* End of sym1.h */ +#endif diff --git a/include/sys/stat.h b/include/sys/stat.h index c7e003808..d8fc09c75 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/sys/types.h b/include/sys/types.h index 9b1e9610f..e75dd7d46 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/include/telestrat.h b/include/telestrat.h index a4648a3dc..c1e0996bc 100644 --- a/include/telestrat.h +++ b/include/telestrat.h @@ -85,6 +85,8 @@ #define CH_LTEE '+' #define CH_RTEE '+' #define CH_CROSS '+' +#define CH_HLINE '-' +#define CH_VLINE '|' #define CH_CURS_UP 11 #define CH_CURS_DOWN 10 #define CH_CURS_LEFT 8 @@ -98,8 +100,8 @@ /* Masks for joy_read */ #define JOY_UP_MASK 0x10 #define JOY_DOWN_MASK 0x08 -#define JOY_LEFT_MASK 0x01 -#define JOY_RIGHT_MASK 0x02 +#define JOY_LEFT_MASK 0x02 +#define JOY_RIGHT_MASK 0x01 #define JOY_BTN_1_MASK 0x04 #define JOY_FIRE_MASK JOY_BTN_1_MASK @@ -119,3 +121,9 @@ void shoot(); void explode(); void kbdclick1(); + +/* 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. +*/ +#define _bordercolor(color) COLOR_BLACK diff --git a/include/tgi.h b/include/tgi.h index 15a6437cc..2ef65b856 100644 --- a/include/tgi.h +++ b/include/tgi.h @@ -82,7 +82,7 @@ void tgi_unload (void); ** necessary. */ -void __fastcall__ tgi_install (void* driver); +void __fastcall__ tgi_install (const void* driver); /* Install an already loaded driver. */ void tgi_uninstall (void); @@ -216,7 +216,7 @@ void __fastcall__ tgi_arc (int x, int y, unsigned char rx, unsigned char ry, /* Draw an ellipse arc with center at x/y and radii rx/ry using the current ** drawing color. The arc covers the angle between sa and ea (startangle and ** endangle), which must be in the range 0..360 (otherwise the function may -** bevave unextectedly). +** behave unexpectedly). */ void __fastcall__ tgi_pieslice (int x, int y, unsigned char rx, unsigned char ry, diff --git a/include/time.h b/include/time.h index 4ee658da9..92cbeee37 100644 --- a/include/time.h +++ b/include/time.h @@ -83,46 +83,31 @@ extern struct _timezone { -#if defined(__ATARI__) -/* The clock depends on the video standard, so read it at runtime */ -unsigned _clocks_per_sec (void); -# define CLK_TCK _clocks_per_sec() -# define CLOCKS_PER_SEC _clocks_per_sec() -#elif defined(__ATARI5200__) -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ +#if defined(__ATARI5200__) +# define CLOCKS_PER_SEC 60 #elif defined(__ATMOS__) -# define CLK_TCK 100 /* POSIX */ -# define CLOCKS_PER_SEC 100 /* ANSI */ +# define CLOCKS_PER_SEC 100 #elif defined(__CBM__) # if defined(__CBM510__) || defined(__CBM610__) /* The 510/610 gets its clock from the AC current */ -# define CLK_TCK 50 /* POSIX */ -# define CLOCKS_PER_SEC 50 /* ANSI */ +# define CLOCKS_PER_SEC 50 # else -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ +# define CLOCKS_PER_SEC 60 # endif #elif defined(__NES__) -# define CLK_TCK 50 /* POSIX */ -# define CLOCKS_PER_SEC 50 /* ANSI */ +# define CLOCKS_PER_SEC 50 #elif defined(__PCE__) -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ -#elif defined(__GAMATE__) -# define CLK_TCK 135 /* POSIX */ /* FIXME */ -# define CLOCKS_PER_SEC 135 /* ANSI */ /* FIXME */ -#elif defined(__GEOS__) -# define CLK_TCK 1 /* POSIX */ -# define CLOCKS_PER_SEC 1 /* ANSI */ -#elif defined(__LYNX__) -/* The clock-rate depends on the video scan-rate; -** so, read it at run-time. -*/ -extern clock_t _clk_tck (void); -# define CLK_TCK _clk_tck() -# define CLOCKS_PER_SEC _clk_tck() +# define CLOCKS_PER_SEC 60 +#elif defined(__GAMATE__) +# define CLOCKS_PER_SEC 135 /* FIXME */ +#elif defined(__GEOS__) +# define CLOCKS_PER_SEC 1 +#elif defined(__ATARI__) || defined (__LYNX__) +/* Read the clock rate at runtime */ +clock_t _clocks_per_sec (void); +# define CLOCKS_PER_SEC _clocks_per_sec() #endif +#define CLK_TCK CLOCKS_PER_SEC #define CLOCK_REALTIME 0 @@ -149,6 +134,3 @@ int __fastcall__ clock_settime (clockid_t clock_id, const struct timespec *tp); /* End of time.h */ #endif - - - diff --git a/include/vic20.h b/include/vic20.h index 44512b3fd..d60c7e270 100644 --- a/include/vic20.h +++ b/include/vic20.h @@ -2,12 +2,12 @@ /* */ /* vic20.h */ /* */ -/* vic20 system specific definitions */ +/* VIC-20 system-specific definitions */ /* */ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -66,14 +66,39 @@ #define COLOR_GREEN 0x05 #define COLOR_BLUE 0x06 #define COLOR_YELLOW 0x07 +/* Only the background and multi-color characters can have these colors. */ #define COLOR_ORANGE 0x08 -#define COLOR_BROWN 0x09 -#define COLOR_LIGHTRED 0x0A -#define COLOR_GRAY1 0x0B -#define COLOR_GRAY2 0x0C +#define COLOR_LIGHTORANGE 0x09 +#define COLOR_PINK 0x0A +#define COLOR_LIGHTCYAN 0x0B +#define COLOR_LIGHTVIOLET 0x0C #define COLOR_LIGHTGREEN 0x0D #define COLOR_LIGHTBLUE 0x0E -#define COLOR_GRAY3 0x0F +#define COLOR_LIGHTYELLOW 0x0F + +/* TGI color defines */ +#define TGI_COLOR_BLACK COLOR_BLACK +#define TGI_COLOR_WHITE COLOR_WHITE +#define TGI_COLOR_RED COLOR_RED +#define TGI_COLOR_CYAN COLOR_CYAN +#define TGI_COLOR_VIOLET COLOR_VIOLET +#define TGI_COLOR_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +/* Only the background and multi-color graphics can have these colors. */ +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_LIGHTORANGE COLOR_LIGHTORANGE +#define TGI_COLOR_PINK COLOR_PINK +#define TGI_COLOR_LIGHTCYAN COLOR_LIGHTCYAN +#define TGI_COLOR_LIGHTVIOLET COLOR_LIGHTVIOLET +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_LIGHTYELLOW COLOR_LIGHTYELLOW + + + +/* tgi_ioctl() commands */ +#define TGI_IOCTL_VIC20_SET_PATTERN 0x01 /* Set 8-byte pattern for tgi_bar(). */ @@ -108,5 +133,9 @@ extern void vic20_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void vic20_rama_emd[]; extern void vic20_georam_emd[]; +extern void vic20_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ + + + /* End of vic20.h */ #endif diff --git a/include/zlib.h b/include/zlib.h index 3f6c2b27b..68439b290 100644 --- a/include/zlib.h +++ b/include/zlib.h @@ -114,7 +114,8 @@ int __fastcall__ uncompress (unsigned char* dest, unsigned* destLen, */ -unsigned long __fastcall__ adler32 (unsigned long adler, const char* buf, +unsigned long __fastcall__ adler32 (unsigned long adler, + const unsigned char* buf, unsigned len); /* @@ -140,7 +141,8 @@ unsigned long __fastcall__ adler32 (unsigned long adler, const char* buf, */ -unsigned long __fastcall__ crc32 (unsigned long crc, const char* buf, +unsigned long __fastcall__ crc32 (unsigned long crc, + const unsigned char* buf, unsigned len); /* Original zlib description: diff --git a/libsrc/Makefile b/libsrc/Makefile index d9ddb3ccd..60946b59f 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -34,6 +34,7 @@ TARGETS = apple2 \ sim6502 \ sim65c02 \ supervision \ + sym1 \ telestrat DRVTYPES = emd \ @@ -133,8 +134,7 @@ MKINC = $(GEOS) \ TARGETUTIL = apple2 \ apple2enh \ - atari \ - geos-apple + atari GEOSDIRS = common \ conio \ diff --git a/libsrc/apple2/cpeekc.s b/libsrc/apple2/cpeekc.s new file mode 100644 index 000000000..a547f7867 --- /dev/null +++ b/libsrc/apple2/cpeekc.s @@ -0,0 +1,28 @@ +; +; 2020-07-12, Oliver Schmidt +; +; char cpeekc (void); +; + + .export _cpeekc + + .include "apple2.inc" + +_cpeekc: + ldy CH + .ifdef __APPLE2ENH__ + bit RD80VID ; In 80 column mode? + bpl peek ; No, just go ahead + tya + lsr ; Div by 2 + tay + bcs peek ; Odd cols are in main memory + bit HISCR ; Assume SET80COL + .endif +peek: lda (BASL),Y ; Get character + .ifdef __APPLE2ENH__ + bit LOWSCR ; Doesn't hurt in 40 column mode + .endif + eor #$80 ; Invert high bit + ldx #$00 + rts diff --git a/libsrc/apple2/read.s b/libsrc/apple2/read.s index 14c80b7e2..99c189cbd 100644 --- a/libsrc/apple2/read.s +++ b/libsrc/apple2/read.s @@ -19,11 +19,14 @@ .segment "ONCE" initprompt: - ; Set prompt <> ']' to let DOS 3.3 know that we're - ; not in Applesoft immediate mode and thus keep it - ; from scanning our device I/O for DOS commands. + ; Set prompt <> ']' and currently executed Applesoft + ; line number hibyte <> $FF to let DOS 3.3 (at $A65E) + ; know that we're not in Applesoft immediate mode and + ; thus keep it from scanning our device I/O for DOS + ; commands. lda #$80 ; Same value used at $D52C sta PROMPT + sta CURLIN+1 ; Any value <> $FF will do rts .code diff --git a/libsrc/apple2/targetutil/Makefile.inc b/libsrc/apple2/targetutil/Makefile.inc index d9d727b0a..b53139a3d 100644 --- a/libsrc/apple2/targetutil/Makefile.inc +++ b/libsrc/apple2/targetutil/Makefile.inc @@ -1,9 +1,17 @@ -DEPS += ../libwrk/$(TARGET)/loader.d +DEPS += ../libwrk/$(TARGET)/convert.d \ + ../libwrk/$(TARGET)/loader.d + +../libwrk/$(TARGET)/convert.o: $(SRCDIR)/targetutil/convert.c | ../libwrk/$(TARGET) + $(COMPILE_recipe) ../libwrk/$(TARGET)/loader.o: $(SRCDIR)/targetutil/loader.s | ../libwrk/$(TARGET) $(ASSEMBLE_recipe) +../target/$(TARGET)/util/convert.system: ../libwrk/$(TARGET)/convert.o ../lib/$(TARGET).lib | ../target/$(TARGET)/util + $(LD65) -o $@ -C $(TARGET)-system.cfg $^ + ../target/$(TARGET)/util/loader.system: ../libwrk/$(TARGET)/loader.o $(SRCDIR)/targetutil/loader.cfg | ../target/$(TARGET)/util $(LD65) -o $@ -C $(filter %.cfg,$^) $(filter-out %.cfg,$^) -$(TARGET): ../target/$(TARGET)/util/loader.system +$(TARGET): ../target/$(TARGET)/util/convert.system \ + ../target/$(TARGET)/util/loader.system diff --git a/libsrc/geos-apple/targetutil/convert.c b/libsrc/apple2/targetutil/convert.c similarity index 100% rename from libsrc/geos-apple/targetutil/convert.c rename to libsrc/apple2/targetutil/convert.c diff --git a/libsrc/atari/clock.s b/libsrc/atari/clock.s index e04416c18..853870520 100644 --- a/libsrc/atari/clock.s +++ b/libsrc/atari/clock.s @@ -3,7 +3,7 @@ ; originally by Ullrich von Bassewitz and Sidney Cadot ; ; clock_t clock (void); -; unsigned _clocks_per_sec (void); +; clock_t _clocks_per_sec (void); ; .export _clock, __clocks_per_sec @@ -30,8 +30,10 @@ .proc __clocks_per_sec - ldx #$00 ; Clear high byte of return value - lda PAL ; use hw register, PALNTS is only supported on XL/XE ROM + ldx #$00 ; Clear byte 1 of return value + stx sreg ; Clear byte 2 of return value + stx sreg+1 ; Clear byte 3 of return value + lda PAL ; Use hw register, PALNTS is only supported on XL/XE ROM and #$0e bne @NTSC lda #50 diff --git a/libsrc/atari/crt0.s b/libsrc/atari/crt0.s index 97b7d7e95..381aa699f 100644 --- a/libsrc/atari/crt0.s +++ b/libsrc/atari/crt0.s @@ -10,6 +10,7 @@ .export __STARTUP__ : absolute = 1 ; Mark as startup .export _exit, start, excexit, SP_save + .export __LMARGN_save ; original LMARGN setting .import initlib, donelib .import callmain, zerobss @@ -82,14 +83,10 @@ start: .endif -; Call the module constructors. - - jsr initlib - ; Set the left margin to 0. lda LMARGN - sta LMARGN_save + sta __LMARGN_save ldy #0 sty LMARGN @@ -104,6 +101,10 @@ start: dey ; Set Y to $FF sty CH ; remove keypress which might be in the input buffer +; Call the module constructors. + + jsr initlib + ; Push the command-line arguments; and, call main(). jsr callmain @@ -119,7 +120,7 @@ excexit:jsr donelib ; Run module destructors; 'excexit' is called fr ; Restore the left margin. - lda LMARGN_save + lda __LMARGN_save sta LMARGN ; Restore the kb mode. @@ -196,7 +197,7 @@ excexit:jsr donelib ; Run module destructors; 'excexit' is called fr SP_save: .res 1 SHFLOK_save: .res 1 -LMARGN_save: .res 1 +__LMARGN_save: .res 1 .ifndef __ATARIXL__ APPMHI_save: .res 2 .endif diff --git a/libsrc/atari/doesclrscr.s b/libsrc/atari/doesclrscr.s index 2e19e4b98..f937f9ac5 100644 --- a/libsrc/atari/doesclrscr.s +++ b/libsrc/atari/doesclrscr.s @@ -1,7 +1,7 @@ ; ; Christian Groessler, June-2016 ; -; unsigned char doesclrscr(void); +; unsigned char doesclrscrafterexit (void); ; ; returns 0/1 if after program termination the screen isn't/is cleared ; diff --git a/libsrc/atari/shadow_ram_timerirq1.s b/libsrc/atari/shadow_ram_timerirq1.s index f8a3e9b4d..e1b7d831b 100644 --- a/libsrc/atari/shadow_ram_timerirq1.s +++ b/libsrc/atari/shadow_ram_timerirq1.s @@ -11,6 +11,7 @@ SHRAM_HANDLERS = 1 .include "atari.inc" .include "romswitch.inc" + .import __CHARGEN_START__ .export set_VTIMR1_handler diff --git a/libsrc/atari/shadow_ram_timerirq2.s b/libsrc/atari/shadow_ram_timerirq2.s index b2cdaecd2..d6d581937 100644 --- a/libsrc/atari/shadow_ram_timerirq2.s +++ b/libsrc/atari/shadow_ram_timerirq2.s @@ -11,6 +11,7 @@ SHRAM_HANDLERS = 1 .include "atari.inc" .include "romswitch.inc" + .import __CHARGEN_START__ .export set_VTIMR2_handler diff --git a/libsrc/atari/waitvsync.s b/libsrc/atari/waitvsync.s new file mode 100644 index 000000000..a19b2375d --- /dev/null +++ b/libsrc/atari/waitvsync.s @@ -0,0 +1,15 @@ +; +; Written by Christian Groessler <chris@groessler.org> +; +; void waitvsync (void); +; + + .include "atari.inc" + .export _waitvsync + +.proc _waitvsync + lda RTCLOK+2 +@lp: cmp RTCLOK+2 + beq @lp + rts +.endproc diff --git a/libsrc/atari5200/clock.s b/libsrc/atari5200/clock.s index f2ef85b0b..fff766093 100644 --- a/libsrc/atari5200/clock.s +++ b/libsrc/atari5200/clock.s @@ -2,7 +2,6 @@ ; from Atari computer version by Christian Groessler, 2014 ; ; clock_t clock (void); -; unsigned _clocks_per_sec (void); ; .export _clock diff --git a/libsrc/atari5200/crt0.s b/libsrc/atari5200/crt0.s index ee3d0de4f..8f6e02273 100644 --- a/libsrc/atari5200/crt0.s +++ b/libsrc/atari5200/crt0.s @@ -13,7 +13,6 @@ .import zerobss, copydata .include "zeropage.inc" - .include "atari5200.inc" start: @@ -45,4 +44,4 @@ _exit: jsr donelib ; Run module destructors ; A 5200 program isn't supposed to exit. -halt: jmp halt +halt: jmp halt diff --git a/libsrc/atari5200/waitvsync.s b/libsrc/atari5200/waitvsync.s new file mode 100644 index 000000000..9a67c6429 --- /dev/null +++ b/libsrc/atari5200/waitvsync.s @@ -0,0 +1,15 @@ +; +; Written by Christian Groessler <chris@groessler.org> +; +; void waitvsync (void); +; + + .include "atari5200.inc" + .export _waitvsync + +.proc _waitvsync + lda RTCLOK+1 +@lp: cmp RTCLOK+1 + beq @lp + rts +.endproc diff --git a/libsrc/atari5200/y2k.inc b/libsrc/atari5200/y2k.inc index a44d027a1..f8531451c 100644 --- a/libsrc/atari5200/y2k.inc +++ b/libsrc/atari5200/y2k.inc @@ -32,10 +32,8 @@ Y2K3 STA $0732,X LDA #$60 ; Store RTS opcode @ end STA $0750 JSR $0600 ; Show title screen - LDY #$00 ; Clear RAM from $0600-$3FFF + LDY #<$0600 ; Clear RAM from $0600-$3FFF STY $80 - LDA #$06 + LDA #>$0600 STA $81 - JSR CLRRAM - RTS - + JMP CLRRAM diff --git a/libsrc/atmos/cputc.s b/libsrc/atmos/cputc.s index f1ce5f2b7..db3fa373d 100644 --- a/libsrc/atmos/cputc.s +++ b/libsrc/atmos/cputc.s @@ -7,7 +7,7 @@ ; .export _cputcxy, _cputc - .export setscrptr, putchar + .export setscrptr, cputdirect, putchar .constructor initcputc .import rvs .import popax @@ -32,13 +32,13 @@ _cputc: cmp #$0D ; CR? rts L1: cmp #$0A ; LF? - bne output + bne cputdirect inc CURS_Y ; Newline rts ; Output the character, then advance the cursor position -output: +cputdirect: jsr putchar advance: diff --git a/libsrc/atmos/read.s b/libsrc/atmos/read.s index 83aa8024e..3f22d8d0e 100644 --- a/libsrc/atmos/read.s +++ b/libsrc/atmos/read.s @@ -20,18 +20,19 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard -L1: inc ptr2 +L1: dec ptr2 bnz L2 - inc ptr2+1 + dec ptr2+1 bze L9 ; no more room in buf ; If there are no more characters in BASIC's input buffer, then get a line from diff --git a/libsrc/atmos/write.s b/libsrc/atmos/write.s index 7865b5698..4a68994ec 100644 --- a/libsrc/atmos/write.s +++ b/libsrc/atmos/write.s @@ -17,17 +17,17 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard -L1: inc ptr2 +L1: dec ptr2 bne L2 - inc ptr2+1 + dec ptr2+1 beq L9 L2: ldy #0 lda (ptr1),y diff --git a/libsrc/c128/cputc.s b/libsrc/c128/cputc.s index 667260843..5c0760e75 100644 --- a/libsrc/c128/cputc.s +++ b/libsrc/c128/cputc.s @@ -62,10 +62,9 @@ cputdirect: ; Handle character if high bit set L5: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L6 lda #$5E ; Load screen code for PI - bne cputdirect L6: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/c128/emd/c128-efnram.s b/libsrc/c128/emd/c128-efnram.s old mode 100755 new mode 100644 diff --git a/libsrc/c128/emd/c128-ifnram.s b/libsrc/c128/emd/c128-ifnram.s old mode 100755 new mode 100644 diff --git a/libsrc/c128/kernal.s b/libsrc/c128/kernal.s index f74ab3b1a..47e80c559 100644 --- a/libsrc/c128/kernal.s +++ b/libsrc/c128/kernal.s @@ -1,11 +1,20 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C128 kernal functions +; C128 Kernal functions ; .include "cbm_kernal.inc" + .export KBDREAD + .export CLRSCR + .export PRINT + .export NEWLINE + .export CURS_SET + .export CURS_ON + .export CURS_OFF + .export NMIEXIT + .export C64MODE .export SWAPPER .export SETBNK @@ -37,7 +46,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export SETTIM diff --git a/libsrc/c128/mcbdefault.s b/libsrc/c128/mcbdefault.s index eb521eb3a..a3ca3d0d1 100644 --- a/libsrc/c128/mcbdefault.s +++ b/libsrc/c128/mcbdefault.s @@ -19,12 +19,12 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on this value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MEM = $0E00 ; Memory location -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MEM = $0E00 ; Memory location +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. diff --git a/libsrc/c128/mou/c128-1351.s b/libsrc/c128/mou/c128-1351.s index d06e942c4..79ccbe0de 100644 --- a/libsrc/c128/mou/c128-1351.s +++ b/libsrc/c128/mou/c128-1351.s @@ -4,7 +4,7 @@ ; ; 2009-09-26, Ullrich von Bassewitz ; 2014-04-26, Christian Groessler -; 2014-04-30, Greg King +; 2020-07-14, Greg King ; .include "zeropage.inc" @@ -377,7 +377,7 @@ IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now IRQ: jsr CPREP lda KEY_COUNT sta old_key_count - lda #$7F + lda #$FF sta CIA1_PRA lda CIA1_PRB ; Read joystick #0 and #$1F diff --git a/libsrc/c128/mou/c128-inkwell.s b/libsrc/c128/mou/c128-inkwell.s index 45d42f2db..b8e71bbb1 100644 --- a/libsrc/c128/mou/c128-inkwell.s +++ b/libsrc/c128/mou/c128-inkwell.s @@ -2,7 +2,7 @@ ; Driver for the Inkwell Systems 170-C and 184-C lightpens. ; ; 2014-04-26, Christian Groessler -; 2014-09-10, Greg King +; 2020-07-14, Greg King ; .include "zeropage.inc" @@ -414,8 +414,8 @@ IRQ: jsr CPREP ldy #%00000000 ; Set ports A and B to input sty CIA1_DDRB sty CIA1_DDRA ; Keyboard won't look like buttons - ;lda #%01111111 ; (Keyboard scan leaves this in port A) - ;sta CIA1_PRA + lda #%11111111 + sta CIA1_PRA lda CIA1_PRB ; Read Control Port 1 dec CIA1_DDRA ; Set port A back to output eor #%11111111 ; Bit goes up when button goes down diff --git a/libsrc/c128/mou/c128-joy.s b/libsrc/c128/mou/c128-joy.s index 7718b89b7..065674dc0 100644 --- a/libsrc/c128/mou/c128-joy.s +++ b/libsrc/c128/mou/c128-joy.s @@ -3,7 +3,7 @@ ; ; 2009-09-26, Ullrich von Bassewitz ; 2014-04-26, Christian Groessler -; 2014-05-01, Greg King +; 2020-07-14, Greg King ; .include "zeropage.inc" @@ -379,7 +379,7 @@ IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls for now IRQ: jsr CPREP lda KEY_COUNT sta old_key_count - lda #$7F + lda #$FF sta CIA1_PRA lda CIA1_PRB ; Read joystick #0 and #$1F diff --git a/libsrc/c128/mou/c128-pot.s b/libsrc/c128/mou/c128-pot.s index f61f88d5d..e582d64fb 100644 --- a/libsrc/c128/mou/c128-pot.s +++ b/libsrc/c128/mou/c128-pot.s @@ -4,7 +4,7 @@ ; 2006-08-20, Stefan Haubenthal ; 2009-09-26, Ullrich von Bassewitz ; 2014-04-26, Christian Groessler -; 2014-05-05, Greg King +; 2020-07-14, Greg King ; .include "zeropage.inc" @@ -385,7 +385,7 @@ IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls for now IRQ: jsr CPREP lda KEY_COUNT sta old_key_count - lda #$7F + lda #$FF sta CIA1_PRA lda CIA1_PRB ; Read port #1 eor #%11111111 ; Make all bits active high diff --git a/libsrc/c128/mou/callback.inc b/libsrc/c128/mou/callback.inc index 9f1d749a7..274c145fa 100644 --- a/libsrc/c128/mou/callback.inc +++ b/libsrc/c128/mou/callback.inc @@ -1,11 +1,12 @@ ; -; Callback routine called from the IRQ handler after the ROM IRQ handler -; had been run. +; Callback routine, called from the IRQ handler after the ROM IRQ handler +; has been run. ; -; Christian Groessler, 24.04.2014 +; 2014-04-24, Christian Groessler +; 2020-07-14, Greg King ; ; Check if there was button/joystick activity before and/or after the ROM handler. -; If there was activity, discard the key presses since they are most +; If there was activity, discard the key presses because they are most ; probably "phantom" key presses. callback: @@ -16,7 +17,7 @@ callback: lda OLD_BUTTONS ; keypress before? bne @discard_key ; yes, discard key - lda #$7F + lda #$FF sta CIA1_PRA lda CIA1_PRB ; Read joystick #0 and #$1F diff --git a/libsrc/c16/crt0.s b/libsrc/c16/crt0.s index bee81a113..1df1e5c62 100644 --- a/libsrc/c16/crt0.s +++ b/libsrc/c16/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "plus4.inc" ; ------------------------------------------------------------------------ ; Startup code diff --git a/libsrc/c16/kernal.s b/libsrc/c16/kernal.s index f814b2c1f..5f70123a2 100644 --- a/libsrc/c16/kernal.s +++ b/libsrc/c16/kernal.s @@ -1,11 +1,14 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C16 kernal functions +; C16 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export CINT .export IOINIT .export RAMTAS @@ -32,7 +35,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export SETTIM diff --git a/libsrc/c64/acc_c65_speed.s b/libsrc/c64/acc_c65_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_chameleon_speed.s b/libsrc/c64/acc_chameleon_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_c65.s b/libsrc/c64/acc_detect_c65.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_chameleon.s b/libsrc/c64/acc_detect_chameleon.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_turbomaster.s b/libsrc/c64/acc_detect_turbomaster.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_turbomaster_speed.s b/libsrc/c64/acc_turbomaster_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/cputc.s b/libsrc/c64/cputc.s index d6b49607a..b893f2102 100644 --- a/libsrc/c64/cputc.s +++ b/libsrc/c64/cputc.s @@ -74,10 +74,9 @@ L5: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect diff --git a/libsrc/c64/crt0.s b/libsrc/c64/crt0.s index 7bd294ca7..4e5c7c9d4 100644 --- a/libsrc/c64/crt0.s +++ b/libsrc/c64/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "c64.inc" ; ------------------------------------------------------------------------ diff --git a/libsrc/c64/joy/c64-stdjoy.s b/libsrc/c64/joy/c64-stdjoy.s index c983d81bb..d11093fba 100644 --- a/libsrc/c64/joy/c64-stdjoy.s +++ b/libsrc/c64/joy/c64-stdjoy.s @@ -93,9 +93,7 @@ joy1: lda #$7F sta CIA1_PRA lda CIA1_PRB cli - and #$1F - eor #$1F - rts + jmp end ; Read joystick 2 @@ -107,8 +105,6 @@ joy2: ldx #0 lda CIA1_PRA sty CIA1_DDRA cli - and #$1F +end: and #$1F eor #$1F rts - - diff --git a/libsrc/c64/kernal.s b/libsrc/c64/kernal.s index acbf22370..cf5511ce8 100644 --- a/libsrc/c64/kernal.s +++ b/libsrc/c64/kernal.s @@ -1,11 +1,16 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C64 kernal functions +; C64 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export UPDCRAMPTR + .export NMIEXIT + .export CINT .export IOINIT .export RAMTAS @@ -33,7 +38,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export SETTIM diff --git a/libsrc/c64/mcbdefault.s b/libsrc/c64/mcbdefault.s index dd2f7cee7..79a8d93e5 100644 --- a/libsrc/c64/mcbdefault.s +++ b/libsrc/c64/mcbdefault.s @@ -21,11 +21,11 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on this value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. diff --git a/libsrc/c64/soft80_cgetc.s b/libsrc/c64/soft80_cgetc.s index 05e9e5b47..28e6b0042 100644 --- a/libsrc/c64/soft80_cgetc.s +++ b/libsrc/c64/soft80_cgetc.s @@ -46,39 +46,33 @@ invertcursor: lda #$34 sta $01 - ldy #$00 - jsr setcolor - - ldx soft80_internal_cursorxlsb -@lp1: - lda (SCREEN_PTR),y - eor nibble,x - sta (SCREEN_PTR),y - iny - cpy #8 - bne @lp1 - - pla - sta $01 ; enable I/O - cli - rts - ; do not use soft80_putcolor here to make sure the cursor is always ; shown using the current textcolor without disturbing the "color voodoo" ; in soft80_cputc -setcolor: - ;ldy #0 ; is 0 + ldy #0 bcs @set ; restore old value lda tmp1 - sta (CRAM_PTR),y ; vram - rts + bcc @lp0 @set: ; save old value lda (CRAM_PTR),y ; vram sta tmp1 lda soft80_internal_cellcolor +@lp0: sta (CRAM_PTR),y ; vram + ldx soft80_internal_cursorxlsb + ldy #7 +@lp1: + lda (SCREEN_PTR),y + eor nibble,x + sta (SCREEN_PTR),y + dey + bpl @lp1 + + pla + sta $01 ; enable I/O + cli rts .rodata diff --git a/libsrc/c64/soft80_conio.s b/libsrc/c64/soft80_conio.s index 48039d288..7036f384d 100644 --- a/libsrc/c64/soft80_conio.s +++ b/libsrc/c64/soft80_conio.s @@ -67,22 +67,20 @@ firstinit: inc soft80_first_init + ; soft80_lo_charset and soft80_hi_charset are page-aligned + ldy #0 lda #<soft80_charset ldx #>soft80_charset sta ptr1 stx ptr1+1 - lda #<soft80_lo_charset ldx #>soft80_lo_charset - sta ptr2 + sty ptr2 stx ptr2+1 - lda #<soft80_hi_charset ldx #>soft80_hi_charset - sta ptr3 + sty ptr3 stx ptr3+1 ldx #4 -@l2: - ldy #0 @l1: lda (ptr1),y sta (ptr2),y @@ -97,17 +95,17 @@ firstinit: inc ptr2+1 inc ptr3+1 dex - bne @l2 + bne @l1 ; copy the kplot tables to ram under I/O ;ldx #0 ; is 0 -@l3: +@l2: lda soft80_tables_data_start,x sta soft80_bitmapxlo,x lda soft80_tables_data_start + (soft80_tables_data_end - soft80_tables_data_start - $0100),x sta soft80_bitmapxlo + (soft80_tables_data_end - soft80_tables_data_start - $0100),x inx - bne @l3 + bne @l2 pla sta $01 diff --git a/libsrc/cbm/c_basin.s b/libsrc/cbm/c_basin.s index da925a1a4..c2211e9ce 100644 --- a/libsrc/cbm/c_basin.s +++ b/libsrc/cbm/c_basin.s @@ -2,14 +2,16 @@ ; Ullrich von Bassewitz, 03.06.1999 ; ; unsigned char cbm_k_basin (void); +; unsigned char cbm_k_chrin (void); ; .include "cbm.inc" - .export _cbm_k_basin + .export _cbm_k_basin, _cbm_k_chrin _cbm_k_basin: +_cbm_k_chrin: jsr BASIN ldx #0 ; Clear high byte rts diff --git a/libsrc/cbm/c_bsout.s b/libsrc/cbm/c_bsout.s index 104878453..cec46bf90 100644 --- a/libsrc/cbm/c_bsout.s +++ b/libsrc/cbm/c_bsout.s @@ -2,10 +2,12 @@ ; Ullrich von Bassewitz, 03.06.1999 ; ; void __fastcall__ cbm_k_bsout (unsigned char C); +; void __fastcall__ cbm_k_chrout (unsigned char C); ; .include "cbm.inc" - .export _cbm_k_bsout + .export _cbm_k_bsout, _cbm_k_chrout -_cbm_k_bsout = BSOUT +_cbm_k_bsout := BSOUT +_cbm_k_chrout := CHROUT diff --git a/libsrc/cbm/c_readst.s b/libsrc/cbm/c_readst.s index 85211dd2f..bcaacbbae 100644 --- a/libsrc/cbm/c_readst.s +++ b/libsrc/cbm/c_readst.s @@ -1,5 +1,6 @@ ; -; Ullrich von Bassewitz, 03.06.1999 +; 1999-06-03, Ullrich von Bassewitz +; 2021-01-12, Greg King ; ; unsigned char cbm_k_readst (void); ; @@ -10,6 +11,5 @@ _cbm_k_readst: - jsr READST - ldx #0 ; Clear high byte - rts + ldx #>$0000 + jmp READST diff --git a/libsrc/cbm/cbm_dir.c b/libsrc/cbm/cbm_dir.c index 8f31c502a..4b8927f98 100644 --- a/libsrc/cbm/cbm_dir.c +++ b/libsrc/cbm/cbm_dir.c @@ -6,6 +6,7 @@ /* 2009-10-10 -- Version 0.3 */ /* 2011-04-07 -- Version 0.4, groepaz */ /* 2011-04-14 -- Version 0.5, Greg King */ +/* 2021-02-15 -- Version 0.6, Greg King */ /* Tested with floppy-drive and IDE64 devices. */ /* Not tested with messed (buggy) directory listings. */ @@ -29,7 +30,7 @@ unsigned char cbm_opendir (unsigned char lfn, unsigned char device, ...) va_list ap; const char* name = "$"; - /* The name used in cbm_open may optionally be passed */ + /* The name used in cbm_open() optionally may be passed */ if (__argsize__ == 4) { va_start (ap, device); name = va_arg (ap, const char*); @@ -76,9 +77,10 @@ unsigned char __fastcall__ cbm_readdir (unsigned char lfn, register struct cbm_d byte = cbm_k_basin(); switch (byte) { - - /* "B" BLOCKS FREE. */ + /* "B" BLOCKS FREE/USED. */ + /* "M" MB FREE. */ case 'b': + case 'm': /* Read until end; careless callers might call us again. */ while (!cbm_k_readst()) { cbm_k_basin(); @@ -168,7 +170,6 @@ unsigned char __fastcall__ cbm_readdir (unsigned char lfn, register struct cbm_d } rv = 0; - goto ret_val; } } diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index b010966a3..29e0e1f19 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -45,11 +45,11 @@ _cbm_read: - eor #$FF - sta ptr1 - txa - eor #$FF - sta ptr1+1 ; Save -size-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; Save size with both bytes incremented separately. jsr popax sta ptr2 @@ -92,9 +92,9 @@ _cbm_read: bne @L3 inc ptr3+1 ; ++bytesread; -@L3: inc ptr1 +@L3: dec ptr1 bne @L1 - inc ptr1+1 + dec ptr1+1 bne @L1 @L4: jsr CLRCH diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index 2d932d04a..5ac07209c 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -39,11 +39,11 @@ _cbm_write: sta ptr3 stx ptr3+1 ; Save size - eor #$FF - sta ptr1 - txa - eor #$FF - sta ptr1+1 ; Save -size-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; Save size with both bytes incremented separately jsr popax sta ptr2 @@ -69,9 +69,9 @@ _cbm_write: @L2: jsr BSOUT ; cbm_k_bsout (A); -@L3: inc ptr1 ; --size; +@L3: dec ptr1 ; --size; bne @L1 - inc ptr1+1 + dec ptr1+1 bne @L1 jsr CLRCH diff --git a/libsrc/cbm/read.s b/libsrc/cbm/read.s index ee01596aa..fb0bb3171 100644 --- a/libsrc/cbm/read.s +++ b/libsrc/cbm/read.s @@ -106,9 +106,9 @@ ; Decrement the count -@L3: inc ptr2 +@L3: dec ptr2 bne @L0 - inc ptr2+1 + dec ptr2+1 bne @L0 beq done ; Branch always diff --git a/libsrc/cbm/readdir.c b/libsrc/cbm/readdir.c index 8d6968977..1512edc4e 100644 --- a/libsrc/cbm/readdir.c +++ b/libsrc/cbm/readdir.c @@ -1,5 +1,7 @@ /* -** Ullrich von Bassewitz, 2012-05-30. Based on code by Groepaz. +** Based on code by Groepaz. +** 2012-05-30, Ullrich von Bassewitz +** 2021-02-15, Greg King */ @@ -52,12 +54,14 @@ struct dirent* __fastcall__ readdir (register DIR* dir) /* Bump the directory offset and include the bytes for line-link and size */ dir->off += count + 4; - /* End of directory is reached if the buffer contains "blocks free". It is - ** sufficient here to check for the leading 'b'. buffer will contain at - ** least one byte if we come here. + /* End of directory is reached if the buffer contains "blocks free/used" or + ** "mb free.". It is sufficient here to check for the leading 'b' and 'm'. + ** buffer will contain at least one byte if we come here. */ - if (buffer[0] == 'b') { - goto exitpoint; + switch (buffer[0]) { + case 'b': + case 'm': + goto exitpoint; } /* Parse the buffer for the filename and file type */ @@ -67,7 +71,6 @@ struct dirent* __fastcall__ readdir (register DIR* dir) b = buffer; while (i < count) { switch (s) { - case 0: /* Searching for start of file name */ if (*b == '"') { @@ -127,6 +130,3 @@ struct dirent* __fastcall__ readdir (register DIR* dir) exitpoint: return 0; } - - - diff --git a/libsrc/cbm/rwcommon.s b/libsrc/cbm/rwcommon.s index a1f92be8c..d13c478b5 100644 --- a/libsrc/cbm/rwcommon.s +++ b/libsrc/cbm/rwcommon.s @@ -21,11 +21,11 @@ .proc rwcommon - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; Save count with each byte incremented separately jsr popptr1 ; Get buf to ptr1, Y=0 by call diff --git a/libsrc/cbm/write.s b/libsrc/cbm/write.s index 7a27f0044..ebc44a0ac 100644 --- a/libsrc/cbm/write.s +++ b/libsrc/cbm/write.s @@ -83,9 +83,9 @@ ; Decrement count -@L2: inc ptr2 +@L2: dec ptr2 bne @L0 - inc ptr2+1 + dec ptr2+1 bne @L0 ; Wrote all chars or disk full. Close the output channel diff --git a/libsrc/cbm510/cputc.s b/libsrc/cbm510/cputc.s index 73d45b422..7dd4cddb2 100644 --- a/libsrc/cbm510/cputc.s +++ b/libsrc/cbm510/cputc.s @@ -65,10 +65,9 @@ L3: sty CURS_X ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/cbm510/kernal.s b/libsrc/cbm510/kernal.s index 7908c40ec..705422709 100644 --- a/libsrc/cbm510/kernal.s +++ b/libsrc/cbm510/kernal.s @@ -28,7 +28,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export STOP diff --git a/libsrc/cbm510/mcbdefault.s b/libsrc/cbm510/mcbdefault.s index 700dcebb1..53eb804c0 100644 --- a/libsrc/cbm510/mcbdefault.s +++ b/libsrc/cbm510/mcbdefault.s @@ -21,12 +21,12 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on that value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MEM = $F400 ; Memory location -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MEM = $F400 ; Memory location +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. diff --git a/libsrc/cbm510/pencalib.c b/libsrc/cbm510/pencalib.c old mode 100755 new mode 100644 diff --git a/libsrc/cbm610/cputc.s b/libsrc/cbm610/cputc.s index 5888580ac..78e2a5581 100644 --- a/libsrc/cbm610/cputc.s +++ b/libsrc/cbm610/cputc.s @@ -73,10 +73,9 @@ L4: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/cbm610/kernal.s b/libsrc/cbm610/kernal.s index 921bf524e..30156e79c 100644 --- a/libsrc/cbm610/kernal.s +++ b/libsrc/cbm610/kernal.s @@ -28,7 +28,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export STOP diff --git a/libsrc/common/_afailed.c b/libsrc/common/_afailed.c index ad50a8750..ab8b94ca6 100644 --- a/libsrc/common/_afailed.c +++ b/libsrc/common/_afailed.c @@ -15,6 +15,6 @@ void __fastcall__ _afailed (char* file, unsigned line) { raise (SIGABRT); - fprintf (stderr, "ASSERTION FAILED IN %s(%u)\n", file, line); + fprintf (stderr, "ASSERTION FAILED IN %s:%u\n", file, line); exit (EXIT_ASSERT); } diff --git a/libsrc/common/_poserror.c b/libsrc/common/_poserror.c index 95dbfdc9d..2777fee98 100644 --- a/libsrc/common/_poserror.c +++ b/libsrc/common/_poserror.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index f5d1784ec..840d42127 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -3,7 +3,7 @@ ; ; Ullrich von Bassewitz, 2000-10-21 ; - + .include "zeropage.inc" .export __printf @@ -32,8 +32,8 @@ FCount = ptr2 .code ; ---------------------------------------------------------------------------- -; Get one character from the format string and increment the pointer. Will -; return zero in Y. +; Get one character from the format string, and increment the pointer. Will +; return zero in .Y. GetFormatChar: ldy #0 @@ -51,7 +51,7 @@ OutputPadChar: lda PadChar ; ---------------------------------------------------------------------------- -; Call the output function with one character in A +; Call the output function with one character in .A Output1: sta CharArg @@ -92,7 +92,7 @@ GetSignedArg: jmp axlong ; Convert to long ; ---------------------------------------------------------------------------- -; Get a long argument from the argument list. Returns 0 in Y. +; Get a long argument from the argument list. Returns 0 in .Y. GetLongArg: jsr GetIntArg ; Get high word @@ -102,7 +102,7 @@ GetLongArg: ; Run into GetIntArg fetching the low word ; ---------------------------------------------------------------------------- -; Get an integer argument from the argument list. Returns 0 in Y. +; Get an integer argument from the argument list. Returns 0 in .Y. GetIntArg: jsr DecArgList2 @@ -114,7 +114,7 @@ GetIntArg: rts ; ---------------------------------------------------------------------------- -; Read an integer from the format string. Will return zero in Y. +; Read an integer from the format string. Will return zero in .Y. ReadInt: ldy #0 @@ -247,10 +247,10 @@ Save: lda regbank,y sta RegSave,y dey bpl Save + pla ; Restore low byte of ap ; Get the parameters from the stack - pla ; Restore low byte of ap sta ArgList ; Argument list pointer stx ArgList+1 @@ -307,7 +307,7 @@ MainLoop: inc Format+1 ; Calculate, how many characters must be output. Beware: This number may -; be zero. A still contains the low byte of the pointer. +; be zero. .A still contains the low byte of the pointer. @L3: sub FSave sta FCount @@ -343,7 +343,7 @@ MainLoop: ; We're back from out(), or we didn't call it. Check for end of string. -@L4: jsr GetFormatChar ; Get one char, zero in Y +@L4: jsr GetFormatChar ; Get one char, zero in .Y tax ; End of format string reached? bne NotDone ; End not reached @@ -357,7 +357,7 @@ Rest: lda RegSave,x rts ; Still a valid format character. Check for '%' and a '%%' sequence. Output -; anything that is not a format specifier. On intro, Y is zero. +; anything that is not a format specifier. On intro, .Y is zero. NotDone: cmp #'%' @@ -371,7 +371,7 @@ NotDone: ; We have a real format specifier ; Format is: %[flags][width][.precision][mod]type -; Y is zero on entry. +; .Y is zero on entry. FormatSpec: @@ -383,7 +383,7 @@ FormatSpec: dex bpl @L1 -; Start with reading the flags if there are any. X is $FF which is used +; Start with reading the flags if there are any. .X is $FF which is used ; for "true" ReadFlags: @@ -410,7 +410,7 @@ ReadFlags: @L4: jsr IncFormatPtr jmp ReadFlags ; ...and start over -; Done with flags, read the pad char. Y is still zero if we come here. +; Done with flags, read the pad char. .Y is still zero if we come here. ReadPadding: ldx #' ' ; PadChar @@ -421,8 +421,8 @@ ReadPadding: lda (Format),y ; Read current for later @L1: stx PadChar -; Read the width. Even here, Y is still zero. A contains the current character -; from the format string +; Read the width. Even here, .Y is still zero. .A contains the current character +; from the format string. ReadWidth: cmp #'*' @@ -435,7 +435,7 @@ ReadWidth: @L2: sta Width stx Width+1 ; ...and remember in Width -; Read the precision. Even here, Y is still zero. +; Read the precision. Even here, .Y is still zero. sty Prec ; Assume Precision is zero sty Prec+1 @@ -456,7 +456,7 @@ ReadPrec: @L2: sta Prec stx Prec+1 -; Read the modifiers. Y is still zero. +; Read the modifiers. .Y is still zero. ReadMod: lda (Format),y @@ -479,9 +479,9 @@ ReadMod: ; Initialize the argument buffer pointers. We use a static buffer (ArgBuf) to ; assemble strings. A zero page index (BufIdx) is used to keep the current -; write position. A pointer to the buffer (Str) is used to point to the the -; argument in case we will not use the buffer but a user supplied string. -; Y is zero when we come here. +; write position. A pointer to the buffer (Str) is used to point to the +; argument in case we will not use the buffer but a user-supplied string. +; .Y is zero when we come here. DoFormat: sty BufIdx ; Clear BufIdx @@ -490,7 +490,7 @@ DoFormat: ldx #>Buf stx Str+1 -; Skip the current format character, then check it (current char in A) +; Skip the current format character, then check it (current char in .A) jsr IncFormatPtr @@ -777,8 +777,5 @@ ArgLen: .res 2 .data -; Stuff from OutData. Is used as a vector and must be aligned +; Stuff from OutData. Is used as a vector CallOutFunc: jmp $0000 - - - diff --git a/libsrc/common/_scanf.c b/libsrc/common/_scanf.c index c18f1e289..ac89e3cb3 100644 --- a/libsrc/common/_scanf.c +++ b/libsrc/common/_scanf.c @@ -183,7 +183,7 @@ static void PushBack (void) asm ("jsr pushax"); /* Copy D into the zero-page. */ - (const struct scanfdata*) __AX__ = D_; + __AX__ = (unsigned) D_; asm ("sta ptr1"); asm ("stx ptr1+1"); @@ -272,7 +272,7 @@ static void __fastcall__ Error (unsigned char /* Code */) /* Does a longjmp using the given code */ { asm ("pha"); - (char*) __AX__ = JumpBuf; + __AX__ = (unsigned) JumpBuf; asm ("jsr pushax"); asm ("pla"); asm ("ldx #>$0000"); @@ -389,7 +389,7 @@ static void AssignInt (void) if (NoAssign == false) { /* Get the next argument pointer */ - (void*) __AX__ = va_arg (ap, void*); + __AX__ = (unsigned) va_arg (ap, void*); /* Put the argument pointer into the zero-page. */ asm ("sta ptr1"); @@ -468,7 +468,7 @@ static char GetFormat (void) /* Pick up the next character from the format string. */ { /* return (F = *format++); */ - (const char*) __AX__ = format; + __AX__ = (unsigned) format; asm ("sta regsave"); asm ("stx regsave+1"); ++format; diff --git a/libsrc/common/getcwd.s b/libsrc/common/getcwd.s index 22b6ceded..4bfc0a5b6 100644 --- a/libsrc/common/getcwd.s +++ b/libsrc/common/getcwd.s @@ -17,22 +17,22 @@ .proc _getcwd -; Remember -size-1 because this simplifies the following loop +; Remember size with each byte incremented because this simplifies the following loop - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; Save size with each byte incremented separately jsr popptr1 ; Get buf to ptr1 ; Copy __cwd to the given buffer checking the length ; ldy #$00 is guaranteed by popptr1 -loop: inc ptr2 +loop: dec ptr2 bne @L1 - inc ptr2+1 + dec ptr2+1 beq overflow ; Copy one character, end the loop if the zero terminator is reached. We diff --git a/libsrc/common/memcmp.s b/libsrc/common/memcmp.s index 93a2c28dc..d651a3cfc 100644 --- a/libsrc/common/memcmp.s +++ b/libsrc/common/memcmp.s @@ -10,14 +10,14 @@ _memcmp: -; Calculate (-count-1) and store it into ptr3. This is some overhead here but -; saves time in the compare loop +; Calculate a special count, and store it into ptr3. That is some overhead here, +; but saves time in the compare loop - eor #$FF - sta ptr3 - txa - eor #$FF - sta ptr3+1 + inx + stx ptr3+1 + tax + inx + stx ptr3 ; Save count with each byte incremented separately ; Get the pointer parameters @@ -29,12 +29,12 @@ _memcmp: ; Loop initialization ;ldy #$00 ; Initialize pointer (Y=0 guaranteed by popptr1) - ldx ptr3 ; Load low counter byte into X + ldx ptr3 ; Load inner counter byte into .X ; Head of compare loop: Test for the end condition -Loop: inx ; Bump low byte of (-count-1) - beq BumpHiCnt ; Jump on overflow +Loop: dex + beq BumpHiCnt ; Jump on end of inner count ; Do the compare @@ -50,10 +50,10 @@ Comp: lda (ptr1),y inc ptr2+1 bne Loop ; Branch always (pointer wrap is illegal) -; Entry on low counter byte overflow +; Entry on inner loop end BumpHiCnt: - inc ptr3+1 ; Bump high byte of (-count-1) + dec ptr3+1 bne Comp ; Jump if not done jmp return0 ; Count is zero, areas are identical @@ -67,4 +67,3 @@ NotEqual: Greater: ldx #$01 ; Make result positive rts - diff --git a/libsrc/common/rand.s b/libsrc/common/rand.s index a272af80a..ec1df9860 100644 --- a/libsrc/common/rand.s +++ b/libsrc/common/rand.s @@ -15,14 +15,14 @@ ; Multiplier must be 1 (mod 4) ; Added value must be 1 (mod 2) ; This guarantees max. period (2**32) -; The lowest bits have poor entropy and -; exhibit easily detectable patterns, so -; only the upper bits 16-22 and 24-31 of the -; 4-byte state are returned. +; The quality of entropy in the bits of the seed are poorest in the lowest +; bits, and best in the highest bits. ; -; The best 8 bits, 24-31 are returned in the -; low byte A to provide the best entropy in the -; most commonly used part of the return value. +; The high 8 bits are used for the low byte A to provide the best entropy in +; the most commonly used part of the return value. +; +; Finally XOR with the lower 2 bytes is used on the output, which breaks up +; some minor deficient sequential patterns. (#951) ; ; Uses the following LCG values for ax + c (mod m) ; a = $01010101 @@ -42,10 +42,16 @@ ; The seed. When srand() is not called, the C standard says that that rand() ; should behave as if srand() was called with an argument of 1 before. -rand: .dword 1 +rand: .dword $B5B5B4B4 .code +_srand: sta rand+0 ; Store the seed + stx rand+1 + sta rand+2 ; argument << 16 is convenient fill for MSW + stx rand+3 + ; fall through to rand() to sufficiently "shuffle" first rand() result + _rand: clc lda rand+0 adc #$B3 @@ -54,18 +60,11 @@ _rand: clc sta rand+1 adc rand+2 sta rand+2 + eor rand+0 and #$7f ; Suppress sign bit (make it positive) tax lda rand+2 adc rand+3 sta rand+3 + eor rand+1 rts ; return bit (16-22,24-31) in (X,A) - -_srand: sta rand+0 ; Store the seed - stx rand+1 - lda #0 - sta rand+2 ; Set MSW to zero - sta rand+3 - rts - - diff --git a/libsrc/common/strlen.s b/libsrc/common/strlen.s index e89039179..8d5bc20fc 100644 --- a/libsrc/common/strlen.s +++ b/libsrc/common/strlen.s @@ -5,17 +5,27 @@ ; the usage of only ptr2 here! Keep in mind when appling changes ; and check the other implementations too! ; -; int strlen (const char* s); +; size_t __fastcall__ strlen (const char* s); ; .export _strlen .importzp ptr2 + .macpack cpu _strlen: sta ptr2 ; Save s stx ptr2+1 +.if (.cpu .bitand ::CPU_ISET_HUC6280) + clx + cly +.else ldx #0 ; YX used as counter +.if (.cpu .bitand ::CPU_ISET_65816) + txy +.else ldy #0 +.endif +.endif L1: lda (ptr2),y beq L9 diff --git a/libsrc/common/strncat.s b/libsrc/common/strncat.s index caa6804fc..060378442 100644 --- a/libsrc/common/strncat.s +++ b/libsrc/common/strncat.s @@ -5,17 +5,17 @@ ; char* strncat (char* dest, const char* src, size_t n); ; - .export _strncat - .import popax, popptr1 - .importzp ptr1, ptr2, ptr3, tmp1, tmp2 - .macpack cpu +.export _strncat +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3, tmp1, tmp2 +.macpack cpu _strncat: - eor #$FF ; one's complement to count upwards - sta tmp1 - txa - eor #$FF - sta tmp2 + inx + stx tmp2 + tax + inx + stx tmp1 ; save count with each byte incremented separately jsr popptr1 ; get src @@ -49,9 +49,9 @@ L2: sty ptr2 L3: ldy #0 ldx tmp1 ; low counter byte -L4: inx +L4: dex bne L5 - inc tmp2 + dec tmp2 beq L6 ; jump if done L5: lda (ptr1),y sta (ptr2),y diff --git a/libsrc/common/strncpy.s b/libsrc/common/strncpy.s index 56387f880..138413ecb 100644 --- a/libsrc/common/strncpy.s +++ b/libsrc/common/strncpy.s @@ -10,11 +10,11 @@ .proc _strncpy - eor #$FF - sta tmp1 - txa - eor #$FF - sta tmp2 ; Store -size - 1 + inx + stx tmp2 + tax + inx + stx tmp1 ; save count with each byte incremented separately jsr popptr1 ; get src jsr popax ; get dest @@ -24,11 +24,11 @@ ; Copy src -> dest up to size bytes - ldx tmp1 ; Load low byte of ones complement of size + ldx tmp1 ldy #$00 -L1: inx +L1: dex bne L2 - inc tmp2 + dec tmp2 beq L9 L2: lda (ptr1),y ; Copy one character @@ -42,8 +42,8 @@ L2: lda (ptr1),y ; Copy one character ; Fill the remaining bytes. -L3: inx ; Counter low byte - beq L6 ; Branch on overflow +L3: dex ; Counter low byte + beq L6 L4: sta (ptr2),y ; Clear one byte L5: iny ; Bump pointer bne L3 @@ -52,7 +52,7 @@ L5: iny ; Bump pointer ; Bump the counter high byte -L6: inc tmp2 +L6: dec tmp2 bne L4 ; Done, return dest diff --git a/libsrc/common/strnicmp.s b/libsrc/common/strnicmp.s index 43d6d0d50..477aa3d66 100644 --- a/libsrc/common/strnicmp.s +++ b/libsrc/common/strnicmp.s @@ -15,17 +15,11 @@ _strnicmp: _strncasecmp: -; Convert the given counter value in a/x from a downward counter into an -; upward counter, so we can increment the counter in the loop below instead -; of decrementing it. This adds some overhead now, but is cheaper than -; executing a more complex test in each iteration of the loop. We do also -; correct the value by one, so we can do the test on top of the loop. - - eor #$FF - sta ptr3 - txa - eor #$FF - sta ptr3+1 + inx + stx ptr3+1 + tax + inx + stx ptr3 ; save count with each byte incremented separately ; Get the remaining arguments @@ -40,8 +34,8 @@ _strncasecmp: ; Start of compare loop. Check the counter. -Loop: inc ptr3 - beq IncHi ; increment high byte +Loop: dec ptr3 ; decrement high byte + beq IncHi ; Compare a byte from the strings @@ -79,7 +73,7 @@ L2: ldx tmp1 ; Increment hi byte -IncHi: inc ptr3+1 +IncHi: dec ptr3+1 bne Comp ; jump if counter not zero ; Exit code if strings are equal. a/x not set diff --git a/libsrc/conio/vcprintf.s b/libsrc/conio/vcprintf.s index 3a9ddf9d7..084efe089 100644 --- a/libsrc/conio/vcprintf.s +++ b/libsrc/conio/vcprintf.s @@ -47,12 +47,12 @@ outdesc: ; Static outdesc structure out: jsr popax ; count sta ptr2 - eor #$FF - sta outdesc+6 - txa - sta ptr2+1 - eor #$FF - sta outdesc+7 + stx ptr2+1 + inx + stx outdesc+7 + tax + inx + stx outdesc+6 jsr popptr1 ; buf @@ -74,7 +74,7 @@ out: jsr popax ; count ; Loop outputting characters -@L1: inc outdesc+6 +@L1: dec outdesc+6 beq @L4 @L2: ldy tmp1 lda (ptr1),y @@ -85,7 +85,7 @@ out: jsr popax ; count jsr _cputc jmp @L1 -@L4: inc outdesc+7 +@L4: dec outdesc+7 bne @L2 rts diff --git a/libsrc/creativision/joy/creativision-stdjoy.s b/libsrc/creativision/joy/creativision-stdjoy.s index 5cf46c39f..73b0c249f 100644 --- a/libsrc/creativision/joy/creativision-stdjoy.s +++ b/libsrc/creativision/joy/creativision-stdjoy.s @@ -1,11 +1,11 @@ ; ; Standard joystick driver for the Creativision. ; -; Christian Groessler, 2017-03-08 +; 2017-03-08, Christian Groessler +; 2021-06-01, Greg King ; .include "zeropage.inc" - .include "joy-kernel.inc" .include "joy-error.inc" .include "creativision.inc" @@ -13,10 +13,12 @@ .macpack module +buttons := tmp2 + ; ------------------------------------------------------------------------ ; Header. Includes jump table - module_header _creativisionstd_joy + module_header _creativisionstd_joy ; Driver signature @@ -39,16 +41,14 @@ JOY_COUNT = 2 ; Number of joysticks we support -; Symbolic names for joystick masks (similar names like the defines in joystick.h, but not related to them) +; Symbolic names for joystick masks (similar names to the macros in joystick.h, +; with the same values as the masks in creativision.h) JOY_UP = $10 JOY_DOWN = $04 JOY_LEFT = $20 JOY_RIGHT = $08 -; ------------------------------------------------------------------------ -; Code - .code ; ------------------------------------------------------------------------ @@ -59,7 +59,7 @@ JOY_RIGHT = $08 ; INSTALL: lda #JOY_ERR_OK - ldx #0 + ldx #>$0000 ; rts ; Fall through ; ------------------------------------------------------------------------ @@ -82,14 +82,14 @@ COUNT: lda #<JOY_COUNT ; READ: Read a particular joystick passed in A. ; -READJOY: and #1 ; fix joystick number - bne READJOY_1 ; read right joystick +READJOY: lsr a ; Get joystick number + bcs READJOY_1 ; Read right joystick ; Read left joystick ldx ZP_JOY0_DIR lda ZP_JOY0_BUTTONS - jmp convert ; convert joystick state to cc65 values + bcc convert ; Convert joystick state to cc65 values ; Read right joystick @@ -97,11 +97,11 @@ READJOY_1: ldx ZP_JOY1_DIR lda ZP_JOY1_BUTTONS lsr a lsr a - ;jmp convert ; convert joystick state to cc65 values - ; fall thru... + ;jmp convert ; Convert joystick state to cc65 values + ; Fall thru... ; ------------------------------------------------------------------------ -; convert: make runtime lib compatible values +; convert: make runtime lib-compatible values ; inputs: ; A - buttons ; X - direction @@ -111,24 +111,24 @@ convert: ; ------ ; buttons: -; Port values are for the left hand joystick (right hand joystick +; Port values are for the left-hand joystick (right-hand joystick ; values were shifted to the right to be identical). ; Why are there two bits indicating a pressed trigger? ; According to the "Second book of programs for the Dick Smith Wizard" -; (pg. 88ff), the left hand button gives the value of -; %00010001 and the right hand button gives %00100010 +; (pg. 88ff), the left-hand button gives the value of +; %00010001 and the right-hand button gives %00100010 ; Why two bits? Can there be cases that just one of those bits is set? -; Until these questions have been answered, we only use the lower two -; bits and ignore the upper ones... +; Until those questions have been answered, we only use the lower two +; bits, and ignore the upper ones. - and #%00000011 ; button status came in in A, strip high bits - sta retval ; initialize 'retval' with button status + and #%00000011 ; Button status came in A, strip high bits + sta buttons ; ------ ; direction: -; CV has a 16-direction joystick +; CV has a 16-direction joystick. ; -; port values: (compass points) +; Port values: (compass points) ; N - $49 - %01001001 ; NNE - $48 - %01001000 ; NE - $47 - %01000111 @@ -147,55 +147,51 @@ convert: ; NNW - $4A - %01001010 ; center - $00 - %00000000 ; -; mapping to cc65 definitions (4-direction joystick with 8 possible directions thru combinations) +; Mapping to cc65 definitions (4-direction joystick with 8 possible directions thru combinations): ; N, E, S, W -> JOY_UP, JOY_RIGHT, JOY_DOWN, JOY_LEFT ; NE, SE, SW, NW -> (JOY_UP | JOY_RIGHT), (JOY_DOWN | JOY_RIGHT), (JOY_DOWN | JOY_LEFT), (JOY_UP | JOY_LEFT) ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW: -; toggle between straight and diagonal direction for every call, e.g. +; toggle between the straight and diagonal directions for each call, e.g., ; NNE: ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT -; etc... +; etc. - txa ; move direction status into A - beq done ; center position (no bits are set), nothing to do + txa ; Move direction status into A + beq done ; Center position (no bits are set), nothing to do - and #$0F ; get rid of the "$40" bit - bit bit0 ; is it a "three letter" direction (NNE, ENE, etc.)? - beq special ; yes (bit #0 is zero) + and #$0F ; Get rid of the "$40" bit + lsr a ; Is it "three-letter" direction (NNE, ENE, etc.)? + tax ; Create index into table + bcc special ; Yes (bit #0 was zero) - lsr a ; create index into table - tax lda dirtable,x -done: ora retval ; include "button" bits - ldx #0 +done: ora buttons ; Include button bits + ldx #>$0000 rts ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW -special: lsr a - tax - - lda toggler ; toggle the toggler +special: lda toggle ; Toggle the flag eor #$01 - sta toggler - bne spec_1 ; toggler is 1, use spectable_1 entry + sta toggle + bne spec_1 ; Flag is 1, use spectable_1 entry - lda spectable_0,x ; toggler is 0, use spectable_0 entry - bne done ; jump always + lda spectable_0,x + bne done ; Jump always spec_1: lda spectable_1,x - bne done ; jump always + bne done ; Jump always ; ------------------------------------------------------------------------ ; .rodata - ; a mapping table of "port values" to "cc65 values" - ; port value had been shifted one bit to the right (range 0..7) + ; A mapping table of "port values" to "cc65 values" + ; Port value had been shifted one bit to the right (range 0..7) dirtable: .byte JOY_DOWN ; S .byte JOY_DOWN | JOY_RIGHT ; SE .byte JOY_RIGHT ; E @@ -205,12 +201,12 @@ dirtable: .byte JOY_DOWN ; S .byte JOY_LEFT ; W .byte JOY_DOWN | JOY_LEFT ; SW - ; two "special" mapping tables for three-letter directions (NNE, etc.) + ; Two "special" mapping tables for three-letter directions (NNE, etc.) spectable_0: .byte JOY_DOWN ; SSW .byte JOY_DOWN ; SSE .byte JOY_RIGHT ; ESE .byte JOY_RIGHT ; ENE - .byte JOY_RIGHT ; NNE + .byte JOY_UP ; NNE .byte JOY_UP ; NNW .byte JOY_LEFT ; WNW .byte JOY_LEFT ; WSW @@ -224,14 +220,10 @@ spectable_1: .byte JOY_DOWN | JOY_LEFT ; SSW .byte JOY_UP | JOY_LEFT ; WNW .byte JOY_DOWN | JOY_LEFT ; WSW -; ------------------------------------------------------------------------ -; -bit0: .byte $01 - ; ------------------------------------------------------------------------ ; .bss -toggler: .res 1 -retval: .res 1 + +toggle: .res 1 .end diff --git a/libsrc/creativision/playsound.s b/libsrc/creativision/playsound.s new file mode 100644 index 000000000..55c7a3fed --- /dev/null +++ b/libsrc/creativision/playsound.s @@ -0,0 +1,40 @@ +; void __fastcall__ bios_playsound (void *a, unsigned char b); + + + .export _bios_playsound + + .import popax + + .include "creativision.inc" + + +songptr := $00 ; Points to current tune data +volptr := $04 ; Points to current volume table + + +;* Creativision Sound Player +;* Based on BIOS song player. +;* +;* Pass a pointer to a set of note triples, terminated with a tempo byte; +;* and pass the length of the triples and tempo (max 255). +;* +;* Note: tune data must be stored backwards. + +_bios_playsound: + php + pha ; Save tune length + sei + + lda #<$FCD5 ; BIOS decreasing-volume table + ldx #>$FCD5 + sta volptr + stx volptr+1 + + jsr popax ; Get tune array pointer + sta songptr + stx songptr+1 + + pla + tay + dey ; Point to tempo byte + jmp BIOS_PLAY_SONG ; Let BIOS do its thing diff --git a/libsrc/creativision/psg.s b/libsrc/creativision/psg.s index a32e87f02..ec878af31 100644 --- a/libsrc/creativision/psg.s +++ b/libsrc/creativision/psg.s @@ -1,63 +1,30 @@ -; void __fastcall__ psg_outb( unsigned char b ); -; void __fastcall__ psg_delayms( unsigned char c); -; void __fastcall__ bios_playsound( void *b, unsigned char c); -; void psg_silence( void ); +; void __fastcall__ psg_outb (unsigned char b); +; void __fastcall__ psg_delay (unsigned char b); +; void psg_silence (void); + + + .export _psg_outb, _psg_silence, _psg_delay - .export _psg_outb, _psg_silence, _psg_delay - .export _bios_playsound - .import popa .include "creativision.inc" -_psg_outb: - ;* Let BIOS output the value - jmp $FE77 +;* Let BIOS output the value. +_psg_outb := BIOS_POKE_PSG -_psg_silence: - jmp $FE54 +_psg_silence := BIOS_QUIET_PSG _psg_delay: - tay -l1: lda #200 -l2: sbc #1 - bne l2 +l1: lda #200 +l2: sbc #1 + bne l2 - lda #200 -l3: sbc #1 - bne l3 + lda #200 +l3: sbc #1 + bne l3 dey - bne l1 - + bne l1 rts - - -;* Creativision Sound Player -;* -;* Based on BIOS sound player. -;* Pass a pointer to a set of note triples, terminated with a tempo byte -;* and the len (max 255) - -_bios_playsound: - - pha ; Save Length Byte - sei - - lda #$D5 ; BIOS volume table low - sta $4 - lda #$FC ; BIOS volume table high - sta $5 - - jsr popa ; Get Sound table pointer low - sta $0 - jsr popa ; Get Sound table pointer high - sta $1 - - pla - tay ; Put length in Y - dey - php - jmp $FBED ; Let BIOS do it's thing diff --git a/libsrc/cx16/cputc.s b/libsrc/cx16/cputc.s index 034c3e389..4a7034e59 100644 --- a/libsrc/cx16/cputc.s +++ b/libsrc/cx16/cputc.s @@ -1,5 +1,5 @@ ; -; 2019-09-23, Greg King +; 2020-10-13, Greg King ; ; void __fastcall__ cputcxy (unsigned char x, unsigned char y, char c); ; void __fastcall__ cputc (char c); @@ -11,52 +11,47 @@ .import gotoxy, PLOT .include "cx16.inc" - .macpack generic -; First, move to a new position. +; Move to a cursor position, then print a character. _cputcxy: pha ; Save C jsr gotoxy ; Set cursor, drop x and y - pla ; Restore C + pla -; Print a character. +; Print a character -- also used as an internal function. -_cputc: cmp #$0D ; LF? +_cputc: cmp #$0D ; X16 '\n'? beq newline - cmp #$0A ; CR? - beq plotx0 + cmp #$0A ; X16 '\r'? + beq cr -; Printable char of some sort +; Printable char. of some sort. +; Convert it from PetSCII into a screen-code. - cmp #' ' - blt cputdirect ; Other control char +convert: tay - bmi L10 - cmp #$60 - blt L2 - and #<~%00100000 - bra cputdirect - -; Handle character if high bit set - -L10: and #<~%10000000 ; Remove high bit - ora #%01000000 - bra cputdirect - -L2: and #<~%01000000 + lsr a ; Divide by 256/8 + lsr a + lsr a + lsr a + lsr a + tax ; .X = %00000xxx + tya + eor pet_to_screen,x cputdirect: jsr putchar ; Write character to screen, return .Y -; Advance cursor position. +; Advance the cursor position. iny cpy LLEN ; Reached end of line? bne L3 - jsr newline ; Next line - ldy #$00 ; + CR + jsr newline ; Wrap around + +cr: ldy #$00 L3: sty CURS_X rts @@ -70,7 +65,6 @@ newline: ; Set the cursor's position, calculate RAM pointer. -plotx0: stz CURS_X plot: ldy CURS_X ldx CURS_Y clc @@ -96,3 +90,9 @@ putchar: lda CHARCOLOR sta VERA::DATA0 rts + + + .rodata +pet_to_screen: + .byte %10000000,%00000000,%01000000,%00100000 ; PetSCII -> screen-code + .byte %01000000,%11000000,%10000000,%10000000 diff --git a/libsrc/cx16/getdevice.s b/libsrc/cx16/getdevice.s index 9b554900f..5f2e74af4 100644 --- a/libsrc/cx16/getdevice.s +++ b/libsrc/cx16/getdevice.s @@ -51,7 +51,7 @@ next: inx jsr closecmdchannel ldx tmp2 -; As we had to reference ST above anyway, we can do so, as well, +; As we had to reference STATUS above anyway, we can do so, as well, ; here too (instead of calling READST). lda STATUS diff --git a/libsrc/cx16/tgi/cx320p1.s b/libsrc/cx16/tgi/cx320p1.s new file mode 100644 index 000000000..1ad5c2cbd --- /dev/null +++ b/libsrc/cx16/tgi/cx320p1.s @@ -0,0 +1,401 @@ +; +; Graphics driver for the 320 pixels across, 200 pixels down, 256 colors mode +; on the Commander X16 +; +; 2020-07-02, Greg King <gregdk@users.sf.net> +; + + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .include "cbm_kernal.inc" + .include "cx16.inc" + + .macpack generic + .macpack module + + +; Macro that copies a word into a pseudo-register + +.mac setReg reg, src + lda src + ldx src+1 + sta gREG::reg + stx gREG::reg+1 +.endmac + + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + +; First part of the header is a structure that has a signature, +; and defines the capabilities of the driver. + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word 320 ; X resolution + .word 200 ; Y resolution + .byte <$0100 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0100 ; Aspect ratio (based on VGA display) + .byte 0 ; TGI driver flags + +; Next, comes the jump table. Currently, all entries must be valid, +; and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + + +; ------------------------------------------------------------------------ +; Constant + +GRAPH320 = $80 + +; ------------------------------------------------------------------------ +; Data + +; Variables mapped to the zero-page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 := ptr1 +Y1 := ptr2 +X2 := ptr3 +Y2 := ptr4 + +; Absolute variables used in the code + +.bss + +; The colors are indicies into a TGI palette. The TGI palette is indicies into +; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels. +; The first 16 RGB elements mimic the Commodore 64's colors. + +defpalette: .res $0100 +palette: .res $0100 + +bcolor := palette + 0 ; Background color +color: .res 1 ; Stroke and fill index +mode: .res 1 ; Old text mode + +.data + +error: .byte TGI_ERR_OK ; Error code + + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO + +INSTALL: +; Create the default palette. + + ldx #$00 +: txa + sta defpalette,x + inx + bnz :- + + ; Fall through. + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL, but is probably empty most of the time. +; +; Must set an error code: NO + +UNINSTALL: + rts + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once; so, any code that is needed +; to initiate variables and so on must go here. Setting the palette is not +; needed because that is called by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active, so there is no need to protect against that. +; +; Must set an error code: YES + +INIT: stz error ; #TGI_ERR_OK + +; Save the current text mode. + + lda SCREEN_MODE + sta mode + +; Switch into (320 x 200 x 256) graphics mode. + + lda #GRAPH320 + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active, +; so there is no need to protect against that. +; +; Must set an error code: NO + +DONE: +; Work around a prerelease 37 Kernal bug. +; VERA (graphics) layer 0 isn't disabled by SCREEN_SET_MODE. + + stz VERA::CTRL + lda VERA::DISP::VIDEO + and #<~VERA::DISP::ENABLE::LAYER0 + sta VERA::DISP::VIDEO + + lda mode + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in .A, and clear it. + +GETERROR: + lda error + stz error + rts + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES + +CONTROL: + lda #TGI_ERR_INV_FUNC + sta error + rts + +; ------------------------------------------------------------------------ +; CLEAR: Clear the screen. +; +; Must set an error code: NO + +CLEAR := GRAPH_CLEAR + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETVIEWPAGE: + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETDRAWPAGE: + rts + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES + +SETPALETTE: + stz error ; #TGI_ERR_OK + ldy #$00 +: lda (ptr1),y + sta palette,y + iny + bnz :- + + lda color ; Get stroke and fill index + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) + +SETCOLOR: + tax + sta color + lda palette,x ; Set stroke and fill color + tax + ldy bcolor ; Get background color + jmp GRAPH_SET_COLORS + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in .XA. Even drivers that cannot +; set the palette should return the default palette here, so there's no +; way for this function to fail. +; +; Must set an error code: NO + +GETPALETTE: + lda #<palette + ldx #>palette + rts + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in .XA. All +; drivers should return something reasonable here, even drivers that don't +; support palettes, otherwise the caller has no way to determine the colors +; of the (not changable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) + +GETDEFPALETTE: + lda #<defpalette + ldx #>defpalette + rts + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area, so there is no need for clipping inside this function. +; +; Must set an error code: NO + +SETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + ldx color + lda palette,x + jmp FB_SET_PIXEL + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel, and return it in .XA. The +; co-ordinates passed to this function never are outside the visible screen +; area, so there is no need for clipping inside this function. + +GETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + jsr FB_GET_PIXEL + ldx #>$0000 + rts + +; ------------------------------------------------------------------------ +; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and +; X2/Y2 = ptr3/ptr4, using the current drawing color. +; +; Must set an error code: NO + +LINE: jsr Point + setReg r2, X2 + setReg r3, Y2 + jmp GRAPH_DRAW_LINE + +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4, using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO + +BAR: +; Set the starting corner. + + jsr Point + +; Set the width. + + lda X2 + sub X1 + sta gREG::r2 + lda X2+1 + sbc X1+1 + sta gREG::r2+1 + +; Set the height. + + lda Y2 + sub Y1 + sta gREG::r3 + lda Y2+1 + sbc Y1+1 + sta gREG::r3+1 + +; Set the corner radius. + + stz gREG::r4 + stz gREG::r4+1 + + sec ; Fill the rectangle + jmp GRAPH_DRAW_RECT + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y +; directions are passed in .X and .Y, the text direction is passed in .A. +; +; Must set an error code: NO + +TEXTSTYLE: + rts + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO + +OUTTEXT: + jsr Point + + ldy #$00 +@next: lda (ptr3),y + bze @end + phy + jsr GRAPH_PUT_CHAR + ply + iny + bnz @next +@end: rts + +; ------------------------------------------------------------------------ +; Point: Set the arguments for the first point of a Kernal graphics function. + +Point: setReg r0, X1 + setReg r1, Y1 + rts diff --git a/libsrc/cx16/tgi_stat_stddrv.s b/libsrc/cx16/tgi_stat_stddrv.s index e501f4c2f..e3cec1c8c 100644 --- a/libsrc/cx16/tgi_stat_stddrv.s +++ b/libsrc/cx16/tgi_stat_stddrv.s @@ -1,10 +1,11 @@ ; ; Address of the static standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const void tgi_static_stddrv[]; ; - .import _cx16_320x8b_tgi - .export _tgi_static_stddrv := _cx16_320x8b_tgi + .import _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + + .export _tgi_static_stddrv := _cx320p1_tgi diff --git a/libsrc/cx16/tgi_stddrv.s b/libsrc/cx16/tgi_stddrv.s index 0e46a6cb3..f05ab7131 100644 --- a/libsrc/cx16/tgi_stddrv.s +++ b/libsrc/cx16/tgi_stddrv.s @@ -1,12 +1,14 @@ ; ; Name of the standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const char tgi_stddrv[]; ; .export _tgi_stddrv +; A FAT32 8+3 file-name (for SD cards) + .rodata -_tgi_stddrv: .asciiz "cx16-320x8b.tgi" +_tgi_stddrv: .asciiz "cx320p1.tgi" diff --git a/libsrc/em/em-kernel.s b/libsrc/em/em-kernel.s index c982dac88..d5be0ae23 100644 --- a/libsrc/em/em-kernel.s +++ b/libsrc/em/em-kernel.s @@ -36,7 +36,7 @@ emd_sig: .byte $65, $6d, $64, EMD_API_VERSION ; "emd", version ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ em_install (void* driver); +; unsigned char __fastcall__ em_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/gamate/conio.s b/libsrc/gamate/conio.s index 6ac439490..33437cc04 100644 --- a/libsrc/gamate/conio.s +++ b/libsrc/gamate/conio.s @@ -1,6 +1,7 @@ .include "gamate.inc" .include "extzp.inc" + .import fontdata .import colors .importzp ptr1, tmp1 @@ -24,8 +25,3 @@ initconio: sta BGCOLOR rts - .segment "RODATA" - - .export fontdata -fontdata: - .include "vga.inc" diff --git a/libsrc/gamate/vga.inc b/libsrc/gamate/vga.s old mode 100644 new mode 100755 similarity index 99% rename from libsrc/gamate/vga.inc rename to libsrc/gamate/vga.s index da20dd4aa..310560015 --- a/libsrc/gamate/vga.inc +++ b/libsrc/gamate/vga.s @@ -1,6 +1,11 @@ - ; VGA charset for the Gamate conio implementation + .export fontdata + + .rodata + +fontdata: + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; 1 diff --git a/libsrc/geos-apple/targetutil/Makefile.inc b/libsrc/geos-apple/targetutil/Makefile.inc deleted file mode 100644 index 3d366f913..000000000 --- a/libsrc/geos-apple/targetutil/Makefile.inc +++ /dev/null @@ -1,14 +0,0 @@ -DEPS += ../libwrk/$(TARGET)/convert.d - -../libwrk/$(TARGET)/convert.o: TARGET = apple2enh - -../libwrk/$(TARGET)/convert.o: $(SRCDIR)/targetutil/convert.c | ../libwrk/$(TARGET) - $(COMPILE_recipe) - -../lib/apple2enh.lib: - @$(MAKE) --no-print-directory apple2enh - -../target/$(TARGET)/util/convert.system: ../libwrk/$(TARGET)/convert.o ../lib/apple2enh.lib | ../target/$(TARGET)/util - $(LD65) -o $@ -C apple2enh-system.cfg $^ - -$(TARGET): ../target/$(TARGET)/util/convert.system diff --git a/libsrc/geos-common/disk/calcblksfree.s b/libsrc/geos-common/disk/calcblksfree.s index 520c8ca61..5d7b98aba 100644 --- a/libsrc/geos-common/disk/calcblksfree.s +++ b/libsrc/geos-common/disk/calcblksfree.s @@ -13,6 +13,10 @@ .include "geossym.inc" _CalcBlksFree: + lda #<curDirHead + ldx #>curDirHead + sta r5L + stx r5H jsr CalcBlksFree stx __oserror lda r4L diff --git a/libsrc/geos-common/drivers/fio_module.s b/libsrc/geos-common/drivers/fio_module.s index 0be5015a7..937ef292e 100644 --- a/libsrc/geos-common/drivers/fio_module.s +++ b/libsrc/geos-common/drivers/fio_module.s @@ -94,11 +94,12 @@ _read: ; popax - fd, must be == to the above one ; return -1+__oserror or number of bytes read - eor #$ff - sta ptr1 - txa - eor #$ff - sta ptr1+1 ; -(# of bytes to read)-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; save count with each byte incremented separately + jsr popax sta ptr2 stx ptr2+1 ; buffer ptr @@ -152,9 +153,9 @@ _read: beq @done ; yes, we're done jmp __mappederrno ; no, we're screwed -@L3: inc ptr1 ; decrement the count +@L3: dec ptr1 ; decrement the count bne @L0 - inc ptr1+1 + dec ptr1+1 bne @L0 @done: diff --git a/libsrc/geos-common/file/get1stdirentry.s b/libsrc/geos-common/file/get1stdirentry.s index 3b99554eb..f0ad59388 100644 --- a/libsrc/geos-common/file/get1stdirentry.s +++ b/libsrc/geos-common/file/get1stdirentry.s @@ -1,11 +1,11 @@ ; -; Maciej 'YTM/Alliance' Witkowiak +; 1999-10-26, Maciej 'YTM/Alliance' Witkowiak +; 2020-10-29, Greg King ; -; 26.10.99 ; struct filehandle* Get1stDirEntry (void); - .import __oserror + .import __oserror, return0 .export _Get1stDirEntry .include "diskdrv.inc" @@ -14,6 +14,10 @@ _Get1stDirEntry: jsr Get1stDirEntry stx __oserror + txa + bne L1 ; jump if disk error lda r5L ldx r5H rts + +L1: jmp return0 ; return NULL if not valid entry diff --git a/libsrc/geos-common/file/getnxtdirentry.s b/libsrc/geos-common/file/getnxtdirentry.s index 912f48ca4..e8ccbf3a2 100644 --- a/libsrc/geos-common/file/getnxtdirentry.s +++ b/libsrc/geos-common/file/getnxtdirentry.s @@ -1,11 +1,11 @@ ; -; Maciej 'YTM/Alliance' Witkowiak +; 1999-10-26, Maciej 'YTM/Alliance' Witkowiak +; 2020-10-29, Greg King ; -; 26.10.99 ; struct filehandle* GetNxtDirEntry (void); - .import __oserror + .import __oserror, return0 .export _GetNxtDirEntry .include "diskdrv.inc" @@ -14,6 +14,12 @@ _GetNxtDirEntry: jsr GetNxtDirEntry stx __oserror + txa + bne L1 ; jump if disk error + tya + bne L1 ; jump when no more entries lda r5L ldx r5H rts + +L1: jmp return0 ; return NULL if not valid entry diff --git a/libsrc/geos-common/graph/graphicsstring.s b/libsrc/geos-common/graph/graphicsstring.s index 86c74f1a6..282649284 100644 --- a/libsrc/geos-common/graph/graphicsstring.s +++ b/libsrc/geos-common/graph/graphicsstring.s @@ -3,7 +3,7 @@ ; ; 25.12.99 -; void GraphicsString (char *myString); +; void GraphicsString (const void *myString); .export _GraphicsString diff --git a/libsrc/joystick/joy-kernel.s b/libsrc/joystick/joy-kernel.s index c2d50c8d8..53d475c57 100644 --- a/libsrc/joystick/joy-kernel.s +++ b/libsrc/joystick/joy-kernel.s @@ -33,7 +33,7 @@ joy_sig: .byte $6A, $6F, $79, JOY_API_VERSION ; "joy", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ joy_install (void* driver); +; unsigned char __fastcall__ joy_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/lynx/clock.s b/libsrc/lynx/clock.s index dbccb32cb..e29799df6 100644 --- a/libsrc/lynx/clock.s +++ b/libsrc/lynx/clock.s @@ -2,18 +2,14 @@ ; 2003-04-13, Ullrich von Bassewitz ; 2012-02-06, Greg King ; -; #include <time.h> +; clock_t clock (void); +; clock_t _clocks_per_sec (void); ; -; typedef unsigned long int clock_t; -; clock_t _clk_tck(void); -; #define CLOCKS_PER_SEC _clk_tck() -; clock_t clock(void); -; -; clk_tck()'s test-values are based on the numbers in "set_tv.s". +; 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, __clk_tck, clock_count + .export _clock, __clocks_per_sec, clock_count .interruptor update_clock, 2 ; (low priority) .constructor init_clock @@ -42,7 +38,7 @@ ;----------------------------------------------------------------------------- ; Return the number of clock ticks in one second. ; -__clk_tck: +__clocks_per_sec: ldx #$00 ; >50, >60, >75 ldy PBKUP lda #<75 diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index ed2ade222..e88e257fd 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -50,16 +50,15 @@ LINEDIST = $20 ; Offset in video RAM between two lines ldx #>load_addr sta load stx load+1 - lda #<load_size - eor #$FF - sta count ; store (-size - 1) - lda #>load_size - eor #$FF - sta count+1 -L1: inc count ; pre-count one's-complement upwards + ldx #(<load_size) + 1 + stx count + ldx #(>load_size) + 1 + stx count+1 ; save size with each byte incremented separately + +L1: dec count bnz L2 - inc count+1 + dec count+1 bze L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y diff --git a/libsrc/pce/_printf.s b/libsrc/pce/_printf.s new file mode 100644 index 000000000..e1d2a1cf4 --- /dev/null +++ b/libsrc/pce/_printf.s @@ -0,0 +1,791 @@ +; +; _printf: Basic layer for all printf type functions. +; +; 2000-10-21, Ullrich von Bassewitz +; 2021-05-04, Greg King +; + + .include "zeropage.inc" + + .export __printf + + .import popax, pushax, pusheax, decsp6, push1, axlong, axulong + .import _ltoa, _ultoa + .import _strlower, _strlen + + .macpack generic + +; ---------------------------------------------------------------------------- +; We will store variables into the register bank in the zeropage. Define +; equates for these variables. + +ArgList = regbank+0 ; Argument list pointer +Format = regbank+2 ; Format string +OutData = regbank+4 ; Function parameters + +; ---------------------------------------------------------------------------- +; Other zero page cells + +Base = ptr1 +FSave = ptr1 +FCount = ptr2 + +.code + +; ---------------------------------------------------------------------------- +; Get one character from the format string, and increment the pointer. Will +; return zero in .Y. + +GetFormatChar: + ldy #0 + lda (Format),y +IncFormatPtr: + inc Format + bne @L1 + inc Format+1 +@L1: rts + +; ---------------------------------------------------------------------------- +; Output a pad character: outfunc (d, &padchar, 1) + +OutputPadChar: + lda PadChar + +; ---------------------------------------------------------------------------- +; Call the output function with one character in .A + +Output1: + sta CharArg + jsr PushOutData + lda #<CharArg + ldx #>CharArg + jsr pushax + jsr push1 + jmp CallOutFunc ; fout (OutData, &CharArg, 1) + +; ---------------------------------------------------------------------------- +; Decrement the argument list pointer by 2 + +DecArgList2: + lda ArgList + sub #2 + sta ArgList + bcs @L1 + dec ArgList+1 +@L1: rts + +; ---------------------------------------------------------------------------- +; Get an unsigned int or long argument depending on the IsLong flag. + +GetUnsignedArg: + lda IsLong ; Check flag + bne GetLongArg ; Long sets all + jsr GetIntArg ; Get an integer argument + jmp axulong ; Convert to unsigned long + +; ---------------------------------------------------------------------------- +; Get an signed int or long argument depending on the IsLong flag. + +GetSignedArg: + lda IsLong ; Check flag + bne GetLongArg ; Long sets all + jsr GetIntArg ; Get an integer argument + jmp axlong ; Convert to long + +; ---------------------------------------------------------------------------- +; Get a long argument from the argument list. Returns 0 in .Y. + +GetLongArg: + jsr GetIntArg ; Get high word + sta sreg + stx sreg+1 + +; Run into GetIntArg fetching the low word + +; ---------------------------------------------------------------------------- +; Get an integer argument from the argument list. Returns 0 in .Y. + +GetIntArg: + jsr DecArgList2 + ldy #1 + lda (ArgList),y + tax + dey + lda (ArgList),y + rts + +; ---------------------------------------------------------------------------- +; Read an integer from the format string. Will return zero in .Y. + +ReadInt: + ldy #0 + sty ptr1 + sty ptr1+1 ; Start with zero +@Loop: lda (Format),y ; Get format string character + sub #'0' ; Make number from ascii digit + bcc @L9 ; Jump if done + cmp #9+1 + bcs @L9 ; Jump if done + +; Skip the digit character + + jsr IncFormatPtr + +; Add the digit to the value we have in ptr1 + + pha ; Save digit value + lda ptr1 + ldx ptr1+1 + asl ptr1 + rol ptr1+1 ; * 2 + asl ptr1 + rol ptr1+1 ; * 4, assume carry clear + adc ptr1 + sta ptr1 + txa + adc ptr1+1 + sta ptr1+1 ; * 5 + asl ptr1 + rol ptr1+1 ; * 10, assume carry clear + pla + adc ptr1 ; Add digit value + sta ptr1 + bcc @Loop + inc ptr1+1 + bcs @Loop ; Branch always + +; We're done converting + +@L9: lda ptr1 + ldx ptr1+1 ; Load result + rts + + +; ---------------------------------------------------------------------------- +; Put a character into the argument buffer and increment the buffer index + +PutBuf: ldy BufIdx + inc BufIdx + sta Buf,y + rts + +; ---------------------------------------------------------------------------- +; Get a pointer to the current buffer end and push it onto the stack + +PushBufPtr: + lda #<Buf + ldx #>Buf + add BufIdx + bcc @L1 + inx +@L1: jmp pushax + +; ---------------------------------------------------------------------------- +; Push OutData onto the software stack + +PushOutData: + lda OutData + ldx OutData+1 + jmp pushax + +; ---------------------------------------------------------------------------- +; Output Width pad characters +; + +PadLoop: + jsr OutputPadChar +OutputPadding: + inc Width + bne PadLoop + inc Width+1 + bne PadLoop + rts + +; ---------------------------------------------------------------------------- +; Output the argument itself: outfunc (d, str, arglen); +; + +OutputArg: + jsr PushOutData + lda Str + ldx Str+1 + jsr pushax + lda ArgLen + ldx ArgLen+1 + jsr pushax + jmp CallOutFunc + +; ---------------------------------------------------------------------------- +; ltoa: Wrapper for _ltoa that pushes all arguments + +ltoa: sty Base ; Save base + jsr pusheax ; Push value + jsr PushBufPtr ; Push the buffer pointer... + lda Base ; Restore base + jmp _ltoa ; ultoa (l, s, base); + + +; ---------------------------------------------------------------------------- +; ultoa: Wrapper for _ultoa that pushes all arguments + +ultoa: sty Base ; Save base + jsr pusheax ; Push value + jsr PushBufPtr ; Push the buffer pointer... + lda Base ; Restore base + jmp _ultoa ; ultoa (l, s, base); + + +; ---------------------------------------------------------------------------- +; + +__printf: + +; Save the register bank variables into the save area + + pha ; Save low byte of ap + ldy #5 + +; The PC-Engine puts the zero-page at $2000. The indexed-by-.Y addressing mode +; doesn't allow zero-page addressing. Therefore, the operand must be redirected +; explicitly. + +Save: lda regbank+$2000,y + sta RegSave,y + dey + bpl Save + pla ; Restore low byte of ap + +; Get the parameters from the stack + + sta ArgList ; Argument list pointer + stx ArgList+1 + + jsr popax ; Format string + sta Format + stx Format+1 + + jsr popax ; Output descriptor + sta OutData + stx OutData+1 + +; Initialize the output counter in the output descriptor to zero + + lda #0 + tay + sta (OutData),y + iny + sta (OutData),y + +; Get the output function from the output descriptor and remember it + + iny + lda (OutData),y + sta CallOutFunc+1 + iny + lda (OutData),y + sta CallOutFunc+2 + +; Start parsing the format string + +MainLoop: + lda Format ; Remember current format pointer + sta FSave + lda Format+1 + sta FSave+1 + + ldy #0 ; Index +@L1: lda (Format),y ; Get next char + beq @L2 ; Jump on end of string + cmp #'%' ; Format spec? + beq @L2 + iny ; Bump pointer + bne @L1 + inc Format+1 ; Bump high byte of pointer + bne @L1 ; Branch always + +; Found a '%' character or end of string. Update the Format pointer so it is +; current (points to this character). + +@L2: tya ; Low byte of offset + add Format + sta Format + bcc @L3 + inc Format+1 + +; Calculate, how many characters must be output. Beware: This number may +; be zero. .A still contains the low byte of the pointer. + +@L3: sub FSave + sta FCount + lda Format+1 + sbc FSave+1 + sta FCount+1 + ora FCount ; Is the result zero? + beq @L4 ; Jump if yes + +; Output the characters that we have until now. To make the call to out +; faster, build the stack frame by hand (don't use pushax) + + jsr decsp6 ; 3 args + ldy #5 + lda OutData+1 + sta (sp),y + dey + lda OutData + sta (sp),y + dey + lda FSave+1 + sta (sp),y + dey + lda FSave + sta (sp),y + dey + lda FCount+1 + sta (sp),y + dey + lda FCount + sta (sp),y + jsr CallOutFunc ; Call the output function + +; We're back from out(), or we didn't call it. Check for end of string. + +@L4: jsr GetFormatChar ; Get one char, zero in .Y + tax ; End of format string reached? + bne NotDone ; End not reached + +; End of format string reached. Restore the zeropage registers and return. + + ldx #5 +Rest: lda RegSave,x + +; The indexed-by-.X addressing mode does allow zero-page addressing. +; Therefore, this operand doesn't need to be redirected explicitly. + + sta regbank,x + dex + bpl Rest + rts + +; Still a valid format character. Check for '%' and a '%%' sequence. Output +; anything that is not a format specifier. On intro, .Y is zero. + +NotDone: + cmp #'%' + bne @L1 + lda (Format),y ; Check for "%%" + cmp #'%' + bne FormatSpec ; Jump if really a format specifier + jsr IncFormatPtr ; Skip the second '%' +@L1: jsr Output1 ; Output the character... + jmp MainLoop ; ...and continue + +; We have a real format specifier +; Format is: %[flags][width][.precision][mod]type +; .Y is zero on entry. + +FormatSpec: + +; Initialize the flags + + lda #0 + ldx #FormatVarSize-1 +@L1: sta FormatVars,x + dex + bpl @L1 + +; Start with reading the flags if there are any. .X is $FF which is used +; for "true" + +ReadFlags: + lda (Format),y ; Get next char... + cmp #'-' + bne @L1 + stx LeftJust + beq @L4 + +@L1: cmp #'+' + bne @L2 + stx AddSign + beq @L4 + +@L2: cmp #' ' + bne @L3 + stx AddBlank + beq @L4 + +@L3: cmp #'#' + bne ReadPadding + stx AltForm + +@L4: jsr IncFormatPtr + jmp ReadFlags ; ...and start over + +; Done with flags, read the pad char. .Y is still zero if we come here. + +ReadPadding: + ldx #' ' ; PadChar + cmp #'0' + bne @L1 + tax ; PadChar is '0' + jsr IncFormatPtr + lda (Format),y ; Read current for later +@L1: stx PadChar + +; Read the width. Even here, .Y is still zero. .A contains the current character +; from the format string. + +ReadWidth: + cmp #'*' + bne @L1 + jsr IncFormatPtr + jsr GetIntArg ; Width is an additional argument + jmp @L2 + +@L1: jsr ReadInt ; Read integer from format string... +@L2: sta Width + stx Width+1 ; ...and remember in Width + +; Read the precision. Even here, .Y is still zero. + + sty Prec ; Assume Precision is zero + sty Prec+1 + lda (Format),y ; Load next format string char + cmp #'.' ; Precision given? + bne ReadMod ; Branch if no precision given + +ReadPrec: + jsr IncFormatPtr ; Skip the '.' + lda (Format),y + cmp #'*' ; Variable precision? + bne @L1 + jsr IncFormatPtr ; Skip the '*' + jsr GetIntArg ; Get integer argument + jmp @L2 + +@L1: jsr ReadInt ; Read integer from format string +@L2: sta Prec + stx Prec+1 + +; Read the modifiers. .Y is still zero. + +ReadMod: + lda (Format),y + cmp #'z' ; size_t - same as unsigned + beq @L2 + cmp #'h' ; short - same as int + beq @L2 + cmp #'t' ; ptrdiff_t - same as int + beq @L2 + cmp #'j' ; intmax_t/uintmax_t - same as long + beq @L1 + cmp #'L' ; long double + beq @L1 + cmp #'l' ; long int + bne DoFormat +@L1: lda #$FF + sta IsLong +@L2: jsr IncFormatPtr + jmp ReadMod + +; Initialize the argument buffer pointers. We use a static buffer (ArgBuf) to +; assemble strings. A zero page index (BufIdx) is used to keep the current +; write position. A pointer to the buffer (Str) is used to point to the +; argument in case we will not use the buffer but a user-supplied string. +; .Y is zero when we come here. + +DoFormat: + sty BufIdx ; Clear BufIdx + ldx #<Buf + stx Str + ldx #>Buf + stx Str+1 + +; Skip the current format character, then check it (current char in .A) + + jsr IncFormatPtr + +; Is it a character? + + cmp #'c' + bne CheckInt + +; 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 + +; Is it an integer? + +CheckInt: + cmp #'d' + beq @L1 + cmp #'i' + bne CheckCount + +; It is an integer + +@L1: ldx #0 + lda AddBlank ; Add a blank for positives? + beq @L2 ; Jump if no + ldx #' ' +@L2: lda AddSign ; Add a plus for positives (precedence)? + beq @L3 + ldx #'+' +@L3: stx Leader + +; Integer argument + + jsr GetSignedArg ; Get argument as a long + ldy sreg+1 ; Check sign + bmi @Int1 + ldy Leader + beq @Int1 + sty Buf + inc BufIdx + +@Int1: ldy #10 ; Base + jsr ltoa ; Push arguments, call _ltoa + jmp HaveArg + +; Is it a count pseudo format? + +CheckCount: + cmp #'n' + bne CheckOctal + +; It is a count pseudo argument + + jsr GetIntArg + sta ptr1 + stx ptr1+1 ; Get user supplied pointer + ldy #0 + lda (OutData),y ; Low byte of OutData->ccount + sta (ptr1),y + iny + lda (OutData),y ; High byte of OutData->ccount + sta (ptr1),y + jmp MainLoop ; Done + +; Check for an octal digit + +CheckOctal: + cmp #'o' + bne CheckPointer + +; Integer in octal representation + + jsr GetSignedArg ; Get argument as a long + ldy AltForm ; Alternative form? + beq @Oct1 ; Jump if no + pha ; Save low byte of value + stx tmp1 + ora tmp1 + ora sreg + ora sreg+1 + ora Prec + ora Prec+1 ; Check if value or Prec != 0 + beq @Oct1 + lda #'0' + jsr PutBuf + pla ; Restore low byte + +@Oct1: ldy #8 ; Load base + jsr ltoa ; Push arguments, call _ltoa + jmp HaveArg + +; Check for a pointer specifier (%p) + +CheckPointer: + cmp #'p' + bne CheckString + +; It's a pointer. Use %#x conversion + + ldx #0 + stx IsLong ; IsLong = 0; + inx + stx AltForm ; AltForm = 1; + lda #'x' + bne IsHex ; Branch always + +; Check for a string specifier (%s) + +CheckString: + cmp #'s' + bne CheckUnsigned + +; It's a string + + jsr GetIntArg ; Get 16bit argument + sta Str + stx Str+1 + jmp HaveArg + +; Check for an unsigned integer (%u) + +CheckUnsigned: + cmp #'u' + bne CheckHex + +; It's an unsigned integer + + jsr GetUnsignedArg ; Get argument as unsigned long + ldy #10 ; Load base + jsr ultoa ; Push arguments, call _ultoa + jmp HaveArg + +; Check for a hexadecimal integer (%x) + +CheckHex: + cmp #'x' + beq IsHex + cmp #'X' + bne UnknownFormat + +; Hexadecimal integer + +IsHex: pha ; Save the format spec + lda AltForm + beq @L1 + lda #'0' + jsr PutBuf + lda #'X' + jsr PutBuf + +@L1: jsr GetUnsignedArg ; Get argument as an unsigned long + ldy #16 ; Load base + jsr ultoa ; Push arguments, call _ultoa + + pla ; Get the format spec + cmp #'x' ; Lower case? + bne @L2 + lda Str + ldx Str+1 + jsr _strlower ; Make characters lower case +@L2: jmp HaveArg + +; Unknown format character, skip it + +UnknownFormat: + jmp MainLoop + +; We have the argument, do argument string formatting + +HaveArg: + +; ArgLen = strlen (Str); + + lda Str + ldx Str+1 + jsr _strlen ; Get length of argument + sta ArgLen + stx ArgLen+1 + +; if (Prec && Prec < ArgLen) ArgLen = Prec; + + lda Prec + ora Prec+1 + beq @L1 + ldx Prec + cpx ArgLen + lda Prec+1 + tay + sbc ArgLen+1 + bcs @L1 + stx ArgLen + sty ArgLen+1 + +; if (Width > ArgLen) { +; Width -= ArgLen; /* padcount */ +; } else { +; Width = 0; +; } +; Since width is used as a counter below, calculate -(width+1) + +@L1: sec + lda Width + sbc ArgLen + tax + lda Width+1 + sbc ArgLen+1 + bcs @L2 + lda #0 + tax +@L2: eor #$FF + sta Width+1 + txa + eor #$FF + sta Width + +; /* Do padding on the left side if needed */ +; if (!leftjust) { +; /* argument right justified */ +; while (width) { +; fout (d, &padchar, 1); +; --width; +; } +; } + + lda LeftJust + bne @L3 + jsr OutputPadding + +; Output the argument itself + +@L3: jsr OutputArg + +; /* Output right padding bytes if needed */ +; if (leftjust) { +; /* argument left justified */ +; while (width) { +; fout (d, &padchar, 1); +; --width; +; } +; } + + lda LeftJust + beq @L4 + jsr OutputPadding + +; Done, parse next chars from format string + +@L4: jmp MainLoop + + +; ---------------------------------------------------------------------------- +; Local data (all static) + +.bss + +; Save area for the zero page registers +RegSave: .res regbanksize + +; One character argument for OutFunc +CharArg: .byte 0 + +; Format variables +FormatVars: +LeftJust: .byte 0 +AddSign: .byte 0 +AddBlank: .byte 0 +AltForm: .byte 0 +PadChar: .byte 0 +Width: .word 0 +Prec: .word 0 +IsLong: .byte 0 +Leader: .byte 0 +BufIdx: .byte 0 ; Argument string pointer +FormatVarSize = * - FormatVars + +; Argument buffer and pointer +Buf: .res 20 +Str: .word 0 +ArgLen: .res 2 + +.data + +; Stuff from OutData. Is used as a vector +CallOutFunc: jmp $0000 diff --git a/libsrc/pce/conio.s b/libsrc/pce/conio.s index 8fefe01f6..ac306f193 100644 --- a/libsrc/pce/conio.s +++ b/libsrc/pce/conio.s @@ -3,6 +3,7 @@ .import vdc_init .import psg_init .import colors + .import _pce_font .importzp ptr1, tmp1 .include "pce.inc" @@ -53,8 +54,8 @@ load_font: ; rts ; (fall through) ; Point to the font data. -copy: lda #<font - ldx #>font +copy: lda #<_pce_font + ldx #>_pce_font sta ptr1 stx ptr1+1 @@ -84,6 +85,3 @@ fillloop: bne charloop ; next character rts - -.rodata -font: .include "vga.inc" diff --git a/libsrc/pce/cpeekc.s b/libsrc/pce/cpeekc.s new file mode 100644 index 000000000..e144f94ac --- /dev/null +++ b/libsrc/pce/cpeekc.s @@ -0,0 +1,24 @@ +; +; 2020-07-14, Groepaz +; +; char cpeekc (void); +; +; get character from current position, do NOT advance cursor + + .export _cpeekc + + .include "pce.inc" + .include "extzp.inc" + +_cpeekc: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_LO ; character + and #<~$80 ; remove reverse bit + ldx #0 + rts diff --git a/libsrc/pce/cpeekcolor.s b/libsrc/pce/cpeekcolor.s new file mode 100644 index 000000000..8b96d29d4 --- /dev/null +++ b/libsrc/pce/cpeekcolor.s @@ -0,0 +1,28 @@ +; +; 2020-07-14, Groepaz +; +; unsigned char cpeekcolor (void); +; +; get color from current position, do NOT advance cursor + + .export _cpeekcolor + + .include "pce.inc" + .include "extzp.inc" + +_cpeekcolor: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_HI + and #<~$02 + lsr a + lsr a + lsr a + lsr a + ldx #0 + rts diff --git a/libsrc/pce/cpeekrevers.s b/libsrc/pce/cpeekrevers.s new file mode 100644 index 000000000..3f208fd10 --- /dev/null +++ b/libsrc/pce/cpeekrevers.s @@ -0,0 +1,26 @@ +; +; 2020-07-14, Groepaz +; +; unsigned char cpeekrevers (void); +; +; get inverse flag from current position, do NOT advance cursor + + .export _cpeekrevers + + .include "pce.inc" + .include "extzp.inc" + +_cpeekrevers: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_LO ; character (bit 7 is revers bit) + rol a + rol a + and #1 + ldx #0 + rts diff --git a/libsrc/pce/cpeeks.s b/libsrc/pce/cpeeks.s new file mode 100644 index 000000000..fe5e28687 --- /dev/null +++ b/libsrc/pce/cpeeks.s @@ -0,0 +1,63 @@ +; +; 2020-07-14, Groepaz +; +; void cpeeks (char* s, unsigned length); +; +; get string from current position, do NOT advance cursor + + .export _cpeeks + + .import popax + .importzp ptr1, ptr2, tmp1, tmp2 + + .macpack generic + + .include "pce.inc" + .include "extzp.inc" + +_cpeeks: + eor #<$FFFF ; counting a word upward is faster + sta ptr2 ; so, we use -(length + 1) + txa + eor #>$FFFF + sta ptr2+1 + + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + + jsr popax + sta tmp1 ; (will be a .Y index) + stx ptr1+1 + + ldx #<$0000 + stx ptr1 + beq L2 ; branch always + +L3: ldy tmp2 + lda VDC_DATA_LO ; get character + bit VDC_DATA_HI ; we need to "read" the highbyte to advance the address + iny + sty tmp2 + and #<~$80 ; remove reverse bit + + ldy tmp1 + sta (ptr1),y + iny + bne L1 + inc ptr1+1 +L1: sty tmp1 + +L2: inc ptr2 ; count length + bne L3 + inc ptr2+1 + bne L3 + + txa ; terminate the string + ldy tmp1 + sta (ptr1),y + rts diff --git a/libsrc/pce/cputc.s b/libsrc/pce/cputc.s index 04d901423..626d3ef8e 100644 --- a/libsrc/pce/cputc.s +++ b/libsrc/pce/cputc.s @@ -68,10 +68,10 @@ putchar: sty VDC_DATA_LO stx VDC_DATA_HI - st0 #VDC_VWR + st0 #VDC_VWR ; VRAM Write Register sta VDC_DATA_LO ; character - lda CHARCOLOR ; pallette number + lda CHARCOLOR ; palette number asl a asl a asl a diff --git a/libsrc/pce/gotox.s b/libsrc/pce/gotox.s new file mode 100644 index 000000000..fe8a23734 --- /dev/null +++ b/libsrc/pce/gotox.s @@ -0,0 +1,14 @@ +; +; void __fastcall__ gotox (unsigned char x); +; + + .export _gotox + + .import plot + + .include "pce.inc" + .include "extzp.inc" + +_gotox: + sta CURS_X ; Set X + jmp plot ; Set the cursor position diff --git a/libsrc/pce/gotoy.s b/libsrc/pce/gotoy.s new file mode 100644 index 000000000..9585b035b --- /dev/null +++ b/libsrc/pce/gotoy.s @@ -0,0 +1,14 @@ +; +; void __fastcall__ gotoy (unsigned char y); +; + + .export _gotoy + + .import plot + + .include "pce.inc" + .include "extzp.inc" + +_gotoy: + sta CURS_Y ; Set Y + jmp plot ; Set the cursor position diff --git a/libsrc/pce/vga.inc b/libsrc/pce/vga.s similarity index 99% rename from libsrc/pce/vga.inc rename to libsrc/pce/vga.s index 72c173146..630fbe8db 100644 --- a/libsrc/pce/vga.inc +++ b/libsrc/pce/vga.s @@ -1,10 +1,15 @@ ;---------------------------------------------------------------------------- ; VGA font for the PC-Engine conio implementation + .export _pce_font + ; The character tiles use only two colors from each pallette. Color zero ; comes from pallette zero; color one is different in each pallette. The ; color of a character is set by choosing one of the 16 pallettes. +.rodata + +_pce_font: .byte $00, $00, $00, $00, $00, $00, $00, $00 .byte %00000000 diff --git a/libsrc/pet/cbm_load.c b/libsrc/pet/cbm_load.c new file mode 100644 index 000000000..1823f6537 --- /dev/null +++ b/libsrc/pet/cbm_load.c @@ -0,0 +1,59 @@ +/* +** 2020-10-15, Greg King +** +** unsigned int __fastcall__ cbm_load (const char* name, +** unsigned char device, +** void* data); +*/ + +#include <cbm.h> +#include <limits.h> + +/* Loads file "name" from the given device to the given address, or to the load +** address of the file if "data" is the null pointer (like load"name",8,1 +** in BASIC). +** Returns the number of bytes that were loaded if loading was successful; +** otherwise 0; "_oserror" contains an error-code, then. +*/ +unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void* data) +{ + void* load; + int length; + unsigned int size = 0; + + if (cbm_open (1, device, CBM_READ, name) != 0) { + /* Can't load from a file that can't be openned. */ + return 0; + } + + /* Get the file's load address. */ + if (cbm_read (1, &load, sizeof load) != sizeof load) { + /* Either the file wasn't found, or it was too short. (Note: + ** the computer openned a file even if the drive couldn't open one.) + */ + cbm_close (1); + return 0; + } + + /* If "data" doesn't hold an address, then use the file's address. */ + if (data == (void*)0x0000) { + data = load; + } + + /* Pull the file into RAM. [Note that, if cbm_read() grabbed more + ** than 32767 bytes at a time, then its result would look negative, + ** which would cancel the load.] + */ + do { + size += (length = cbm_read (1, data, INT_MAX)); + data = (unsigned char*)data + length; + } while (length == INT_MAX && cbm_k_readst() == 0); + cbm_close (1); + + /* "length" is -1 if there was an error. */ + if (length < 0) { + size = 0; + } + + return size; +} diff --git a/libsrc/pet/crt0.s b/libsrc/pet/crt0.s index 520a147f7..e56a7eca4 100644 --- a/libsrc/pet/crt0.s +++ b/libsrc/pet/crt0.s @@ -12,7 +12,6 @@ .include "zeropage.inc" .include "pet.inc" - .include "../cbm/cbm.inc" ; ------------------------------------------------------------------------ ; Startup code diff --git a/libsrc/pet/kbsout.s b/libsrc/pet/kbsout.s index 178ac8205..8b21378af 100644 --- a/libsrc/pet/kbsout.s +++ b/libsrc/pet/kbsout.s @@ -1,17 +1,20 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; BSOUT replacement function for the PETs +; BSOUT/CHROUT replacement function for the PETs ; .export BSOUT + .export CHROUT + .import checkst .proc BSOUT - jsr $FFD2 ; Call kernal function + jsr $FFD2 ; Call Kernal function jmp checkst ; Check status, return carry on error .endproc +CHROUT := BSOUT diff --git a/libsrc/pet/kernal.s b/libsrc/pet/kernal.s index ba66c4653..fc8d62c4a 100644 --- a/libsrc/pet/kernal.s +++ b/libsrc/pet/kernal.s @@ -8,6 +8,7 @@ .export CLRCH .export BASIN + .export CHRIN .export STOP .export GETIN .export CLALL diff --git a/libsrc/pet/waitvsync.s b/libsrc/pet/waitvsync.s new file mode 100644 index 000000000..39b562e43 --- /dev/null +++ b/libsrc/pet/waitvsync.s @@ -0,0 +1,16 @@ +; +; Written by Robin Harbron, requires 12" monitor +; +; void waitvsync (void); +; + + .export _waitvsync + + .include "pet.inc" + +_waitvsync: +@l1: + lda VIA_PB + and #%00100000 + bne @l1 + rts diff --git a/libsrc/plus4/cputc.s b/libsrc/plus4/cputc.s index 49b3a84dd..a72b4012a 100644 --- a/libsrc/plus4/cputc.s +++ b/libsrc/plus4/cputc.s @@ -74,10 +74,9 @@ L5: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect diff --git a/libsrc/plus4/crt0.s b/libsrc/plus4/crt0.s index 2262b4c42..6b44a2b7e 100644 --- a/libsrc/plus4/crt0.s +++ b/libsrc/plus4/crt0.s @@ -198,5 +198,3 @@ irqcount: .byte 0 .segment "INIT" zpsave: .res zpspace - - diff --git a/libsrc/plus4/kbasin.s b/libsrc/plus4/kbasin.s index 507043866..3bbee9f74 100644 --- a/libsrc/plus4/kbasin.s +++ b/libsrc/plus4/kbasin.s @@ -1,10 +1,11 @@ ; ; Ullrich von Bassewitz, 22.11.2002 ; -; BASIN replacement function +; BASIN/CHRIN replacement function ; - + .export BASIN + .export CHRIN .include "plus4.inc" @@ -17,4 +18,4 @@ rts ; Return to caller .endproc - +CHRIN := BASIN diff --git a/libsrc/plus4/kbsout.s b/libsrc/plus4/kbsout.s index a86a334b2..8b6df19ff 100644 --- a/libsrc/plus4/kbsout.s +++ b/libsrc/plus4/kbsout.s @@ -1,10 +1,11 @@ ; ; Ullrich von Bassewitz, 22.11.2002 ; -; BSOUT replacement function +; BSOUT/CHROUT replacement function ; .export BSOUT + .export CHROUT .include "plus4.inc" @@ -17,4 +18,4 @@ rts ; Return to caller .endproc - +CHROUT := BSOUT diff --git a/libsrc/runtime/bcast.s b/libsrc/runtime/bcast.s new file mode 100644 index 000000000..bc228d1b7 --- /dev/null +++ b/libsrc/runtime/bcast.s @@ -0,0 +1,21 @@ +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast +; + + .export bcasta, bcastax + +bcastax: + cpx #0 + bne L1 + +bcasta: + tax + beq L0 ; Zero already in X + +L1: ldx #0 + lda #1 + +L0: rts + diff --git a/libsrc/runtime/lbcast.s b/libsrc/runtime/lbcast.s new file mode 100644 index 000000000..208442c44 --- /dev/null +++ b/libsrc/runtime/lbcast.s @@ -0,0 +1,20 @@ +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast for longs +; + + .export bcasteax + .importzp sreg, tmp1 + +bcasteax: + stx tmp1 + ldx #0 ; High byte of result + ora tmp1 + ora sreg + ora sreg+1 + beq L0 + + lda #1 +L0: rts + diff --git a/libsrc/runtime/lmul.s b/libsrc/runtime/lmul.s index 860d58cba..d3c34637c 100644 --- a/libsrc/runtime/lmul.s +++ b/libsrc/runtime/lmul.s @@ -63,7 +63,7 @@ L0: lsr tmp4 bcc L1 clc adc ptr3 - pha + tax lda ptr3+1 adc tmp2 sta tmp2 @@ -73,7 +73,7 @@ L0: lsr tmp4 lda ptr4+1 adc tmp4 sta tmp4 - pla + txa L1: dey bpl L0 lda ptr1 ; Load the low result word diff --git a/libsrc/runtime/umul8x16r24.s b/libsrc/runtime/umul8x16r24.s index c006082a4..54d730558 100644 --- a/libsrc/runtime/umul8x16r24.s +++ b/libsrc/runtime/umul8x16r24.s @@ -41,20 +41,19 @@ umul8x16r16m: .endif ldy #8 ; Number of bits - ldx ptr3 ; Get into register for speed lda ptr1 ror a ; Get next bit into carry @L0: bcc @L1 clc - pha - txa + tax + lda ptr3 adc ptr1+1 sta ptr1+1 lda ptr3+1 adc sreg sta sreg - pla + txa @L1: ror sreg ror ptr1+1 diff --git a/libsrc/serial/ser-kernel.s b/libsrc/serial/ser-kernel.s index 4c5b455b6..b6c57a3b5 100644 --- a/libsrc/serial/ser-kernel.s +++ b/libsrc/serial/ser-kernel.s @@ -39,7 +39,7 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ ser_install (void* driver); +; unsigned char __fastcall__ ser_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/supervision/crt0.s b/libsrc/supervision/crt0.s index ac61c8215..fbae1fc46 100644 --- a/libsrc/supervision/crt0.s +++ b/libsrc/supervision/crt0.s @@ -67,16 +67,14 @@ not_dma: ; Removing this segment gives only a warning. .segment "FFF0" .proc reset32kcode - lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON + lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON sta sv_bank ; Now, the 32Kbyte image can reside in the top of 64Kbyte and 128Kbyte ROMs. jmp reset .endproc - .segment "VECTOR" + .segment "VECTORS" .word nmi .word reset32kcode .word irq - - diff --git a/libsrc/sym1/beep.s b/libsrc/sym1/beep.s new file mode 100644 index 000000000..40a3d42c6 --- /dev/null +++ b/libsrc/sym1/beep.s @@ -0,0 +1,18 @@ +; +; Wayne Parham (wayne@parhamdata.com) +; +; void beep (void); +; + +.include "sym1.inc" + +.export _beep + +.segment "CODE" + +.proc _beep: near + + jsr BEEP ; Beep + rts + +.endproc diff --git a/libsrc/sym1/crt0.s b/libsrc/sym1/crt0.s new file mode 100644 index 000000000..5d398b311 --- /dev/null +++ b/libsrc/sym1/crt0.s @@ -0,0 +1,57 @@ +; +; Startup code for cc65 (Sym-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 "sym1.inc" + + +; Place the startup code in a special segment + +.segment "STARTUP" + + +; A little light housekeeping + +_init: jsr ACCESS ; Unlock System RAM + cld ; Clear decimal mode + +; Turn off console echo + + lda TECHO + and #$7F + sta TECHO + +; 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) + +_exit: jsr donelib ; Run destructors + lda TECHO + ora #$80 ; Re-enable console echo + sta TECHO + jsr NACCES ; Lock System RAM + rts ; Re-enter Sym-1 monitor diff --git a/libsrc/sym1/ctype.s b/libsrc/sym1/ctype.s new file mode 100644 index 000000000..1301965eb --- /dev/null +++ b/libsrc/sym1/ctype.s @@ -0,0 +1,5 @@ +; Character specification table. +; +; uses the "common" definition + + .include "ctype_common.inc" diff --git a/libsrc/sym1/display.s b/libsrc/sym1/display.s new file mode 100644 index 000000000..f3b2923d6 --- /dev/null +++ b/libsrc/sym1/display.s @@ -0,0 +1,18 @@ +; +; Wayne Parham (wayne@parhamdata.com) +; +; void fdisp (void); +; + +.include "sym1.inc" + +.export _fdisp + +.segment "CODE" + +.proc _fdisp: near + + jsr SCAND ; Flash Display + rts + +.endproc diff --git a/libsrc/sym1/read.s b/libsrc/sym1/read.s new file mode 100644 index 000000000..c041664da --- /dev/null +++ b/libsrc/sym1/read.s @@ -0,0 +1,53 @@ +; +; Wayne Parham (wayne@parhamdata.com) +; +; int __fastcall__ read (int fd, void* buf, unsigned count); +; + +.include "sym1.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 + jsr OUTCHR ; Echo it + and #$7F ; Clear top bit + cmp #$07 ; Check for '\a' + bne chkcr ; ...if BEL character + jsr BEEP ; Make beep sound +chkcr: cmp #$0D ; Check for '\r' + bne putch ; ...if CR character + lda #$0A ; Replace with '\n' + jsr OUTCHR ; and echo it + +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/sym1/tapeio.s b/libsrc/sym1/tapeio.s new file mode 100644 index 000000000..078ea7abd --- /dev/null +++ b/libsrc/sym1/tapeio.s @@ -0,0 +1,46 @@ +; +; Wayne Parham (wayne@parhamdata.com) +; +; int __fastcall__ loadt (unsigned char id); +; int __fastcall__ dumpt (unsigned char id, void* start_addr, void* end_addr); +; + +.include "sym1.inc" + +.import popa, popax, return0, return1 + +.export _loadt, _dumpt + +.segment "CODE" + +.proc _loadt: near + + sta P1L ; Tape record ID to P1L + ldx #$00 + stx P1H + ldy #$80 + 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 P3L ; End address + stx P3H + jsr popax + sta P2L ; Start address + stx P2H + jsr popa + sta P1L ; Tape Record ID + ldx #$00 + stx P1H + ldy #$80 + 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/sym1/write.s b/libsrc/sym1/write.s new file mode 100644 index 000000000..dbe738468 --- /dev/null +++ b/libsrc/sym1/write.s @@ -0,0 +1,51 @@ +; +; Wayne Parham (wayne@parhamdata.com) +; +; int __fastcall__ write (int fd, const void* buf, int count); +; + +.include "sym1.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 + jsr OUTCHR ; Send character using Monitor call + cmp #$07 ; Check for '\a' + bne chklf ; ...if BEL character + jsr BEEP ; Make beep sound +chklf: 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/telestrat/bordercolor.s b/libsrc/telestrat/bordercolor.s new file mode 100644 index 000000000..c29ecc0e7 --- /dev/null +++ b/libsrc/telestrat/bordercolor.s @@ -0,0 +1,14 @@ +; +; Ullrich von Bassewitz, 06.08.1998 +; +; unsigned char __fastcall__ bordercolor (unsigned char color); +; + + .export _bordercolor + + .import return0 + + .include "telestrat.inc" + +_bordercolor := return0 + diff --git a/libsrc/telestrat/cclear.s b/libsrc/telestrat/cclear.s index a32919962..b9fce4708 100644 --- a/libsrc/telestrat/cclear.s +++ b/libsrc/telestrat/cclear.s @@ -6,7 +6,7 @@ ; .export _cclearxy, _cclear - .import update_adscr + .import update_adscr, display_conio .importzp tmp1 .import popax @@ -27,7 +27,7 @@ _cclear: @L1: stx tmp1 ; Save X lda #' ' ; Erase current char - BRK_TELEMON XFWR + jsr display_conio ldx tmp1 dex bne @L1 diff --git a/libsrc/telestrat/chline.s b/libsrc/telestrat/chline.s index 6ead10eee..91f3bdc9f 100644 --- a/libsrc/telestrat/chline.s +++ b/libsrc/telestrat/chline.s @@ -1,22 +1,34 @@ ; -; jede jede@oric.org 2018-04-17 -; - +; void chlinexy (unsigned char x, unsigned char y, unsigned char length); ; void chline (unsigned char length); ; - .export _chline + .export _chlinexy, _chline + + .import rvs, display_conio, update_adscr + .import popax + .include "telestrat.inc" - .include "zeropage.inc" -.proc _chline - sta tmp1 -@loop: - lda #'-' ; horizontal line screen code - BRK_TELEMON XWR0 ; macro send char to screen (channel 0 in telemon terms) - dec tmp1 - bne @loop - rts -.endproc +_chlinexy: + pha ; Save the length + jsr popax ; Get X and Y + sta SCRY ; Store Y + stx SCRX ; Store X + jsr update_adscr + pla ; Restore the length and run into _chline + +_chline: + tax ; Is the length zero? + beq @L9 ; Jump if done +@L1: + lda #'-' ; Horizontal line screen code + ora rvs + + jsr display_conio + +@L2: dex + bne @L1 +@L9: rts diff --git a/libsrc/telestrat/clock.s b/libsrc/telestrat/clock.s new file mode 100644 index 000000000..c5478c7ac --- /dev/null +++ b/libsrc/telestrat/clock.s @@ -0,0 +1,28 @@ +; +; Jede, 2021-03-10 +; +; clock_t clock (void); +; + + .export _clock + .importzp sreg + + .include "telestrat.inc" + +.proc _clock + +; Clear the timer high 16 bits + + ldy #$00 + sty sreg + sty sreg+1 + +; Read the timer + + sei ; Disable interrupts + lda TIMEUD ; TIMED contains 1/10 of a second from clock. Telestrat main cardridge simulate a clock from VIA6522 timer + ldx TIMEUD+1 + cli ; Reenable interrupts + + rts +.endproc diff --git a/libsrc/telestrat/clrscr.s b/libsrc/telestrat/clrscr.s index 39c2f7724..c02c26647 100644 --- a/libsrc/telestrat/clrscr.s +++ b/libsrc/telestrat/clrscr.s @@ -3,7 +3,7 @@ ; .export _clrscr - .import OLD_CHARCOLOR, OLD_BGCOLOR + .import OLD_CHARCOLOR, OLD_BGCOLOR, BGCOLOR, CHARCOLOR .include "telestrat.inc" @@ -23,22 +23,25 @@ ; reset prompt position - lda #<(SCREEN+40) - sta ADSCRL - lda #>(SCREEN+40) - sta ADSCRH + lda #<SCREEN + sta ADSCR + lda #>SCREEN + sta ADSCR+1 + + lda #$00 + sta SCRDY ; reset display position - ldx #$01 + ldx #$00 stx SCRY - dex stx SCRX - ; At this step X is equal to $00 - dex - ; At this step X is equal to $FF - stx OLD_BGCOLOR + stx OLD_BGCOLOR ; Black + stx BGCOLOR + + ldx #$07 ; White stx OLD_CHARCOLOR - + stx CHARCOLOR + rts .endproc diff --git a/libsrc/telestrat/cputc.s b/libsrc/telestrat/cputc.s index 38f8af84b..16b13f8cd 100644 --- a/libsrc/telestrat/cputc.s +++ b/libsrc/telestrat/cputc.s @@ -4,24 +4,50 @@ ; void cputc (char c); ; - .export _cputc, CHARCOLOR, OLD_CHARCOLOR, BGCOLOR, OLD_BGCOLOR + .export _cputc, _cputcxy, cputdirect, display_conio + .export CHARCOLOR, OLD_CHARCOLOR, BGCOLOR, OLD_BGCOLOR + + .import update_adscr + .import popax .include "telestrat.inc" -.proc _cputc + +_cputcxy: + pha ; Save C + jsr popax ; Get X and Y + sta SCRY ; Store Y + stx SCRX ; Store X + jsr update_adscr + pla + +_cputc: + cmp #$0D + bne @not_CR + ldy #$00 + sty SCRX + rts +@not_CR: + cmp #$0A + bne not_LF + + inc SCRY + jmp update_adscr + +cputdirect: +not_LF: ldx CHARCOLOR cpx OLD_CHARCOLOR beq do_not_change_color_foreground - + stx OLD_CHARCOLOR ; Store CHARCOLOR into OLD_CHARCOLOR - dec SCRX - dec SCRX pha txa ; Swap X to A because, X contains CHARCOLOR - BRK_TELEMON XFWR ; Change color on the screen (foreground) - inc SCRX + + jsr display_conio + pla do_not_change_color_foreground: @@ -31,24 +57,44 @@ do_not_change_color_foreground: stx OLD_BGCOLOR - dec SCRX ; Dec SCRX in order to place attribute before the right position - pha txa ; Swap X to A because, X contains BGCOLOR - ORA #%00010000 ; Add 16 because background color is an attribute between 16 and 23. 17 is red background for example - BRK_TELEMON XFWR ; Change color on the screen (background) + ora #%00010000 ; Add 16 because background color is an attribute between 16 and 23. 17 is red background for example + + jsr display_conio pla do_not_change_color: - BRK_TELEMON XFWR ; Macro send char to screen (channel 0) + ; it continues to display_conio + + + +.proc display_conio + ; This routine is used to displays char on screen + ldy SCRX + sta (ADSCR),y + iny + cpy #SCREEN_XSIZE + bne @no_inc + ldy #$00 + sty SCRX + + inc SCRY + + jmp update_adscr + +@no_inc: + sty SCRX rts .endproc + .bss CHARCOLOR: .res 1 OLD_CHARCOLOR: - .res 1 + .res 1 BGCOLOR: - .res 1 + .res 1 OLD_BGCOLOR: .res 1 + diff --git a/libsrc/telestrat/crt0.s b/libsrc/telestrat/crt0.s index 59b1ea642..df75520ce 100644 --- a/libsrc/telestrat/crt0.s +++ b/libsrc/telestrat/crt0.s @@ -12,7 +12,6 @@ .import __MAIN_START__, __MAIN_SIZE__ .include "zeropage.inc" - .include "telestrat.inc" ; ------------------------------------------------------------------------ ; Place the startup code in a special segment. diff --git a/libsrc/telestrat/cvline.s b/libsrc/telestrat/cvline.s new file mode 100644 index 000000000..0d6086216 --- /dev/null +++ b/libsrc/telestrat/cvline.s @@ -0,0 +1,40 @@ +; +; Ullrich von Bassewitz, 2003-04-13 +; +; void cvlinexy (unsigned char x, unsigned char y, unsigned char length); +; void cvline (unsigned char length); +; + + .export _cvlinexy, _cvline + + .import rvs, display_conio, update_adscr + + .import popax + + .include "telestrat.inc" + + +_cvlinexy: + pha ; Save the length + jsr popax ; Get X and Y + sta SCRY ; Store Y + stx SCRX ; Store X + jsr update_adscr + pla ; Restore the length and run into _cvline + +_cvline: + tax ; Is the length zero? + beq @L9 ; Jump if done +@L1: + lda #'|' + ora rvs + ldy SCRX + sta (ADSCR),y + ; compute next line + inc SCRY + jsr update_adscr +@L2: dex + bne @L1 +@L9: rts + + diff --git a/libsrc/telestrat/gotox.s b/libsrc/telestrat/gotox.s index 7a172071c..72004bc0a 100644 --- a/libsrc/telestrat/gotox.s +++ b/libsrc/telestrat/gotox.s @@ -8,9 +8,5 @@ .proc _gotox sta SCRX - - lda #$FF - sta OLD_CHARCOLOR - sta OLD_BGCOLOR rts .endproc diff --git a/libsrc/telestrat/gotoxy.s b/libsrc/telestrat/gotoxy.s index 3387efe40..ea7ed5623 100644 --- a/libsrc/telestrat/gotoxy.s +++ b/libsrc/telestrat/gotoxy.s @@ -29,30 +29,23 @@ gotoxy: jsr popa ; Get Y .endproc .proc update_adscr -; Force to set again color if cursor moves -; $FF is used because we know that it's impossible to have this value with a color -; It prevents a bug : If bgcolor or textcolor is set to black for example with no char displays, -; next cputsxy will not set the attribute if y coordinate changes - lda #$FF - sta OLD_CHARCOLOR - sta OLD_BGCOLOR lda #<SCREEN - sta ADSCRL + sta ADSCR lda #>SCREEN - sta ADSCRH + sta ADSCR+1 ldy SCRY beq out loop: - lda ADSCRL + lda ADSCR clc adc #SCREEN_XSIZE bcc skip - inc ADSCRH + inc ADSCR+1 skip: - sta ADSCRL + sta ADSCR dey bne loop out: diff --git a/libsrc/telestrat/initcwd.s b/libsrc/telestrat/initcwd.s index dc3c53257..7d38dd563 100644 --- a/libsrc/telestrat/initcwd.s +++ b/libsrc/telestrat/initcwd.s @@ -10,8 +10,7 @@ initcwd: - ldx #PWD_PTR - BRK_TELEMON XVARS + BRK_TELEMON(XGETCWD) sta ptr1 sty ptr1+1 diff --git a/libsrc/telestrat/kbhit.s b/libsrc/telestrat/kbhit.s new file mode 100644 index 000000000..54e4bf4d8 --- /dev/null +++ b/libsrc/telestrat/kbhit.s @@ -0,0 +1,17 @@ +; +; Jede, 2021-02-01 +; +; int kbhit (void); +; + + .export _kbhit + + .include "telestrat.inc" + +_kbhit: + BRK_TELEMON XRD0 + ldx #$00 + txa + rol + eor #$01 + rts diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index 76de9d0ac..db764fc84 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -12,21 +12,21 @@ ;int read (int fd, void* buf, unsigned count); .proc _read - sta ptr1 ; count - stx ptr1+1 ; count - jsr popax ; get buf + + sta ptr1 ; Count + stx ptr1+1 ; Count + jsr popax ; Get buf sta PTR_READ_DEST stx PTR_READ_DEST+1 - sta ptr2 ; in order to calculate nb of bytes read + sta ptr2 ; In order to calculate nb of bytes read stx ptr2+1 ; - ; jsr popax ; fp pointer don't care in this version lda ptr1 ; ldy ptr1+1 ; BRK_TELEMON XFREAD ; calls telemon30 routine - ; compute nb of bytes read + ; Compute nb of bytes read lda PTR_READ_DEST+1 sec sbc ptr2+1 diff --git a/libsrc/telestrat/revers.s b/libsrc/telestrat/revers.s new file mode 100644 index 000000000..ad3e1f909 --- /dev/null +++ b/libsrc/telestrat/revers.s @@ -0,0 +1,37 @@ +; +; Ullrich von Bassewitz, 07.08.1998 +; +; unsigned char revers (unsigned char onoff); +; + + .export _revers + .export rvs + +; ------------------------------------------------------------------------ +; + +.code +.proc _revers + + ldx #$00 ; Assume revers off + tay ; Test onoff + beq L1 ; Jump if off + ldx #$80 ; Load on value + ldy #$00 ; Assume old value is zero +L1: lda rvs ; Load old value + stx rvs ; Set new value + beq L2 ; Jump if old value zero + iny ; Make old value = 1 +L2: ldx #$00 ; Load high byte of result + tya ; Load low byte, set CC + rts + +.endproc + +; ------------------------------------------------------------------------ +; + +.bss +rvs: .res 1 + + diff --git a/libsrc/telestrat/syschdir.s b/libsrc/telestrat/syschdir.s new file mode 100644 index 000000000..09763bdbb --- /dev/null +++ b/libsrc/telestrat/syschdir.s @@ -0,0 +1,32 @@ +; +; Jede (jede@oric.org), 2021-02-22 +; +; unsigned char _syschdir (const char* name, ...); +; + + .export __syschdir + .import addysp, popax + .importzp tmp1 + .import initcwd + + .include "telestrat.inc" + .include "zeropage.inc" + + +__syschdir: + ; Throw away all parameters except the name + dey + dey + jsr addysp + + ; Get name + jsr popax + + stx tmp1 + ldy tmp1 + + ; Call telemon primitive + + BRK_TELEMON(XPUTCWD) + + jmp initcwd ; Update cwd diff --git a/libsrc/telestrat/textcolor.s b/libsrc/telestrat/textcolor.s index 7d16c9e19..d851aaaab 100644 --- a/libsrc/telestrat/textcolor.s +++ b/libsrc/telestrat/textcolor.s @@ -2,12 +2,13 @@ ; .export _textcolor - .import CHARCOLOR + .import CHARCOLOR, OLD_CHARCOLOR .include "telestrat.inc" .proc _textcolor ldx CHARCOLOR ; Get previous color sta CHARCOLOR + stx OLD_CHARCOLOR txa ; Return previous color rts .endproc diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 68aef42d6..215db3e52 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -13,11 +13,11 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard @@ -51,9 +51,9 @@ next: rts -L1: inc ptr2 +L1: dec ptr2 bne L2 - inc ptr2+1 + dec ptr2+1 beq L9 L2: ldy #0 lda (ptr1),y diff --git a/libsrc/tgi/tgi-kernel.s b/libsrc/tgi/tgi-kernel.s index 3a388b6dc..cbd655279 100644 --- a/libsrc/tgi/tgi-kernel.s +++ b/libsrc/tgi/tgi-kernel.s @@ -88,7 +88,7 @@ tgi_sig: .byte $74, $67, $69, TGI_API_VERSION ; "tgi", version .code ;---------------------------------------------------------------------------- -; void __fastcall__ tgi_install (void* driver); +; void __fastcall__ tgi_install (const void* driver); ; /* Install an already loaded driver. */ diff --git a/libsrc/vic20/c_readst.s b/libsrc/vic20/c_readst.s new file mode 100644 index 000000000..2cb11bb21 --- /dev/null +++ b/libsrc/vic20/c_readst.s @@ -0,0 +1,28 @@ +; +; 1999-06-03, Ullrich von Bassewitz +; 2021-01-12, Greg King +; +; unsigned char cbm_k_readst (void); +; +; This version works around a bug in VIC-20 Kernal's READST function. +; + + .include "vic20.inc" + .include "../cbm/cbm.inc" + + .export _cbm_k_readst + + +_cbm_k_readst: + ldx #>$0000 + lda DEVNUM + cmp #CBMDEV_RS232 + beq @L1 + + jmp READST + +; Work-around: Read the RS-232 status variable directly. + +@L1: lda RSSTAT + stx RSSTAT ; reset the status bits + rts diff --git a/libsrc/vic20/cputc.s b/libsrc/vic20/cputc.s index 1db818546..a6919ceaf 100644 --- a/libsrc/vic20/cputc.s +++ b/libsrc/vic20/cputc.s @@ -1,5 +1,6 @@ ; -; Ullrich von Bassewitz, 06.08.1998 +; 1998-08-06, Ullrich von Bassewitz +; 2020-10-09, Greg King ; ; void cputcxy (unsigned char x, unsigned char y, char c); ; void cputc (char c); @@ -39,70 +40,64 @@ _cputcxy: jsr gotoxy ; Set cursor, drop x and y pla ; Restore C -; Plot a character - also used as internal function +; Plot a character -- also used as an internal function -_cputc: cmp #$0A ; CR? - bne L1 - lda #0 - sta CURS_X - beq plot ; Recalculate pointers - -L1: cmp #$0D ; LF? +_cputc: cmp #$0D ; Is it CBM '\n'? beq newline ; Recalculate pointers + cmp #$0A ; Is it CBM '\r'? + beq cr -; Printable char of some sort +; Printable char. of some sort +; Convert it from PetSCII into a screen-code - cmp #' ' - bcc cputdirect ; Other control char + cmp #$FF ; BASIC token? + bne convert + lda #$DE ; Pi symbol +convert: tay - bmi L10 - cmp #$60 - bcc L2 - and #$DF - bne cputdirect ; Branch always -L2: and #$3F + lsr a ; Divide by 256/8 + lsr a + lsr a + lsr a + lsr a + tax ; .X = %00000xxx + tya + eor pet_to_screen,x cputdirect: jsr putchar ; Write the character to the screen -; Advance cursor position +; Advance the cursor position advance: iny cpy #XSIZE bne L3 - jsr newline ; new line - ldy #0 ; + cr + jsr newline ; Wrap around + +cr: ldy #$00 ; Do carriage-return L3: sty CURS_X rts + +; Move down by one full screen-line. Note: this routine doesn't scroll. +; +; (Both screen RAM and color RAM are aligned to page boundaries. +; Therefore, the lower bytes of their addresses have the same values. +; Shorten the code by taking advantage of that fact.) + newline: clc lda #XSIZE adc SCREEN_PTR sta SCREEN_PTR - bcc L4 - inc SCREEN_PTR+1 - clc -L4: lda #XSIZE - adc CRAM_PTR sta CRAM_PTR bcc L5 + inc SCREEN_PTR+1 inc CRAM_PTR+1 L5: inc CURS_Y rts -; Handle character if high bit set - -L10: and #$7F - cmp #$7E ; PI? - bne L11 - lda #$5E ; Load screen code for PI - bne cputdirect -L11: ora #$40 - bne cputdirect - - ; Set cursor position, calculate RAM pointers @@ -112,14 +107,19 @@ plot: ldy CURS_X jmp PLOT ; Set the new cursor - -; Write one character to the screen without doing anything else, return X -; position in Y +; Write one character to the screen without doing anything else, +; return the X position in .Y putchar: ora RVS ; Set revers bit ldy CURS_X - sta (SCREEN_PTR),y ; Set char + sta (SCREEN_PTR),y ; Set char. lda CHARCOLOR sta (CRAM_PTR),y ; Set color rts + + + .rodata +pet_to_screen: + .byte %10000000,%00000000,%01000000,%00100000 ; PetSCII -> screen-code + .byte %01000000,%11000000,%10000000,%10000000 diff --git a/libsrc/vic20/crt0.s b/libsrc/vic20/crt0.s index 68ab3ed12..c5486063b 100644 --- a/libsrc/vic20/crt0.s +++ b/libsrc/vic20/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "vic20.inc" ; ------------------------------------------------------------------------ ; Startup code diff --git a/libsrc/vic20/emd/vic20-georam.s b/libsrc/vic20/emd/vic20-georam.s old mode 100755 new mode 100644 diff --git a/libsrc/vic20/kernal.s b/libsrc/vic20/kernal.s index ff16a019c..7c54b205f 100644 --- a/libsrc/vic20/kernal.s +++ b/libsrc/vic20/kernal.s @@ -1,11 +1,14 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; VIC20 kernal functions +; VIC20 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export CINT .export IOINIT .export RAMTAS @@ -33,7 +36,9 @@ .export CKOUT .export CLRCH .export BASIN + .export CHRIN .export BSOUT + .export CHROUT .export LOAD .export SAVE .export SETTIM diff --git a/libsrc/vic20/libref.s b/libsrc/vic20/libref.s index 468c540f1..333a66894 100644 --- a/libsrc/vic20/libref.s +++ b/libsrc/vic20/libref.s @@ -1,10 +1,14 @@ ; -; Oliver Schmidt, 2013-05-31 +; 2013-05-31, Oliver Schmidt +; 2018-03-11, Sven Michael Klose ; - .export joy_libref .export em_libref + .export joy_libref + .export tgi_libref + .import _exit -joy_libref := _exit em_libref := _exit +joy_libref := _exit +tgi_libref := _exit diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s new file mode 100644 index 000000000..826a09c14 --- /dev/null +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -0,0 +1,1008 @@ +; +; Graphics driver for a 160x192x2 mode on the VIC-20. +; +; Based on C64 TGI driver +; +; 2018-03-11, Sven Michael Klose +; 2020-07-06, Greg King +; + + .include "zeropage.inc" + .include "vic20.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .macpack generic + .macpack module + + +; ------------------------------------------------------------------------ +; Constants + +COLS = 20 +ROWS = 12 +XRES = COLS * 8 +YRES = ROWS * 16 + +TGI_IOCTL_VIC20_SET_PATTERN = $01 + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _vic20_hi_tgi + +; First part of the header is a structure that has a magic and defines the +; capabilities of the driver + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word XRES ; X resolution + .word YRES ; Y resolution + .byte 2 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0180 ; Aspect ratio 2.5:3 + .byte 0 ; TGI driver flags + +; Next comes the jump table. With the exception of IRQ, all entries must be +; valid and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + +; ------------------------------------------------------------------------ +; Data. + +; Variables mapped to the zero-page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 := ptr1 +Y1 := ptr2 +X2 := ptr3 +Y2 := ptr4 +TEXT := ptr3 + +POINT := regsave +SOURCE := tmp1 +DEST := tmp3 + +; Absolute variables used in the code + +.bss + +ERROR: .res 1 ; Error code +PALETTE: .res 2 ; The current palette + +CURCOL: .res 1 ; Current color. +BITMASK: .res 1 ; $00 = clear, $FF = set pixels + +; BAR variables + +XPOSR: .res 1 ; Used by BAR. +PATTERN: .res 2 ; Address of pattern. +USERPATTERN: .res 2 ; User-defined pattern set via CONTROL. +COUNTER: .res 2 +TMP: .res 1 +MASKS: .res 1 +MASKD: .res 1 +XCPOS: .res 1 +HEIGHT: .res 1 + +; Line variables + +CHUNK := X2 ; Used in the line routine +OLDCHUNK := X2+1 ; Ditto +TEMP := tmp4 +TEMP2 := sreg +DX: .res 2 +DY: .res 2 + +; Text output stuff + +TEXTMAGX: .res 1 +TEXTMAGY: .res 1 +TEXTDIR: .res 1 + +; Constants and tables + +.rodata + +DEFPALETTE: .byte $00, $01 ; White on black +PALETTESIZE = * - DEFPALETTE + +BITTAB: .byte $80, $40, $20, $10, $08, $04, $02, $01 +BITCHUNK: .byte $FF, $7F, $3F, $1F, $0F, $07, $03, $01 + +CHARROM := $8000 ; Character ROM base address +CBASE := $9400 ; Color memory base address +SBASE := $1000 ; Screen memory base address +.assert (<SBASE) = $0, error, "Error, SBASE must be page aligned" +VBASE := $1100 ; Video memory base address + +; These numbers are added to Kernal's default VIC settings. +; They make the Video Interface Controller show graphics instead of text. + +.proc VICREGS + .byte <+2 ; Left_edge + 2 + .byte <-2 ; Top_edge - 2 + .byte <-2 ; Columns - 2 + .byte <-(11 << 1) + 1 ; Rows - 11, chars. are 8 x 16 pixels + .byte 0 + .byte <+$0C ; Font_address = $1000 +.endproc + +XADDRS_L: + .repeat COLS, n + .byte <(VBASE + YRES * n) + .endrep + +XADDRS_H: + .repeat COLS, n + .byte >(VBASE + YRES * n) + .endrep + +MASKS_LEFT: + .byte %11111111 +MASKD_RIGHT: + .byte %01111111 + .byte %00111111 + .byte %00011111 + .byte %00001111 + .byte %00000111 + .byte %00000011 + .byte %00000001 +MASKD_LEFT: + .byte %00000000 +MASKS_RIGHT: + .byte %10000000 + .byte %11000000 + .byte %11100000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %11111110 + .byte %11111111 + +PATTERN_EMPTY: + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + +PATTERN_SOLID: + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO +; + +.proc INSTALL + +; Reset user-defined pattern. + + lda #$00 + sta PATTERN + sta PATTERN+1 + rts +.endproc + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL; but is probably empty most of the time. +; +; Must set an error code: NO +; + +.proc UNINSTALL + rts +.endproc + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once, so any code that is needed +; to initialize variables and so on must go here. Setting the palette +; is not needed because that is done by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active; so, there is no need to protect against that. +; +; Must set an error code: YES +; + +.proc INIT + +; Initialize variables + + ldy #$FF + sty BITMASK + iny ; (ldy #$00) + sty ERROR ; Set to TGI_ERR_OK + +; Make screen columns. + + sty tmp2 + lda #>SBASE + sta tmp2+1 + clc + ldx #$10 + +@NEXT_ROW: + txa + +@NEXT_COLUMN: + sta (tmp2),y + iny + adc #ROWS + bcc @NEXT_COLUMN + +; Step to next row on screen. + + inx + cpx #ROWS+$10 + bne @NEXT_ROW + +; Set up VIC. + + ldx #.sizeof(VICREGS) - 1 +@L2: clc + lda $EDE4,x + adc VICREGS,x + sta VIC,x + dex + bpl @L2 + + jmp CLEAR +.endproc + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active; +; so, there is no need to protect against that. +; +; Must set an error code: NO +; + +.proc DONE + jmp $E518 ; Kernal console init. +.endproc + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in A, and clear it. + +.proc GETERROR + ldx #TGI_ERR_OK + lda ERROR + stx ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES +; + +.proc CONTROL + +; Set user-defined pattern. + + cmp #TGI_IOCTL_VIC20_SET_PATTERN + bne @INVALID_FUNC + + lda ptr1 + sta USERPATTERN + lda ptr1+1 + sta USERPATTERN+1 + + lda #TGI_ERR_OK + .byte $2C ;(bit $1234) + +; Return with error code for invalid function index. + +@INVALID_FUNC: + lda #TGI_ERR_INV_FUNC + sta ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CLEAR: Clears the screen. All pixels are set to the background color. +; +; Must set an error code: NO +; + +.proc CLEAR + lda #%00000000 + tay ; (ldy #$00) +@L1: sta VBASE + $0000,y + sta VBASE + $0100,y + sta VBASE + $0200,y + sta VBASE + $0300,y + sta VBASE + $0400,y + sta VBASE + $0500,y + sta VBASE + $0600,y + sta VBASE + $0700,y + sta VBASE + $0800,y + sta VBASE + $0900,y + sta VBASE + $0A00,y + sta VBASE + $0B00,y + sta VBASE + $0C00,y + sta VBASE + $0D00,y + sta VBASE + $0E00,y + iny + bne @L1 + rts +.endproc + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n). +; The page number already is checked, to be valid, by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) +; + +.proc SETVIEWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked, to be valid, by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) +; + +.proc SETDRAWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) +; + +.proc SETCOLOR + sta CURCOL + tax + beq @L1 + lda #$FF +@L1: sta BITMASK + rts +.endproc + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES +; + +.proc SETPALETTE + ldy #PALETTESIZE - 1 +@L1: lda (ptr1),y ; Copy the palette + and #$0F ; Make a valid color + sta PALETTE,y + dey + bpl @L1 + +; Initialize the color map with the new color settings. + + iny ; Set .Y to $00 + lda PALETTE+1 ; Foreground color +@L2: sta CBASE + $0000,y + sta CBASE + $0100,y + iny + bne @L2 + + lda PALETTE ; Background color + asl a ; Put it in high nybble + asl a + asl a + asl a + sta tmp2 + lda VIC_COLOR + and #$0F + ora tmp2 + sta VIC_COLOR + +; Done, reset the error code + + ;ldy #TGI_ERR_OK ; (Already zero) + sty ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in ".XA". Even drivers that cannot +; set the palette should return the default palette here; so, there's no +; way for this function to fail. +; +; Must set an error code: NO +; + +.proc GETPALETTE + lda #<PALETTE + ldx #>PALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in ".XA". All +; drivers should return something reasonable here, even drivers that don't +; support palettes; otherwise, the caller has no way to determine the colors +; of the (not changeable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) +; + +.proc GETDEFPALETTE + lda #<DEFPALETTE + ldx #>DEFPALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area; so, there's no need for clipping inside this function. +; +; Must set an error code: NO +; + +.proc SETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + eor BITMASK + and BITTAB,X + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel; and return it in ".XA". The +; co-ordinates passed to this function never are outside the visible screen +; area; so, there's no need for clipping inside this function. + + +.proc GETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + and BITTAB,X + beq @L1 + iny + tya ; Get color value into .A +@L1: ldx #>$0000 ; Clear high byte + rts +.endproc + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scalings in x and y +; directions are passed in .X/.Y; the text direction is passed in .A. +; +; Must set an error code: NO +; + +.proc TEXTSTYLE + stx TEXTMAGX + sty TEXTMAGY + sta TEXTDIR + rts +.endproc + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO +; + +.proc OUTTEXT + rts +.endproc + +; ------------------------------------------------------------------------ +; Calculate address and X offset in char. line, to plot the pixel at X1/Y1. + +.proc CALC + lda X1+1 + bne @L1 + lda Y1+1 + bne @L1 + + lda X1 + lsr + lsr + lsr + tay + + lda XADDRS_L,y + clc + adc Y1 + sta POINT + lda XADDRS_H,y + adc #$00 + sta POINT+1 + + lda X1 + and #7 + tax + +@L1: rts +.endproc + +; ------------------------------------------------------------------------ +; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and +; X2/Y2 = ptr3/ptr4 using the current drawing color. +; +; X1,X2 etc. are set up above (x2=LINNUM in particular) +; Format is LINE x2,y2,x1,y1 +; +; Must set an error code: NO +; + +.proc LINE + +@CHECK: lda X2 ; Make sure x1 < x2 + sec + sbc X1 + tax + lda X2+1 + sbc X1+1 + bpl @CONT + lda Y2 ; If not, swap P1 and P2 + ldy Y1 + sta Y1 + sty Y2 + lda Y2+1 + ldy Y1+1 + sta Y1+1 + sty Y2+1 + lda X1 + ldy X2 + sty X1 + sta X2 + lda X2+1 + ldy X1+1 + sta X1+1 + sty X2+1 + bcc @CHECK + +@CONT: sta DX+1 + stx DX + + ldx #$C8 ; INY + lda Y2 ; Calculate dy + sec + sbc Y1 + tay + lda Y2+1 + sbc Y1+1 + bpl @DYPOS ; Is y2 >= y1? + lda Y1 ; Otherwise, dy = y1 - y2 + sec + sbc Y2 + tay + ldx #$88 ; DEY + +@DYPOS: sty DY ; 8-bit DY -- FIX ME? + stx YINCDEC + stx XINCDEC + + lda X1 + lsr + lsr + lsr + tay + lda XADDRS_L,y + sta POINT + lda XADDRS_H,y + sta POINT+1 + ldy Y1 + + lda X1 + and #7 + tax + lda BITCHUNK,X + sta OLDCHUNK + sta CHUNK + + ldx DY + cpx DX ; Who's bigger: dy or dx? + bcc STEPINX ; If dx, then... + lda DX+1 + bne STEPINX + +; +; Big steps in Y +; +; X is now counter, Y is y co-ordinate +; +; On entry, X=DY=number of loop iterations, and Y=Y1 +STEPINY: + lda #$00 + sta OLDCHUNK ; So plotting routine will work right + lda CHUNK + lsr ; Strip the bit + eor CHUNK + sta CHUNK + txa + bne @CONT ; If dy=0, it's just a point + inx +@CONT: lsr ; Init. counter to dy/2 +; +; Main loop +; +YLOOP: sta TEMP + + lda (POINT),y ; Plot + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y +YINCDEC: + iny ; Advance Y co-ordinate + lda TEMP ; Restore A + sec + sbc DX + bcc YFIXX +YCONT: dex ; X is counter + bne YLOOP +YCONT2: lda (POINT),y ; Plot endpoint + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y + rts + +YFIXX: ; x=x+1 + adc DY + lsr CHUNK + bne YCONT ; If we pass a column boundary, + ror CHUNK ; then reset CHUNK to $80 + sta TEMP2 + lda POINT + adc #YRES + sta POINT + bcc @CONT + inc POINT+1 +@CONT: lda TEMP2 + dex + bne YLOOP + beq YCONT2 + +; +; Big steps in X direction +; +; On entry, X=DY=number of loop iterations, and Y=Y1 + +.bss +COUNTHI: + .byte $00 ; Temporary counter, + ; used only once +.code +STEPINX: + ldx DX + lda DX+1 + sta COUNTHI + cmp #$80 + ror ; Need bit for initialization + sta Y1 ; High byte of counter + txa + bne @CONT ; Could be $100 + dec COUNTHI +@CONT: ror +; +; Main loop +; +XLOOP: lsr CHUNK + beq XFIXC ; If we pass a column boundary... +XCONT1: sbc DY + bcc XFIXY ; Time to step in Y? +XCONT2: dex + bne XLOOP + dec COUNTHI ; High bits set? + bpl XLOOP + + lsr CHUNK ; Advance to last point + jmp LINEPLOT ; Plot the last chunk +; +; CHUNK has passed a column; so, plot and increment pointer; +; and fix up CHUNK, OLDCHUNK. +; +XFIXC: sta TEMP + jsr LINEPLOT + lda #$FF + sta CHUNK + sta OLDCHUNK + lda POINT + clc + adc #YRES + sta POINT + lda TEMP + bcc XCONT1 + inc POINT+1 + jmp XCONT1 +; +; Check to make sure there isn't a high bit; plot chunk; +; and update Y co-ordinate. +; +XFIXY: dec Y1 ; Maybe high bit set + bpl XCONT2 + adc DX + sta TEMP + lda DX+1 + adc #$FF ; Hi byte + sta Y1 + + jsr LINEPLOT ; Plot chunk + lda CHUNK + sta OLDCHUNK + + lda TEMP +XINCDEC: + iny ; Y co-ord + jmp XCONT2 + +; +; Subroutine to plot chunks/points (to save a little +; room, gray hair, etc.) +; +LINEPLOT: ; Plot the line chunk + lda (POINT),Y + eor BITMASK + ora CHUNK + and OLDCHUNK + eor CHUNK + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; In: xpos, ypos, width, height +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so, on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO +; + +.proc BAR + +; Set user pattern if available. + + lda USERPATTERN + ora USERPATTERN+1 + beq @GET_PATTERN_BY_COLOR + + lda USERPATTERN + sta PATTERN + lda USERPATTERN+1 + sta PATTERN+1 + jmp @GOT_PATTERN + +; Determine pattern based on current colour. + +@GET_PATTERN_BY_COLOR: + lda #<PATTERN_SOLID + ldx #>PATTERN_SOLID + ldy CURCOL + bne @L2 + lda #<PATTERN_EMPTY + ldx #>PATTERN_EMPTY +@L2: sta PATTERN + stx PATTERN+1 + +@GOT_PATTERN: + +; Get starting POINT on screen. + + jsr CALC + sty XCPOS + lda POINT ; One off for VFILL/VCOPY. + sec + sbc #1 + sta POINT + bcs @L3 + dec POINT+1 +@L3: + +; Get height for VFILL. + + lda Y2 + sec + sbc Y1 + sta HEIGHT + +; Get rightmost char column. + + lda X2 + and #7 + sta XPOSR + +; Get width in characters. + + lda X2 + lsr + lsr + lsr + sec + sbc XCPOS + beq @DRAW_SINGLE_COLUMN + sta COUNTER + +; Draw left end. + + lda X1 + and #7 + tax + lda MASKD_LEFT,x + sta MASKD + lda MASKS_LEFT,x + sta MASKS + jsr VFILL + jsr INCPOINTX + +; Draw middle. + + dec COUNTER + beq @DRAW_RIGHT_END +@L1: jsr VCOPY + jsr INCPOINTX + dec COUNTER + bne @L1 + +; Draw right end. + +@DRAW_RIGHT_END: + ldx XPOSR + lda MASKD_RIGHT,x + sta MASKD + lda MASKS_RIGHT,x + sta MASKS + jmp VFILL + +; Draw left end. + +@DRAW_SINGLE_COLUMN: + lda X1 + and #7 + tax + ldy XPOSR + lda MASKS_LEFT,x + and MASKS_RIGHT,y + sta MASKS + lda MASKD_LEFT,x + ora MASKD_RIGHT,y + sta MASKD + jmp VFILL +.endproc + +; In: HEIGHT, PATTERN +; MASKS: Source mask (ANDed with pattern). +; MASKD: Destination mask (ANDed with screen). +; POINT: Starting address. +; ------------------------------------------------------------------------ +; Fill column with pattern using masks. +; + +.proc VFILL + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@L1: lda (POINT),y + and MASKD + sta TMP +@MOD_PATTERN: + lda $FFFF,x + and MASKS + ora TMP + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +; In: HEIGHT, PATTERN, POINT +; ------------------------------------------------------------------------ +; Fill column with pattern. +; + +.proc VCOPY + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@MOD_PATTERN: +@L1: lda $FFFF,x + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +.proc INCPOINTX + lda POINT + clc + adc #YRES + sta POINT + bcc @L1 + inc POINT+1 +@L1: rts +.endproc diff --git a/libsrc/vic20/tgi_stat_stddrv.s b/libsrc/vic20/tgi_stat_stddrv.s new file mode 100644 index 000000000..6a94d66aa --- /dev/null +++ b/libsrc/vic20/tgi_stat_stddrv.s @@ -0,0 +1,8 @@ +; +; Address of the static standard TGI driver +; +; const void tgi_static_stddrv[]; +; + + .import _vic20_hi_tgi + .export _tgi_static_stddrv := _vic20_hi_tgi diff --git a/libsrc/vic20/tgi_stddrv.s b/libsrc/vic20/tgi_stddrv.s new file mode 100644 index 000000000..d56af37e7 --- /dev/null +++ b/libsrc/vic20/tgi_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard TGI driver +; +; 2018-03-11, Sven Michael Klose +; +; const char tgi_stddrv[]; +; + + .export _tgi_stddrv + +.rodata + +_tgi_stddrv: .asciiz "vic20-hi.tgi" diff --git a/libsrc/vic20/tgihdr.s b/libsrc/vic20/tgihdr.s new file mode 100644 index 000000000..30258162d --- /dev/null +++ b/libsrc/vic20/tgihdr.s @@ -0,0 +1,67 @@ +; +; This code sits immediately after the BASIC stub program. +; Therefore, it's executed by that stub. +; +; 2018-04-17, Greg King +; + + .export __TGIHDR__ : abs = 1 ; Mark as TGI housekeeper + + .import __MAIN_LAST__ + .importzp ptr1, ptr2, tmp1 + + +basic_reset := $C000 ; vector to BASIC's cold-start code + +; This code moves the program to $2000. That move allows $1000 - $1FFF +; to be used by the TGI driver to hold its graphics data. + +.segment "TGI1HDR" + + lda #<(tgi1end + prog_size) ; source + ldx #>(tgi1end + prog_size) + sta ptr1 + stx ptr1+1 + + lda #<(tgi2hdr + prog_size) ; destination + ldx #>(tgi2hdr + prog_size) + sta ptr2 + stx ptr2+1 + + ldx #<~prog_size + lda #>~prog_size ; use -(prog_size + 1) + sta tmp1 + + ldy #<$0000 + +; Copy loop + +@L1: inx ; bump counter's low byte + beq @L4 + +@L2: tya ; will .Y underflow? + bne @L3 + dec ptr1+1 ; yes, do next lower page + dec ptr2+1 +@L3: dey + + lda (ptr1),y + sta (ptr2),y + jmp @L1 + +@L4: inc tmp1 ; bump counter's high byte + bne @L2 + + jmp tgi2hdr ; go to moved program +tgi1end: + +.segment "TGI2HDR" + +tgi2hdr: + jsr tgi2end ; run actual program + jmp (basic_reset) +tgi2end: + +; The length of the TGI program (including the TGI2HDR segment) + +prog_size = __MAIN_LAST__ - tgi2hdr diff --git a/libsrc/zlib/adler32.s b/libsrc/zlib/adler32.s index 8aa641a13..1bb5588db 100644 --- a/libsrc/zlib/adler32.s +++ b/libsrc/zlib/adler32.s @@ -2,7 +2,8 @@ ; 2001-11-18, Piotr Fusik ; 2018-05-20, Christian Kruger ; -; unsigned long __fastcall__ adler32 (unsigned long adler, unsigned char* buf, +; unsigned long __fastcall__ adler32 (unsigned long adler, +; const unsigned char* buf, ; unsigned len); ; diff --git a/libsrc/zlib/crc32.s b/libsrc/zlib/crc32.s index f019736ad..41b5fe9db 100644 --- a/libsrc/zlib/crc32.s +++ b/libsrc/zlib/crc32.s @@ -2,7 +2,8 @@ ; 2001-11-14, Piotr Fusik ; 2018-05-20, Christian Kruger ; -; unsigned long __fastcall__ crc32 (unsigned long crc, unsigned char* buf, +; unsigned long __fastcall__ crc32 (unsigned long crc, +; const unsigned char* buf, ; unsigned len); ; diff --git a/samples/Makefile b/samples/Makefile index adfe870e4..1dc3aef8a 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -151,22 +151,13 @@ else endif # -------------------------------------------------------------------------- -# Lists of executables +# Lists of subdirectories -EXELIST_c64 = \ - ascii \ - enumdevdir \ - fire \ - gunzip65 \ - hello \ - mandelbrot \ - mousedemo \ - multdemo \ - nachtm \ - ovrldemo \ - plasma \ - sieve \ - tgidemo +# disasm depends on cpp +DIRLIST = tutorial geos atari2600 supervision cbm + +# -------------------------------------------------------------------------- +# Lists of executables EXELIST_apple2 = \ ascii \ @@ -198,9 +189,134 @@ EXELIST_atarixl = $(EXELIST_atari) EXELIST_atari2600 = \ atari2600hello - + +EXELIST_atari5200 = \ + notavailable + +EXELIST_atmos = \ + ascii \ + hello \ + mandelbrot \ + sieve + +EXELIST_bbc = \ + notavailable + +EXELIST_c64 = \ + ascii \ + enumdevdir \ + gunzip65 \ + hello \ + mandelbrot \ + mousedemo \ + multdemo \ + ovrldemo \ + sieve \ + tgidemo + +EXELIST_c128 = \ + ascii \ + enumdevdir \ + gunzip65 \ + hello \ + mandelbrot \ + mousedemo \ + sieve \ + tgidemo + +EXELIST_c16 = \ + ascii \ + enumdevdir \ + hello + +EXELIST_cbm510 = \ + ascii \ + gunzip65 \ + hello \ + mousedemo \ + sieve + +EXELIST_cbm610 = \ + ascii \ + gunzip65 \ + hello \ + sieve + +EXELIST_creativision = \ + ascii \ + hello + +EXELIST_cx16 = \ + ascii \ + enumdevdir \ + gunzip65 \ + hello \ + mandelbrot \ + mousedemo \ + sieve \ + tgidemo + +EXELIST_gamate = \ + hello + +EXELIST_geos-cbm = \ + ascii \ + diodemo + +EXELIST_geos-apple = \ + ascii + +EXELIST_lunix = \ + notavailable + +EXELIST_lynx = \ + notavailable + +EXELIST_nes = \ + hello + +EXELIST_osic1p = \ + notavailable + +EXELIST_pce = \ + hello + +EXELIST_pet = \ + ascii \ + enumdevdir \ + hello \ + sieve + +EXELIST_plus4 = \ + ascii \ + enumdevdir \ + gunzip65 \ + hello \ + sieve + +EXELIST_sim6502 = \ + gunzip65 + +EXELIST_sim65c02 = $(EXELIST_sim6502) + EXELIST_supervision = \ - supervisionhello + notavailable + +EXELIST_sym1 = \ + notavailable + +EXELIST_telestrat = \ + ascii \ + gunzip65 \ + hello + +EXELIST_vic20 = \ + ascii \ + enumdevdir \ + hello \ + mandelbrot \ + sieve \ + tgidemo # Unlisted targets will try to build everything. # That lets us learn what they cannot build, and what settings @@ -209,10 +325,23 @@ ifndef EXELIST_$(SYS) EXELIST_$(SYS) := ${patsubst %.c,%,$(wildcard *.c)} endif +define SUBDIR_recipe + +@$(MAKE) -C $(dir) --no-print-directory $@ + +endef # SUBDIR_recipe + # -------------------------------------------------------------------------- # Rules to make the binaries and the disk samples: $(EXELIST_$(SYS)) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: generic samples not available for" $(SYS) +endif disk: $(DISK_$(SYS)) @@ -230,21 +359,41 @@ ovrldemo: overlaydemo.o OVERLAYLIST := $(foreach I,1 2 3,multdemo.$I ovrldemo.$I) +# -------------------------------------------------------------------------- +# TGI programs on the VIC-20 need a special ld65 configuration file. + +ifeq ($(SYS),vic20) +mandelbrot.o: override CFLAGS += -D DYN_DRV=0 +mandelbrot: mandelbrot.o + $(LD) $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib + +# tgidemo needs at least 16K of RAM expansion. +tgidemo.o: override CFLAGS += -D DYN_DRV=0 +tgidemo: tgidemo.o + $(LD) -D __HIMEM__=0x6000 $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib +endif + # -------------------------------------------------------------------------- # Rule to make a CBM disk with all samples. Needs the c1541 program that comes # with the VICE emulator. -define D64_WRITE_recipe +define D64_WRITE_PRG_recipe -$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)) >$(NULLDEV) +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV) -endef # D64_WRITE_recipe +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 samples.d64: samples - @$(C1541) -format samples,AA d64 $@ >$(NULLDEV) - $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_recipe)) - $(foreach file,$(OVERLAYLIST),$(D64_WRITE_recipe)) - $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_recipe)) + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) # -------------------------------------------------------------------------- # Rule to make an Apple II disk with all samples. Needs the AppleCommander @@ -303,11 +452,17 @@ install: $(INSTALL) -d $(DESTDIR)$(samplesdir) $(INSTALL) -d $(DESTDIR)$(samplesdir)/geos $(INSTALL) -d $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -d $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -d $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -d $(DESTDIR)$(samplesdir)/supervision $(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir) - $(INSTALL) -m0644 README $(DESTDIR)$(samplesdir) + $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos $(INSTALL) -m0644 tutorial/*.* $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -m0644 atari2600/*.* $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -m0644 cbm/*.* $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -m0644 supervision/*.* $(DESTDIR)$(samplesdir)/supervision # -------------------------------------------------------------------------- # Packaging rules @@ -324,3 +479,4 @@ mostlyclean: clean: mostlyclean @$(DEL) $(EXELIST_$(SYS)) $(DISK_$(SYS)) 2>$(NULLDEV) @$(DEL) multdemo.? ovrldemo.? 2>$(NULLDEV) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/atari2600/Makefile b/samples/atari2600/Makefile new file mode 100644 index 000000000..a02ec9e80 --- /dev/null +++ b/samples/atari2600/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= atari2600 + +# 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 + SP = $(CC65_HOME)/bin/sp65 +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) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_atari2600 = \ + hello + +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: atari 2600 samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_atari2600) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/atari2600hello.c b/samples/atari2600/hello.c similarity index 100% rename from samples/atari2600hello.c rename to samples/atari2600/hello.c diff --git a/samples/cbm/Makefile b/samples/cbm/Makefile new file mode 100644 index 000000000..989710932 --- /dev/null +++ b/samples/cbm/Makefile @@ -0,0 +1,164 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# 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 + SP = $(CC65_HOME)/bin/sp65 +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) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),) + ifdef CC65_HOME + TARGET_PATH = $(CC65_HOME)/target + else + TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path)) + endif + + # If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make + # has very limited support for paths containing spaces. $(wildcard) is the only function + # that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped + # spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a + # return value consisting of 12 plain words :-(( + # + # Fortunately we can work around that behaviour here because we know that the files we + # are looking for have known extensions. So we can $(filter) the in our example above 12 + # words for file extensions so we come up with 4 path fragments. Then we remove those + # path fragments with $(notdir) from the file names. + # + # So far so good. But here we want to process files from different paths in a single + # recipe further down below and therefore want to prepend the paths to the files with + # $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is). + # Therefore, we need to replace the spaces with some other character temporarily in order + # to have $(foreach) generate one invocation per file. We use the character '?' for that + # purpose here, just because it is known to not be part of file names. + # + # Inside the recipe generated per file we then replace the '?' again with a space. As we + # want to be compatible with cmd.exe for execution we're not using an escaped space but + # rather double-quote the whole path. + # + # Note: The "strange" $(wildcard) further down below just serves the purpose to unescape + # spaces for cmd.exe. This could have as well been done with another $(subst). + + SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH)) + + EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*) + MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*) + TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*) + + EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD)))) + MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU)))) + TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI)))) + + # This one comes with the VICE emulator. + # See http://vice-emu.sourceforge.net/ + C1541 ?= c1541 +endif + +DISK_c64 = samples.d64 + +EXELIST_c64 = \ + fire \ + plasma \ + nachtm + +EXELIST_c128 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm510 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm610 = \ + nachtm + +EXELIST_plus4 = \ + plasma + +EXELIST_c16 = \ + notavailable + +EXELIST_pet = \ + notavailable + +EXELIST_vic20 = \ + notavailable + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +disk: $(DISK_$(SYS)) + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: cbm samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +fire: fire.c + $(CL) -t $(SYS) -O -o fire -m fire.map fire.c +plasma: plasma.c + $(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c +nachtm: nachtm.c + $(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c + +# -------------------------------------------------------------------------- +# Rule to make a CBM disk with all samples. Needs the c1541 program that comes +# with the VICE emulator. + +define D64_WRITE_PRG_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(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 + +samples.d64: samples + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) + +clean: + @$(DEL) $(EXELIST_$(SYS)) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) + @$(DEL) $(DISK_$(SYS)) 2>$(NULLDEV) diff --git a/samples/fire.c b/samples/cbm/fire.c similarity index 100% rename from samples/fire.c rename to samples/cbm/fire.c diff --git a/samples/nachtm.c b/samples/cbm/nachtm.c similarity index 99% rename from samples/nachtm.c rename to samples/cbm/nachtm.c index 0b962fa5d..02801da24 100644 --- a/samples/nachtm.c +++ b/samples/cbm/nachtm.c @@ -972,12 +972,12 @@ static void MakeNiceScreen (void) /* Clear the screen hide the cursor, set colors */ #ifdef __CBM610__ - textcolor (COLOR_WHITE); + (void)textcolor (COLOR_WHITE); #else - textcolor (COLOR_GRAY3); + (void)textcolor (COLOR_GRAY3); #endif - bordercolor (COLOR_BLACK); - bgcolor (COLOR_BLACK); + (void)bordercolor (COLOR_BLACK); + (void)bgcolor (COLOR_BLACK); clrscr (); cursor (0); diff --git a/samples/plasma.c b/samples/cbm/plasma.c similarity index 98% rename from samples/plasma.c rename to samples/cbm/plasma.c index ac17265f3..f48d6dc77 100644 --- a/samples/plasma.c +++ b/samples/cbm/plasma.c @@ -1,7 +1,7 @@ /*****************************************************************************\ ** plasma test program for cc65. ** ** ** -** (w)2001 by groepaz/hitmen ** +** (w)2001 by groepaz ** ** ** ** Cleanup and porting by Ullrich von Bassewitz. ** ** ** @@ -54,7 +54,6 @@ #pragma static-locals (1); - static const unsigned char sinustable[0x100] = { 0x80, 0x7d, 0x7a, 0x77, 0x74, 0x70, 0x6d, 0x6a, 0x67, 0x64, 0x61, 0x5e, 0x5b, 0x58, 0x55, 0x52, @@ -131,8 +130,6 @@ static void doplasma (register unsigned char* scrn) } } - - static void makechar (void) { static const unsigned char bittab[8] = { @@ -147,7 +144,7 @@ static void makechar (void) for (i = 0; i < 8; ++i){ b = 0; for (ii = 0; ii < 8; ++ii) { - if ((rand() & 0xFF) > s) { + if ((rand() & 0xFFu) > s) { b |= bittab[ii]; } } diff --git a/testcode/disasm/.gitignore b/samples/disasm/.gitignore similarity index 100% rename from testcode/disasm/.gitignore rename to samples/disasm/.gitignore diff --git a/samples/disasm/Makefile b/samples/disasm/Makefile new file mode 100644 index 000000000..f1d93f5da --- /dev/null +++ b/samples/disasm/Makefile @@ -0,0 +1,59 @@ +# Sample makefile using a preprocessor against info files +# and the --sync-lines option + +# 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 + DA = $(CC65_HOME)/bin/da65 +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) + DA := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) +endif + +CPP = cpp +#CPPFLAGS = -DTEST_ERROR + +ASMS = fixed.s bank0.s bank1.s +DAIS = fixed.dai bank0.dai bank1.dai + +.SUFFIXES: .da .dai .s + +samples: image.bin $(ASMS) + +$(DAIS): fixed.da + +.da.dai: + $(CPP) -o $@ $(CPPFLAGS) $< + +.dai.s: + $(DA) --sync-lines -o $@ -i $< image.bin + +image.bin: image.s image.cfg + $(CL) -t none -C image.cfg -o image.bin image.s + +clean: + @$(DEL) $(ASMS) 2>$(NULLDEV) + @$(DEL) $(DAIS) 2>$(NULLDEV) + @$(DEL) image.bin 2>$(NULLDEV) + diff --git a/testcode/disasm/bank0.da b/samples/disasm/bank0.da similarity index 100% rename from testcode/disasm/bank0.da rename to samples/disasm/bank0.da diff --git a/testcode/disasm/bank1.da b/samples/disasm/bank1.da similarity index 100% rename from testcode/disasm/bank1.da rename to samples/disasm/bank1.da diff --git a/testcode/disasm/fixed.da b/samples/disasm/fixed.da similarity index 97% rename from testcode/disasm/fixed.da rename to samples/disasm/fixed.da index e8aa03427..f8ad4ba27 100644 --- a/testcode/disasm/fixed.da +++ b/samples/disasm/fixed.da @@ -7,7 +7,7 @@ #ifndef TARGET_BANK #define TARGET_BANK -1 global { - inputoffs $1C010; + inputoffs $0C010; inputsize $4000; startaddr $C000; cpu "6502"; diff --git a/samples/disasm/image.cfg b/samples/disasm/image.cfg new file mode 100644 index 000000000..2225c722f --- /dev/null +++ b/samples/disasm/image.cfg @@ -0,0 +1,15 @@ + +MEMORY { + HEADER: file = %O, start = $0000, size = $0010, fill = yes; + BANK00: file = %O, start = $8000, size = $4000, fill = yes; + BANK01: file = %O, start = $8000, size = $4000, fill = yes; + BANK02: file = %O, start = $8000, size = $4000, fill = yes; + FIXED: file = %O, start = $c000, size = $4000, fill = yes; +} +SEGMENTS { + HDR: load = HEADER, type = rw, optional = yes, define = yes; + BANK0: load = BANK00, type = rw, optional = yes, define = yes; + BANK1: load = BANK01, type = rw, optional = yes, define = yes; + BANK2: load = BANK02, type = rw, optional = yes, define = yes; + FIX: load = FIXED, type = rw, optional = yes, define = yes; +} diff --git a/samples/enumdevdir.c b/samples/enumdevdir.c index ce2dc99ec..bf1ff3bbb 100644 --- a/samples/enumdevdir.c +++ b/samples/enumdevdir.c @@ -11,36 +11,52 @@ #include <string.h> #include <unistd.h> #include <stdlib.h> +#include <stdbool.h> #include <device.h> #include <dirent.h> #include <cc65.h> -void printdir (char *newdir) +/* returns true for error, false for OK */ +bool printdir (char *newdir) { - char olddir[FILENAME_MAX]; - char curdir[FILENAME_MAX]; + char *olddir; + char *curdir; DIR *dir; struct dirent *ent; char *subdirs = NULL; unsigned dirnum = 0; unsigned num; - getcwd (olddir, sizeof (olddir)); + olddir = malloc (FILENAME_MAX); + if (olddir == NULL) { + perror ("cannot allocate memory"); + return true; + } + + getcwd (olddir, FILENAME_MAX); if (chdir (newdir)) { /* If chdir() fails we just print the ** directory name - as done for files. */ printf (" Dir %s\n", newdir); - return; + free (olddir); + return false; + } + + curdir = malloc (FILENAME_MAX); + if (curdir == NULL) { + perror ("cannot allocate memory"); + return true; } /* We call getcwd() in order to print the ** absolute pathname for a subdirectory. */ - getcwd (curdir, sizeof (curdir)); + getcwd (curdir, FILENAME_MAX); printf (" Dir %s:\n", curdir); + free (curdir); /* Calling opendir() always with "." avoids ** fiddling around with pathname separators. @@ -65,18 +81,27 @@ void printdir (char *newdir) closedir (dir); for (num = 0; num < dirnum; ++num) { - printdir (subdirs + FILENAME_MAX * num); + if (printdir (subdirs + FILENAME_MAX * num)) + break; } free (subdirs); chdir (olddir); + free (olddir); + return false; } void main (void) { unsigned char device; - char devicedir[FILENAME_MAX]; + char *devicedir; + + devicedir = malloc (FILENAME_MAX); + if (devicedir == NULL) { + perror ("cannot allocate memory"); + return; + } /* Calling getfirstdevice()/getnextdevice() does _not_ turn on the motor ** of a drive-type device and does _not_ check for a disk in the drive. @@ -88,7 +113,7 @@ void main (void) /* Calling getdevicedir() _does_ check for a (formatted) disk in a ** floppy-disk-type device and returns NULL if that check fails. */ - if (getdevicedir (device, devicedir, sizeof (devicedir))) { + if (getdevicedir (device, devicedir, FILENAME_MAX)) { printdir (devicedir); } else { printf (" N/A\n"); @@ -100,4 +125,6 @@ void main (void) if (doesclrscrafterexit ()) { getchar (); } + + free (devicedir); } diff --git a/samples/geos/Makefile b/samples/geos/Makefile new file mode 100644 index 000000000..e792c52f1 --- /dev/null +++ b/samples/geos/Makefile @@ -0,0 +1,135 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= geos-cbm + +# If SYS was given on the commandline, redirect "c64" to "geos-cbm" and +# "apple2enh" to "geos-apple" +ifeq ($(origin SYS),command line) + ifeq ($(SYS),c64) + override SYS = geos-cbm + $(info GEOS: c64 -> geos-cbm) + endif + ifeq ($(SYS),apple2enh) + override SYS = geos-apple + $(info GEOS: apple2enh -> geos-apple) + endif +endif + +# 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 + SP = $(CC65_HOME)/bin/sp65 +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) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +DIRLIST = grc + +define SUBDIR_recipe + +@$(MAKE) SYS=$(SYS) -C $(dir) --no-print-directory $@ + +endef # SUBDIR_recipe + +EXELIST_geos-cbm = \ + bitmap-demo.cvt \ + filesel.cvt \ + geosver.cvt \ + getid.cvt \ + hello1.cvt \ + hello2.cvt \ + overlay-demo.cvt \ + vector-demo.cvt \ + yesno.cvt + +EXELIST_geos-apple = \ + bitmap-demo.cvt \ + filesel.cvt \ + hello1.cvt \ + hello2.cvt \ + overlay-demo.cvt \ + vector-demo.cvt \ + yesno.cvt + +# omitted: dialog.c grphstr.c inittab.c menu.c +# TODO: geosconio.cvt rmvprot.cvt + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) +else +samples: + ifeq ($(MAKELEVEL),0) + @echo "info: geos samples not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) + endif +endif + +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 + +filesel.cvt: fileselres.grc filesel.c + $(CL) -t $(SYS) -O -o filesel.cvt -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 + +geosver.cvt: geosverres.grc geosver.c + $(CL) -t $(SYS) -O -o geover.cvt -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 + +hello1.cvt: hello1res.grc hello1.c + $(CL) -t $(SYS) -O -o hello1.cvt -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 + +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 + +rmvprot.cvt: rmvprotres.grc rmvprot.c + $(CL) -t $(SYS) -O -o rmvprot.cvt -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 + +yesno.cvt: yesnores.grc yesno.c + $(CL) -t $(SYS) -O -o yesno.cvt -m yesno.map yesnores.grc yesno.c + + +clean: + @$(DEL) overlay-demores.h 2>$(NULLDEV) + @$(DEL) bitmap.c 2>$(NULLDEV) + @$(DEL) *.cvt 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/geos/geosver.c b/samples/geos/geosver.c index 1402d148e..3d68798a2 100644 --- a/samples/geos/geosver.c +++ b/samples/geos/geosver.c @@ -8,8 +8,8 @@ struct window wholeScreen = {0, SC_PIX_HEIGHT-1, 0, SC_PIX_WIDTH-1}; void main (void) { unsigned char os = get_ostype(); - unsigned char *machine = NULL; - unsigned char *version = NULL; + char *machine = NULL; + char *version = NULL; unsigned char good = 1; SetPattern(0); diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile new file mode 100644 index 000000000..9dd9ebc5e --- /dev/null +++ b/samples/geos/grc/Makefile @@ -0,0 +1,70 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= geos-cbm + +# 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 + GRC = $(CC65_HOME)/bin/grc65 +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) + GRC := $(if $(wildcard ../../../bin/grc65*),../../../bin/grc65,grc65) +endif + +EXELIST_geos-cbm = \ + test.s \ + vlir.cvt + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: + ifeq ($(MAKELEVEL),0) + @echo "info: grc sample not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) + endif +endif + +test.s: test.grc + $(GRC) -s test.s test.grc + +vlir.cvt: vlir.grc vlir0.s vlir1.s vlir2.s +# using seperate calls here for demonstration purposes: + $(GRC) -t $(SYS) -s vlir.s vlir.grc + $(AS) -t $(SYS) vlir.s + $(AS) -t $(SYS) vlir0.s + $(AS) -t $(SYS) vlir1.s + $(AS) -t $(SYS) vlir2.s + $(LD) -t $(SYS) -o vlir.cvt vlir.o vlir0.o vlir1.o vlir2.o $(SYS).lib + +# you can also do the above in one command: +# $(CL) -t $(SYS) -o vlir.cvt vlir.grc vlir0.s vlir1.s vlir2.s + +clean: + @$(DEL) test.s test.h 2>$(NULLDEV) + @$(DEL) vlir.s vlir.cvt vlir.c vlir.h 2>$(NULLDEV) + @$(DEL) *.o 2>$(NULLDEV) diff --git a/testcode/grc/test.grc b/samples/geos/grc/test.grc similarity index 100% rename from testcode/grc/test.grc rename to samples/geos/grc/test.grc diff --git a/testcode/grc/vlir.grc b/samples/geos/grc/vlir.grc similarity index 100% rename from testcode/grc/vlir.grc rename to samples/geos/grc/vlir.grc diff --git a/testcode/grc/vlir0.s b/samples/geos/grc/vlir0.s similarity index 91% rename from testcode/grc/vlir0.s rename to samples/geos/grc/vlir0.s index 2e9a3ffd9..fd869835c 100644 --- a/testcode/grc/vlir0.s +++ b/samples/geos/grc/vlir0.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" ; import load addresses for all VLIR chains ; these labels are defined upon linking with ld65 diff --git a/testcode/grc/vlir1.s b/samples/geos/grc/vlir1.s similarity index 82% rename from testcode/grc/vlir1.s rename to samples/geos/grc/vlir1.s index eae34565e..170442af7 100644 --- a/testcode/grc/vlir1.s +++ b/samples/geos/grc/vlir1.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" ; export names of functions that will be used in the main program diff --git a/testcode/grc/vlir2.s b/samples/geos/grc/vlir2.s similarity index 78% rename from testcode/grc/vlir2.s rename to samples/geos/grc/vlir2.s index 9d180c847..8dfb79464 100644 --- a/testcode/grc/vlir2.s +++ b/samples/geos/grc/vlir2.s @@ -5,10 +5,10 @@ ; similar to vlir1.s except the fact that this is chain #2 - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" .export OVERLAY2_Function1 .export OVERLAY2_Function2 diff --git a/samples/mandelbrot.c b/samples/mandelbrot.c index d7291c5b5..519f3d823 100644 --- a/samples/mandelbrot.c +++ b/samples/mandelbrot.c @@ -31,6 +31,7 @@ /* Workaround missing clock stuff */ #ifdef __APPLE2__ # define clock() 0 +# undef CLK_TCK # define CLK_TCK 1 #endif @@ -51,6 +52,7 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, register signed short r, r1, i; register signed short xs, ys, xx, yy; register signed short x, y; + register unsigned char maxcol = MAXCOL; /* Calc stepwidth */ xs = ((x2 - x1) / (SCREEN_X)); @@ -76,10 +78,15 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, if (count == maxiterations) { tgi_setcolor (0); } else { - if (MAXCOL == 2) { + switch (maxcol) { + case 2: tgi_setcolor (1); - } else { - tgi_setcolor (count % MAXCOL); + break; + case 0: /* 256 colors */ + tgi_setcolor (count); + break; + default: + tgi_setcolor (count % maxcol); } } /* Set pixel */ diff --git a/samples/README b/samples/readme.txt similarity index 84% rename from samples/README rename to samples/readme.txt index 73d048fca..56b275764 100644 --- a/samples/README +++ b/samples/readme.txt @@ -10,14 +10,20 @@ Please note: similar systems. If you're using Windows, then consider installing Cygwin or MSys2. - * The makefile specifies the C64 as the default target system because all - but three of the programs run on that platform. When compiling for another - system, you will have to change the line that specifies the target system - at the top of the makefile, specify the system with SYS=<target> on the - make command line, or set a SYS environment variable. + * The makefile specifies the C64 as the default target system because most + of the programs run on that platform. When compiling for another system, + you will have to change the line that specifies the target system at the + top of the makefile, specify the system with SYS=<target> on the make + command line, or set a SYS environment variable. For example: + + make SYS=apple2 * Use "make disk" to build a disk image with all sample programs. + * All programs in the root of the "samples" directory have been written to + be portable and work on more than one target. Programs that are specific + to a certain target live in a subdirectory with the name of the target. + List of supplied sample programs: ----------------------------------------------------------------------------- @@ -27,11 +33,6 @@ Description: Shows the ASCII (or ATASCII, PETSCII) codes of typed <greg.king5@verizon.com>. Platforms: All platforms with conio or stdio (compile time configurable). ------------------------------------------------------------------------------ -Name: atari2600hello -Description: A "Hello world" type program. -Platforms: Runs on only the Atari 2600 Video Console System. - ----------------------------------------------------------------------------- Name: diodemo Description: A disc copy program written and contributed by Oliver @@ -48,12 +49,6 @@ Platforms: All systems with device enumeration and directory access (currently the Commodore machines, the Commander X16, and the Apple ][). ------------------------------------------------------------------------------ -Name: fire -Description: Another graphics demo written by groepaz/hitmen. -Platforms: The program currently is running on only the C64, but should - be portable to the C128 and CBM510 (and maybe more machines). - ----------------------------------------------------------------------------- Name: gunzip65 Description: A gunzip utility for 6502-based machines, written by Piotr @@ -72,8 +67,8 @@ Platforms: Runs on all platforms that support conio, which means: ----------------------------------------------------------------------------- Name: mandelbrot Description: A mandelbrot demo using integer arithmetic. The demo was - written by groepaz/hitmen, and converted to cc65 using TGI - graphics by Stephan Haubenthal. + written by groepaz, and converted to cc65 using TGI graphics + by Stephan Haubenthal. Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. @@ -93,13 +88,6 @@ Platforms: All systems with an overlay linker config., disk directory access, and EMD support (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: nachtm -Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. -Platforms: All systems that have the Commodore SID (Sound Interface - Device): - C64, C128, CBM510, CBM610. - ----------------------------------------------------------------------------- Name: overlaydemo Description: Shows how to load overlay files from disk. Written and @@ -107,13 +95,6 @@ Description: Shows how to load overlay files from disk. Written and Platforms: All systems with an overlay linker config. (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: plasma -Description: A fancy graphics demo written by groepaz/hitmen. -Platforms: The program needs a VIC-II or a TED, so it runs on the following - systems: - C64, C128, CBM510, Plus/4. - ----------------------------------------------------------------------------- Name: sieve Description: Implements the "Sieve of Eratosthenes" as a way to find all @@ -124,11 +105,6 @@ Platforms: All systems with conio and clock support: Commander X16, Apple ][ (without timing due to missing clock support). ------------------------------------------------------------------------------ -Name: supervisionhello -Description: A "Hello world" type program. -Platforms: Runs on only the Watara Supervision game console. - ----------------------------------------------------------------------------- Name: tgidemo Description: Shows some of the graphics capabilities of the "Tiny Graphics @@ -136,3 +112,46 @@ Description: Shows some of the graphics capabilities of the "Tiny Graphics Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. + +============================================================================= + +Platform specific samples follow: + +atari 2600: +----------- + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Atari 2600 Video Console System. +----------------------------------------------------------------------------- + +cbm: +---- + +Name: fire +Description: Another graphics demo written by groepaz. +Platforms: C64, C128, CBM510 + +----------------------------------------------------------------------------- +Name: nachtm +Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. +Platforms: All systems that have the Commodore SID (Sound Interface + Device): + C64, C128, CBM510, CBM610. + +----------------------------------------------------------------------------- +Name: plasma +Description: A fancy graphics demo written by groepaz. +Platforms: The program needs a VIC-II or a TED, so it runs on the following + systems: + C64, C128, CBM510, Plus/4. +----------------------------------------------------------------------------- + + +supervision: +------------ + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Watara Supervision game console. +---------------------------------------------------------------------------- diff --git a/samples/supervision/Makefile b/samples/supervision/Makefile new file mode 100644 index 000000000..5829b3f01 --- /dev/null +++ b/samples/supervision/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= supervision + +# 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 + SP = $(CC65_HOME)/bin/sp65 +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) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_supervision = \ + hello + +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: supervision samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_supervision) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/supervision/hello.c b/samples/supervision/hello.c new file mode 100644 index 000000000..ed03e82f3 --- /dev/null +++ b/samples/supervision/hello.c @@ -0,0 +1,95 @@ +/*****************************************************************************/ +/* */ +/* Watara Supervision sample C program */ +/* */ +/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ +/* Greg King (greg.king5@verizon.net), 2021 */ +/* */ +/*****************************************************************************/ + +#include <supervision.h> +#include <string.h> + +/* Number of words per screen line (Remark: Last 4 words aren't displayed) */ +#define WORDS_PER_LINE (160/8+4) + +struct sv_vram { + unsigned int v[160/8][8][WORDS_PER_LINE]; +}; +#define SV_VRAM ((*(struct sv_vram *)0x4000).v) + +/* Character definitions in 8x8 format */ +/* That format gives us a screen of 20 columns and 20 rows */ +static const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; +static const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; +static const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; +static const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; +static const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; +static const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; +static const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; + +static void clear_screen(void) +{ + memset(SV_VIDEO, 0, 0x2000); +} + +/* Necessary conversion to have 2 bits per pixel with darkest hue */ +/* Remark: The Supervision uses 2 bits per pixel, and bits are mapped into pixels in reversed order */ +static unsigned int __fastcall__ double_reversed_bits(unsigned char) +{ + __asm__("stz ptr2"); + __asm__("stz ptr2+1"); + __asm__("ldy #$08"); +L1: __asm__("lsr a"); + __asm__("php"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("plp"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("dey"); + __asm__("bne %g", L1); + __asm__("lda ptr2"); + __asm__("ldx ptr2+1"); + return __AX__; +} + +static void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) +{ + unsigned char k; + + for(k=0;k<8;++k) + { + SV_VRAM[y][k][x] = double_reversed_bits(ch[k]); + } +} + +static void init_lcd(void) +{ + SV_LCD.width = 160; + SV_LCD.height = 160; +} + +static void hello(unsigned char x, unsigned char y) +{ + display_char(x+ 0,y,h_char); + display_char(x+ 1,y,e_char); + display_char(x+ 2,y,l_char); + display_char(x+ 3,y,l_char); + display_char(x+ 4,y,o_char); + + display_char(x+ 6,y,w_char); + display_char(x+ 7,y,o_char); + display_char(x+ 8,y,r_char); + display_char(x+ 9,y,l_char); + display_char(x+10,y,d_char); +} + +void main(void) +{ + init_lcd(); + clear_screen(); + + hello(2,3); + hello(7,16); +} diff --git a/samples/supervisionhello.c b/samples/supervisionhello.c deleted file mode 100755 index db2b5f66c..000000000 --- a/samples/supervisionhello.c +++ /dev/null @@ -1,91 +0,0 @@ -/*****************************************************************************/ -/* */ -/* Watara Supervision sample C program */ -/* */ -/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ -/* */ -/*****************************************************************************/ - -#include <supervision.h> -#include <peekpoke.h> - -// Number of bytes per screen line (Remark: Last 8 bytes are not displayed) -#define BYTES_PER_LINE 48 - -// Character definitions in 8x8 format -const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; -const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; -const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; -const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; -const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; -const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; -const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; - -void clear_screen(void) -{ - unsigned short i; - - for(i=0;i<0x2000;++i) - { - POKE(SV_VIDEO+i,0); - } -} - -// Necessary conversion to have 2 bits per pixel with darkest hue -// Remark: The Supervision uses 2 bits per pixel and bits are mapped into pixel in reversed order -unsigned char reversed_map_one_to_two_lookup[16] = -{ - 0x00, 0xC0, 0x30, 0xF0, 0x0C, 0xCC, 0x3C, 0xFC, - 0x03, 0xC3, 0x33, 0xF3, 0x0F, 0xCF, 0x3F, 0xFF -}; - -unsigned char left_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n >> 4]; -} - -unsigned char right_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n&0x0F]; -} - -void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) -{ - unsigned char k; - - for(k=0;k<8;++k) - { \ - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)] = left_map_one_to_two(ch[k]); - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)+1] = right_map_one_to_two(ch[k]); - } -} - -void init_lcd(void) -{ - SV_LCD.width = 160; - SV_LCD.height = 160; -} - -int main() -{ - init_lcd(); - clear_screen(); - - display_char(3,2, h_char); - display_char(3,3, e_char); - display_char(3,4, l_char); - display_char(3,5, l_char); - display_char(3,6, o_char); - - display_char(3,8, w_char); - display_char(3,9, o_char); - display_char(3,10,r_char); - display_char(3,11,l_char); - display_char(3,12,d_char); - - while(1) {}; - - return 0; -} - - diff --git a/samples/tgidemo.c b/samples/tgidemo.c index 93e91899b..b431cfb98 100644 --- a/samples/tgidemo.c +++ b/samples/tgidemo.c @@ -70,9 +70,10 @@ static void DoCircles (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_ORANGE }; unsigned char I; - unsigned char Color = COLOR_FORE; - unsigned X = MaxX / 2; - unsigned Y = MaxY / 2; + unsigned char Color = COLOR_BACK; + const unsigned X = MaxX / 2; + const unsigned Y = MaxY / 2; + const unsigned Limit = (X < Y) ? Y : X; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); @@ -82,7 +83,7 @@ static void DoCircles (void) while (!kbhit ()) { Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE; tgi_setcolor (Color); - for (I = 10; I < 240; I += 10) { + for (I = 10; I <= Limit; I += 10) { tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio)); } } @@ -132,7 +133,7 @@ static void DoDiagram (void) tgi_setcolor (COLOR_FORE); tgi_clear (); - /* Determine zero and aplitude */ + /* Determine zero and amplitude */ YOrigin = MaxY / 2; XOrigin = 10; Amp = (MaxY - 19) / 2; @@ -168,16 +169,17 @@ static void DoLines (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLACK }; unsigned X; + const unsigned Min = (MaxX < MaxY) ? MaxX : MaxY; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); tgi_clear (); - for (X = 0; X <= MaxY; X += 10) { - tgi_line (0, 0, MaxY, X); - tgi_line (0, 0, X, MaxY); - tgi_line (MaxY, MaxY, 0, MaxY-X); - tgi_line (MaxY, MaxY, MaxY-X, 0); + for (X = 0; X <= Min; X += 10) { + tgi_line (0, 0, Min, X); + tgi_line (0, 0, X, Min); + tgi_line (Min, Min, 0, Min-X); + tgi_line (Min, Min, Min-X, 0); } cgetc (); diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile new file mode 100644 index 000000000..af5062588 --- /dev/null +++ b/samples/tutorial/Makefile @@ -0,0 +1,92 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# 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_atari2600 = \ + notavailable + +EXELIST_bbc = \ + notavailable + +EXELIST_creativision = \ + notavailable + +EXELIST_gamate = \ + notavailable + +EXELIST_geos-cbm = \ + notavailable + +EXELIST_geos-apple = \ + notavailable + +EXELIST_lunix = \ + notavailable + +EXELIST_lynx = \ + notavailable + +EXELIST_nes = \ + notavailable + +EXELIST_osic1p = \ + notavailable + +EXELIST_pce = \ + notavailable + +EXELIST_supervision = \ + notavailable + +# 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. +ifndef EXELIST_$(SYS) +EXELIST_$(SYS) := ${patsubst %.c,%,$(wildcard *.c)} +endif + +samples: $(EXELIST_$(SYS)) + +hello: hello.c text.s + $(CL) -t $(SYS) -o hello hello.c text.s + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: tutorial sample not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +clean: + @$(DEL) hello 2>$(NULLDEV) diff --git a/src/.gitignore b/src/.gitignore index a2c8bb5d4..3b4d0f7ad 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,4 @@ +.vs/ ipch/ *.suo *.sdf diff --git a/src/ar65.vcxproj b/src/ar65.vcxproj index a57bec813..27d12dadc 100644 --- a/src/ar65.vcxproj +++ b/src/ar65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{5E8C19C6-B167-440C-8BEF-3CBF109CDB49}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>ar65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/ar65/error.c b/src/ar65/error.c index 47ee90aa5..7f1e0f611 100644 --- a/src/ar65/error.c +++ b/src/ar65/error.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ar65/error.h b/src/ar65/error.h index 4052ee3eb..b311287a1 100644 --- a/src/ar65/error.h +++ b/src/ar65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ar65/library.c b/src/ar65/library.c index 4d18168d8..d12fb198a 100644 --- a/src/ar65/library.c +++ b/src/ar65/library.c @@ -36,6 +36,12 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#if defined(_WIN32) +# include <process.h> +#else +# include <sys/types.h> +# include <unistd.h> +#endif /* common */ #include "cmdline.h" @@ -249,9 +255,8 @@ void LibOpen (const char* Name, int MustExist, int NeedTemp) if (NeedTemp) { /* Create the temporary library name */ - NewLibName = xmalloc (strlen (Name) + strlen (".temp") + 1); - strcpy (NewLibName, Name); - strcat (NewLibName, ".temp"); + NewLibName = xmalloc (strlen (Name) + (sizeof (".temp-") - 1) + 2 * sizeof (unsigned int) + 1); + sprintf (NewLibName, "%s.temp-%X", Name, (unsigned int)getpid ()); /* Create the temporary library */ NewLib = fopen (NewLibName, "w+b"); @@ -399,9 +404,11 @@ void LibClose (void) Error ("Problem closing '%s': %s", LibName, strerror (errno)); } if (NewLib && fclose (NewLib) != 0) { - Error ("Problem closing temporary library file: %s", strerror (errno)); + Error ("Problem closing temporary library file '%s': %s", + NewLibName, strerror (errno)); } if (NewLibName && remove (NewLibName) != 0) { - Error ("Problem deleting temporary library file: %s", strerror (errno)); + Error ("Problem deleting temporary library file '%s': %s", + NewLibName, strerror (errno)); } } diff --git a/src/ca65.vcxproj b/src/ca65.vcxproj index df246a59f..3cc6019f2 100644 --- a/src/ca65.vcxproj +++ b/src/ca65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>ca65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\bin\</OutDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <TreatWarningAsError>true</TreatWarningAsError> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <TreatWarningAsError>true</TreatWarningAsError> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/ca65/condasm.c b/src/ca65/condasm.c index b8bda4c7d..6198f4017 100644 --- a/src/ca65/condasm.c +++ b/src/ca65/condasm.c @@ -416,6 +416,16 @@ void DoConditionals (void) CalcOverallIfCond (); break; + case TOK_IFPDTV: + D = AllocIf (".IFPDTV", 1); + NextTok (); + if (IfCond) { + SetIfCond (D, GetCPU() == CPU_6502DTV); + } + ExpectSep (); + CalcOverallIfCond (); + break; + case TOK_IFPSC02: D = AllocIf (".IFPSC02", 1); NextTok (); @@ -470,6 +480,7 @@ int CheckConditionals (void) case TOK_IFP4510: case TOK_IFP816: case TOK_IFPC02: + case TOK_IFPDTV: case TOK_IFPSC02: case TOK_IFREF: DoConditionals (); diff --git a/src/ca65/ea.h b/src/ca65/ea.h index 9a038047c..d861e9a6c 100644 --- a/src/ca65/ea.h +++ b/src/ca65/ea.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ca65/ea65.h b/src/ca65/ea65.h index 066d9b0cc..c5b139572 100644 --- a/src/ca65/ea65.h +++ b/src/ca65/ea65.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ca65/easw16.h b/src/ca65/easw16.h index 81dfd0fa2..b8b06d466 100644 --- a/src/ca65/easw16.h +++ b/src/ca65/easw16.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ca65/enum.h b/src/ca65/enum.h index 7d73a97b8..b954588dd 100644 --- a/src/ca65/enum.h +++ b/src/ca65/enum.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ca65/error.c b/src/ca65/error.c index 69446b3fc..7e1457964 100644 --- a/src/ca65/error.c +++ b/src/ca65/error.c @@ -84,7 +84,7 @@ static void VPrintMsg (const FilePos* Pos, const char* Desc, SB_Terminate (&Msg); /* Format the message header */ - SB_Printf (&S, "%s(%u): %s: ", + SB_Printf (&S, "%s:%u: %s: ", SB_GetConstBuf (GetFileName (Pos->Name)), Pos->Line, Desc); diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 58011eb8f..faeff2026 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -92,7 +92,7 @@ static void PutSEP (const InsDesc* Ins); /* Emit a SEP instruction (65816), track register sizes */ static void PutTAMn (const InsDesc* Ins); -/* Emit a TAMn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TAMn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the ** second operand byte. The TAM instruction is the more generic form, it takes ** an immediate argument. @@ -104,9 +104,9 @@ static void PutTMA (const InsDesc* Ins); */ static void PutTMAn (const InsDesc* Ins); -/* Emit a TMAn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TMAn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the -** second operand byte. The TAM instruction is the more generic form, it takes +** second operand byte. The TMA instruction is the more generic form, it takes ** an immediate argument. */ @@ -303,6 +303,91 @@ static const struct { } }; +/* Instruction table for the 6502 with DTV extra opcodes (DTV) and +** those illegal instructions (X) which are supported by DTV. +** Note: illegals opcodes which contain more subinstructions +** (ASO, DCM, LSE, LXA, SBX and SHS) are not enlisted. +*/ +static const struct { + unsigned Count; + InsDesc Ins[71]; +} InsTab6502DTV = { + sizeof (InsTab6502DTV.Ins) / sizeof (InsTab6502DTV.Ins[0]), + { + { "ADC", 0x080A26C, 0x60, 0, PutAll }, + { "ALR", 0x0800000, 0x4B, 0, PutAll }, /* X */ + { "ANC", 0x0800000, 0x0B, 0, PutAll }, /* X */ + { "AND", 0x080A26C, 0x20, 0, PutAll }, + { "ANE", 0x0800000, 0x8B, 0, PutAll }, /* X */ + { "ARR", 0x0800000, 0x6B, 0, PutAll }, /* X */ + { "ASL", 0x000006e, 0x02, 1, PutAll }, + { "AXS", 0x0800000, 0xCB, 0, PutAll }, /* X */ + { "BCC", 0x0020000, 0x90, 0, PutPCRel8 }, + { "BCS", 0x0020000, 0xb0, 0, PutPCRel8 }, + { "BEQ", 0x0020000, 0xf0, 0, PutPCRel8 }, + { "BIT", 0x000000C, 0x00, 2, PutAll }, + { "BMI", 0x0020000, 0x30, 0, PutPCRel8 }, + { "BNE", 0x0020000, 0xd0, 0, PutPCRel8 }, + { "BPL", 0x0020000, 0x10, 0, PutPCRel8 }, + { "BRA", 0x0020000, 0x12, 0, PutPCRel8 }, /* DTV */ + { "BRK", 0x0000001, 0x00, 0, PutAll }, + { "BVC", 0x0020000, 0x50, 0, PutPCRel8 }, + { "BVS", 0x0020000, 0x70, 0, PutPCRel8 }, + { "CLC", 0x0000001, 0x18, 0, PutAll }, + { "CLD", 0x0000001, 0xd8, 0, PutAll }, + { "CLI", 0x0000001, 0x58, 0, PutAll }, + { "CLV", 0x0000001, 0xb8, 0, PutAll }, + { "CMP", 0x080A26C, 0xc0, 0, PutAll }, + { "CPX", 0x080000C, 0xe0, 1, PutAll }, + { "CPY", 0x080000C, 0xc0, 1, PutAll }, + { "DEC", 0x000006C, 0x00, 3, PutAll }, + { "DEX", 0x0000001, 0xca, 0, PutAll }, + { "DEY", 0x0000001, 0x88, 0, PutAll }, + { "EOR", 0x080A26C, 0x40, 0, PutAll }, + { "INC", 0x000006c, 0x00, 4, PutAll }, + { "INX", 0x0000001, 0xe8, 0, PutAll }, + { "INY", 0x0000001, 0xc8, 0, PutAll }, + { "JMP", 0x0000808, 0x4c, 6, PutJMP }, + { "JSR", 0x0000008, 0x20, 7, PutAll }, + { "LAS", 0x0000200, 0xBB, 0, PutAll }, /* X */ + { "LAX", 0x080A30C, 0xA3, 11, PutAll }, /* X */ + { "LDA", 0x080A26C, 0xa0, 0, PutAll }, + { "LDX", 0x080030C, 0xa2, 1, PutAll }, + { "LDY", 0x080006C, 0xa0, 1, PutAll }, + { "LSR", 0x000006F, 0x42, 1, PutAll }, + { "NOP", 0x080006D, 0x00, 10, PutAll }, /* X */ + { "ORA", 0x080A26C, 0x00, 0, PutAll }, + { "PHA", 0x0000001, 0x48, 0, PutAll }, + { "PHP", 0x0000001, 0x08, 0, PutAll }, + { "PLA", 0x0000001, 0x68, 0, PutAll }, + { "PLP", 0x0000001, 0x28, 0, PutAll }, + { "RLA", 0x000A26C, 0x23, 0, PutAll }, /* X */ + { "ROL", 0x000006F, 0x22, 1, PutAll }, + { "ROR", 0x000006F, 0x62, 1, PutAll }, + { "RRA", 0x000A26C, 0x63, 0, PutAll }, /* X */ + { "RTI", 0x0000001, 0x40, 0, PutAll }, + { "RTS", 0x0000001, 0x60, 0, PutAll }, + { "SAC", 0x0800000, 0x32, 1, PutAll }, /* DTV */ + { "SBC", 0x080A26C, 0xe0, 0, PutAll }, + { "SEC", 0x0000001, 0x38, 0, PutAll }, + { "SED", 0x0000001, 0xf8, 0, PutAll }, + { "SEI", 0x0000001, 0x78, 0, PutAll }, + { "SHA", 0x0002200, 0x93, 1, PutAll }, /* X */ + { "SHX", 0x0000200, 0x9e, 1, PutAll }, /* X */ + { "SHY", 0x0000040, 0x9c, 1, PutAll }, /* X */ + { "SIR", 0x0800000, 0x42, 1, PutAll }, /* DTV */ + { "STA", 0x000A26C, 0x80, 0, PutAll }, + { "STX", 0x000010c, 0x82, 1, PutAll }, + { "STY", 0x000002c, 0x80, 1, PutAll }, + { "TAX", 0x0000001, 0xaa, 0, PutAll }, + { "TAY", 0x0000001, 0xa8, 0, PutAll }, + { "TSX", 0x0000001, 0xba, 0, PutAll }, + { "TXA", 0x0000001, 0x8a, 0, PutAll }, + { "TXS", 0x0000001, 0x9a, 0, PutAll }, + { "TYA", 0x0000001, 0x98, 0, PutAll } + } +}; + /* Instruction table for the 65SC02 */ static const struct { unsigned Count; @@ -930,6 +1015,7 @@ static const InsTable* InsTabs[CPU_COUNT] = { (const InsTable*) &InsTabNone, (const InsTable*) &InsTab6502, (const InsTable*) &InsTab6502X, + (const InsTable*) &InsTab6502DTV, (const InsTable*) &InsTab65SC02, (const InsTable*) &InsTab65C02, (const InsTable*) &InsTab65816, @@ -1093,7 +1179,7 @@ static int EvalEA (const InsDesc* Ins, EffAddr* A) A->AddrModeSet &= Ins->AddrMode; /* If we have an expression, check it and remove any addressing modes that - ** are too small for the expression size. Since we have to study the + ** are too small for the expression size. Because we have to study the ** expression anyway, do also replace it by a simpler one if possible. */ if (A->Expr) { @@ -1414,7 +1500,7 @@ static void PutSEP (const InsDesc* Ins) static void PutTAMn (const InsDesc* Ins) -/* Emit a TAMn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TAMn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the ** second operand byte. The TAM instruction is the more generic form, it takes ** an immediate argument. @@ -1444,7 +1530,7 @@ static void PutTMA (const InsDesc* Ins) } else { /* Make sure just one bit is set */ if ((Val & (Val - 1)) != 0) { - Error ("Argument to TAM must be a power of two"); + Error ("Argument of TMA must be a power of two"); } } } @@ -1452,9 +1538,9 @@ static void PutTMA (const InsDesc* Ins) static void PutTMAn (const InsDesc* Ins) -/* Emit a TMAn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TMAn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the -** second operand byte. The TAM instruction is the more generic form, it takes +** second operand byte. The TMA instruction is the more generic form, it takes ** an immediate argument. */ { diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 988493976..d6c807035 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -438,9 +438,7 @@ void MacDef (unsigned Style) /* Parse the parameter list */ if (HaveParams) { - while (CurTok.Tok == TOK_IDENT) { - /* Create a struct holding the identifier */ IdDesc* I = NewIdDesc (&CurTok.SVal); @@ -449,6 +447,7 @@ void MacDef (unsigned Style) M->Params = I; } else { IdDesc* List = M->Params; + while (1) { if (SB_Compare (&List->Id, &CurTok.SVal) == 0) { Error ("Duplicate symbol '%m%p'", &CurTok.SVal); @@ -490,6 +489,22 @@ 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) { @@ -512,9 +527,7 @@ void MacDef (unsigned Style) /* Check for a .LOCAL declaration */ if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) { - while (1) { - IdDesc* I; /* Skip .local or comma */ @@ -553,6 +566,7 @@ void MacDef (unsigned Style) if (CurTok.Tok == TOK_IDENT) { unsigned Count = 0; IdDesc* I = M->Params; + while (I) { if (SB_Compare (&I->Id, &CurTok.SVal) == 0) { /* Local param name, replace it */ diff --git a/src/ca65/main.c b/src/ca65/main.c index ab19d0b4d..0eaf4ba6b 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -334,6 +334,10 @@ static void SetSys (const char* Sys) CBMSystem ("__CX16__"); break; + case TGT_SYM1: + NewSymbol ("__SYM1__", 1); + break; + default: AbEnd ("Invalid target name: '%s'", Sys); diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 5b2ef0573..843f5b9d2 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -566,8 +566,8 @@ static void DoBss (void) -static void DoByte (void) -/* Define bytes */ +static void DoByteBase (int EnableTranslation) +/* Define bytes or literals */ { /* Element type for the generated array */ static const char EType[1] = { GT_BYTE }; @@ -579,8 +579,12 @@ static void DoByte (void) /* Parse arguments */ while (1) { if (CurTok.Tok == TOK_STRCON) { - /* A string, translate into target charset and emit */ - TgtTranslateStrBuf (&CurTok.SVal); + /* A string, translate into target charset + if appropriate */ + if (EnableTranslation) { + TgtTranslateStrBuf (&CurTok.SVal); + } + /* Emit */ EmitStrBuf (&CurTok.SVal); NextTok (); } else { @@ -613,6 +617,14 @@ static void DoByte (void) +static void DoByte (void) +/* Define bytes with translation */ +{ + DoByteBase (1); +} + + + static void DoCase (void) /* Switch the IgnoreCase option */ { @@ -1415,6 +1427,14 @@ static void DoList (void) +static void DoLiteral (void) +/* Define bytes without translation */ +{ + DoByteBase (0); +} + + + static void DoLoBytes (void) /* Define bytes, extracting the lo byte from each expression in the list */ { @@ -1552,6 +1572,14 @@ static void DoP4510 (void) +static void DoPDTV (void) +/* Switch to C64DTV CPU */ +{ + SetCPU (CPU_6502DTV); +} + + + static void DoPageLength (void) /* Set the page length for the listing */ { @@ -1560,6 +1588,19 @@ static void DoPageLength (void) +static void DoPopCharmap (void) +/* Restore a charmap */ +{ + if (TgtTranslateStackIsEmpty ()) { + ErrorSkip ("Charmap stack is empty"); + return; + } + + TgtTranslatePop (); +} + + + static void DoPopCPU (void) /* Pop an old CPU setting from the CPU stack */ { @@ -1649,6 +1690,16 @@ static void DoPSC02 (void) +static void DoPushCharmap (void) +/* Save the current charmap */ +{ + if (!TgtTranslatePush ()) { + ErrorSkip ("Charmap stack overflow"); + } +} + + + static void DoPushCPU (void) /* Push the current CPU setting onto the CPU stack */ { @@ -1679,6 +1730,17 @@ static void DoPushSeg (void) +static void DoReferTo (void) +/* Mark given symbol as referenced */ +{ + SymEntry* Sym = ParseAnySymName (SYM_ALLOC_NEW); + if (Sym) { + SymRef (Sym); + } +} + + + static void DoReloc (void) /* Enter relocatable mode */ { @@ -2058,6 +2120,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccKeepToken, DoConditionals }, /* .IFP4510 */ { ccKeepToken, DoConditionals }, /* .IFP816 */ { ccKeepToken, DoConditionals }, /* .IFPC02 */ + { ccKeepToken, DoConditionals }, /* .IFPDTV */ { ccKeepToken, DoConditionals }, /* .IFPSC02 */ { ccKeepToken, DoConditionals }, /* .IFREF */ { ccNone, DoImport }, @@ -2071,6 +2134,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoLineCont }, { ccNone, DoList }, { ccNone, DoListBytes }, + { ccNone, DoLiteral }, { ccNone, DoUnexpected }, /* .LOBYTE */ { ccNone, DoLoBytes }, { ccNone, DoUnexpected }, /* .LOCAL */ @@ -2091,13 +2155,17 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoPageLength }, { ccNone, DoUnexpected }, /* .PARAMCOUNT */ { ccNone, DoPC02 }, + { ccNone, DoPDTV }, + { ccNone, DoPopCharmap }, { ccNone, DoPopCPU }, { ccNone, DoPopSeg }, { ccNone, DoProc }, { ccNone, DoPSC02 }, + { ccNone, DoPushCharmap }, { ccNone, DoPushCPU }, { ccNone, DoPushSeg }, { ccNone, DoUnexpected }, /* .REFERENCED */ + { ccNone, DoReferTo }, /* .REFERTO */ { ccNone, DoReloc }, { ccNone, DoRepeat }, { ccNone, DoRes }, @@ -2173,4 +2241,7 @@ void CheckPseudo (void) if (!IS_IsEmpty (&CPUStack)) { Warning (1, "CPU stack is not empty"); } + if (!TgtTranslateStackIsEmpty ()) { + Warning (1, "Charmap stack is not empty"); + } } diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index 361a817c1..bf0a85183 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -219,6 +219,7 @@ struct DotKeyword { { ".IFP4510", TOK_IFP4510 }, { ".IFP816", TOK_IFP816 }, { ".IFPC02", TOK_IFPC02 }, + { ".IFPDTV", TOK_IFPDTV }, { ".IFPSC02", TOK_IFPSC02 }, { ".IFREF", TOK_IFREF }, { ".IMPORT", TOK_IMPORT }, @@ -233,6 +234,7 @@ struct DotKeyword { { ".LINECONT", TOK_LINECONT }, { ".LIST", TOK_LIST }, { ".LISTBYTES", TOK_LISTBYTES }, + { ".LITERAL", TOK_LITERAL }, { ".LOBYTE", TOK_LOBYTE }, { ".LOBYTES", TOK_LOBYTES }, { ".LOCAL", TOK_LOCAL }, @@ -258,14 +260,19 @@ struct DotKeyword { { ".PAGELENGTH", TOK_PAGELENGTH }, { ".PARAMCOUNT", TOK_PARAMCOUNT }, { ".PC02", TOK_PC02 }, + { ".PDTV", TOK_PDTV }, + { ".POPCHARMAP", TOK_POPCHARMAP }, { ".POPCPU", TOK_POPCPU }, { ".POPSEG", TOK_POPSEG }, { ".PROC", TOK_PROC }, { ".PSC02", TOK_PSC02 }, + { ".PUSHCHARMAP", TOK_PUSHCHARMAP }, { ".PUSHCPU", TOK_PUSHCPU }, { ".PUSHSEG", TOK_PUSHSEG }, { ".REF", TOK_REFERENCED }, { ".REFERENCED", TOK_REFERENCED }, + { ".REFERTO", TOK_REFERTO }, + { ".REFTO", TOK_REFERTO }, { ".RELOC", TOK_RELOC }, { ".REPEAT", TOK_REPEAT }, { ".RES", TOK_RES }, diff --git a/src/ca65/struct.h b/src/ca65/struct.h index 1fdaf9d60..e927c477c 100644 --- a/src/ca65/struct.h +++ b/src/ca65/struct.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ca65/token.h b/src/ca65/token.h index 8998cc162..b8bbb6d6e 100644 --- a/src/ca65/token.h +++ b/src/ca65/token.h @@ -196,6 +196,7 @@ typedef enum token_t { TOK_IFP4510, TOK_IFP816, TOK_IFPC02, + TOK_IFPDTV, TOK_IFPSC02, TOK_IFREF, TOK_IMPORT, @@ -209,6 +210,7 @@ typedef enum token_t { TOK_LINECONT, TOK_LIST, TOK_LISTBYTES, + TOK_LITERAL, TOK_LOBYTE, TOK_LOBYTES, TOK_LOCAL, @@ -229,13 +231,17 @@ typedef enum token_t { TOK_PAGELENGTH, TOK_PARAMCOUNT, TOK_PC02, + TOK_PDTV, + TOK_POPCHARMAP, TOK_POPCPU, TOK_POPSEG, TOK_PROC, TOK_PSC02, + TOK_PUSHCHARMAP, TOK_PUSHCPU, TOK_PUSHSEG, TOK_REFERENCED, + TOK_REFERTO, TOK_RELOC, TOK_REPEAT, TOK_RES, diff --git a/src/cc65.props b/src/cc65.props new file mode 100644 index 000000000..ef5a37fea --- /dev/null +++ b/src/cc65.props @@ -0,0 +1,62 @@ +<Project> + <PropertyGroup Label="UserMacros"> + </PropertyGroup> + <!-- VS2017 compatibility. --> + <PropertyGroup Condition="$(MSBuildVersion.Split('.')[0]) == '15'"> + <PlatformToolset Condition="'$(PlatformToolset)' == ''">v141</PlatformToolset> + <WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion> + </PropertyGroup> + <!-- Common settings. --> + <PropertyGroup> + <PlatformToolset Condition="'$(PlatformToolset)' == ''">v142</PlatformToolset> + <WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0</WindowsTargetPlatformVersion> + <IntDir>$(SolutionDir)..\wrk\$(MSBuildProjectName)\$(Configuration)\</IntDir> + <OutDir>$(SolutionDir)..\bin\</OutDir> + <OutDir Condition="$(MSBuildProjectName) == 'common'">$(IntDir)</OutDir> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0601;WINVER=0x0601;NTDDI_VERSION=0x06010000;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="$(MSBuildProjectName) != 'common'">common</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <AdditionalDependencies Condition="$(MSBuildProjectName) != 'common'">$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <!-- Debug settings. --> + <PropertyGroup Condition="'$(Configuration)'=='Debug'"> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + </ClCompile> + <Link> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <!-- Release settings. --> + <PropertyGroup Condition="'$(Configuration)'=='Release'"> + <LinkIncremental>false</LinkIncremental> + <WholeProgramOptimization>true</WholeProgramOptimization> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> + <ClCompile> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <Optimization>MaxSpeed</Optimization> + <BufferSecurityCheck>false</BufferSecurityCheck> + <ControlFlowGuard>false</ControlFlowGuard> + </ClCompile> + <Link> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> + </Link> + </ItemDefinitionGroup> +</Project> diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index a56ecf880..14500296d 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{B17EDBD5-DC04-4970-9CBD-56A98B6A3FCA}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>cc65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\bin\</OutDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <WarningLevel>Level3</WarningLevel> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> @@ -87,12 +60,15 @@ <ClInclude Include="cc65\codeinfo.h" /> <ClInclude Include="cc65\codelab.h" /> <ClInclude Include="cc65\codeopt.h" /> + <ClInclude Include="cc65\codeoptutil.h" /> <ClInclude Include="cc65\codeseg.h" /> <ClInclude Include="cc65\compile.h" /> <ClInclude Include="cc65\coptadd.h" /> <ClInclude Include="cc65\coptc02.h" /> <ClInclude Include="cc65\coptcmp.h" /> <ClInclude Include="cc65\coptind.h" /> + <ClInclude Include="cc65\coptjmp.h" /> + <ClInclude Include="cc65\coptmisc.h" /> <ClInclude Include="cc65\coptneg.h" /> <ClInclude Include="cc65\coptptrload.h" /> <ClInclude Include="cc65\coptptrstore.h" /> @@ -135,6 +111,7 @@ <ClInclude Include="cc65\shiftexpr.h" /> <ClInclude Include="cc65\stackptr.h" /> <ClInclude Include="cc65\standard.h" /> + <ClInclude Include="cc65\staticassert.h" /> <ClInclude Include="cc65\stdfunc.h" /> <ClInclude Include="cc65\stdnames.h" /> <ClInclude Include="cc65\stmt.h" /> @@ -160,12 +137,15 @@ <ClCompile Include="cc65\codeinfo.c" /> <ClCompile Include="cc65\codelab.c" /> <ClCompile Include="cc65\codeopt.c" /> + <ClCompile Include="cc65\codeoptutil.c" /> <ClCompile Include="cc65\codeseg.c" /> <ClCompile Include="cc65\compile.c" /> <ClCompile Include="cc65\coptadd.c" /> <ClCompile Include="cc65\coptc02.c" /> <ClCompile Include="cc65\coptcmp.c" /> <ClCompile Include="cc65\coptind.c" /> + <ClCompile Include="cc65\coptjmp.c" /> + <ClCompile Include="cc65\coptmisc.c" /> <ClCompile Include="cc65\coptneg.c" /> <ClCompile Include="cc65\coptptrload.c" /> <ClCompile Include="cc65\coptptrstore.c" /> @@ -209,6 +189,7 @@ <ClCompile Include="cc65\shiftexpr.c" /> <ClCompile Include="cc65\stackptr.c" /> <ClCompile Include="cc65\standard.c" /> + <ClCompile Include="cc65\staticassert.c" /> <ClCompile Include="cc65\stdfunc.c" /> <ClCompile Include="cc65\stdnames.c" /> <ClCompile Include="cc65\stmt.c" /> diff --git a/src/cc65/anonname.c b/src/cc65/anonname.c index d02253d82..23891bf0e 100644 --- a/src/cc65/anonname.c +++ b/src/cc65/anonname.c @@ -61,6 +61,17 @@ static const char AnonTag[] = "$anon"; +char* AnonFieldName (char* Buf, const char* Spec, int ANumber) +/* Get a name for an anonymous field of a struct or union. The given buffer is +** expected to be IDENTSIZE characters long. A pointer to the buffer is returned. +*/ +{ + xsprintf (Buf, IDENTSIZE, "%s-%s-%04X", AnonTag, Spec, ANumber); + return Buf; +} + + + char* AnonName (char* Buf, const char* Spec) /* Get a name for an anonymous variable or type. The given buffer is expected ** to be IDENTSIZE characters long. A pointer to the buffer is returned. diff --git a/src/cc65/anonname.h b/src/cc65/anonname.h index dfaa1bdaa..a88b131b5 100644 --- a/src/cc65/anonname.h +++ b/src/cc65/anonname.h @@ -44,6 +44,11 @@ +char* AnonFieldName (char* Buf, const char* Spec, int ANumber); +/* Get a name for an anonymous field of a struct or union. The given buffer is +** expected to be IDENTSIZE characters long. A pointer to the buffer is returned. +*/ + char* AnonName (char* Buf, const char* Spec); /* Get a name for an anonymous variable or type. The given buffer is expected ** to be IDENTSIZE characters long. A pointer to the buffer is returned. diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index 09aee3b92..7d5db75e6 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -42,6 +42,17 @@ /* cc65 */ #include "asmlabel.h" #include "error.h" +#include "segments.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static struct Segments* CurrentFunctionSegment; @@ -51,19 +62,26 @@ -unsigned GetLocalLabel (void) -/* Get an unused label. Will never return zero. */ +void UseLabelPoolFromSegments (struct Segments* Seg) +/* Use the info in segments for generating new label numbers */ { - /* Number to generate unique labels */ - static unsigned NextLabel = 0; + CurrentFunctionSegment = Seg; +} + + + +unsigned GetLocalLabel (void) +/* Get an unused assembler label for the function. Will never return zero. */ +{ + PRECONDITION (CurrentFunctionSegment != 0); /* Check for an overflow */ - if (NextLabel >= 0xFFFF) { + if (CurrentFunctionSegment->NextLabel >= 0xFFFF) { Internal ("Local label overflow"); } /* Return the next label */ - return ++NextLabel; + return ++CurrentFunctionSegment->NextLabel; } @@ -98,3 +116,60 @@ int IsLocalLabelName (const char* Name) /* Local label name */ return 1; } + + + +unsigned GetLocalDataLabel (void) +/* Get an unused local data label. Will never return zero. */ +{ + PRECONDITION (CurrentFunctionSegment != 0); + + /* Check for an overflow */ + if (CurrentFunctionSegment->NextDataLabel >= 0xFFFF) { + Internal ("Local data label overflow"); + } + + /* Return the next label */ + return ++CurrentFunctionSegment->NextDataLabel; +} + + + +const char* LocalDataLabelName (unsigned L) +/* Make a label name from the given data label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ +{ + static char Buf[64]; + sprintf (Buf, "M%04X", L); + return Buf; +} + + + +unsigned GetPooledLiteralLabel (void) +/* Get an unused literal label. Will never return zero. */ +{ + /* Number to generate unique labels */ + static unsigned NextLabel = 0; + + /* Check for an overflow */ + if (NextLabel >= 0xFFFF) { + Internal ("Literal label overflow"); + } + + /* Return the next label */ + return ++NextLabel; +} + + + +const char* PooledLiteralLabelName (unsigned L) +/* Make a litral label name from the given label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ +{ + static char Buf[64]; + sprintf (Buf, "S%04X", L); + return Buf; +} diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index 4a76643a5..dbfe2f443 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -38,14 +38,27 @@ +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +struct Segments; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ +void UseLabelPoolFromSegments (struct Segments* Seg); +/* Use the info in segments for generating new label numbers */ + unsigned GetLocalLabel (void); -/* Get an unused assembler label. Will never return zero. */ +/* Get an unused assembler label for the function. Will never return zero. */ const char* LocalLabelName (unsigned L); /* Make a label name from the given label number. The label name will be @@ -56,6 +69,22 @@ const char* LocalLabelName (unsigned L); int IsLocalLabelName (const char* Name); /* Return true if Name is the name of a local label */ +unsigned GetLocalDataLabel (void); +/* Get an unused local data label. Will never return zero. */ + +const char* LocalDataLabelName (unsigned L); +/* Make a label name from the given data label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ + +unsigned GetPooledLiteralLabel (void); +/* Get an unused literal label. Will never return zero. */ + +const char* PooledLiteralLabelName (unsigned L); +/* Make a litral label name from the given label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ + /* End of asmlabel.h */ diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index ae4308522..d182140fd 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -129,14 +129,13 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) static void ParseByteArg (StrBuf* T, unsigned Arg) /* Parse the %b format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -163,14 +162,13 @@ static void ParseByteArg (StrBuf* T, unsigned Arg) static void ParseWordArg (StrBuf* T, unsigned Arg) /* Parse the %w format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -197,14 +195,13 @@ static void ParseWordArg (StrBuf* T, unsigned Arg) static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) /* Parse the %l format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Convert into a hex number */ xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF); @@ -216,7 +213,9 @@ static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) static void ParseGVarArg (StrBuf* T, unsigned Arg) -/* Parse the %v format specifier */ +/* Parse the %v format specifier. +** ### FIXME: Asm names should be generated in the same place. +*/ { /* Parse the symbol name parameter and check the type */ SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); @@ -228,7 +227,6 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) /* Check for external linkage */ if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) { /* External linkage or a function */ - /* ### FIXME: Asm name should be generated by codegen */ SB_AppendChar (T, '_'); SB_AppendStr (T, Sym->Name); } else if (Sym->Flags & SC_REGISTER) { @@ -237,9 +235,7 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) SB_AppendStr (T, Buf); } else { /* Static variable */ - char Buf [16]; - xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.L.Label); - SB_AppendStr (T, Buf); + SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label)); } } @@ -289,7 +285,7 @@ static void ParseLabelArg (StrBuf* T, unsigned Arg attribute ((unused))) } else { - /* Add a new label symbol if we don't have one until now */ + /* Add a new C label symbol if we don't have one until now */ SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF); /* Append the label name to the buffer */ @@ -328,7 +324,7 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) break; default: - ConstAbsIntExpr (hie1, &Expr); + Expr = NoCodeConstAbsIntExpr (hie1); xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal); SB_AppendStr (T, Buf); break; diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index f0607ac58..05a6d9a96 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -42,156 +42,434 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "stackptr.h" #include "stdnames.h" #include "typecmp.h" #include "typeconv.h" +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Map a generator function and its attributes to a token */ +typedef struct GenDesc { + token_t Tok; /* Token to map to */ + unsigned Flags; /* Flags for generator function */ + void (*Func) (unsigned, unsigned long); /* Generator func */ +} GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -void Assignment (ExprDesc* Expr) -/* Parse an assignment */ +static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) +/* Copy the struct/union represented by RExpr to the one represented by LExpr */ +{ + /* If the size is that of a basic type (char, int, long), we will copy + ** the struct using the primary register, otherwise we will use memcpy. + */ + const Type* ltype = LExpr->Type; + const Type* stype = GetStructReplacementType (ltype); + int UseReg = (stype != ltype); + + if (UseReg) { + /* Back up the address of lhs only if it is in the primary */ + PushAddr (LExpr); + } else { + /* Push the address of lhs as the destination of memcpy */ + ED_AddrExpr (LExpr); + LoadExpr (CF_NONE, LExpr); + g_push (CF_PTR | CF_UNSIGNED, 0); + } + + /* Get the expression on the right of the '=' */ + hie1 (RExpr); + + /* Check for equality of the structs/unions */ + if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) { + TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, + "Incompatible types in assignment to '%s' from '%s'"); + } + + /* Do we copy the value directly using the primary? */ + if (UseReg) { + + /* Check if the value of the rhs is not in the primary yet */ + if (!ED_IsLocPrimary (RExpr)) { + /* Just load the value into the primary as the replacement type. */ + LoadExpr (TypeOf (stype) | CF_FORCECHAR, RExpr); + } + + /* Store it into the location referred in the primary */ + Store (LExpr, stype); + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (LExpr); + + } else { + + /* The rhs cannot happen to be loaded in the primary as it is too big */ + if (!ED_IsLocExpr (RExpr)) { + ED_AddrExpr (RExpr); + LoadExpr (CF_NONE, RExpr); + } + + /* Push the address of the rhs as the source of memcpy */ + g_push (CF_PTR | CF_UNSIGNED, 0); + + /* Load the size of the struct or union into the primary */ + g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, SizeOf (ltype), 0); + + /* Call the memcpy function */ + g_call (CF_FIXARGC, Func_memcpy, 4); + + /* The result is an rvalue referenced in the primary */ + ED_FinalizeRValLoad (LExpr); + + /* Restore the indirection level of lhs */ + ED_IndExpr (LExpr); + } + + /* Clear the tested flag set during loading. This is not neccessary + ** currently (and probably ever) as a struct/union cannot be converted + ** to a boolean in C, but there is no harm to be future-proof. + */ + ED_MarkAsUntested (LExpr); +} + + + +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) +/* Process inc/dec for bit-field */ +{ + int AddrSP; + unsigned Flags; /* Internal codegen flags */ + unsigned Mask; + unsigned ChunkFlags; + const Type* ChunkType; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We 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. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + /* Handle for add and sub */ + if (Val > 0) { + g_inc (Flags | CF_CONST, Val); + } else if (Val < 0) { + g_dec (Flags | CF_CONST, -Val); + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + if (KeepResult == OA_NEED_OLD) { + /* Save the original expression value */ + g_save (ChunkFlags | CF_FORCECHAR); + } + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + if (KeepResult == OA_NEED_OLD) { + /* Restore the original expression value */ + g_restore (ChunkFlags | CF_FORCECHAR); + } +} + + + +static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for bit-field lhs */ { ExprDesc Expr2; - Type* ltype = Expr->Type; + CodeMark PushPos; + int AddrSP; + unsigned Mask; + unsigned Flags; + unsigned ChunkFlags; + const Type* ChunkType; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - /* We must have an lvalue for an assignment */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - } - - /* Check for assignment to const */ - if (IsQualConst (ltype)) { - Error ("Assignment to const"); - } - - /* Skip the '=' token */ - NextToken (); - - /* cc65 does not have full support for handling structs by value. Since - ** assigning structs is one of the more useful operations from this - ** family, allow it here. + /* If the bit-field fits within one byte, do the following operations + ** with bytes. */ - if (IsClassStruct (ltype)) { - - /* Get the size of the left hand side. */ - unsigned Size = SizeOf (ltype); - - /* If the size is that of a basic type (char, int, long), we will copy - ** the struct using the primary register, otherwise we use memcpy. In - ** the former case, push the address only if really needed. + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We 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. */ - int UseReg = 1; - Type* stype; - switch (Size) { - case SIZEOF_CHAR: stype = type_uchar; break; - case SIZEOF_INT: stype = type_uint; break; - case SIZEOF_LONG: stype = type_ulong; break; - default: stype = ltype; UseReg = 0; break; - } - if (UseReg) { - PushAddr (Expr); - } else { - ED_MakeRVal (Expr); - LoadExpr (CF_NONE, Expr); - g_push (CF_PTR | CF_UNSIGNED, 0); - } + ChunkType = Expr->Type + 1; + } - /* Get the expression on the right of the '=' into the primary */ - hie1 (&Expr2); + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } - /* Check for equality of the structs */ - if (TypeCmp (ltype, Expr2.Type) < TC_STRICT_COMPATIBLE) { - Error ("Incompatible types"); - } + /* Get the address on stack for the store */ + PushAddr (Expr); - /* Check if the right hand side is an lvalue */ - if (ED_IsLVal (&Expr2)) { - /* We have an lvalue. Do we copy using the primary? */ - if (UseReg) { - /* Just use the replacement type */ - Expr2.Type = stype; + /* We may need the pushed address later */ + AddrSP = StackPtr; - /* Load the value into the primary */ - LoadExpr (CF_FORCECHAR, &Expr2); + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; - /* Store it into the new location */ - Store (Expr, stype); + if (Gen != 0) { - } else { - - /* We will use memcpy. Push the address of the rhs */ - ED_MakeRVal (&Expr2); - LoadExpr (CF_NONE, &Expr2); - - /* Push the address (or whatever is in ax in case of errors) */ - g_push (CF_PTR | CF_UNSIGNED, 0); - - /* Load the size of the struct into the primary */ - g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, CheckedSizeOf (ltype), 0); - - /* Call the memcpy function */ - g_call (CF_FIXARGC, Func_memcpy, 4); - } - - } else { - - /* We have an rvalue. This can only happen if a function returns - ** a struct, since there is no other way to generate an expression - ** that has a struct as an rvalue result. We allow only 1, 2, and 4 - ** byte sized structs, and do direct assignment. - */ - if (UseReg) { - /* Do the store */ - Store (Expr, stype); - } else { - /* Print a diagnostic */ - Error ("Structs of this size are not supported"); - /* Adjust the stack so we won't run in an internal error later */ - pop (CF_PTR); - } - - } - - } else if (ED_IsBitField (Expr)) { - - CodeMark AndPos; - CodeMark PushPos; - - unsigned Mask; - unsigned Flags; - - /* If the bit-field fits within one byte, do the following operations - ** with bytes. - */ - if (Expr->BitOffs / CHAR_BITS == (Expr->BitOffs + Expr->BitWidth - 1) / CHAR_BITS) { - Expr->Type = type_uchar; - } - - /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); - - /* Assignment to a bit field. Get the address on stack for the store. */ - PushAddr (Expr); - - /* Load the value from the location */ - Expr->Flags &= ~E_BITFIELD; + /* Fetch the lhs into the primary register if needed */ LoadExpr (CF_NONE, Expr); - /* Mask unwanted bits */ - Mask = (0x0001U << Expr->BitWidth) - 1U; - GetCodePos (&AndPos); - g_and (Flags | CF_CONST, ~(Mask << Expr->BitOffs)); - - /* Push it on stack */ + /* Backup them on stack */ GetCodePos (&PushPos); - g_push (Flags, 0); + g_push (Flags & ~CF_FORCECHAR, 0); + + } + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right 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. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + if (Gen == 0) { + + /* Get the value and apply the mask */ + unsigned Val = (unsigned)(Expr2.IVal & Mask); + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* If the value is equal to the mask now, all bits are one, and we + ** can skip the mask operation. + */ + if (Val != Mask) { + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + } + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags | CF_CONST, Val << Expr->Type->A.B.Offs); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Done */ + goto Done; + + } else { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } 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"); + } + } + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); + } else { + Gen->Func ((Flags & ~CF_FORCECHAR) | CF_CONST, Expr2.IVal); + } + } + + } + + } else { + + /* Do 'op' if provided */ + if (Gen != 0) { + + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); + } else { + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + } + + } else { + + /* Do type conversion if necessary */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load rhs into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } + + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + +Done: + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for arithmetic lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + + unsigned Flags; + int MustScale; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type); + + /* Determine the type of the lhs */ + MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && + IsTypePtr (Expr->Type); + + /* Get the address on stack for the store */ + PushAddr (Expr); + + if (Gen == 0) { /* Read the expression on the right side of the '=' */ MarkedExprWithCheck (hie1, &Expr2); @@ -199,70 +477,274 @@ void Assignment (ExprDesc* Expr) /* Do type conversion if necessary. Beware: Do not use char type ** here! */ - TypeConversion (&Expr2, ltype); + TypeConversion (&Expr2, Expr->Type); - /* Special treatment if the value is constant. */ - /* Beware: Expr2 may contain side effects, so there must not be + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } else { + + /* Load the original value if necessary */ + LoadExpr (CF_NONE, Expr); + + /* Push lhs on stack */ + GetCodePos (&PushPos); + g_push (Flags, 0); + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right 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. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be ** code generated for Expr2. */ if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - /* Get the value and apply the mask */ - unsigned Val = (unsigned) (Expr2.IVal & Mask); - - /* Since we will do the OR with a constant, we can remove the push */ - RemoveCode (&PushPos); - - /* If the value is equal to the mask now, all bits are one, and we - ** can remove the mask operation from above. + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. */ - if (Val == Mask) { - RemoveCode (&AndPos); + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + if (MustScale) { + /* lhs is a pointer, scale rhs */ + Expr2.IVal *= CheckedSizeOf (Expr->Type+1); } - /* Generate the or operation */ - g_or (Flags | CF_CONST, Val << Expr->BitOffs); + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } 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"); + } + } + Gen->Func (Flags | CF_CONST, Expr2.IVal); + } } else { /* If necessary, load the value into the primary register */ LoadExpr (CF_NONE, &Expr2); - /* Apply the mask */ - g_and (Flags | CF_CONST, Mask); + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + } - /* Shift it into the right position */ - g_asl (Flags | CF_CONST, Expr->BitOffs); + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Adjust the types of the operands if needed */ + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); - /* Or both values */ - g_or (Flags, 0); } - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Restore the expression type */ - Expr->Type = ltype; - - } else { - - /* Get the address on stack if needed */ - PushAddr (Expr); - - /* Read the expression on the right side of the '=' */ - hie1 (&Expr2); - - /* Do type conversion if necessary */ - TypeConversion (&Expr2, ltype); - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Generate a store instruction */ - Store (Expr, 0); - } - /* Value is still in primary and not an lvalue */ - ED_MakeRValExpr (Expr); + /* Generate a store instruction */ + Store (Expr, 0); + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ +{ + const Type* ltype = Expr->Type; + + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Only "=" accept struct/union */ + if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (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. + */ + } else { + /* Check for assignment to incomplete type */ + if (IsIncompleteESUType (ltype)) { + Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); + } else if (ED_IsRVal (Expr)) { + /* Assignment can only be used with lvalues */ + if (IsTypeArray (ltype)) { + Error ("Array type '%s' is not assignable", GetFullTypeName (ltype)); + } else if (IsTypeFunc (ltype)) { + Error ("Function type '%s' is not assignable", GetFullTypeName (ltype)); + } else { + Error ("Assignment to rvalue"); + } + } else if (IsQualConst (ltype)) { + /* Check for assignment to const */ + Error ("Assignment to const"); + } + } + + /* Skip the '=' or 'op=' token */ + NextToken (); + + /* cc65 does not have full support for handling structs or unions. Since + ** assigning structs is one of the more useful operations from this family, + ** allow it here. + ** Note: IsClassStruct() is also true for union types. + */ + if (IsClassStruct (ltype)) { + /* Copy the struct or union by value */ + CopyStruct (Expr, &Expr2); + } else if (IsTypeBitField (ltype)) { + /* Special care is needed for bit-field 'op=' */ + OpAssignBitField (Gen, Expr, Op); + } else { + /* Normal straight 'op=' */ + OpAssignArithmetic (Gen, Expr, Op); + } +} + + + +void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) +/* Parse a "+=" or "-=" operation */ +{ + ExprDesc Expr2; + unsigned lflags; + unsigned rflags; + int MustScale; + + /* We currently only handle non-bit-fields in some addressing modes here */ + if (IsTypeBitField (Expr->Type) || ED_IsLocPrimaryOrExpr (Expr)) { + /* Use generic routine instead */ + OpAssign (Gen, Expr, Op); + return; + } + + /* There must be an integer or pointer on the left side */ + if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { + 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. + */ + } else { + /* We must have an lvalue */ + if (ED_IsRVal (Expr)) { + Error ("Invalid lvalue in assignment"); + } else if (IsQualConst (Expr->Type)) { + /* The left side must not be const qualified */ + Error ("Assignment to const"); + } + } + + /* Skip the operator */ + NextToken (); + + /* Check if we have a pointer expression and must scale rhs */ + MustScale = IsTypePtr (Expr->Type); + + /* Initialize the code generator flags */ + lflags = 0; + rflags = 0; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Evaluate the rhs. We expect an integer here, since float is not + ** supported + */ + hie1 (&Expr2); + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right 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. + */ + } + + /* Setup the code generator flags */ + lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; + rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + + if (ED_IsConstAbs (&Expr2)) { + /* The resulting value is a constant */ + rflags |= CF_CONST; + lflags |= CF_CONST; + + /* Scale it */ + if (MustScale) { + Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); + } + } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Convert the type of the rhs to that of the lhs */ + g_typecast (lflags, rflags & ~CF_FORCECHAR); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + } + } + + /* Output apropriate code depending on the location */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Absolute numeric addressed variable, global variable, local + ** static variable, register variable, pooled literal or code + ** label location. + */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } else { + g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } + break; + + case E_LOC_STACK: + /* Value on the stack */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); + } else { + g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); + } + break; + + default: + Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); + } + + /* Expression is an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } diff --git a/src/cc65/assignment.h b/src/cc65/assignment.h index 278c5ef72..6098118b4 100644 --- a/src/cc65/assignment.h +++ b/src/cc65/assignment.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2002-2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -43,14 +43,38 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Whether to save/restore the original lhs or result value */ +enum { + OA_NEED_NONE, + OA_NEED_OLD, + OA_NEED_NEW, +}; + +/* Forward */ +struct GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -void Assignment (ExprDesc* lval); -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult); +/* Process inc/dec for bit-field */ + +void OpAssign (const struct GenDesc* Gen, ExprDesc* lval, const char* Op); +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ + +void OpAddSubAssign (const struct GenDesc* Gen, ExprDesc *Expr, const char* Op); +/* Parse a "+=" or "-=" operation */ diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 7dd90833b..0a1b917db 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -33,6 +33,8 @@ +#include <errno.h> +#include <limits.h> #include <stdlib.h> /* common */ @@ -43,13 +45,16 @@ #include "xsprintf.h" /* cc65 */ +#include "asmlabel.h" #include "codeent.h" #include "codeinfo.h" +#include "codelab.h" #include "error.h" #include "global.h" -#include "codelab.h" +#include "ident.h" #include "opcodes.h" #include "output.h" +#include "reginfo.h" @@ -94,42 +99,11 @@ static char* GetArgCopy (const char* Arg) -static int NumArg (const char* Arg, unsigned long* Num) -/* If the given argument is numerical, convert it and return true. Otherwise -** set Num to zero and return false. -*/ +static void FreeParsedArg (char* ArgBase) +/* Free a code entry parsed argument */ { - char* End; - unsigned long Val; - - /* Determine the base */ - int Base = 10; - if (*Arg == '$') { - ++Arg; - Base = 16; - } else if (*Arg == '%') { - ++Arg; - Base = 2; - } - - /* Convert the value. strtol is not exactly what we want here, but it's - ** cheap and may be replaced by something fancier later. - */ - Val = strtoul (Arg, &End, Base); - - /* Check if the conversion was successful */ - if (*End != '\0') { - - /* Could not convert */ - *Num = 0; - return 0; - - } else { - - /* Conversion ok */ - *Num = Val; - return 1; - + if (ArgBase != 0 && ArgBase != EmptyArg) { + xfree (ArgBase); } } @@ -198,6 +172,10 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) if (Info && Info->ByteUse != REG_NONE) { /* These addressing modes will never change the zp loc */ E->Use |= Info->WordUse; + + if ((E->Use & REG_SP) != 0) { + E->Use |= SLV_IND; + } } break; @@ -205,6 +183,145 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) /* Keep gcc silent */ break; } + + /* Append processor flags as well as special usages */ + switch (E->OPC) { + + case OP65_ADC: + case OP65_SBC: + E->Use |= PSTATE_C; + E->Chg |= PSTATE_CZVN; + break; + case OP65_ROL: + case OP65_ROR: + E->Use |= PSTATE_C; + E->Chg |= PSTATE_CZN; + break; + case OP65_ASL: + case OP65_LSR: + E->Chg |= PSTATE_CZN; + break; + case OP65_CMP: + case OP65_CPX: + case OP65_CPY: + E->Chg |= PSTATE_CZN; + break; + case OP65_BIT: + E->Chg |= PSTATE_ZVN; + if (E->AM != AM65_IMM) { + E->Chg &= ~(PSTATE_V | PSTATE_N); + } + break; + case OP65_BRK: + E->Chg |= PSTATE_B; + break; + case OP65_CLC: + case OP65_SEC: + E->Chg |= PSTATE_C; + break; + case OP65_CLD: + case OP65_SED: + E->Chg |= PSTATE_D; + break; + case OP65_CLI: + case OP65_SEI: + E->Chg |= PSTATE_I; + break; + case OP65_CLV: + E->Chg |= PSTATE_V; + break; + case OP65_TRB: + case OP65_TSB: + E->Chg |= PSTATE_Z; + break; + case OP65_BCC: + case OP65_BCS: + case OP65_JCC: + case OP65_JCS: + E->Use |= PSTATE_C; + break; + case OP65_BEQ: + case OP65_BNE: + case OP65_JEQ: + case OP65_JNE: + E->Use |= PSTATE_Z; + break; + case OP65_BMI: + case OP65_BPL: + case OP65_JMI: + case OP65_JPL: + E->Use |= PSTATE_N; + break; + case OP65_BVC: + case OP65_BVS: + case OP65_JVC: + case OP65_JVS: + E->Use |= PSTATE_V; + break; + case OP65_BRA: + case OP65_JMP: + break; + case OP65_AND: + case OP65_EOR: + case OP65_ORA: + case OP65_DEA: + case OP65_DEC: + case OP65_DEX: + case OP65_DEY: + case OP65_INA: + case OP65_INC: + case OP65_INX: + case OP65_INY: + case OP65_LDA: + case OP65_LDX: + case OP65_LDY: + case OP65_TAX: + case OP65_TAY: + case OP65_TXA: + case OP65_TYA: + E->Chg |= PSTATE_ZN; + break; + case OP65_TSX: + E->Use |= SLV_SP65; + E->Chg |= PSTATE_ZN; + break; + case OP65_TXS: + E->Chg |= SLV_SP65; + break; + case OP65_PLA: + case OP65_PLX: + case OP65_PLY: + E->Use |= SLV_SP65; + E->Chg |= SLV_PL65 | PSTATE_ZN; + break; + case OP65_PLP: + E->Use |= SLV_SP65; + E->Chg |= SLV_PL65 | PSTATE_ALL; + break; + case OP65_PHA: + case OP65_PHX: + case OP65_PHY: + E->Use |= SLV_SP65; + E->Chg |= SLV_PH65; + break; + case OP65_PHP: + E->Use |= SLV_SP65 | PSTATE_ALL; + E->Chg |= SLV_PH65; + break; + case OP65_RTI: + E->Chg |= PSTATE_ALL; + break; + case OP65_RTS: + break; + case OP65_STA: + case OP65_STX: + case OP65_STY: + case OP65_STZ: + case OP65_JSR: + case OP65_NOP: + default: + break; + } } } @@ -216,6 +333,244 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) +int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Name, long* Offset) +/* Break the opcode argument string into a symbol name/label part plus an offset. +** Both parts are optional, but if there are any characters in the string that +** can't be parsed, it's an failure. +** The caller is responsible for managing the StrBuf. +** Return whether parsing succeeds or not. +*/ +{ + unsigned short Flags = 0; + const char* OffsetPart = 0; + const char* NameEnd = 0; + int Negative = 0; + int Parentheses = 0; + unsigned long NumVal = 0; + long long AccOffset = 0; + char* End; /* Used for checking errors */ + + if (ArgInfo != 0) { + *ArgInfo = 0; + } + *Offset = 0; + + /* A numeric address is treated as an unnamed address with the numeric part as the offset */ + if (IsDigit (Arg[0]) || Arg[0] == '$') { + /* A call to a numeric address */ + SB_Clear (Name); + SB_Terminate (Name); + OffsetPart = Arg; + } else { + /* <, >, ^ */ + if (Arg[0] == '<') { + Flags |= AIF_LOBYTE; + } else if (Arg[0] == '>') { + Flags |= AIF_HIBYTE; + } else if (Arg[0] == '^') { + Flags |= AIF_BANKBYTE; + } + + if ((Flags & (AIF_FAR)) != 0) { + /* Skip this char */ + ++Arg; + } + + /* Skip spaces */ + while (Arg[0] == ' ') { + ++Arg; + } + + /* Strip parentheses off if exist */ + if (Arg[0] == '(') { + /* Skip this char */ + ++Arg; + + End = strchr (Arg, ')'); + if (End == 0 || End[1] != '\0') { + /* Not closed at the end, bail out */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* Found */ + Parentheses = 1; + + /* Skip spaces */ + while (Arg[0] == ' ') { + ++Arg; + } + } + + /* If the symbol name starts with an underline, it is an external symbol. + ** If the symbol does not start with an underline, it may be a built-in + ** symbol. + */ + if (Arg[0] == '_') { + Flags |= AIF_EXTERNAL; + } else { + Flags |= AIF_BUILTIN; + } + + /* Rip off the offset if present. */ + OffsetPart = strchr (Arg, '+'); + if (OffsetPart == 0) { + OffsetPart = strchr (Arg, '-'); + } + + if (OffsetPart != 0) { + /* Get the real arg name */ + NameEnd = strchr (Arg, ' '); + if (NameEnd == 0 || NameEnd > OffsetPart) { + NameEnd = OffsetPart; + } + SB_CopyBuf (Name, Arg, NameEnd - Arg); + SB_Terminate (Name); + + } else { + /* No offset */ + if (Parentheses == 0) { + SB_CopyStr (Name, Arg); + } else { + SB_CopyBuf (Name, Arg, End - Arg); + } + SB_Terminate (Name); + } + + if ((Flags & AIF_EXTERNAL) == 0) { + if (SB_GetLen (Name) > 0) { + Flags |= AIF_HAS_NAME; + + /* See if the name is a local label */ + if (IsLocalLabelName (SB_GetConstBuf (Name))) { + Flags |= AIF_LOCAL; + } + } + + } else { + if (SB_GetLen (Name) <= 0) { + /* Invalid external name */ + Flags &= ~AIF_EXTERNAL; + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + Flags |= AIF_HAS_NAME; + } + + /* A byte size expression with no parentheses but an offset is not + ** handled correctly for now, so just bail out in such cases. + */ + if ((Flags & AIF_FAR) != 0 && + Parentheses == 0 && + OffsetPart != 0 && + OffsetPart[0] != '\0') { + /* Bail out */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + } + + /* Get the offset */ + while (OffsetPart != 0 && + OffsetPart[0] != '\0' && + OffsetPart[0] != ')') { + /* Skip spaces */ + while (OffsetPart[0] == ' ') { + ++OffsetPart; + } + + Negative = 0; + if (OffsetPart[0] == '+') { + ++OffsetPart; + } else if (OffsetPart[0] == '-') { + Negative = 1; + ++OffsetPart; + } + + /* Skip spaces */ + while (OffsetPart[0] == ' ') { + ++OffsetPart; + } + + /* Determine the base and convert the value. strtol/strtoul is not + ** exactly what we want here, but it's cheap and may be replaced by + ** something fancier later. + */ + if (OffsetPart[0] == '$') { + /* Base 16 hexedemical */ + NumVal = strtoul (OffsetPart+1, &End, 16); + } else if (OffsetPart[0] != '%') { + /* Base 10 decimal */ + NumVal = strtoul (OffsetPart, &End, 10); + } else { + /* Base 2 binary */ + NumVal = strtoul (OffsetPart+1, &End, 2); + } + + /* Check if the conversion was successful */ + if (*End != '\0' && *End != ' ' && *End != '+' && *End != '-' && *End != ')') { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* Check for out of range result */ + if (NumVal == ULONG_MAX && errno == ERANGE) { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* This argument does have an offset */ + Flags |= AIF_HAS_OFFSET; + + if (Negative) { + AccOffset -= (long long)NumVal; + } else { + AccOffset += (long long)NumVal; + } + + /* See if there are more */ + Arg = OffsetPart; + OffsetPart = strchr (Arg, '+'); + if (OffsetPart == 0) { + OffsetPart = strchr (Arg, '-'); + } + } + + if (AccOffset > LONG_MAX || AccOffset < LONG_MIN) { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + *Offset = (long)AccOffset; + if (ArgInfo != 0) { + *ArgInfo = Flags & ~AIF_FAILURE; + } + + return 1; +} + + + const char* MakeHexArg (unsigned Num) /* Convert Num into a string in the form $XY, suitable for passing it as an ** argument to NewCodeEntry, and return a pointer to the string. @@ -231,6 +586,34 @@ const char* MakeHexArg (unsigned Num) +void PreparseArg (CodeEntry* E) +/* Parse the argument string and memorize the result for the code entry */ +{ + StrBuf B = AUTO_STRBUF_INITIALIZER; + + /* Parse the argument string */ + if (ParseOpcArgStr (E->Arg, &E->ArgInfo, &B, &E->ArgOff)) { + E->ArgBase = SB_GetBuf (&B); + + if ((E->ArgInfo & (AIF_HAS_NAME | AIF_HAS_OFFSET)) == AIF_HAS_OFFSET) { + E->Flags |= CEF_NUMARG; + + /* Use the new numerical value */ + E->Num = E->ArgOff; + } + + } else { + /* Parsing fails. Issue an error/warning so that this could be spotted and fixed. */ + E->ArgBase = EmptyArg; + SB_Done (&B); + if (Debug) { + Warning ("Parsing argument \"%s\" failed!", E->Arg); + } + } +} + + + CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeLabel* JumpTo, LineInfo* LI) /* Create a new code entry, initialize and return it */ @@ -242,15 +625,24 @@ CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeEntry* E = xmalloc (sizeof (CodeEntry)); /* Initialize the fields */ - E->OPC = D->OPC; - E->AM = AM; - E->Size = GetInsnSize (E->OPC, E->AM); - E->Arg = GetArgCopy (Arg); - E->Flags = NumArg (E->Arg, &E->Num)? CEF_NUMARG : 0; /* Needs E->Arg */ - E->Info = D->Info; - E->JumpTo = JumpTo; - E->LI = UseLineInfo (LI); - E->RI = 0; + E->OPC = D->OPC; + E->AM = AM; + E->Size = GetInsnSize (E->OPC, E->AM); + E->Arg = GetArgCopy (Arg); + E->Flags = 0; + E->Info = D->Info; + E->ArgInfo = 0; + E->JumpTo = JumpTo; + E->LI = UseLineInfo (LI); + E->RI = 0; + + /* Parse the argument string if it's given */ + if (Arg == 0 || Arg[0] == '\0') { + E->ArgBase = EmptyArg; + } else { + PreparseArg (E); + } + SetUseChgInfo (E, D); InitCollection (&E->Labels); @@ -268,6 +660,9 @@ CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, void FreeCodeEntry (CodeEntry* E) /* Free the given code entry */ { + /* Free the argument base string if we have one */ + FreeParsedArg (E->ArgBase); + /* Free the string argument if we have one */ FreeArg (E->Arg); @@ -332,9 +727,8 @@ void CE_ClearJumpTo (CodeEntry* E) /* Clear the JumpTo entry */ E->JumpTo = 0; - /* Clear the argument and assign the empty one */ - FreeArg (E->Arg); - E->Arg = EmptyArg; + /* Clear the argument */ + CE_SetArg (E, 0); } @@ -353,17 +747,106 @@ void CE_MoveLabel (CodeLabel* L, CodeEntry* E) void CE_SetArg (CodeEntry* E, const char* Arg) -/* Replace the argument by the new one. */ +/* Replace the whole argument by the new one. */ { + /* Free the old parsed argument base */ + FreeParsedArg (E->ArgBase); + /* Free the old argument */ FreeArg (E->Arg); /* Assign the new one */ E->Arg = GetArgCopy (Arg); + /* Parse the new argument string */ + PreparseArg (E); + /* Update the Use and Chg in E */ - const OPCDesc* D = GetOPCDesc (E->OPC); - SetUseChgInfo (E, D); + SetUseChgInfo (E, GetOPCDesc (E->OPC)); +} + + + +void CE_SetArgBaseAndOff (CodeEntry* E, const char* ArgBase, long ArgOff) +/* Replace the new argument base and offset. Argument base is always applied. +** Argument offset is applied if and only if E has the AIF_HAS_OFFSET flag set. +*/ +{ + if (ArgBase != 0 && ArgBase[0] != '\0') { + + /* The argument base is not blank */ + char Buf[IDENTSIZE + 16]; + char* Str = Buf; + size_t Len = strlen (ArgBase) + 16; + if (Len >= sizeof (Buf)) { + Str = xmalloc (Len); + } + + if ((E->ArgInfo & AIF_FAR) == 0) { + if (CE_HasArgOffset (E)) { + sprintf (Str, "%s%+ld", ArgBase, ArgOff); + } else { + sprintf (Str, "%s", ArgBase); + } + CE_SetArg (E, Str); + } else { + /* A byte expression */ + const char* Expr = ""; + if ((E->ArgInfo & AIF_FAR) == AIF_LOBYTE) { + Expr = "<"; + } else if ((E->ArgInfo & AIF_FAR) == AIF_HIBYTE) { + Expr = ">"; + } else if ((E->ArgInfo & AIF_FAR) == AIF_BANKBYTE) { + Expr = "^"; + } else { + Internal ("Invalid byte size flag in CE_SetArgBaseAndOff"); + } + + if (CE_HasArgOffset (E)) { + sprintf (Str, "%s(%s%+ld)", Expr, ArgBase, ArgOff); + } else { + sprintf (Str, "%s(%s)", Expr, ArgBase); + } + } + + CE_SetArg (E, Str); + + if (Str != Buf) { + xfree (Str); + } + + } else { + /* The argument has no base */ + if ((E->ArgInfo & AIF_HAS_OFFSET) != 0) { + /* This is a numeric argument */ + E->Flags |= CEF_NUMARG; + CE_SetNumArg (E, ArgOff); + } else { + /* Empty argument */ + CE_SetArg (E, EmptyArg); + } + } +} + + + +void CE_SetArgBase (CodeEntry* E, const char* ArgBase) +/* Replace the argument base by the new one. +** The entry must have an existing base. +*/ +{ + /* Check that the entry has a base name */ + CHECK (CE_HasArgBase (E)); + + CE_SetArgBaseAndOff (E, ArgBase, E->ArgOff); +} + + + +void CE_SetArgOffset (CodeEntry* E, long ArgOff) +/* Replace the argument offset by the new one */ +{ + CE_SetArgBaseAndOff (E, E->ArgBase, ArgOff); } @@ -376,24 +859,45 @@ void CE_SetNumArg (CodeEntry* E, long Num) char Buf[16]; /* Check that the entry has a numerical argument */ - CHECK (E->Flags & CEF_NUMARG); + CHECK (CE_HasNumArg (E)); /* Make the new argument string */ if (E->Size == 2) { Num &= 0xFF; xsprintf (Buf, sizeof (Buf), "$%02X", (unsigned) Num); - } else if (E->Size == 3) { + } else if (E->Size == 3 || E->Size == 5) { Num &= 0xFFFF; xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned) Num); } else { Internal ("Invalid instruction size in CE_SetNumArg"); } - /* Replace the argument by the new one */ + /* Replace the whole argument by the new one */ CE_SetArg (E, Buf); +} - /* Use the new numerical value */ - E->Num = Num; + + +int CE_IsArgStrParsed (const CodeEntry* E) +/* Return true if the argument of E was successfully parsed last time */ +{ + return (E->ArgInfo & AIF_FAILURE) == 0; +} + + + +int CE_HasArgBase (const CodeEntry* E) +/* Return true if the argument of E has a non-blank base name */ +{ + return (E->ArgInfo & AIF_HAS_NAME) != 0 && E->ArgBase[0] != '\0'; +} + + + +int CE_HasArgOffset (const CodeEntry* E) +/* Return true if the argument of E has a non-zero offset */ +{ + return (E->ArgInfo & AIF_HAS_OFFSET) != 0 && E->ArgOff != 0; } @@ -476,6 +980,11 @@ int CE_UseLoadFlags (CodeEntry* E) } } + /* PHP will use all flags */ + if (E->OPC == OP65_PHP) { + return 1; + } + /* Anything else */ return 0; } @@ -493,6 +1002,228 @@ void CE_FreeRegInfo (CodeEntry* E) +static void DeduceZ (RegContents* C, short Val) +/* Auto-set Z flag */ +{ + if (RegValIsUnknown (Val)) { + C->PFlags |= UNKNOWN_PFVAL_Z; + } else { + C->PFlags &= ~UNKNOWN_PFVAL_Z; + if (Val == 0) { + C->PFlags |= PFVAL_Z; + } + } +} + + + +static void DeduceZN (RegContents* C, short Val) +/* Auto-set Z/N flags */ +{ + if (RegValIsUnknown (Val)) { + C->PFlags |= UNKNOWN_PFVAL_ZN; + } else { + C->PFlags &= ~UNKNOWN_PFVAL_ZN; + if (Val == 0) { + C->PFlags |= PFVAL_Z; + } else if (Val & PFVAL_N) { + C->PFlags |= PFVAL_N; + } + } +} + + + +static short KnownOpADCDeduceCZVN (RegContents* Out, RegContents* In, short Rhs) +/* Do the ADC and auto-set C/Z/V/N flags. +** Both operands and the C flag must be known. +*/ +{ + short SVal, UVal; + SVal = (signed char)(In->RegA & 0xFF) + (signed char)(Rhs & 0xFF); + UVal = (In->RegA & 0xFF) + (Rhs & 0xFF); + if (In->PFlags & PFVAL_C) { + ++SVal; + ++UVal; + } + + Out->PFlags &= ~UNKNOWN_PFVAL_CZVN; + if (UVal > 0xFF) { + Out->PFlags |= PFVAL_C; + } + + if (SVal < -128 || SVal > 127) { + Out->PFlags |= PFVAL_V; + } + + DeduceZN (Out, UVal); + + return UVal; +} + + + +static short KnownOpSBCDeduceCZVN (RegContents* Out, RegContents* In, short Rhs) +/* Do the SBC and auto-set C/Z/V/N flags. +** Both operands and the C flag must be known. +*/ +{ + short SVal, UVal; + SVal = (signed char)(In->RegA & 0xFF) - (signed char)(Rhs & 0xFF); + UVal = (In->RegA & 0xFF) - (Rhs & 0xFF); + if ((In->PFlags & PFVAL_C) == 0) { + --SVal; + --UVal; + } + + Out->PFlags &= ~UNKNOWN_PFVAL_CZVN; + if (UVal >= 0) { + Out->PFlags |= PFVAL_C; + } + + if (SVal < -128 || SVal > 127) { + Out->PFlags |= PFVAL_V; + } + + DeduceZN (Out, UVal); + + return UVal; +} + + + +static short KnownOpCmpDeduceCZN (RegContents* C, short Lhs, short Rhs) +/* Do the CMP and auto-set C/Z/N flags. +** Both operands must be known. + */ +{ + short Val = (Lhs & 0xFF) - (Rhs & 0xFF); + + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Val >= 0) { + C->PFlags |= PFVAL_C; + } + DeduceZN (C, Val); + + return Val; +} + + + +static short AnyOpASLDeduceCZN (RegContents* C, short Shiftee) +/* Do the ASL and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x80U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee << 1) & 0xFF; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpLSRDeduceCZN (RegContents* C, short Shiftee) +/* Do the LSR and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x01U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee >> 1) & 0xFF; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpROLDeduceCZN (RegContents* C, short PFlags, short Shiftee) +/* Do the ROL and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee) && PStatesAreKnown (PFlags, PSTATE_C)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x80U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee << 1) & 0xFF; + if (PFlags & PFVAL_C) { + Shiftee |= 0x01U; + } + } else { + Shiftee = UNKNOWN_REGVAL; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpRORDeduceCZN (RegContents* C, short PFlags, short Shiftee) +/* Do the ROR and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee) && PStatesAreKnown (PFlags, PSTATE_C)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x01U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee >> 1) & 0xFF; + if (PFlags & PFVAL_C) { + Shiftee |= 0x80U; + } + } else { + Shiftee = UNKNOWN_REGVAL; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static void BranchDeduceOnProcessorFlag (RegContents* True, RegContents* False, unsigned short PTrueFlag) +/* Auto-set the corresponding C/Z/V/N flag output for both True/Flase code flows */ +{ + PTrueFlag &= 0xFF; + unsigned short Mask = ~(PTrueFlag * 0x0101U) & 0xFFFFU; + True->PFlags &= Mask; + True->PFlags |= PTrueFlag; + False->PFlags &= Mask; +} + + + +static int MightAffectKnownZP (CodeEntry* E, RegContents* In) +/* TODO: This is supposed to check if any builtin ZP could be affected. +** It simply returns TRUE in most cases for now. +*/ +{ + unsigned Index = 0; + + if (E->AM == AM65_ZP || + E->AM == AM65_ABS || + (E->AM == AM65_ZPX && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ZPY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF)) || + (E->AM == AM65_ABSX && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ABSY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF))) { + return 1; + } else if ((E->AM == AM65_ZP_IND) || + (E->AM == AM65_ZPX_IND && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ZP_INDY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF))) { + return 1; + } + return 1; +} + + + void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Generate register info for this instruction. If an old info exists, it is ** overwritten. @@ -501,9 +1232,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Pointers to the register contents */ RegContents* In; RegContents* Out; + RegContents* BranchOut; /* Function register usage */ - unsigned short Use, Chg; + unsigned Use, Chg; + + /* Value in question */ + short Val = UNKNOWN_REGVAL; /* If we don't have a register info struct, allocate one. */ if (E->RI == 0) { @@ -520,18 +1255,46 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Get pointers to the register contents */ In = &E->RI->In; Out = &E->RI->Out; + BranchOut = &E->RI->Out2; /* Handle the different instructions */ switch (E->OPC) { case OP65_ADC: - /* We don't know the value of the carry, so the result is - ** always unknown. - */ Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_CZVN; + Out->ZNRegs = ZNREG_A; + if (PStatesAreKnown (In->PFlags, PSTATE_C) && + RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + Out->RegA = KnownOpADCDeduceCZVN (Out, In, (short) E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Tmp1); + break; + case REG_PTR1_LO: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Ptr1Lo); + break; + case REG_PTR1_HI: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Ptr1Hi); + break; + case REG_SREG_LO: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->SRegLo); + break; + case REG_SREG_HI: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_AND: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA & (short) E->Num; @@ -553,145 +1316,315 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA & In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - Out->RegA = UNKNOWN_REGVAL; } } else if (CE_IsKnownImm (E, 0)) { /* A and $00 does always give zero */ Out->RegA = 0; } + DeduceZN (Out, Out->RegA); break; case OP65_ASL: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA << 1) & 0xFF; + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + Out->RegA = AnyOpASLDeduceCZN (Out, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 << 1) & 0xFF; + Out->Tmp1 = AnyOpASLDeduceCZN (Out, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo << 1) & 0xFF; + Out->Ptr1Lo = AnyOpASLDeduceCZN (Out, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi << 1) & 0xFF; + Out->Ptr1Hi = AnyOpASLDeduceCZN (Out, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo << 1) & 0xFF; + Out->SRegLo = AnyOpASLDeduceCZN (Out, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi << 1) & 0xFF; + Out->SRegHi = AnyOpASLDeduceCZN (Out, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_BCC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_C); break; case OP65_BCS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_C); break; case OP65_BEQ: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_Z); break; case OP65_BIT: + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Chg & REG_ZP, In)) { + case REG_TMP1: + Val = In->Tmp1; + break; + case REG_PTR1_LO: + Val = In->Ptr1Lo; + break; + case REG_PTR1_HI: + Val = In->Ptr1Hi; + break; + case REG_SREG_LO: + Val = In->SRegLo; + break; + case REG_SREG_HI: + Val = In->SRegHi; + break; + } + } else if (CE_IsConstImm (E)) { + /* 65C02 special */ + Val = (short) E->Num; + } + + /* BIT is unique with regards to the Z/V/N flags: + ** - The Z is set/cleared according to whether the AND result is zero. + ** - The V is coped directly from Bit 6 of the orginal argument. + ** - The N is coped directly from Bit 7 of the orginal argument. + ** Note the V/N flags are not affected in imm addressing mode supported by 65c02! + */ + if (E->AM == AM65_IMM) { + if (RegValIsKnown (Val)) { + Out->PFlags &= ~(UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_N); + if (Val & PFVAL_V) { + Out->PFlags |= PFVAL_V; + } + Out->PFlags &= ~UNKNOWN_PFVAL_V; + if (Val & PFVAL_V) { + Out->PFlags |= PFVAL_V; + } + } else { + Out->PFlags |= UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_N; + } + } + if ((RegValIsKnown (Val) && RegValIsKnown (In->RegA))) { + Val &= In->RegA; + } else if (((RegValIsKnown (Val) && Val == 0) || + (RegValIsKnown (In->RegA) && In->RegA == 0))) { + Val = 0; + } + DeduceZ (Out, Val); break; case OP65_BMI: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_N); break; case OP65_BNE: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_Z); break; case OP65_BPL: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_N); break; case OP65_BRA: break; case OP65_BRK: + Out->PFlags &= ~UNKNOWN_PFVAL_B; + Out->PFlags |= PFVAL_B; break; case OP65_BVC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_V); break; case OP65_BVS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_V); break; case OP65_CLC: + Out->PFlags &= ~UNKNOWN_PFVAL_C; break; case OP65_CLD: + Out->PFlags &= ~UNKNOWN_PFVAL_D; break; case OP65_CLI: + Out->PFlags &= ~UNKNOWN_PFVAL_I; break; case OP65_CLV: + Out->PFlags &= ~UNKNOWN_PFVAL_V; break; case OP65_CMP: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegA, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegA, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegA, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_CPX: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegX)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegX, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegX, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegX, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_CPY: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegY)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegY, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegY, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegY, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_DEA: + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { Out->RegA = (In->RegA - 1) & 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_DEC: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA - 1) & 0xFF; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + if (RegValIsKnown (In->RegA)) { + Val = Out->RegA = (In->RegA - 1) & 0xFF; + } + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 - 1) & 0xFF; + Val = Out->Tmp1 = (In->Tmp1 - 1) & 0xFF; + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo - 1) & 0xFF; + Val = Out->Ptr1Lo = (In->Ptr1Lo - 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi - 1) & 0xFF; + Val = Out->Ptr1Hi = (In->Ptr1Hi - 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo - 1) & 0xFF; + Val = Out->SRegLo = (In->SRegLo - 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi - 1) & 0xFF; + Val = Out->SRegHi = (In->SRegHi - 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } + DeduceZN (Out, Val); break; case OP65_DEX: + Out->ZNRegs = ZNREG_X; if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX - 1) & 0xFF; } + DeduceZN (Out, Out->RegX); break; case OP65_DEY: + Out->ZNRegs = ZNREG_Y; if (RegValIsKnown (In->RegY)) { Out->RegY = (In->RegY - 1) & 0xFF; } + DeduceZN (Out, Out->RegY); break; case OP65_EOR: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA ^ (short) E->Num; @@ -713,82 +1646,104 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA ^ In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - Out->RegA = UNKNOWN_REGVAL; } } + DeduceZN (Out, Out->RegA); break; case OP65_INA: + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { Out->RegA = (In->RegA + 1) & 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_INC: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA + 1) & 0xFF; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + if (RegValIsKnown (In->RegA)) { + Val = Out->RegA = (In->RegA + 1) & 0xFF; + } + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 + 1) & 0xFF; + Val = Out->Tmp1 = (In->Tmp1 + 1) & 0xFF; + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo + 1) & 0xFF; + Val = Out->Ptr1Lo = (In->Ptr1Lo + 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi + 1) & 0xFF; + Val = Out->Ptr1Hi = (In->Ptr1Hi + 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo + 1) & 0xFF; + Val = Out->SRegLo = (In->SRegLo + 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi + 1) & 0xFF; + Val = Out->SRegHi = (In->SRegHi + 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } + DeduceZN (Out, Val); break; case OP65_INX: + Out->ZNRegs = ZNREG_X; if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX + 1) & 0xFF; } + DeduceZN (Out, Out->RegX); break; case OP65_INY: + Out->ZNRegs = ZNREG_Y; if (RegValIsKnown (In->RegY)) { Out->RegY = (In->RegY + 1) & 0xFF; } + DeduceZN (Out, Out->RegY); break; case OP65_JCC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_C); break; case OP65_JCS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_C); break; case OP65_JEQ: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_Z); break; case OP65_JMI: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_N); break; case OP65_JMP: break; case OP65_JNE: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_Z); break; case OP65_JPL: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_N); break; case OP65_JSR: + Out->ZNRegs = ZNREG_NONE; + /* Get the code info for the function */ GetFuncInfo (E->Arg, &Use, &Chg); if (Chg & REG_A) { @@ -815,6 +1770,9 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (Chg & REG_SREG_HI) { Out->SRegHi = UNKNOWN_REGVAL; } + /* FIXME: Quick hack to set flags on process status: */ + Out->PFlags |= ((Chg & PSTATE_ALL) >> PSTATE_BITS_SHIFT) * 0x0101U; + /* ## FIXME: Quick hack for some known functions: */ if (strcmp (E->Arg, "complax") == 0) { if (RegValIsKnown (In->RegA)) { @@ -823,11 +1781,18 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX ^ 0xFF); } + } else if (strncmp (E->Arg, "asrax", 5) == 0 || + strncmp (E->Arg, "shrax", 5) == 0) { + if (RegValIsKnown (In->RegX)) { + if (In->RegX == 0x00 || In->RegX == 0xFF) { + Out->RegX = In->RegX; + } + } } else if (strcmp (E->Arg, "tosandax") == 0) { - if (In->RegA == 0) { + if (RegValIsKnown (In->RegA) && In->RegA == 0) { Out->RegA = 0; } - if (In->RegX == 0) { + if (RegValIsKnown (In->RegX) && In->RegX == 0) { Out->RegX = 0; } } else if (strcmp (E->Arg, "tosaslax") == 0) { @@ -836,17 +1801,23 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = 0; } } else if (strcmp (E->Arg, "tosorax") == 0) { - if (In->RegA == 0xFF) { + if (RegValIsKnown (In->RegA) && In->RegA == 0xFF) { Out->RegA = 0xFF; } - if (In->RegX == 0xFF) { + if (RegValIsKnown (In->RegX) && In->RegX == 0xFF) { Out->RegX = 0xFF; } } else if (strcmp (E->Arg, "tosshlax") == 0) { - if ((In->RegA & 0x0F) >= 8) { + if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) { Out->RegA = 0; } - } else if (FindBoolCmpCond (E->Arg) != CMP_INV || + } else if (strcmp (E->Arg, "tosshrax") == 0) { + if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) { + Out->RegX = 0; + } + } else if (strcmp (E->Arg, "bcastax") == 0 || + strcmp (E->Arg, "bnegax") == 0 || + FindBoolCmpCond (E->Arg) != CMP_INV || FindTosCmpCond (E->Arg) != CMP_INV) { /* Result is boolean value, so X is zero on output */ Out->RegX = 0; @@ -854,12 +1825,16 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; case OP65_JVC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_V); break; case OP65_JVS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_V); break; case OP65_LDA: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (CE_IsConstImm (E)) { Out->RegA = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -880,16 +1855,15 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - /* A is now unknown */ - Out->RegA = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegA); break; case OP65_LDX: + Out->RegX = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_X; if (CE_IsConstImm (E)) { Out->RegX = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -910,16 +1884,15 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegX = In->SRegHi; break; default: - Out->RegX = UNKNOWN_REGVAL; break; } - } else { - /* X is now unknown */ - Out->RegX = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegX); break; case OP65_LDY: + Out->RegY = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_Y; if (CE_IsConstImm (E)) { Out->RegY = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -940,37 +1913,42 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegY = In->SRegHi; break; default: - Out->RegY = UNKNOWN_REGVAL; break; } - } else { - /* Y is now unknown */ - Out->RegY = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegY); break; case OP65_LSR: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA >> 1) & 0xFF; + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + Out->RegA = AnyOpLSRDeduceCZN (Out, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 >> 1) & 0xFF; + Out->Tmp1 = AnyOpLSRDeduceCZN (Out, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo >> 1) & 0xFF; + Out->Ptr1Lo = AnyOpLSRDeduceCZN (Out, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi >> 1) & 0xFF; + Out->Ptr1Hi = AnyOpLSRDeduceCZN (Out, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo >> 1) & 0xFF; + Out->SRegLo = AnyOpLSRDeduceCZN (Out, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi >> 1) & 0xFF; + Out->SRegHi = AnyOpLSRDeduceCZN (Out, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -980,6 +1958,8 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; case OP65_ORA: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA | (short) E->Num; @@ -1001,17 +1981,14 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA | In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - /* A is now unknown */ - Out->RegA = UNKNOWN_REGVAL; } } else if (CE_IsKnownImm (E, 0xFF)) { /* ORA with 0xFF does always give 0xFF */ Out->RegA = 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_PHA: @@ -1028,93 +2005,150 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_PLA: Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_A; break; case OP65_PLP: + Out->PFlags = UNKNOWN_PFVAL_ALL; + Out->ZNRegs = ZNREG_NONE; break; case OP65_PLX: Out->RegX = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_X; break; case OP65_PLY: Out->RegY = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_Y; break; case OP65_ROL: - /* We don't know the value of the carry bit */ + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; if (E->AM == AM65_ACC) { - Out->RegA = UNKNOWN_REGVAL; + Out->RegA = AnyOpROLDeduceCZN (Out, In->PFlags, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = UNKNOWN_REGVAL; + Out->Tmp1 = AnyOpROLDeduceCZN (Out, In->PFlags, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = UNKNOWN_REGVAL; + Out->Ptr1Lo = AnyOpROLDeduceCZN (Out, In->PFlags, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = UNKNOWN_REGVAL; + Out->Ptr1Hi = AnyOpROLDeduceCZN (Out, In->PFlags, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = UNKNOWN_REGVAL; + Out->SRegLo = AnyOpROLDeduceCZN (Out, In->PFlags, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = UNKNOWN_REGVAL; + Out->SRegHi = AnyOpROLDeduceCZN (Out, In->PFlags, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_ROR: - /* We don't know the value of the carry bit */ + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; if (E->AM == AM65_ACC) { - Out->RegA = UNKNOWN_REGVAL; + Out->RegA = AnyOpRORDeduceCZN (Out, In->PFlags, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = UNKNOWN_REGVAL; + Out->Tmp1 = AnyOpRORDeduceCZN (Out, In->PFlags, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = UNKNOWN_REGVAL; + Out->Ptr1Lo = AnyOpRORDeduceCZN (Out, In->PFlags, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = UNKNOWN_REGVAL; + Out->Ptr1Hi = AnyOpRORDeduceCZN (Out, In->PFlags, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = UNKNOWN_REGVAL; + Out->SRegLo = AnyOpRORDeduceCZN (Out, In->PFlags, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = UNKNOWN_REGVAL; + Out->SRegHi = AnyOpRORDeduceCZN (Out, In->PFlags, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_RTI: + Out->PFlags |= UNKNOWN_PFVAL_ALL; break; case OP65_RTS: break; case OP65_SBC: - /* We don't know the value of the carry bit */ Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_CZVN; + Out->ZNRegs = ZNREG_A; + if (PStatesAreKnown (In->PFlags, PSTATE_C) && + RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, (short) E->Num); + } + else if (E->AM == AM65_ZP) { + switch (GetKnownReg(E->Use & REG_ZP, In)) { + case REG_TMP1: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Tmp1); + break; + case REG_PTR1_LO: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Ptr1Lo); + break; + case REG_PTR1_HI: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Ptr1Hi); + break; + case REG_SREG_LO: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->SRegLo); + break; + case REG_SREG_HI: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_SEC: + Out->PFlags &= ~UNKNOWN_PFVAL_C; + Out->PFlags |= PFVAL_C; break; case OP65_SED: + Out->PFlags &= ~UNKNOWN_PFVAL_D; + Out->PFlags |= PFVAL_D; break; case OP65_SEI: + Out->PFlags &= ~UNKNOWN_PFVAL_I; + Out->PFlags |= PFVAL_I; break; case OP65_STA: @@ -1136,7 +2170,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegA; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1164,7 +2198,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegX; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1189,7 +2223,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegY; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1214,7 +2248,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = 0; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1222,37 +2256,44 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_TAX: Out->RegX = In->RegA; + Out->ZNRegs = ZNREG_AX; + DeduceZN (Out, Out->RegX); break; case OP65_TAY: Out->RegY = In->RegA; + Out->ZNRegs = ZNREG_AY; + DeduceZN (Out, Out->RegY); break; case OP65_TRB: - if (E->AM == AM65_ZPX) { - /* Invalidates all ZP registers */ - RC_InvalidateZP (Out); - } else if (E->AM == AM65_ZP) { + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { if (RegValIsKnown (In->RegA)) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: + Val = Out->Tmp1 & In->RegA; Out->Tmp1 &= ~In->RegA; break; case REG_PTR1_LO: + Val = Out->Ptr1Lo & In->RegA; Out->Ptr1Lo &= ~In->RegA; break; case REG_PTR1_HI: + Val = Out->Ptr1Hi & In->RegA; Out->Ptr1Hi &= ~In->RegA; break; case REG_SREG_LO: + Val = Out->SRegLo & In->RegA; Out->SRegLo &= ~In->RegA; break; case REG_SREG_HI: + Val = Out->SRegHi & In->RegA; Out->SRegHi &= ~In->RegA; break; } } else { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: Out->Tmp1 = UNKNOWN_REGVAL; break; @@ -1270,34 +2311,41 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; } } + } else if (MightAffectKnownZP (E, In)) { + /* Invalidates all ZP registers */ + RC_InvalidateZP (Out); } + DeduceZ (Out, Val); break; case OP65_TSB: - if (E->AM == AM65_ZPX) { - /* Invalidates all ZP registers */ - RC_InvalidateZP (Out); - } else if (E->AM == AM65_ZP) { + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { if (RegValIsKnown (In->RegA)) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: + Val = Out->Tmp1 & In->RegA; Out->Tmp1 |= In->RegA; break; case REG_PTR1_LO: + Val = Out->Ptr1Lo & In->RegA; Out->Ptr1Lo |= In->RegA; break; case REG_PTR1_HI: + Val = Out->Ptr1Hi & In->RegA; Out->Ptr1Hi |= In->RegA; break; case REG_SREG_LO: + Val = Out->SRegLo & In->RegA; Out->SRegLo |= In->RegA; break; case REG_SREG_HI: + Val = Out->SRegHi & In->RegA; Out->SRegHi |= In->RegA; break; } } else { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: Out->Tmp1 = UNKNOWN_REGVAL; break; @@ -1315,22 +2363,34 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; } } + } else if (MightAffectKnownZP (E, In)) { + /* Invalidates all ZP registers */ + RC_InvalidateZP (Out); } + DeduceZ (Out, Val); break; case OP65_TSX: Out->RegX = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_X; break; case OP65_TXA: Out->RegA = In->RegX; + Out->ZNRegs = ZNREG_AX; + DeduceZN (Out, Out->RegA); break; case OP65_TXS: + Out->ZNRegs = ZNREG_X; + DeduceZN (Out, In->RegX); break; case OP65_TYA: Out->RegA = In->RegY; + Out->ZNRegs = ZNREG_AY; + DeduceZN (Out, Out->RegA); break; default: @@ -1356,6 +2416,7 @@ static char* RegInfoDesc (unsigned U, char* Buf) strcat (Buf, U & REG_PTR2? "2" : "_"); strcat (Buf, U & REG_SAVE? "V" : "_"); strcat (Buf, U & REG_SP? "S" : "_"); + sprintf (Buf + 10, "_%02X", (U & PSTATE_ALL) >> PSTATE_BITS_SHIFT); return Buf; } @@ -1380,11 +2441,59 @@ static char* RegContentDesc (const RegContents* RC, char* Buf) } B += 5; if (RegValIsUnknown (RC->RegY)) { - strcpy (B, "Y:XX"); + strcpy (B, "Y:XX "); } else { - sprintf (B, "Y:%02X", RC->RegY); + sprintf (B, "Y:%02X ", RC->RegY); } - B += 4; + B += 5; + if (PStatesAreUnknown (RC->PFlags, PSTATE_C)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_C ? "C" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_Z)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_Z ? "Z" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_I)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_I ? "I" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_D)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_D ? "D" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_U)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_U ? "U" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_B)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_B ? "B" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_V)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_V ? "V" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_N)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_N ? "N" : "_"); + } + B += 1; return Buf; } @@ -1479,7 +2588,7 @@ void CE_Output (const CodeEntry* E) if (Debug) { char Use [128]; char Chg [128]; - WriteOutput ("%*s; USE: %-12s CHG: %-12s SIZE: %u", + WriteOutput ("%*s; USE: %-15s CHG: %-15s SIZE: %u", (int)(30-Chars), "", RegInfoDesc (E->Use, Use), RegInfoDesc (E->Chg, Chg), @@ -1488,7 +2597,7 @@ void CE_Output (const CodeEntry* E) if (E->RI) { char RegIn[32]; char RegOut[32]; - WriteOutput (" In %s Out %s", + WriteOutput (" In %s Out %s", RegContentDesc (&E->RI->In, RegIn), RegContentDesc (&E->RI->Out, RegOut)); } diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 96373a8f9..ee1dd0220 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -73,14 +73,32 @@ struct CodeEntry { char* Arg; /* Argument as string */ unsigned long Num; /* Numeric argument */ unsigned short Info; /* Additional code info */ - unsigned short Use; /* Registers used */ - unsigned short Chg; /* Registers changed/destroyed */ + unsigned short ArgInfo; /* Additional argument info */ + unsigned int Use; /* Registers used */ + unsigned int Chg; /* Registers changed/destroyed */ CodeLabel* JumpTo; /* Jump label */ Collection Labels; /* Labels for this instruction */ LineInfo* LI; /* Source line info for this insn */ RegInfo* RI; /* Register info for this insn */ + char* ArgBase; /* Argument broken into a base and an offset, */ + long ArgOff; /* only done when requested. */ }; +/* */ +#define AIF_HAS_NAME 0x0001U /* Argument has a name part */ +#define AIF_HAS_OFFSET 0x0002U /* Argument has a numeric part */ +#define AIF_BUILTIN 0x0004U /* The name is built-in */ +#define AIF_EXTERNAL 0x0008U /* The name is external */ +#define AIF_LOCAL 0x0010U /* The name is a local label */ +#define AIF_ZP_NAME 0x0020U /* The name is a zp location */ +#define AIF_LOBYTE 0x0100U +#define AIF_HIBYTE 0x0200U +#define AIF_BANKBYTE 0x0400U +#define AIF_FAILURE 0x8000U /* Argument was not parsed successfully */ + +#define AIF_WORD (AIF_LOBYTE | AIF_HIBYTE) +#define AIF_FAR (AIF_LOBYTE | AIF_HIBYTE | AIF_BANKBYTE) + /*****************************************************************************/ @@ -89,6 +107,14 @@ struct CodeEntry { +int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Name, long* Offset); +/* Break the opcode argument string into a symbol name/label part plus an offset. +** Both parts are optional, but if there are any characters in the string that +** can't be parsed, it's an failure. +** The caller is responsible for managing the StrBuf. +** Return whether parsing succeeds or not. +*/ + const char* MakeHexArg (unsigned Num); /* Convert Num into a string in the form $XY, suitable for passing it as an ** argument to NewCodeEntry, and return a pointer to the string. @@ -97,6 +123,9 @@ const char* MakeHexArg (unsigned Num); ** safe). */ +void PreparseArg (CodeEntry* E); +/* Parse the argument string and memorize the result for the code entry */ + CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeLabel* JumpTo, LineInfo* LI); /* Create a new code entry, initialize and return it */ @@ -151,6 +180,16 @@ INLINE CodeLabel* CE_GetLabel (CodeEntry* E, unsigned Index) # define CE_GetLabel(E, Index) CollAt (&(E)->Labels, (Index)) #endif +#if defined(HAVE_INLINE) +INLINE void CE_ReplaceLabel (CodeEntry* E, CodeLabel* L, unsigned Index) +/* Replace the code label at the specified index with L */ +{ + CollReplace (&E->Labels, L, Index); +} +#else +# define CE_ReplaceLabel(E, L, Index) CollReplace (&(E)->Labels, (L), (Index)) +#endif + void CE_MoveLabel (CodeLabel* L, CodeEntry* E); /* Move the code label L from it's former owner to the code entry E. */ @@ -197,11 +236,33 @@ INLINE int CE_HasNumArg (const CodeEntry* E) void CE_SetArg (CodeEntry* E, const char* Arg); /* Replace the argument by the new one. */ +void CE_SetArgBaseAndOff (CodeEntry* E, const char* ArgBase, long ArgOff); +/* Replace the new argument base and offset. Argument base is always applied. +** Argument offset is applied if and only if E has the AIF_HAS_OFFSET flag set. +*/ + +void CE_SetArgBase (CodeEntry* E, const char* ArgBase); +/* Replace the argument base by the new one. +** The entry must have an existing base. +*/ + +void CE_SetArgOffset (CodeEntry* E, long ArgOff); +/* Replace the argument offset by the new one */ + void CE_SetNumArg (CodeEntry* E, long Num); /* Set a new numeric argument for the given code entry that must already ** have a numeric argument. */ +int CE_IsArgStrParsed (const CodeEntry* E); +/* Return true if the argument of E was successfully parsed last time */ + +int CE_HasArgBase (const CodeEntry* E); +/* Return true if the argument of E has a non-blank base name */ + +int CE_HasArgOffset (const CodeEntry* E); +/* Return true if the argument of E has a non-zero offset */ + int CE_IsConstImm (const CodeEntry* E); /* Return true if the argument of E is a constant immediate value */ diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 35af5281e..cf10314b9 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -33,11 +33,13 @@ +#include <limits.h> #include <stdio.h> #include <string.h> #include <stdarg.h> /* common */ +#include "addrsize.h" #include "check.h" #include "cpu.h" #include "inttypes.h" @@ -100,12 +102,17 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) /* Create the correct label name */ switch (Flags & CF_ADDRMASK) { + case CF_IMM: + /* Immediate constant values */ + xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned)((Offs) & 0xFFFF)); + break; + case CF_STATIC: - /* Static memory cell */ + /* Local static memory cell */ if (Offs) { - xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalLabelName (Label), Offs); + xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalDataLabelName (Label), Offs); } else { - xsprintf (Buf, sizeof (Buf), "%s", LocalLabelName (Label)); + xsprintf (Buf, sizeof (Buf), "%s", LocalDataLabelName (Label)); } break; @@ -118,6 +125,16 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) } break; + case CF_LITERAL: + /* Literal */ + /* Static memory cell */ + if (Offs) { + xsprintf (Buf, sizeof (Buf), "%s%+ld", PooledLiteralLabelName (Label), Offs); + } else { + xsprintf (Buf, sizeof (Buf), "%s", PooledLiteralLabelName (Label)); + } + break; + case CF_ABSOLUTE: /* Absolute address */ xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned)((Label+Offs) & 0xFFFF)); @@ -128,6 +145,15 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) xsprintf (Buf, sizeof (Buf), "regbank+%u", (unsigned)((Label+Offs) & 0xFFFF)); break; + case CF_CODE: + /* Code label location */ + if (Offs) { + xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalLabelName (Label), Offs); + } else { + xsprintf (Buf, sizeof (Buf), "%s", LocalLabelName (Label)); + } + break; + default: Internal ("Invalid address flags: %04X", Flags); } @@ -160,6 +186,7 @@ void g_preamble (void) switch (CPU) { case CPU_6502: AddTextLine ("\t.setcpu\t\t\"6502\""); break; case CPU_6502X: AddTextLine ("\t.setcpu\t\t\"6502X\""); break; + case CPU_6502DTV: AddTextLine ("\t.setcpu\t\t\"6502DTV\""); break; case CPU_65SC02: AddTextLine ("\t.setcpu\t\t\"65SC02\""); break; case CPU_65C02: AddTextLine ("\t.setcpu\t\t\"65C02\""); break; case CPU_65816: AddTextLine ("\t.setcpu\t\t\"65816\""); break; @@ -235,6 +262,9 @@ void g_usebss (void) void g_segname (segment_t Seg) /* Emit the name of a segment if necessary */ { + unsigned char AddrSize; + const char* Name; + /* Emit a segment directive for the data style segments */ DataSeg* S; switch (Seg) { @@ -244,7 +274,13 @@ void g_segname (segment_t Seg) default: S = 0; break; } if (S) { - DS_AddLine (S, ".segment\t\"%s\"", GetSegName (Seg)); + Name = GetSegName (Seg); + AddrSize = GetSegAddrSize (Name); + if (AddrSize != ADDR_SIZE_INVALID) { + DS_AddLine (S, ".segment\t\"%s\": %s", Name, AddrSizeToStr (AddrSize)); + } else { + DS_AddLine (S, ".segment\t\"%s\"", Name); + } } } @@ -344,25 +380,7 @@ void g_defcodelabel (unsigned label) void g_defdatalabel (unsigned label) /* Define a local data label */ { - AddDataLine ("%s:", LocalLabelName (label)); -} - - - -void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs) -/* Define label as a local alias for baselabel+offs */ -{ - /* We need an intermediate buffer here since LocalLabelName uses a - ** static buffer which changes with each call. - */ - StrBuf L = AUTO_STRBUF_INITIALIZER; - SB_AppendStr (&L, LocalLabelName (label)); - SB_Terminate (&L); - AddDataLine ("%s\t:=\t%s+%ld", - SB_GetConstBuf (&L), - LocalLabelName (baselabel), - offs); - SB_Done (&L); + AddDataLine ("%s:", LocalDataLabelName (label)); } @@ -382,6 +400,33 @@ void g_defgloblabel (const char* Name) +void g_defliterallabel (unsigned label) +/* Define a literal data label */ +{ + /* Literal labels are always data labels */ + AddDataLine ("%s:", PooledLiteralLabelName (label)); +} + + + +void g_aliasliterallabel (unsigned label, unsigned baselabel, long offs) +/* Define label as an alias for baselabel+offs */ +{ + /* We need an intermediate buffer here since LocalLabelName uses a + ** static buffer which changes with each call. + */ + StrBuf L = AUTO_STRBUF_INITIALIZER; + SB_AppendStr (&L, PooledLiteralLabelName (label)); + SB_Terminate (&L); + AddDataLine ("%s\t:=\t%s+%ld", + SB_GetConstBuf (&L), + PooledLiteralLabelName (baselabel), + offs); + SB_Done (&L); +} + + + void g_defexport (const char* Name, int ZP) /* Export the given label */ { @@ -1121,7 +1166,9 @@ void g_putind (unsigned Flags, unsigned Offs) /* Overflow - we need to add the low byte also */ AddCodeLine ("ldy #$00"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", Offs & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); @@ -1129,7 +1176,9 @@ void g_putind (unsigned Flags, unsigned Offs) AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Complete address is on stack, new offset is zero */ Offs = 0; @@ -1139,12 +1188,15 @@ void g_putind (unsigned Flags, unsigned Offs) /* We can just add the high byte */ AddCodeLine ("ldy #$01"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); - + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Offset is now just the low byte */ Offs &= 0x00FF; } @@ -1322,52 +1374,133 @@ void g_reglong (unsigned Flags) +static unsigned g_intpromotion (unsigned flags) +/* Return new flags for integral promotions for types smaller than int. */ +{ + /* 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 ((flags & CF_TYPEMASK) == CF_CHAR) { + /* int can represent all unsigned chars, so unsigned char is promoted to int. */ + flags &= ~CF_TYPEMASK; + flags &= ~CF_UNSIGNED; + flags |= CF_INT; + return flags; + } else if ((flags & CF_TYPEMASK) == CF_SHORT) { + /* int cannot represent all unsigned shorts, so unsigned short is promoted to + ** unsigned int. + */ + flags &= ~CF_TYPEMASK; + flags |= CF_INT; + return flags; + } else { + /* Otherwise, the type is not smaller than int, so leave it alone. */ + return flags; + } +} + + + unsigned g_typeadjust (unsigned lhs, unsigned rhs) /* Adjust the integer operands before doing a binary operation. lhs is a flags ** value, that corresponds to the value on TOS, rhs corresponds to the value ** in (e)ax. The return value is the the flags value for the resulting type. */ { - unsigned ltype, rtype; - unsigned result; - /* Get the type spec from the flags */ - ltype = lhs & CF_TYPEMASK; - rtype = rhs & CF_TYPEMASK; + unsigned ltype = lhs & CF_TYPEMASK; + unsigned rtype = rhs & CF_TYPEMASK; /* Check if a conversion is needed */ if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) { /* We must promote the primary register to long */ g_reglong (rhs); - /* Get the new rhs type */ - rhs = (rhs & ~CF_TYPEMASK) | CF_LONG; - rtype = CF_LONG; } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) { /* We must promote the lhs to long */ - if (lhs & CF_REG) { + if (lhs & CF_PRIMARY) { g_reglong (lhs); } else { g_toslong (lhs); } - /* Get the new rhs type */ - lhs = (lhs & ~CF_TYPEMASK) | CF_LONG; - ltype = CF_LONG; } - /* Determine the result type for the operation: - ** - The result is const if both operands are const. - ** - The result is unsigned if one of the operands is unsigned. - ** - The result is long if one of the operands is long. - ** - Otherwise the result is int sized. + /* Result is const if both operands are const. */ + unsigned const_flag = (lhs & CF_CONST) & (rhs & CF_CONST); + + /* 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. */ - result = (lhs & CF_CONST) & (rhs & CF_CONST); - result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED); - if (rtype == CF_LONG || ltype == CF_LONG) { - result |= CF_LONG; - } else { - result |= CF_INT; + + /* Note that this logic is largely duplicated by ArithmeticConvert. */ + + /* Before we apply the integral promotions, we check if both types are the same character type. + ** If so, we return that type, rather than int, which would be returned by the standard + ** rules. This is only a performance optimization allowing the use of unsigned and/or char + ** operations; it does not affect correctness, as the flags are only used for code generation, + ** and not to determine types of other expressions containing this one. For codgen, CF_CHAR + ** means the operands are char and the result is int (unless CF_FORCECHAR is also set, in + ** which case the result is char). This special case part is not duplicated by + ** ArithmeticConvert. + */ + if ((lhs & CF_TYPEMASK) == CF_CHAR && (rhs & CF_TYPEMASK) == CF_CHAR && + (lhs & CF_UNSIGNED) == (rhs & CF_UNSIGNED)) { + /* Signedness flags are the same, so just use one of them. */ + const unsigned unsigned_flag = lhs & CF_UNSIGNED; + return const_flag | unsigned_flag | CF_CHAR; } - return result; + + /* Apply integral promotions for types char/short. */ + lhs = g_intpromotion (lhs); + rhs = g_intpromotion (rhs); + ltype = lhs & CF_TYPEMASK; + rtype = rhs & CF_TYPEMASK; + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((ltype == CF_LONG && (lhs & CF_UNSIGNED)) || + (rtype == CF_LONG && (rhs & CF_UNSIGNED))) { + return const_flag | CF_UNSIGNED | CF_LONG; + } + + /* 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 ((ltype == CF_LONG && rtype == CF_INT && (rhs & CF_UNSIGNED)) || + (rtype == CF_LONG && ltype == CF_INT && (rhs & CF_UNSIGNED))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return const_flag | CF_LONG; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. + */ + if (ltype == CF_LONG || rtype == CF_LONG) { + return const_flag | CF_LONG; + } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((ltype == CF_INT && (lhs & CF_UNSIGNED)) || + (rtype == CF_INT && (rhs & CF_UNSIGNED))) { + return const_flag | CF_UNSIGNED | CF_INT; + } + + /* Otherwise, both operands have type int. */ + CHECK (ltype == CF_INT); + CHECK (!(lhs & CF_UNSIGNED)); + CHECK (rtype == CF_INT); + CHECK (!(rhs & CF_UNSIGNED)); + return const_flag | CF_INT; } @@ -1425,106 +1558,24 @@ void g_scale (unsigned flags, long val) ** pointer points to. */ { - int p2; - /* Value may not be zero */ if (val == 0) { Internal ("Data type has no size"); } else if (val > 0) { - /* Scale up */ - if ((p2 = PowerOf2 (val)) > 0 && p2 <= 4) { - - /* Factor is 2, 4, 8 and 16, use special function */ - switch (flags & CF_TYPEMASK) { - - case CF_CHAR: - if (flags & CF_FORCECHAR) { - while (p2--) { - AddCodeLine ("asl a"); - } - break; - } - /* FALLTHROUGH */ - - case CF_INT: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shlax%d", p2); - } else { - AddCodeLine ("jsr aslax%d", p2); - } - break; - - case CF_LONG: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shleax%d", p2); - } else { - AddCodeLine ("jsr asleax%d", p2); - } - break; - - default: - typeerror (flags); - - } - - } else if (val != 1) { - - /* Use a multiplication instead */ + /* Use a multiplication instead */ + if (val != 1) { g_mul (flags | CF_CONST, val); - } } else { /* Scale down */ val = -val; - if ((p2 = PowerOf2 (val)) > 0 && p2 <= 4) { - /* Factor is 2, 4, 8 and 16 use special function */ - switch (flags & CF_TYPEMASK) { - - case CF_CHAR: - if (flags & CF_FORCECHAR) { - if (flags & CF_UNSIGNED) { - while (p2--) { - AddCodeLine ("lsr a"); - } - break; - } else if (p2 <= 2) { - AddCodeLine ("cmp #$80"); - AddCodeLine ("ror a"); - break; - } - } - /* FALLTHROUGH */ - - case CF_INT: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr lsrax%d", p2); - } else { - AddCodeLine ("jsr asrax%d", p2); - } - break; - - case CF_LONG: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr lsreax%d", p2); - } else { - AddCodeLine ("jsr asreax%d", p2); - } - break; - - default: - typeerror (flags); - - } - - } else if (val != 1) { - - /* Use a division instead */ + /* Use a division instead */ + if (val != 1) { g_div (flags | CF_CONST, val); - } } } @@ -1652,7 +1703,9 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("inc %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1682,8 +1735,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bne %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("lda %s", lbuf); /* Hmmm... */ - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); /* Hmmm... */ + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1694,13 +1749,17 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcc %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } } else { @@ -1710,8 +1769,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("txa"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; @@ -1793,9 +1854,11 @@ void g_addeqlocal (unsigned flags, int Offs, unsigned long val) AddCodeLine ("lda #$%02X", (int) ((val >> 8) & 0xFF)); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("tax"); - AddCodeLine ("dey"); - AddCodeLine ("lda (sp),y"); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("dey"); + AddCodeLine ("lda (sp),y"); + } } else { g_getimmed (flags, val, 0); AddCodeLine ("jsr addeqysp"); @@ -1875,7 +1938,9 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("dec %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda %s", lbuf); AddCodeLine ("sec"); @@ -1909,13 +1974,17 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcs %s", LocalLabelName (L)); AddCodeLine ("dec %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda %s+1", lbuf); AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } else { AddCodeLine ("eor #$FF"); @@ -1926,8 +1995,10 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("eor #$FF"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; @@ -2068,29 +2139,38 @@ void g_addaddr_local (unsigned flags attribute ((unused)), int offs) /* Add the offset */ offs -= StackPtr; - if (offs != 0) { - /* We cannot address more then 256 bytes of locals anyway */ - L = GetLocalLabel(); - CheckLocalOffs (offs); - AddCodeLine ("clc"); - AddCodeLine ("adc #$%02X", offs & 0xFF); - /* Do also skip the CLC insn below */ - AddCodeLine ("bcc %s", LocalLabelName (L)); - AddCodeLine ("inx"); - } + if (IS_Get (&CodeSizeFactor) <= 100) { + if (offs != 0) { + /* We cannot address more then 256 bytes of locals anyway */ + g_inc (CF_INT | CF_CONST, offs); + } + /* Add the current stackpointer value */ + AddCodeLine ("jsr leaaxsp"); + } else { + if (offs != 0) { + /* We cannot address more then 256 bytes of locals anyway */ + L = GetLocalLabel(); + CheckLocalOffs (offs); + AddCodeLine ("clc"); + AddCodeLine ("adc #$%02X", offs & 0xFF); + /* Do also skip the CLC insn below */ + AddCodeLine ("bcc %s", LocalLabelName (L)); + AddCodeLine ("inx"); + } - /* Add the current stackpointer value */ - AddCodeLine ("clc"); - if (L != 0) { - /* Label was used above */ - g_defcodelabel (L); + /* Add the current stackpointer value */ + AddCodeLine ("clc"); + if (L != 0) { + /* Label was used above */ + g_defcodelabel (L); + } + AddCodeLine ("adc sp"); + AddCodeLine ("tay"); + AddCodeLine ("txa"); + AddCodeLine ("adc sp+1"); + AddCodeLine ("tax"); + AddCodeLine ("tya"); } - AddCodeLine ("adc sp"); - AddCodeLine ("tay"); - AddCodeLine ("txa"); - AddCodeLine ("adc sp+1"); - AddCodeLine ("tax"); - AddCodeLine ("tya"); } @@ -2178,7 +2258,7 @@ void g_restore (unsigned flags) void g_cmp (unsigned flags, unsigned long val) -/* Immidiate compare. The primary register will not be changed, Z flag +/* Immediate compare. The primary register will not be changed, Z flag ** will be set. */ { @@ -2375,7 +2455,7 @@ void g_call (unsigned Flags, const char* Label, unsigned ArgSize) void g_callind (unsigned Flags, unsigned ArgSize, int Offs) /* Call subroutine indirect */ { - if ((Flags & CF_LOCAL) == 0) { + if ((Flags & CF_ADDRMASK) != CF_STACK) { /* Address is in a/x */ if ((Flags & CF_FIXARGC) == 0) { /* Pass arg count */ @@ -2426,15 +2506,32 @@ void g_falsejump (unsigned flags attribute ((unused)), unsigned label) } + +void g_branch (unsigned Label) +/* Branch unconditionally to Label if the CPU has the BRA instruction. +** Otherwise, jump to Label. +** Use this function, instead of g_jump(), only where it is certain that +** the label cannot be farther away from the branch than -128/+127 bytes. +*/ +{ + if ((CPUIsets[CPU] & (CPU_ISET_65SC02 | CPU_ISET_6502DTV)) != 0) { + AddCodeLine ("bra %s", LocalLabelName (Label)); + } else { + g_jump (Label); + } +} + + + void g_lateadjustSP (unsigned label) /* Adjust stack based on non-immediate data */ { AddCodeLine ("pha"); - AddCodeLine ("lda %s", LocalLabelName (label)); + AddCodeLine ("lda %s", LocalDataLabelName (label)); AddCodeLine ("clc"); AddCodeLine ("adc sp"); AddCodeLine ("sta sp"); - AddCodeLine ("lda %s+1", LocalLabelName (label)); + AddCodeLine ("lda %s+1", LocalDataLabelName (label)); AddCodeLine ("adc sp+1"); AddCodeLine ("sta sp+1"); AddCodeLine ("pla"); @@ -2561,13 +2658,26 @@ void g_mul (unsigned flags, unsigned long val) "tosmulax", "tosumulax", "tosmuleax", "tosumuleax" }; - int p2; - /* Do strength reduction if the value is constant and a power of two */ - if (flags & CF_CONST && (p2 = PowerOf2 (val)) >= 0) { - /* Generate a shift instead */ - g_asl (flags, p2); - return; + if (flags & CF_CONST) { + + /* Deal with negative values if it's signed multiplication */ + int Negation = (flags & CF_UNSIGNED) == 0 && (long)val < 0; + int p2 = PowerOf2 (Negation ? 0UL - val : val); + + /* Check if we can use shift instead of multiplication */ + if (p2 == 0 || (p2 > 0 && IS_Get (&CodeSizeFactor) >= (Negation ? 100 : 0))) { + /* Generate a shift instead */ + g_asl (flags, p2); + + /* Negate the result if val is negative */ + if (Negation) { + g_neg (flags); + } + + /* Done */ + return; + } } /* If the right hand side is const, the lhs is not on stack but still @@ -2669,19 +2779,105 @@ void g_div (unsigned flags, unsigned long val) }; /* Do strength reduction if the value is constant and a power of two */ - int p2; - if ((flags & CF_CONST) && (p2 = PowerOf2 (val)) >= 0) { + if (flags & CF_CONST) { + + /* Deal with negative values as well as different sizes */ + int Negation = (flags & CF_UNSIGNED) == 0 && (long)val < 0; + unsigned long NegatedVal = 0UL - val; + int p2 = PowerOf2 (Negation ? NegatedVal : val); + /* Generate a shift instead */ - g_asr (flags, p2); - } else { - /* Generate a division */ - if (flags & CF_CONST) { - /* lhs is not on stack */ - flags &= ~CF_FORCECHAR; /* Handle chars as ints */ - g_push (flags & ~CF_CONST, 0); + if ((flags & CF_UNSIGNED) != 0 && p2 > 0) { + g_asr (flags, p2); + return; } - oper (flags, val, ops); + + /* Check if we can afford using shift instead of multiplication at the + ** cost of code size */ + if (p2 == 0 || (p2 > 0 && IS_Get (&CodeSizeFactor) >= (Negation ? 200 : 170))) { + /* Generate a conditional shift instead */ + if (p2 > 0) { + unsigned int DoShiftLabel = GetLocalLabel (); + unsigned int EndLabel = GetLocalLabel (); + unsigned long MaskedVal = Negation ? val : NegatedVal; + + /* GitHub #169 - if abs(expr) < abs(val), the result is always 0. + ** First, check whether expr >= 0 and skip to the shift if true. + */ + switch (flags & CF_TYPEMASK) { + case CF_CHAR: + if (flags & CF_FORCECHAR) { + MaskedVal &= 0xFF; + AddCodeLine ("cmp #$00"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + MaskedVal &= 0xFFFF; + AddCodeLine ("cpx #$00"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + + case CF_LONG: + MaskedVal &= 0xFFFFFFFF; + AddCodeLine ("ldy sreg+1"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + + default: + typeerror (flags); + break; + } + /* Second, check whether expr <= -asb(val) and skip to the + ** shift if true. The original content of expr has to be saved + ** before the checking comparison and restored after that, as + ** the content in Primary register will be destroyed. + ** The result of the comparison is a boolean. We can store + ** it in the Carry flag with a LSR and branch on it later. + */ + g_save (flags); + g_le (flags | CF_UNSIGNED, MaskedVal); + AddCodeLine ("lsr a"); + g_restore (flags); + AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); + + /* The result is 0. We can just load 0 and skip the shifting. */ + g_getimmed (flags | CF_ABSOLUTE, 0, 0); + + /* TODO: replace with BEQ? Would it be optimized? */ + g_jump (EndLabel); + + /* Do the shift. The sign of the result may need to be corrected + ** later. + */ + g_defcodelabel (DoShiftLabel); + g_asr (flags, p2); + g_defcodelabel (EndLabel); + } + + /* Negate the result as long as val < 0, even if val == -1 and no + ** shift was generated. + */ + if (Negation) { + g_neg (flags); + } + + /* Done */ + return; + } + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + ** into the normal, non-optimized stuff. + */ + flags &= ~CF_FORCECHAR; /* Handle chars as ints */ + g_push (flags & ~CF_CONST, 0); } + + /* Generate a division */ + oper (flags, val, ops); + } @@ -2948,24 +3144,64 @@ void g_asr (unsigned flags, unsigned long val) "tosasrax", "tosshrax", "tosasreax", "tosshreax" }; - /* If the right hand side is const, the lhs is not on stack but still + /* If the right hand side is const, the lhs is not on stack, but still ** in the primary register. */ if (flags & CF_CONST) { - switch (flags & CF_TYPEMASK) { - case CF_CHAR: + if (flags & CF_FORCECHAR) { + val &= 7; + if ((flags & CF_UNSIGNED) != 0) { + /* Instead of `val` right shifts, we can also do `9 - val` left rotates + ** and a mask. This saves 3 bytes and 8 cycles for `val == 7` and + ** 1 byte and 4 cycles for `val == 6`. + */ + if (val < 6) { + while (val--) { + AddCodeLine ("lsr a"); /* 1 byte, 2 cycles */ + } + } else { + unsigned i; + /* The first ROL shifts in garbage and sets carry to the high bit. + ** The garbage is cleaned up by the mask. + */ + for (i = val; i < 9; ++i) { + AddCodeLine ("rol a"); /* 1 byte, 2 cycles */ + } + /* 2 bytes, 2 cycles */ + AddCodeLine ("and #$%02X", 0xFF >> val); + } + return; + } else if (val <= 2) { + while (val--) { + AddCodeLine ("cmp #$80"); + AddCodeLine ("ror a"); + } + return; + } + AddCodeLine ("ldx #$00"); + if ((flags & CF_UNSIGNED) == 0) { + unsigned L = GetLocalLabel (); + + AddCodeLine ("cmp #$80"); /* Sign bit into carry */ + AddCodeLine ("bcc %s", LocalLabelName (L)); + AddCodeLine ("dex"); /* Make $FF */ + g_defcodelabel (L); + } + } + /* FALLTHROUGH */ + case CF_INT: val &= 0x0F; if (val >= 8) { + AddCodeLine ("txa"); if (flags & CF_UNSIGNED) { - AddCodeLine ("txa"); AddCodeLine ("ldx #$00"); } else { - unsigned L = GetLocalLabel(); + unsigned L = GetLocalLabel (); + AddCodeLine ("cpx #$80"); /* Sign bit into carry */ - AddCodeLine ("txa"); AddCodeLine ("ldx #$00"); AddCodeLine ("bcc %s", LocalLabelName (L)); AddCodeLine ("dex"); /* Make $FF */ @@ -2983,9 +3219,9 @@ void g_asr (unsigned flags, unsigned long val) } if (val > 0) { if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shrax%ld", val); + AddCodeLine ("jsr shrax%lu", val); } else { - AddCodeLine ("jsr asrax%ld", val); + AddCodeLine ("jsr asrax%lu", val); } } return; @@ -2996,7 +3232,8 @@ void g_asr (unsigned flags, unsigned long val) AddCodeLine ("ldx #$00"); AddCodeLine ("lda sreg+1"); if ((flags & CF_UNSIGNED) == 0) { - unsigned L = GetLocalLabel(); + unsigned L = GetLocalLabel (); + AddCodeLine ("bpl %s", LocalLabelName (L)); AddCodeLine ("dex"); g_defcodelabel (L); @@ -3009,7 +3246,8 @@ void g_asr (unsigned flags, unsigned long val) AddCodeLine ("ldy #$00"); AddCodeLine ("ldx sreg+1"); if ((flags & CF_UNSIGNED) == 0) { - unsigned L = GetLocalLabel(); + unsigned L = GetLocalLabel (); + AddCodeLine ("bpl %s", LocalLabelName (L)); AddCodeLine ("dey"); g_defcodelabel (L); @@ -3025,7 +3263,8 @@ void g_asr (unsigned flags, unsigned long val) AddCodeLine ("ldy sreg+1"); AddCodeLine ("sty sreg"); if ((flags & CF_UNSIGNED) == 0) { - unsigned L = GetLocalLabel(); + unsigned L = GetLocalLabel (); + AddCodeLine ("cpy #$80"); AddCodeLine ("ldy #$00"); AddCodeLine ("bcc %s", LocalLabelName (L)); @@ -3047,9 +3286,9 @@ void g_asr (unsigned flags, unsigned long val) } if (val > 0) { if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shreax%ld", val); + AddCodeLine ("jsr shreax%lu", val); } else { - AddCodeLine ("jsr asreax%ld", val); + AddCodeLine ("jsr asreax%lu", val); } } return; @@ -3079,15 +3318,32 @@ void g_asl (unsigned flags, unsigned long val) "tosaslax", "tosshlax", "tosasleax", "tosshleax" }; - - /* If the right hand side is const, the lhs is not on stack but still + /* If the right hand side is const, the lhs is not on stack, but still ** in the primary register. */ if (flags & CF_CONST) { - switch (flags & CF_TYPEMASK) { - case CF_CHAR: + if ((flags & CF_FORCECHAR) != 0) { + val &= 7; + /* Large shifts are faster and smaller with ROR. See g_asr for detailed + ** byte and cycle counts. + */ + if (val < 6) { + while (val--) { + AddCodeLine ("asl a"); + } + } else { + unsigned i; + for (i = val; i < 9; ++i) { + AddCodeLine ("ror a"); + } + AddCodeLine ("and #$%02X", (~0U << val) & 0xFF); + } + return; + } + /* FALLTHROUGH */ + case CF_INT: val &= 0x0F; if (val >= 8) { @@ -3105,9 +3361,9 @@ void g_asl (unsigned flags, unsigned long val) } if (val > 0) { if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shlax%ld", val); + AddCodeLine ("jsr shlax%lu", val); } else { - AddCodeLine ("jsr aslax%ld", val); + AddCodeLine ("jsr aslax%lu", val); } } return; @@ -3146,9 +3402,9 @@ void g_asl (unsigned flags, unsigned long val) } if (val > 0) { if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shleax%ld", val); + AddCodeLine ("jsr shleax%lu", val); } else { - AddCodeLine ("jsr asleax%ld", val); + AddCodeLine ("jsr asleax%lu", val); } } return; @@ -3580,7 +3836,7 @@ void g_lt (unsigned flags, unsigned long val) /* Give a warning in some special cases */ if (val == 0) { - Warning ("Condition is never true"); + Warning ("Comparison of unsigned type < 0 is always false"); AddCodeLine ("jsr return0"); return; } @@ -4297,6 +4553,127 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size) +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + + + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth) +/* Test bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* If we need to do a test, then we avoid shifting (ASR only shifts one bit at a time, + ** so is slow) and just AND with the appropriate mask, then test the result of that. + */ + + /* Avoid overly large shift on host platform. */ + if (EndBit == sizeof (unsigned long) * CHAR_BIT) { + g_and (Flags | CF_CONST, (~0UL << BitOffs)); + } else { + g_and (Flags | CF_CONST, ((1UL << EndBit) - 1) & (~0UL << BitOffs)); + } + + /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes when + ** EndBit <= 24 is possible. + */ + g_test (Flags | CF_CONST); +} + + + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, + unsigned BitOffs, unsigned BitWidth) +/* Extract bits from bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ + g_asr (Flags | CF_CONST, BitOffs); + + /* Since we have now shifted down, we could do char ops when the width fits in a char, but we + ** also need to clear (or set) the high byte since we've been using CF_FORCECHAR up to now. + */ + unsigned Mask = (1U << BitWidth) - 1; + + /* To zero-extend, we will and by the width if the field doesn't end on a char or + ** int boundary. If it does end on a boundary, then zeros will have already been shifted in, + ** but we need to clear the high byte for char. g_and emits no code if the mask is all ones. + ** This is here so the signed and unsigned branches can use it. + */ + unsigned ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */ + if (EndBit == CHAR_BITS) { + /* We need to clear the high byte, since CF_FORCECHAR was set. */ + ZeroExtendMask = 0xFF; + } else if (EndBit != INT_BITS) { + ZeroExtendMask = (1U << BitWidth) - 1; + } + + /* Handle signed bit-fields. */ + if (IsSigned) { + /* Save .A because the sign-bit test will destroy it. */ + AddCodeLine ("tay"); + + /* Check sign bit */ + unsigned SignBitPos = BitWidth - 1U; + unsigned SignBitByte = SignBitPos / CHAR_BITS; + unsigned SignBitPosInByte = SignBitPos % CHAR_BITS; + unsigned SignBitMask = 1U << SignBitPosInByte; + + /* Move the correct byte to .A. This can be only .X for now, + ** but more cases will be needed to support long. + */ + switch (SignBitByte) { + case 0: + break; + case 1: + AddCodeLine ("txa"); + break; + default: + FAIL ("Invalid Byte for sign bit"); + } + + /* Test the sign bit */ + AddCodeLine ("and #$%02X", SignBitMask); + unsigned ZeroExtendLabel = GetLocalLabel (); + AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel)); + + /* Get back .A and sign-extend if required; operating on the full result needs + ** to sign-extend into the high byte, too. + */ + AddCodeLine ("tya"); + g_or (FullWidthFlags | CF_CONST, ~Mask); + + /* We can generate a branch, instead of a jump, here because we know + ** that only a few instructions will be put between here and where + ** DoneLabel will be defined. + */ + unsigned DoneLabel = GetLocalLabel (); + g_branch (DoneLabel); + + /* Get back .A, then zero-extend. We need to duplicate the TYA, rather than move it before + ** the branch to share with the other label, because TYA changes some condition codes. + */ + g_defcodelabel (ZeroExtendLabel); + AddCodeLine ("tya"); + + /* Zero the upper bits, the same as the unsigned path. */ + if (ZeroExtendMask != 0) { + g_and (FullWidthFlags | CF_CONST, ZeroExtendMask); + } + + g_defcodelabel (DoneLabel); + } else { + /* Unsigned bit-field, needs only zero-extension. */ + if (ZeroExtendMask != 0) { + g_and (FullWidthFlags | CF_CONST, ZeroExtendMask); + } + } +} + + + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 6f61b33a6..d6d3d2370 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -62,34 +62,38 @@ #define CF_NONE 0x0000 /* No special flags */ /* Values for the actual type */ -#define CF_CHAR 0x0003 /* Operation on characters */ -#define CF_INT 0x0001 /* Operation on ints */ +#define CF_CHAR 0x0007 /* Operation on characters */ +#define CF_INT 0x0003 /* Operation on ints */ +#define CF_SHORT CF_INT /* Alias */ #define CF_PTR CF_INT /* Alias for readability */ -#define CF_LONG 0x0000 /* Operation on longs */ -#define CF_FLOAT 0x0004 /* Operation on a float */ +#define CF_LONG 0x0001 /* Operation on longs */ +#define CF_FLOAT 0x0010 /* Operation on a float */ /* Signedness */ #define CF_UNSIGNED 0x0008 /* Value is unsigned */ /* Masks for retrieving type information */ -#define CF_TYPEMASK 0x0007 /* Type information */ -#define CF_STYPEMASK 0x000F /* Includes signedness */ +#define CF_TYPEMASK 0x0017 /* Type information */ +#define CF_STYPEMASK 0x001F /* Includes signedness */ -#define CF_NOKEEP 0x0010 /* Value may get destroyed when storing */ -#define CF_CONST 0x0020 /* Constant value available */ -#define CF_CONSTADDR 0x0040 /* Constant address value available */ +#define CF_CONST 0x0040 /* Constant value available */ #define CF_TEST 0x0080 /* Test value */ #define CF_FIXARGC 0x0100 /* Function has fixed arg count */ #define CF_FORCECHAR 0x0200 /* Handle chars as chars, not ints */ -#define CF_REG 0x0800 /* Value is in primary register */ +#define CF_NOKEEP 0x0400 /* Value may get destroyed when storing */ -/* Type of static address */ -#define CF_ADDRMASK 0xF000 /* Type of address */ -#define CF_STATIC 0x0000 /* Static local */ -#define CF_EXTERNAL 0x1000 /* Static external */ -#define CF_ABSOLUTE 0x2000 /* Numeric absolute address */ -#define CF_LOCAL 0x4000 /* Auto variable */ -#define CF_REGVAR 0x8000 /* Register variable */ +/* Type of address */ +#define CF_ADDRMASK 0xF000 /* Bit mask of address type */ +#define CF_IMM 0x0000 /* Value is pure rvalue and has no storage */ +#define CF_ABSOLUTE 0x1000 /* Numeric absolute address */ +#define CF_EXTERNAL 0x2000 /* External */ +#define CF_REGVAR 0x4000 /* Register variable */ +#define CF_LITERAL 0x7000 /* Literal */ +#define CF_PRIMARY 0x8000 /* Value is in primary register */ +#define CF_EXPR 0x9000 /* Value is addressed by primary register */ +#define CF_STATIC 0xA000 /* Local static */ +#define CF_CODE 0xB000 /* C code label location */ +#define CF_STACK 0xC000 /* Function-local auto on stack */ @@ -144,9 +148,6 @@ void g_defcodelabel (unsigned label); void g_defdatalabel (unsigned label); /* Define a local data label */ -void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs); -/* Define label as a local alias for baselabel+offs */ - /*****************************************************************************/ @@ -158,6 +159,12 @@ void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs); void g_defgloblabel (const char* Name); /* Define a global label with the given name */ +void g_defliterallabel (unsigned label); +/* Define a literal data label */ + +void g_aliasliterallabel (unsigned label, unsigned baselabel, long offs); +/* Define label as an alias for baselabel+offs */ + void g_defexport (const char* Name, int ZP); /* Export the given label */ @@ -376,7 +383,7 @@ void g_restore (unsigned flags); /* Copy hold register to primary. */ void g_cmp (unsigned flags, unsigned long val); -/* Immidiate compare. The primary register will not be changed, Z flag +/* Immediate compare. The primary register will not be changed, Z flag ** will be set. */ @@ -406,6 +413,13 @@ void g_truejump (unsigned flags, unsigned label); void g_falsejump (unsigned flags, unsigned label); /* Jump to label if zero flag set */ +void g_branch (unsigned Label); +/* Branch unconditionally to Label if the CPU has the BRA instruction. +** Otherwise, jump to Label. +** Use this function, instead of g_jump(), only where it is certain that +** the label cannot be farther away from the branch than -128/+127 bytes. +*/ + void g_lateadjustSP (unsigned label); /* Adjust stack based on non-immediate data */ @@ -467,6 +481,17 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size); +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth); +/* Test bit-field in ax. */ + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, + unsigned BitOffs, unsigned BitWidth); +/* Extract bits from bit-field in ax. */ + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index b1e5668ce..88f8a5138 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -46,6 +46,7 @@ #include "codeseg.h" #include "datatype.h" #include "error.h" +#include "funcdesc.h" #include "global.h" #include "reginfo.h" #include "symtab.h" @@ -64,298 +65,335 @@ static const char CmpSuffixTab [][4] = { "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule" }; +/* Table with the bool transformers */ +static const char BoolTransformerTab [][8] = { + "booleq", "boolne", + "boolgt", "boolge", "boollt", "boolle", + "boolugt", "booluge", "boolult", "boolule" +}; + /* Table listing the function names and code info values for known internally ** used functions. This table should get auto-generated in the future. */ typedef struct FuncInfo FuncInfo; struct FuncInfo { const char* Name; /* Function name */ - unsigned short Use; /* Register usage */ - unsigned short Chg; /* Changed/destroyed registers */ + unsigned Use; /* Register usage */ + unsigned Chg; /* Changed/destroyed registers */ }; -/* Note for the shift functions: Shifts are done modulo 32, so all shift +/* Functions that change the SP are regarded as using the SP as well. +** The callax/jmpvec functions may call a function that uses/changes more +** registers, so we should further check the info of the called function +** or just play it safe. +** Note for the shift functions: Shifts are done modulo 32, so all shift ** routines are marked to use only the A register. The remainder is ignored ** anyway. */ static const FuncInfo FuncInfoTable[] = { - { "addeq0sp", REG_AX, REG_AXY }, - { "addeqysp", REG_AXY, REG_AXY }, - { "addysp", REG_Y, REG_NONE }, - { "aslax1", REG_AX, REG_AX | REG_TMP1 }, - { "aslax2", REG_AX, REG_AX | REG_TMP1 }, - { "aslax3", REG_AX, REG_AX | REG_TMP1 }, - { "aslax4", REG_AX, REG_AX | REG_TMP1 }, - { "aslaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "asleax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "asrax1", REG_AX, REG_AX | REG_TMP1 }, - { "asrax2", REG_AX, REG_AX | REG_TMP1 }, - { "asrax3", REG_AX, REG_AX | REG_TMP1 }, - { "asrax4", REG_AX, REG_AX | REG_TMP1 }, - { "asraxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "asreax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "bnega", REG_A, REG_AX }, - { "bnegax", REG_AX, REG_AX }, - { "bnegeax", REG_EAX, REG_EAX }, - { "booleq", REG_NONE, REG_AX }, - { "boolge", REG_NONE, REG_AX }, - { "boolgt", REG_NONE, REG_AX }, - { "boolle", REG_NONE, REG_AX }, - { "boollt", REG_NONE, REG_AX }, - { "boolne", REG_NONE, REG_AX }, - { "booluge", REG_NONE, REG_AX }, - { "boolugt", REG_NONE, REG_AX }, - { "boolule", REG_NONE, REG_AX }, - { "boolult", REG_NONE, REG_AX }, - { "callax", REG_AX, REG_ALL }, - { "complax", REG_AX, REG_AX }, - { "decax1", REG_AX, REG_AX }, - { "decax2", REG_AX, REG_AX }, - { "decax3", REG_AX, REG_AX }, - { "decax4", REG_AX, REG_AX }, - { "decax5", REG_AX, REG_AX }, - { "decax6", REG_AX, REG_AX }, - { "decax7", REG_AX, REG_AX }, - { "decax8", REG_AX, REG_AX }, - { "decaxy", REG_AXY, REG_AX | REG_TMP1 }, - { "deceaxy", REG_EAXY, REG_EAX }, - { "decsp1", REG_NONE, REG_Y }, - { "decsp2", REG_NONE, REG_A }, - { "decsp3", REG_NONE, REG_A }, - { "decsp4", REG_NONE, REG_A }, - { "decsp5", REG_NONE, REG_A }, - { "decsp6", REG_NONE, REG_A }, - { "decsp7", REG_NONE, REG_A }, - { "decsp8", REG_NONE, REG_A }, - { "incax1", REG_AX, REG_AX }, - { "incax2", REG_AX, REG_AX }, - { "incax3", REG_AX, REG_AXY | REG_TMP1 }, - { "incax4", REG_AX, REG_AXY | REG_TMP1 }, - { "incax5", REG_AX, REG_AXY | REG_TMP1 }, - { "incax6", REG_AX, REG_AXY | REG_TMP1 }, - { "incax7", REG_AX, REG_AXY | REG_TMP1 }, - { "incax8", REG_AX, REG_AXY | REG_TMP1 }, - { "incaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "incsp1", REG_NONE, REG_NONE }, - { "incsp2", REG_NONE, REG_Y }, - { "incsp3", REG_NONE, REG_Y }, - { "incsp4", REG_NONE, REG_Y }, - { "incsp5", REG_NONE, REG_Y }, - { "incsp6", REG_NONE, REG_Y }, - { "incsp7", REG_NONE, REG_Y }, - { "incsp8", REG_NONE, REG_Y }, - { "laddeq", REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeq0sp", REG_EAX, REG_EAXY }, - { "laddeq1", REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeqa", REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeqysp", REG_EAXY, REG_EAXY }, - { "ldaidx", REG_AXY, REG_AX | REG_PTR1 }, - { "ldauidx", REG_AXY, REG_AX | REG_PTR1 }, - { "ldax0sp", REG_NONE, REG_AXY }, - { "ldaxi", REG_AX, REG_AXY | REG_PTR1 }, - { "ldaxidx", REG_AXY, REG_AXY | REG_PTR1 }, - { "ldaxysp", REG_Y, REG_AXY }, - { "ldeax0sp", REG_NONE, REG_EAXY }, - { "ldeaxi", REG_AX, REG_EAXY | REG_PTR1 }, - { "ldeaxidx", REG_AXY, REG_EAXY | REG_PTR1 }, - { "ldeaxysp", REG_Y, REG_EAXY }, - { "leaa0sp", REG_A, REG_AX }, - { "leaaxsp", REG_AX, REG_AX }, - { "lsubeq", REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeq0sp", REG_EAX, REG_EAXY }, - { "lsubeq1", REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeqa", REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeqysp", REG_EAXY, REG_EAXY }, - { "mulax10", REG_AX, REG_AX | REG_PTR1 }, - { "mulax3", REG_AX, REG_AX | REG_PTR1 }, - { "mulax5", REG_AX, REG_AX | REG_PTR1 }, - { "mulax6", REG_AX, REG_AX | REG_PTR1 }, - { "mulax7", REG_AX, REG_AX | REG_PTR1 }, - { "mulax9", REG_AX, REG_AX | REG_PTR1 }, - { "negax", REG_AX, REG_AX }, - { "push0", REG_NONE, REG_AXY }, - { "push0ax", REG_AX, REG_Y | REG_SREG }, - { "push1", REG_NONE, REG_AXY }, - { "push2", REG_NONE, REG_AXY }, - { "push3", REG_NONE, REG_AXY }, - { "push4", REG_NONE, REG_AXY }, - { "push5", REG_NONE, REG_AXY }, - { "push6", REG_NONE, REG_AXY }, - { "push7", REG_NONE, REG_AXY }, - { "pusha", REG_A, REG_Y }, - { "pusha0", REG_A, REG_XY }, - { "pusha0sp", REG_NONE, REG_AY }, - { "pushaFF", REG_A, REG_Y }, - { "pushax", REG_AX, REG_Y }, - { "pushaysp", REG_Y, REG_AY }, - { "pushc0", REG_NONE, REG_A | REG_Y }, - { "pushc1", REG_NONE, REG_A | REG_Y }, - { "pushc2", REG_NONE, REG_A | REG_Y }, - { "pusheax", REG_EAX, REG_Y }, - { "pushl0", REG_NONE, REG_AXY }, - { "pushw", REG_AX, REG_AXY | REG_PTR1 }, - { "pushw0sp", REG_NONE, REG_AXY }, - { "pushwidx", REG_AXY, REG_AXY | REG_PTR1 }, - { "pushwysp", REG_Y, REG_AXY }, - { "regswap", REG_AXY, REG_AXY | REG_TMP1 }, - { "regswap1", REG_XY, REG_A }, - { "regswap2", REG_XY, REG_A | REG_Y }, - { "return0", REG_NONE, REG_AX }, - { "return1", REG_NONE, REG_AX }, - { "shlax1", REG_AX, REG_AX | REG_TMP1 }, - { "shlax2", REG_AX, REG_AX | REG_TMP1 }, - { "shlax3", REG_AX, REG_AX | REG_TMP1 }, - { "shlax4", REG_AX, REG_AX | REG_TMP1 }, - { "shlaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "shleax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "shrax1", REG_AX, REG_AX | REG_TMP1 }, - { "shrax2", REG_AX, REG_AX | REG_TMP1 }, - { "shrax3", REG_AX, REG_AX | REG_TMP1 }, - { "shrax4", REG_AX, REG_AX | REG_TMP1 }, - { "shraxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "shreax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "staspidx", REG_A | REG_Y, REG_Y | REG_TMP1 | REG_PTR1 }, - { "stax0sp", REG_AX, REG_Y }, - { "staxspidx", REG_AXY, REG_TMP1 | REG_PTR1 }, - { "staxysp", REG_AXY, REG_Y }, - { "steax0sp", REG_EAX, REG_Y }, - { "steaxysp", REG_EAXY, REG_Y }, - { "subeq0sp", REG_AX, REG_AXY }, - { "subeqysp", REG_AXY, REG_AXY }, - { "subysp", REG_Y, REG_AY }, - { "tosadd0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosadda0", REG_A, REG_AXY }, - { "tosaddax", REG_AX, REG_AXY }, - { "tosaddeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosand0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosanda0", REG_A, REG_AXY }, - { "tosandax", REG_AX, REG_AXY }, - { "tosandeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosaslax", REG_A, REG_AXY | REG_TMP1 }, - { "tosasleax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosasrax", REG_A, REG_AXY | REG_TMP1 }, - { "tosasreax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosdiv0ax", REG_AX, REG_ALL }, - { "tosdiva0", REG_A, REG_ALL }, - { "tosdivax", REG_AX, REG_ALL }, - { "tosdiveax", REG_EAX, REG_ALL }, - { "toseq00", REG_NONE, REG_AXY | REG_SREG }, - { "toseqa0", REG_A, REG_AXY | REG_SREG }, - { "toseqax", REG_AX, REG_AXY | REG_SREG }, - { "toseqeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosge00", REG_NONE, REG_AXY | REG_SREG }, - { "tosgea0", REG_A, REG_AXY | REG_SREG }, - { "tosgeax", REG_AX, REG_AXY | REG_SREG }, - { "tosgeeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosgt00", REG_NONE, REG_AXY | REG_SREG }, - { "tosgta0", REG_A, REG_AXY | REG_SREG }, - { "tosgtax", REG_AX, REG_AXY | REG_SREG }, - { "tosgteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosicmp", REG_AX, REG_AXY | REG_SREG }, - { "tosicmp0", REG_A, REG_AXY | REG_SREG }, - { "toslcmp", REG_EAX, REG_A | REG_Y | REG_PTR1 }, - { "tosle00", REG_NONE, REG_AXY | REG_SREG }, - { "toslea0", REG_A, REG_AXY | REG_SREG }, - { "tosleax", REG_AX, REG_AXY | REG_SREG }, - { "tosleeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "toslt00", REG_NONE, REG_AXY | REG_SREG }, - { "toslta0", REG_A, REG_AXY | REG_SREG }, - { "tosltax", REG_AX, REG_AXY | REG_SREG }, - { "toslteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosmod0ax", REG_AX, REG_ALL }, - { "tosmodeax", REG_EAX, REG_ALL }, - { "tosmul0ax", REG_AX, REG_ALL }, - { "tosmula0", REG_A, REG_ALL }, - { "tosmulax", REG_AX, REG_ALL }, - { "tosmuleax", REG_EAX, REG_ALL }, - { "tosne00", REG_NONE, REG_AXY | REG_SREG }, - { "tosnea0", REG_A, REG_AXY | REG_SREG }, - { "tosneax", REG_AX, REG_AXY | REG_SREG }, - { "tosneeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosor0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosora0", REG_A, REG_AXY | REG_TMP1 }, - { "tosorax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosoreax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosrsub0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosrsuba0", REG_A, REG_AXY | REG_TMP1 }, - { "tosrsubax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosrsubeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosshlax", REG_A, REG_AXY | REG_TMP1 }, - { "tosshleax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosshrax", REG_A, REG_AXY | REG_TMP1 }, - { "tosshreax", REG_A, REG_EAXY | REG_TMP1 }, - { "tossub0ax", REG_AX, REG_EAXY }, - { "tossuba0", REG_A, REG_AXY }, - { "tossubax", REG_AX, REG_AXY }, - { "tossubeax", REG_EAX, REG_EAXY }, - { "tosudiv0ax", REG_AX, REG_ALL & ~REG_SAVE }, - { "tosudiva0", REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudivax", REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudiveax", REG_EAX, REG_ALL & ~REG_SAVE }, - { "tosuge00", REG_NONE, REG_AXY | REG_SREG }, - { "tosugea0", REG_A, REG_AXY | REG_SREG }, - { "tosugeax", REG_AX, REG_AXY | REG_SREG }, - { "tosugeeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosugt00", REG_NONE, REG_AXY | REG_SREG }, - { "tosugta0", REG_A, REG_AXY | REG_SREG }, - { "tosugtax", REG_AX, REG_AXY | REG_SREG }, - { "tosugteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosule00", REG_NONE, REG_AXY | REG_SREG }, - { "tosulea0", REG_A, REG_AXY | REG_SREG }, - { "tosuleax", REG_AX, REG_AXY | REG_SREG }, - { "tosuleeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosult00", REG_NONE, REG_AXY | REG_SREG }, - { "tosulta0", REG_A, REG_AXY | REG_SREG }, - { "tosultax", REG_AX, REG_AXY | REG_SREG }, - { "tosulteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosumod0ax", REG_AX, REG_ALL & ~REG_SAVE }, - { "tosumoda0", REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodax", REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodeax", REG_EAX, REG_ALL & ~REG_SAVE }, - { "tosumul0ax", REG_AX, REG_ALL }, - { "tosumula0", REG_A, REG_ALL }, - { "tosumulax", REG_AX, REG_ALL }, - { "tosumuleax", REG_EAX, REG_ALL }, - { "tosxor0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosxora0", REG_A, REG_AXY | REG_TMP1 }, - { "tosxorax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosxoreax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tsteax", REG_EAX, REG_Y }, - { "utsteax", REG_EAX, REG_Y }, + { "addeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "addeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "addysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP }, + { "along", REG_A, PSTATE_ALL | REG_X | REG_SREG }, + { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "aulong", REG_NONE, PSTATE_ALL | REG_X | REG_SREG }, + { "axlong", REG_X, PSTATE_ALL | REG_Y | REG_SREG }, + { "axulong", REG_NONE, PSTATE_ALL | REG_Y | REG_SREG }, + { "bcasta", REG_A, PSTATE_ALL | REG_AX }, + { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, + { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "bnega", REG_A, PSTATE_ALL | REG_AX }, + { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, + { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, + { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, + { "callax", REG_AX, PSTATE_ALL | REG_ALL }, /* PSTATE_ZN | REG_PTR1 */ + { "complax", REG_AX, PSTATE_ALL | REG_AX }, + { "decax1", REG_AX, PSTATE_ALL | REG_AX }, + { "decax2", REG_AX, PSTATE_ALL | REG_AX }, + { "decax3", REG_AX, PSTATE_ALL | REG_AX }, + { "decax4", REG_AX, PSTATE_ALL | REG_AX }, + { "decax5", REG_AX, PSTATE_ALL | REG_AX }, + { "decax6", REG_AX, PSTATE_ALL | REG_AX }, + { "decax7", REG_AX, PSTATE_ALL | REG_AX }, + { "decax8", REG_AX, PSTATE_ALL | REG_AX }, + { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, + { "decsp1", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "decsp2", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp3", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp4", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp5", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp6", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp7", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp8", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "enter", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "incax1", REG_AX, PSTATE_ALL | REG_AX }, + { "incax2", REG_AX, PSTATE_ALL | REG_AX }, + { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incsp1", REG_SP, PSTATE_ALL | REG_SP }, + { "incsp2", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp3", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp4", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp5", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp6", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp7", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp8", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "jmpvec", REG_EVERYTHING, PSTATE_ALL | REG_ALL }, /* NONE */ + { "laddeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldax0sp", SLV_TOP, PSTATE_ALL | REG_AXY }, + { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_AXY }, + { "ldeax0sp", SLV_TOP, PSTATE_ALL | REG_EAXY }, + { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_EAXY }, + { "leaa0sp", REG_SP | REG_A, PSTATE_ALL | REG_AX }, + { "leaaxsp", REG_SP | REG_AX, PSTATE_ALL | REG_AX }, + { "leave00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leave0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leave", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "leavey00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leavey0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leavey", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_Y }, + { "lsubeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "negax", REG_AX, PSTATE_ALL | REG_AX }, + { "negeax", REG_EAX, PSTATE_ALL | REG_EAX }, + { "popa", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "popax", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "popeax", SLV_TOP, PSTATE_ALL | REG_SP | REG_EAXY }, + { "push0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push0ax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y | REG_SREG }, + { "push1", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push2", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push3", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push4", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push5", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push6", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push7", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pusha", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pusha0", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_XY }, + { "pusha0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "pushaFF", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pushax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushaysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "pushc0", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc1", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc2", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pusheax", REG_SP | REG_EAX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushl0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushw", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushw0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushwidx", REG_SP | REG_AXY, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushwysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AXY }, + { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "regswap1", REG_XY, PSTATE_ALL | REG_A }, + { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, + { "resteax", REG_SAVE, PSTATE_ZN | REG_EAX }, /* also uses regsave+2/+3 */ + { "return0", REG_NONE, PSTATE_ALL | REG_AX }, + { "return1", REG_NONE, PSTATE_ALL | REG_AX }, + { "saveeax", REG_EAX, PSTATE_ZN | REG_Y | REG_SAVE }, /* also regsave+2/+3 */ + { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "staspidx", SLV_TOP | REG_AY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, + { "stax0sp", REG_SP | REG_AX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "staxspidx", SLV_TOP | REG_AXY, PSTATE_ALL | REG_SP | REG_TMP1 | REG_PTR1 }, + { "staxysp", REG_SP | REG_AXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "steax0sp", REG_SP | REG_EAX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "steaxspidx", SLV_TOP | REG_EAXY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, /* also tmp2, tmp3 */ + { "steaxysp", REG_SP | REG_EAXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "subeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "subeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "subysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "swapstk", SLV_TOP | REG_AX, PSTATE_ALL | SLV_TOP | REG_AXY }, /* also ptr4 */ + { "tosadd0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosadda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosand0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosanda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosaslax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosasrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosdiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiveax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_ALL }, + { "toseq00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqa0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosgt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosicmp", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosicmp0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosint", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslcmp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_A | REG_Y | REG_PTR1 }, + { "tosle00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "toslong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosltax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosmod0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmodeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosmul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosmulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosne00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosnea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshlax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tossub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tossuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tosudiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosudiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudiveax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosuge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosugt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosule00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosulong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "tosult00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosultax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosumod0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumoda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodeax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosumulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosxor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosxora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, }; #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) /* Table with names of zero page locations used by the compiler */ static const ZPInfo ZPInfoTable[] = { - { 0, "ptr1", REG_PTR1_LO, REG_PTR1 }, - { 0, "ptr1+1", REG_PTR1_HI, REG_PTR1 }, - { 0, "ptr2", REG_PTR2_LO, REG_PTR2 }, - { 0, "ptr2+1", REG_PTR2_HI, REG_PTR2 }, - { 4, "ptr3", REG_NONE, REG_NONE }, - { 4, "ptr4", REG_NONE, REG_NONE }, - { 7, "regbank", REG_NONE, REG_NONE }, - { 0, "regsave", REG_SAVE_LO, REG_SAVE }, - { 0, "regsave+1", REG_SAVE_HI, REG_SAVE }, - { 0, "sp", REG_SP_LO, REG_SP }, - { 0, "sp+1", REG_SP_HI, REG_SP }, - { 0, "sreg", REG_SREG_LO, REG_SREG }, - { 0, "sreg+1", REG_SREG_HI, REG_SREG }, - { 0, "tmp1", REG_TMP1, REG_TMP1 }, - { 0, "tmp2", REG_NONE, REG_NONE }, - { 0, "tmp3", REG_NONE, REG_NONE }, - { 0, "tmp4", REG_NONE, REG_NONE }, + { 0, "ptr1", 2, REG_PTR1_LO, REG_PTR1 }, + { 0, "ptr1+1", 1, REG_PTR1_HI, REG_PTR1 }, + { 0, "ptr2", 2, REG_PTR2_LO, REG_PTR2 }, + { 0, "ptr2+1", 1, REG_PTR2_HI, REG_PTR2 }, + { 4, "ptr3", 2, REG_NONE, REG_NONE }, + { 4, "ptr4", 2, REG_NONE, REG_NONE }, + { 7, "regbank", 6, REG_NONE, REG_NONE }, + { 0, "regsave", 4, REG_SAVE_LO, REG_SAVE }, + { 0, "regsave+1", 3, REG_SAVE_HI, REG_SAVE }, + { 0, "sp", 2, REG_SP_LO, REG_SP }, + { 0, "sp+1", 1, REG_SP_HI, REG_SP }, + { 0, "sreg", 2, REG_SREG_LO, REG_SREG }, + { 0, "sreg+1", 1, REG_SREG_HI, REG_SREG }, + { 0, "tmp1", 1, REG_TMP1, REG_TMP1 }, + { 0, "tmp2", 1, REG_NONE, REG_NONE }, + { 0, "tmp3", 1, REG_NONE, REG_NONE }, + { 0, "tmp4", 1, REG_NONE, REG_NONE }, }; #define ZPInfoCount (sizeof(ZPInfoTable) / sizeof(ZPInfoTable[0])) @@ -367,6 +405,83 @@ static const ZPInfo ZPInfoTable[] = { +static int IsAddrOnZP (long Address) +/* Return true if the Address is within the ZP range. +** FIXME: ZP range may vary depending on the CPU settings. +*/ +{ + /* ZP in range [0x00, 0xFF] */ + return Address >= 0 && Address < 0x100; +} + + + +int IsZPArg (const char* Name) +/* Exam if the main part of the arg string indicates a ZP loc */ +{ + unsigned short ArgInfo = 0; + long Offset = 0; + StrBuf NameBuf = AUTO_STRBUF_INITIALIZER; + SymEntry* E = 0; + const ZPInfo* Info = 0; + + if (!ParseOpcArgStr (Name, &ArgInfo, &NameBuf, &Offset)) { + /* Parsing failed */ + SB_Done (&NameBuf); + return 0; + } + + if ((ArgInfo & AIF_HAS_NAME) == 0) { + /* Numeric locs have no names */ + SB_Done (&NameBuf); + + /* We can check it against the ZP boundary if it is known */ + return IsAddrOnZP (Offset); + } + + if ((ArgInfo & AIF_BUILTIN) != 0) { + /* Search for the name in the list of builtin ZPs */ + Info = GetZPInfo (SB_GetConstBuf (&NameBuf)); + + SB_Done (&NameBuf); + + /* Do we know the ZP? */ + if (Info != 0) { + /* Use the information we have */ + return Offset >= 0 && Offset < (int)Info->Size; + } + + /* Assume it be non-ZP */ + return 0; + } + + if ((ArgInfo & AIF_EXTERNAL) == 0) { + /* We don't support local variables on ZP */ + SB_Done (&NameBuf); + return 0; + } + + /* Search for the symbol in the global symbol table skipping the underline + ** in its name. + */ + E = FindGlobalSym (SB_GetConstBuf (&NameBuf) + 1); + + SB_Done (&NameBuf); + + /* We are checking the offset against the symbol size rather than the actual + ** zeropage boundary, since we can't magically ensure that until linking and + ** can only trust the user in writing the correct code for now. + */ + if (E != 0 && (E->Flags & SC_ZEROPAGE) != 0) { + return Offset >= 0 && (unsigned)Offset < CheckedSizeOf (E->Type); + } + + /* Not found on ZP */ + return 0; +} + + + static int CompareFuncInfo (const void* Key, const void* Info) /* Compare function for bsearch */ { @@ -375,10 +490,10 @@ static int CompareFuncInfo (const void* Key, const void* Info) -fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) +fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* For the given function, lookup register information and store it into ** the given variables. If the function is unknown, assume it will use and -** load all registers. +** load all registers as well as touching the processor flags. */ { /* If the function name starts with an underline, it is an external @@ -392,7 +507,8 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { - FuncDesc* D = E->V.F.Func; + FuncDesc* D = GetFuncDesc (E->Type); + *Use = REG_NONE; /* A variadic function will use the Y register (the parameter list ** size is passed there). A fastcall function will use the A or A/X @@ -400,26 +516,40 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) ** we assume that any function will destroy all registers. */ if ((D->Flags & FD_VARIADIC) != 0) { - *Use = REG_Y; + *Use = REG_Y | REG_SP | SLV_TOP; } else if (D->Flags & FD_CALL_WRAPPER) { /* Wrappers may go to any functions, so mark them as using all ** registers. */ *Use = REG_EAXY; - } else if (D->ParamCount > 0 && - (AutoCDecl ? - IsQualFastcall (E->Type) : - !IsQualCDecl (E->Type))) { - /* Will use registers depending on the last param. */ - switch (CheckedSizeOf (D->LastParam->Type)) { - case 1u: - *Use = REG_A; - break; - case 2u: - *Use = REG_AX; - break; - default: - *Use = REG_EAX; + } else if (D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) { + /* Will use registers depending on the last param. If the last + ** param has incomplete type, or if the function has not been + ** prototyped yet, just assume __EAX__. + */ + if (IsFastcallFunc (E->Type)) { + if (D->LastParam != 0) { + switch (SizeOf (D->LastParam->Type)) { + case 1u: + *Use = REG_A; + break; + case 2u: + *Use = REG_AX; + break; + default: + *Use = REG_EAX; + } + if (D->ParamCount > 1) { + /* Passes other params on the stack */ + *Use |= REG_SP | SLV_TOP; + } + } else { + /* We'll assume all */ + *Use = REG_EAX | REG_SP | SLV_TOP; + } + } else { + /* Passes all params on the stack */ + *Use = REG_SP | SLV_TOP; } } else { /* Will not use any registers */ @@ -429,6 +559,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Will destroy all registers */ *Chg = REG_ALL; + /* and will destroy all processor flags */ + *Chg |= PSTATE_ALL; + /* Done */ return FNCLS_GLOBAL; } @@ -441,6 +574,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) */ *Use = REG_ALL; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; return FNCLS_NUMERIC; } else { @@ -454,6 +588,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Use the information we have */ *Use = Info->Use; *Chg = Info->Chg; + if ((*Use & (SLV_TOP | SLV_IND)) != 0) { + *Use |= REG_SP; + } } else { /* It's an internal function we have no information for. If in ** debug mode, output an additional warning, so we have a chance @@ -465,15 +602,17 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) } *Use = REG_ALL; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; } return FNCLS_BUILTIN; } /* Function not found - assume that the primary register is input, and all - ** registers are changed + ** registers and processor flags are changed */ *Use = REG_EAXY; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; return FNCLS_UNKNOWN; } @@ -838,3 +977,112 @@ cmp_t FindTosCmpCond (const char* Name) return CMP_INV; } } + + + +const char* GetCmpSuffix (cmp_t Cond) +/* Return the compare suffix by the given a compare condition or 0 on failure */ +{ + /* Check for the correct subroutine name */ + if (Cond >= 0 && + Cond != CMP_INV && + (unsigned)Cond < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab[0])) { + return CmpSuffixTab[Cond]; + } else { + /* Not found */ + return 0; + } +} + + + +char* GetBoolCmpSuffix (char* Buf, cmp_t Cond) +/* Search for a boolean transformer subroutine (eg. booleq) by the given compare +** condition. +** Return the output buffer filled with the name of the correct subroutine or 0 +** on failure. +*/ +{ + /* Check for the correct boolean transformer subroutine name */ + const char* Suf = GetCmpSuffix (Cond); + + if (Suf != 0) { + sprintf (Buf, "bool%s", Suf); + return Buf; + } else { + /* Not found */ + return 0; + } +} + + + +char* GetTosCmpSuffix (char* Buf, cmp_t Cond) +/* Search for a TOS compare function (eg. tosgtax) by the given compare condition. +** Return the output buffer filled with the name of the correct function or 0 on +** failure. +*/ +{ + /* Check for the correct TOS function name */ + const char* Suf = GetCmpSuffix (Cond); + + if (Suf != 0) { + sprintf (Buf, "tos%sax", Suf); + return Buf; + } else { + /* Not found */ + return 0; + } +} + + + +const char* GetBoolTransformer (cmp_t Cond) +/* Get the bool transformer corresponding to the given compare condition */ +{ + if (Cond > CMP_INV && Cond < CMP_END) { + return BoolTransformerTab[Cond]; + } + + /* Not found */ + return 0; +} + + +cmp_t GetNegatedCond (cmp_t Cond) +/* Get the logically opposite compare condition */ +{ + switch (Cond) { + case CMP_EQ: return CMP_NE; + case CMP_NE: return CMP_EQ; + case CMP_GT: return CMP_LE; + case CMP_GE: return CMP_LT; + case CMP_LT: return CMP_GE; + case CMP_LE: return CMP_GT; + case CMP_UGT: return CMP_ULE; + case CMP_UGE: return CMP_ULT; + case CMP_ULT: return CMP_UGE; + case CMP_ULE: return CMP_UGT; + default: return CMP_INV; + } +} + + + +cmp_t GetRevertedCond (cmp_t Cond) +/* Get the compare condition in reverted order of operands */ +{ + switch (Cond) { + case CMP_EQ: return CMP_EQ; + case CMP_NE: return CMP_NE; + case CMP_GT: return CMP_LT; + case CMP_GE: return CMP_LE; + case CMP_LT: return CMP_GT; + case CMP_LE: return CMP_GE; + case CMP_UGT: return CMP_ULT; + case CMP_UGE: return CMP_ULE; + case CMP_ULT: return CMP_UGT; + case CMP_ULE: return CMP_UGE; + default: return CMP_INV; + } +} diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 38e196bcf..14ef54d8f 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -57,7 +57,7 @@ struct CodeSeg; /* Forward to struct RegContents */ struct RegContents; -/* Defines for registers. */ +/* Defines for registers */ #define REG_NONE 0x0000U #define REG_A 0x0001U #define REG_X 0x0002U @@ -74,6 +74,23 @@ struct RegContents; #define REG_SP_LO 0x1000U #define REG_SP_HI 0x2000U +/* Defines for some special register usage */ +#define SLV_IND 0x00010000U /* Accesses (sp),y */ +#define SLV_TOP 0x00020000U /* Accesses (sp),0 */ +#define SLV_SP65 0x00200000U /* Accesses 6502 stack pointer */ +#define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ +#define SLV_PL65 0x00800000U /* Pops from 6502 stack */ + +/* Defines for processor states */ +#define PSTATE_NONE 0x00000000U +#define PSTATE_C 0x01000000U /* Carry */ +#define PSTATE_Z 0x02000000U /* Zero */ +#define PSTATE_I 0x04000000U /* Interrupt */ +#define PSTATE_D 0x08000000U /* Decimal */ +#define PSTATE_U 0x10000000U /* Unused */ +#define PSTATE_B 0x20000000U /* Break */ +#define PSTATE_V 0x40000000U /* Overflow */ +#define PSTATE_N 0x80000000U /* Negative */ /* Combined register defines */ #define REG_PTR1 (REG_PTR1_LO | REG_PTR1_HI) @@ -90,13 +107,23 @@ struct RegContents; #define REG_ZP 0xFFF8U #define REG_ALL 0xFFFFU +#define PSTATE_CZ (PSTATE_C | PSTATE_Z) +#define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N) +#define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N) +#define PSTATE_ZN (PSTATE_Z | PSTATE_N) +#define PSTATE_ZVN (PSTATE_Z | PSTATE_V | PSTATE_N) +#define PSTATE_6502 0xE7000000U +#define PSTATE_ALL 0xFF000000U +#define REG_EVERYTHING 0xFFFFFFFFU + /* Zero page register info */ typedef struct ZPInfo ZPInfo; struct ZPInfo { unsigned char Len; /* Length of the following string */ - char Name[11]; /* Name of zero page symbol */ + char Name[10]; /* Name of zero page symbol */ + unsigned char Size; /* Maximum buffer size of this register */ unsigned short ByteUse; /* Register info for this symbol */ unsigned short WordUse; /* Register info for 16 bit access */ }; @@ -115,7 +142,10 @@ typedef enum { CMP_UGT, CMP_UGE, CMP_ULT, - CMP_ULE + CMP_ULE, + + /* End of the enumeration */ + CMP_END } cmp_t; @@ -136,7 +166,10 @@ typedef enum { -fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg); +int IsZPArg (const char* Arg); +/* Exam if the main part of the arg string indicates a ZP loc */ + +fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg); /* For the given function, lookup register information and store it into ** the given variables. If the function is unknown, assume it will use and ** load all registers. @@ -185,7 +218,30 @@ cmp_t FindTosCmpCond (const char* Name); ** Return the condition code or CMP_INV on failure. */ +const char* GetBoolTransformer (cmp_t Cond); +/* Get the bool transformer corresponding to the given compare condition */ +cmp_t GetNegatedCond (cmp_t Cond); +/* Get the logically opposite compare condition */ + +cmp_t GetRevertedCond (cmp_t Cond); +/* Get the compare condition in reverted order of operands */ + +const char* GetCmpSuffix (cmp_t Cond); +/* Return the compare suffix by the given a compare condition or 0 on failure */ + +char* GetBoolCmpSuffix (char* Buf, cmp_t Cond); +/* Search for a boolean transformer subroutine (eg. booleq) by the given compare +** condition. +** Return the output buffer filled with the name of the correct subroutine or 0 +** on failure. +*/ + +char* GetTosCmpSuffix (char* Buf, cmp_t Cond); +/* Search for a TOS compare function (eg. tosgtax) by the given compare condition. +** Return the output buffer filled with the name of the correct function or 0 on +** failure. +*/ /* End of codeinfo.h */ diff --git a/src/cc65/codelab.c b/src/cc65/codelab.c index f36520835..0909702fd 100644 --- a/src/cc65/codelab.c +++ b/src/cc65/codelab.c @@ -90,8 +90,12 @@ void CL_AddRef (CodeLabel* L, struct CodeEntry* E) /* The insn at E jumps to this label */ E->JumpTo = L; - /* Replace the code entry argument with the name of the new label */ - CE_SetArg (E, L->Name); + if (CE_HasArgBase (E)) { + /* Replace the code entry argument base with the name of the new label */ + CE_SetArgBase (E, L->Name); + } else { + CE_SetArgBaseAndOff (E, L->Name, 0); + } /* Remember that in the label */ CollAppend (&L->JumpFrom, E); @@ -112,6 +116,7 @@ void CL_MoveRefs (CodeLabel* OldLabel, CodeLabel* NewLabel) CodeEntry* E = CL_GetRef (OldLabel, Count); /* Change the reference to the new label */ + CHECK (E->JumpTo != NULL); CHECK (E->JumpTo == OldLabel); CL_AddRef (NewLabel, E); diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 498aa0ff2..29fa79d26 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -48,7 +48,6 @@ #include "xsprintf.h" /* cc65 */ -#include "asmlabel.h" #include "codeent.h" #include "codeinfo.h" #include "codeopt.h" @@ -56,6 +55,8 @@ #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" +#include "coptjmp.h" +#include "coptmisc.h" #include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" @@ -69,660 +70,8 @@ #include "error.h" #include "global.h" #include "output.h" -#include "symtab.h" -/*****************************************************************************/ -/* Optimize loads */ -/*****************************************************************************/ - - - -static unsigned OptLoad1 (CodeSeg* S) -/* Search for a call to ldaxysp where X is not used later and replace it by -** a load of just the A register. -*/ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* E; - - /* Get next entry */ - E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (E, "ldaxysp") && - RegValIsKnown (E->RI->In.RegY) && - !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Reload the Y register */ - const char* Arg = MakeHexArg (E->RI->In.RegY - 1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Load from stack */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad2 (CodeSeg* S) -/* Replace calls to ldaxysp by inline code */ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[3]; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (L[0], "ldaxysp")) { - - CodeEntry* X; - - /* Followed by sta abs/stx abs? */ - if (CS_GetEntries (S, L+1, I+1, 2) && - L[1]->OPC == OP65_STA && - L[2]->OPC == OP65_STX && - (L[1]->Arg == 0 || - L[2]->Arg == 0 || - strcmp (L[1]->Arg, L[2]->Arg) != 0) && - !CS_RangeHasLabel (S, I+1, 2) && - !RegXUsed (S, I+3)) { - - /* A/X are stored into memory somewhere and X is not used - ** later - */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); - CS_InsertEntry (S, X, I+4); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+5); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+6); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I+7); - - /* Now remove the call to the subroutine and the sta/stx */ - CS_DelEntries (S, I, 3); - - } else { - - /* Standard replacement */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+1); - - /* tax */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+4); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - } - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad3 (CodeSeg* S) -/* Remove repeated loads from one and the same memory location */ -{ - unsigned Changes = 0; - CodeEntry* Load = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Forget a preceeding load if we have a label */ - if (Load && CE_HasLabel (E)) { - Load = 0; - } - - /* Check if this insn is a load */ - if (E->Info & OF_LOAD) { - - CodeEntry* N; - - /* If we had a preceeding load that is identical, remove this one. - ** If it is not identical, or we didn't have one, remember it. - */ - if (Load != 0 && - E->OPC == Load->OPC && - E->AM == Load->AM && - ((E->Arg == 0 && Load->Arg == 0) || - strcmp (E->Arg, Load->Arg) == 0) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_CBRA) == 0) { - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Next insn */ - continue; - - } else { - - Load = E; - - } - - } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { - /* Forget the first load on occurance of any insn we don't like */ - Load = 0; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Decouple operations */ -/*****************************************************************************/ - - - -static unsigned OptDecouple (CodeSeg* S) -/* Decouple operations, that is, do the following replacements: -** -** dex -> ldx #imm -** inx -> ldx #imm -** dey -> ldy #imm -** iny -> ldy #imm -** tax -> ldx #imm -** txa -> lda #imm -** tay -> ldy #imm -** tya -> lda #imm -** lda zp -> lda #imm -** ldx zp -> ldx #imm -** ldy zp -> ldy #imm -** -** Provided that the register values are known of course. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - const char* Arg; - - /* Get next entry and it's input register values */ - CodeEntry* E = CS_GetEntry (S, I); - const RegContents* In = &E->RI->In; - - /* Assume we have no replacement */ - CodeEntry* X = 0; - - /* Check the instruction */ - switch (E->OPC) { - - case OP65_DEA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA - 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX - 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY - 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA + 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX + 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY + 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_LDA: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDX: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDY: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_TAX: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TAY: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TXA: - if (E->RI->In.RegX >= 0) { - Arg = MakeHexArg (In->RegX); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TYA: - if (E->RI->In.RegY >= 0) { - Arg = MakeHexArg (In->RegY); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - default: - /* Avoid gcc warnings */ - break; - - } - - /* Insert the replacement if we have one */ - if (X) { - CS_InsertEntry (S, X, I+1); - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize stack pointer ops */ -/*****************************************************************************/ - - - -static unsigned IsDecSP (const CodeEntry* E) -/* Check if this is an insn that decrements the stack pointer. If so, return -** the decrement. If not, return zero. -** The function expects E to be a subroutine call. -*/ -{ - if (strncmp (E->Arg, "decsp", 5) == 0) { - if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { - return (E->Arg[5] - '0'); - } - } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { - return E->RI->In.RegY; - } - - /* If we come here, it's not a decsp op */ - return 0; -} - - - -static unsigned OptStackPtrOps (CodeSeg* S) -/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all -** known cases! -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - unsigned Dec1; - unsigned Dec2; - const CodeEntry* N; - - /* Get the next entry */ - const CodeEntry* E = CS_GetEntry (S, I); - - /* Check for decspn or subysp */ - if (E->OPC == OP65_JSR && - (Dec1 = IsDecSP (E)) > 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - (Dec2 = IsDecSP (N)) > 0 && - (Dec1 += Dec2) <= 255 && - !CE_HasLabel (N)) { - - CodeEntry* X; - char Buf[20]; - - /* We can combine the two */ - if (Dec1 <= 8) { - /* Insert a call to decsp */ - xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); - CS_InsertEntry (S, X, I+2); - } else { - /* Insert a call to subysp */ - const char* Arg = MakeHexArg (Dec1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); - CS_InsertEntry (S, X, I+2); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); - CS_InsertEntry (S, X, I+3); - } - - /* Delete the old code */ - CS_DelEntries (S, I, 2); - - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} - -static unsigned OptGotoSPAdj (CodeSeg* S) -/* Optimize SP adjustment for forward 'goto' */ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[10], *X; - unsigned short adjustment; - const char* Arg; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence generated by g_lateadjustSP */ - if (L[0]->OPC == OP65_PHA && - CS_GetEntries (S, L+1, I+1, 9) && - L[1]->OPC == OP65_LDA && - L[1]->AM == AM65_ABS && - L[2]->OPC == OP65_CLC && - L[3]->OPC == OP65_ADC && - strcmp (L[3]->Arg, "sp") == 0 && - L[6]->OPC == OP65_ADC && - strcmp (L[6]->Arg, "sp+1") == 0 && - L[9]->OPC == OP65_JMP) { - adjustment = FindSPAdjustment (L[1]->Arg); - - if (adjustment == 0) { - /* No SP adjustment needed, remove the whole sequence */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 8) { - /* If adjustment is in range [-8, 0) we use decsp* calls */ - char Buf[20]; - adjustment = 65536 - adjustment; - xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 255) { - /* For range [-255, -8) we have ldy #, jsr subysp */ - adjustment = 65536 - adjustment; - Arg = MakeHexArg (adjustment); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment > 255) { - /* For ranges [-32768, 255) and (255, 32767) the only modification - ** is to replace the absolute with immediate addressing - */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 1); - Arg = MakeHexArg (adjustment >> 8); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); - CS_InsertEntry (S, X, I + 6); - - /* Delete the old code */ - CS_DelEntry (S, I + 2); - CS_DelEntry (S, I + 6); - } - else if (adjustment > 8) { - /* For range (8, 255] we have ldy #, jsr addysp */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else { - /* If adjustment is in range (0, 8] we use incsp* calls */ - char Buf[20]; - xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - Changes++; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} /*****************************************************************************/ /* struct OptFunc */ @@ -796,9 +145,6 @@ static OptFunc DOptLoad2 = { OptLoad2, "OptLoad2", 200, 0, static OptFunc DOptLoad3 = { OptLoad3, "OptLoad3", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX1 = { OptNegAX1, "OptNegAX1", 165, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX2 = { OptNegAX2, "OptNegAX2", 200, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPrecalc = { OptPrecalc, "OptPrecalc", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPtrLoad1 = { OptPtrLoad1, "OptPtrLoad1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPtrLoad2 = { OptPtrLoad2, "OptPtrLoad2", 100, 0, 0, 0, 0, 0 }; @@ -821,13 +167,19 @@ static OptFunc DOptPtrStore2 = { OptPtrStore2, "OptPtrStore2", 65, 0, static OptFunc DOptPtrStore3 = { OptPtrStore3, "OptPtrStore3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, 0, 0, 0, 0 }; static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; -static OptFunc DOptPushPop = { OptPushPop, "OptPushPop", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop1 = { OptPushPop1, "OptPushPop1", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop2 = { OptPushPop2, "OptPushPop2", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift1 = { OptShift1, "OptShift1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift2 = { OptShift2, "OptShift2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift3 = { OptShift3, "OptShift3", 17, 0, 0, 0, 0, 0 }; static OptFunc DOptShift4 = { OptShift4, "OptShift4", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift5 = { OptShift5, "OptShift5", 110, 0, 0, 0, 0, 0 }; static OptFunc DOptShift6 = { OptShift6, "OptShift6", 200, 0, 0, 0, 0, 0 }; +static OptFunc DOptShiftBack = { OptShiftBack, "OptShiftBack", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptSignExtended = { OptSignExtended, "OptSignExtended", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSize1 = { OptSize1, "OptSize1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSize2 = { OptSize2, "OptSize2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptStackOps = { OptStackOps, "OptStackOps", 100, 0, 0, 0, 0, 0 }; @@ -920,7 +272,7 @@ static OptFunc* OptFuncs[] = { &DOptPtrStore3, &DOptPush1, &DOptPush2, - &DOptPushPop, + &DOptPushPop1, &DOptRTS, &DOptRTSJumps1, &DOptRTSJumps2, @@ -930,6 +282,8 @@ static OptFunc* OptFuncs[] = { &DOptShift4, &DOptShift5, &DOptShift6, + &DOptShiftBack, + &DOptSignExtended, &DOptSize1, &DOptSize2, &DOptStackOps, @@ -1026,10 +380,12 @@ void ListOptSteps (FILE* F) /* List all optimization steps */ { unsigned I; - + fprintf (F, "any\n"); for (I = 0; I < OPTFUNC_COUNT; ++I) { - fprintf (F, "%s\n", OptFuncs[I]->Name); + if (OptFuncs[I]->Func != 0) { + fprintf (F, "%s\n", OptFuncs[I]->Name); + } } } @@ -1190,10 +546,10 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max) { unsigned Changes, C; - /* Don't run the function if it is disabled or if it is prohibited by the + /* Don't run the function if it is removed, disabled or prohibited by the ** code size factor */ - if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) { + if (F->Func == 0 || F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) { return 0; } @@ -1239,10 +595,10 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptGotoSPAdj, 1); Changes += RunOptFunc (S, &DOptStackPtrOps, 5); + Changes += RunOptFunc (S, &DOptAdd3, 1); /* Before OptPtrLoad5! */ Changes += RunOptFunc (S, &DOptPtrStore1, 1); Changes += RunOptFunc (S, &DOptPtrStore2, 1); Changes += RunOptFunc (S, &DOptPtrStore3, 1); - Changes += RunOptFunc (S, &DOptAdd3, 1); /* Before OptPtrLoad5! */ Changes += RunOptFunc (S, &DOptPtrLoad1, 1); Changes += RunOptFunc (S, &DOptPtrLoad2, 1); Changes += RunOptFunc (S, &DOptPtrLoad3, 1); @@ -1330,7 +686,6 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptAdd6, 1); C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); - C += RunOptFunc (S, &DOptRTS, 1); C += RunOptFunc (S, &DOptDeadCode, 1); C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); @@ -1359,8 +714,11 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptTransfers4, 1); C += RunOptFunc (S, &DOptStore1, 1); C += RunOptFunc (S, &DOptStore5, 1); - C += RunOptFunc (S, &DOptPushPop, 1); + C += RunOptFunc (S, &DOptPushPop1, 1); + C += RunOptFunc (S, &DOptPushPop2, 1); C += RunOptFunc (S, &DOptPrecalc, 1); + C += RunOptFunc (S, &DOptShiftBack, 1); + C += RunOptFunc (S, &DOptSignExtended, 1); Changes += C; @@ -1485,11 +843,16 @@ static unsigned RunOptGroup7 (CodeSeg* S) /* Adjust branch distances */ Changes += RunOptFunc (S, &DOptBranchDist, 3); - /* Replace conditional branches to RTS. If we had changes, we must run dead - ** code elimination again, since the change may have introduced dead code. - */ + /* Replace conditional branches to RTS */ C = RunOptFunc (S, &DOptRTSJumps2, 1); + + /* Replace JSR followed by RTS to JMP */ + C += RunOptFunc (S, &DOptRTS, 1); + Changes += C; + /* If we had changes, we must run dead code elimination again, + ** since the changes may have introduced dead code. + */ if (C) { Changes += RunOptFunc (S, &DOptDeadCode, 1); } diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c new file mode 100644 index 000000000..173d5185f --- /dev/null +++ b/src/cc65/codeoptutil.c @@ -0,0 +1,3192 @@ +/*****************************************************************************/ +/* */ +/* codeoptutil.c */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include <stdlib.h> + +/* common */ +#include "chartype.h" +#include "xmalloc.h" + +/* cc65 */ +#include "codeinfo.h" +#include "codeoptutil.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +void ClearLoadRegInfo (LoadRegInfo* LRI) +/* Clear a LoadRegInfo struct */ +{ + LRI->Flags = LI_NONE; + LRI->LoadIndex = -1; + LRI->LoadEntry = 0; + LRI->LoadYIndex = -1; + LRI->LoadYEntry = 0; + LRI->ChgIndex = -1; + LRI->ChgEntry = 0; + LRI->Offs = 0; +} + + + +void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) +/* Copy a LoadRegInfo struct */ +{ + To->Flags = From->Flags; + To->LoadIndex = From->LoadIndex; + To->LoadEntry = From->LoadEntry; + To->LoadYIndex = From->LoadYIndex; + To->LoadYEntry = From->LoadYEntry; + To->ChgIndex = From->ChgIndex; + To->ChgEntry = From->ChgEntry; + To->Offs = From->Offs; +} + + + +void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S) +/* Prepare a LoadRegInfo struct for use */ +{ + /* Get the entries */ + if (LRI->LoadIndex >= 0) { + LRI->LoadEntry = CS_GetEntry (S, LRI->LoadIndex); + } else { + LRI->LoadEntry = 0; + } + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); + } else { + LRI->LoadYEntry = 0; + } + if (LRI->ChgIndex >= 0) { + LRI->ChgEntry = CS_GetEntry (S, LRI->ChgIndex); + } else { + LRI->ChgEntry = 0; + } + + /* Load from src not modified before op can be treated as direct */ + if ((LRI->Flags & (LI_SRC_CHG | LI_Y_SRC_CHG)) == 0 && + (LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + LRI->Flags |= LI_DIRECT; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + LRI->Flags |= LI_RELOAD_Y; + } + } + /* We cannot ldy src,y or reload unknown Y */ + if ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == (LI_CHECK_Y | LI_RELOAD_Y) && + (LRI->LoadYEntry == 0 || + (LRI->LoadYEntry->Use & REG_Y) == REG_Y)) { + LRI->Flags &= ~LI_DIRECT; + } +} + + + +void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change) +/* Adjust a load register info struct after deleting or inserting an entry +** with a given index +*/ +{ + CHECK (abs (Change) == 1); + if (Change < 0) { + /* Deletion */ + if (Index < LRI->LoadIndex) { + --LRI->LoadIndex; + } else if (Index == LRI->LoadIndex) { + /* Has been removed */ + LRI->LoadIndex = -1; + LRI->LoadEntry = 0; + } + if (Index < LRI->LoadYIndex) { + --LRI->LoadIndex; + } else if (Index == LRI->LoadYIndex) { + /* Has been removed */ + LRI->LoadYIndex = -1; + LRI->LoadYEntry = 0; + } + if (Index < LRI->ChgIndex) { + --LRI->ChgIndex; + } else if (Index == LRI->ChgIndex) { + /* Has been removed */ + LRI->ChgIndex = -1; + LRI->ChgEntry = 0; + } + } else { + /* Insertion */ + if (Index <= LRI->LoadIndex) { + ++LRI->LoadIndex; + } + if (Index <= LRI->LoadYIndex) { + ++LRI->LoadYIndex; + } + if (Index <= LRI->ChgIndex) { + ++LRI->ChgIndex; + } + } +} + + + +void ClearLoadInfo (LoadInfo* LI) +/* Clear a LoadInfo struct */ +{ + ClearLoadRegInfo (&LI->A); + ClearLoadRegInfo (&LI->X); + ClearLoadRegInfo (&LI->Y); +} + + + +void CopyLoadInfo (LoadInfo* To, LoadInfo* From) +/* Copy a LoadInfo struct */ +{ + CopyLoadRegInfo (&To->A, &From->A); + CopyLoadRegInfo (&To->X, &From->X); + CopyLoadRegInfo (&To->Y, &From->Y); +} + + + +void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S) +/* Prepare a LoadInfo struct for use */ +{ + /* Get the entries */ + FinalizeLoadRegInfo (&LI->A, S); + FinalizeLoadRegInfo (&LI->X, S); + FinalizeLoadRegInfo (&LI->Y, S); +} + + + +void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) +/* Adjust a load info struct after deleting entry with a given index */ +{ + AdjustLoadRegInfo (&LI->A, Index, Change); + AdjustLoadRegInfo (&LI->X, Index, Change); + AdjustLoadRegInfo (&LI->Y, Index, Change); +} + + + +RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) +/* Get RegInfo of the last insn entry that changed the reg */ +{ + CodeEntry* E; + + if (Reg->ChgIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->ChgIndex)) != 0) { + return E->RI; + } + + return 0; +} + + + +static int Affected (LoadRegInfo* LRI, const CodeEntry* E) +/* Check if the result of the same loading code as in LRI may be changed by E. +** If any part of the arg is used, it could be unsafe to add such a store before E. +** If any part of the arg is changed, it could be unsafe to add such a load after E. +*/ +{ + fncls_t fncls; + unsigned int Use; + unsigned int Chg; + unsigned int UseToCheck = 0; + unsigned int ChgToCheck = 0; + const ZPInfo* ZI = 0; + unsigned Res = 0; + CodeEntry* AE = 0; + CodeEntry* YE = 0; + + if ((LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Nothing to check */ + return 0; + } + + if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) { + goto L_Result; + } + CHECK ((LRI->Flags & LI_CHECK_ARG) == 0 || LRI->LoadIndex < 0 || LRI->LoadEntry != 0); + CHECK ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == 0 || LRI->LoadYIndex < 0 || LRI->LoadYEntry != 0); + + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + AE = LRI->LoadEntry; + if (AE != 0) { + /* We ignore processor flags for loading args. + ** Further more, Reg A can't be used as the index. + */ + UseToCheck |= AE->Use & ~REG_A & REG_ALL; + ChgToCheck |= AE->Chg & ~REG_A & REG_ALL; + + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (AE)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + /* We have to manually set up the use/chg flags for builtin functions */ + ZI = GetZPInfo (AE->ArgBase); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + ChgToCheck |= ZI->ByteUse; + } + } else { + /* We don't know what regs could have been used for the src. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + ChgToCheck |= ~REG_A & REG_ALL; + } + } + + if ((LRI->Flags & LI_CHECK_Y) != 0) { + YE = LRI->LoadYEntry; + if (YE != 0) { + UseToCheck |= YE->Use; + + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (YE)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + /* We have to manually set up the use/chg flags for builtin functions */ + ZI = GetZPInfo (YE->ArgBase); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + ChgToCheck |= ZI->ByteUse; + } + } else { + /* We don't know what regs could have been used by Y. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + ChgToCheck |= ~REG_A & REG_ALL; + } + } + + if (E->OPC == OP65_JSR) { + /* Try to know about the function */ + fncls = GetFuncInfo (E->Arg, &Use, &Chg); + if (fncls == FNCLS_BUILTIN) { + /* Builtin functions are usually harmless */ + if ((ChgToCheck & Use & REG_ALL) != 0) { + Res |= LI_SRC_USE; + } + if ((UseToCheck & Chg & REG_ALL) != 0) { + Res |= LI_SRC_CHG; + } + goto L_Result; + } + /* Otherwise play it safe */ + goto L_Affected; + + } else { + if ((E->Info & (OF_READ | OF_WRITE)) != 0) { + + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (E)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + + /* These opc may operate on memory locations. In some cases we can + ** be sure that the src is unaffected as E doesn't overlap with it. + ** However, if we don't know what memory locations could have been + ** used for the src, we just assume all. + */ + if (E->AM == AM65_ABS || + E->AM == AM65_ZP || + (E->AM == AM65_ZP_INDY && strcmp (E->ArgBase, "sp") == 0) + ) { + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + if (AE == 0 || + (AE->AM != AM65_ABS && + AE->AM != AM65_ZP && + (AE->AM != AM65_ZP_INDY || + strcmp (AE->ArgBase, "sp") != 0)) || + (AE->ArgOff == E->ArgOff && + strcmp (AE->ArgBase, E->ArgBase) == 0)) { + + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_SRC_USE; + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_SRC_CHG; + } + } + } + + if ((LRI->Flags & LI_CHECK_Y) != 0) { + /* If we don't know what memory location could have been + ** used by Y, we just assume all. + */ + if (YE == 0 || + (YE->ArgOff == E->ArgOff && strcmp (YE->ArgBase, E->ArgBase) == 0)) { + + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_Y_SRC_USE; + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_Y_SRC_CHG; + } + } + } + + /* Otherwise unaffected */ + goto L_Result; + } + /* We could've check further for more cases where the load target + ** isn't modified, but for now let's save the trouble and just play + ** it safe. + */ + goto L_Affected; + } + } + +L_Affected: + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_SRC_USE; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + Res |= LI_Y_SRC_USE; + } + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_SRC_CHG; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + Res |= LI_Y_SRC_CHG; + } + } + +L_Result: + if ((LRI->Flags & LI_RELOAD_Y) != 0 && + (E->Use & REG_Y) != 0) { + Res |= LI_Y_USE; + } + if ((LRI->Flags & LI_CHECK_Y) != 0 && + (E->Chg & REG_Y) != 0) { + Res |= LI_Y_CHG; + } + + return Res; +} + + + +static void HonourUseAndChg (LoadRegInfo* LRI, unsigned Reg, const CodeEntry* E, int I) +/* Honour use and change flags for an instruction */ +{ + if ((E->Chg & Reg) != 0) { + /* This changes the content of the reg */ + ClearLoadRegInfo (LRI); + LRI->ChgIndex = I; + LRI->Flags = 0; + } else { + LRI->Flags |= Affected (LRI, E); + } +} + + + +void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E) +/* Set the load src flags and remember to check for load src change if necessary. +** Note: this doesn't assume reloading Y. +*/ +{ + if (E->AM == AM65_IMM) { + /* These insns are all ok and replaceable */ + LRI->Flags |= LI_DIRECT; + } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG; + } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } else if ((E->AM == AM65_ZP_INDY) && + strcmp (E->Arg, "sp") == 0) { + /* A load from the stack with known offset is also ok, but in this + ** case we must reload the index register later. Please note that + ** a load indirect via other zero page locations is not ok, since + ** these locations may change between the push and the actual + ** operation. + */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } + + /* If the load offset has a known value, we can just remember and reload + ** it into the index register later. + */ + if ((LRI->Flags & LI_CHECK_Y) != 0) { + if (RegValIsKnown (E->RI->In.RegY)) { + LRI->Offs = (unsigned char)E->RI->In.RegY; + LRI->Flags &= ~LI_CHECK_Y; + LRI->Flags |= LI_RELOAD_Y; + } + } + + /* Watch for any change of the load target */ + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + LRI->LoadIndex = CS_GetEntryIndex (S, E); + LRI->LoadEntry = E; + } + + /* We need to check if the src of Y is changed */ + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); + } else { + LRI->LoadYEntry = 0; + } +} + + + +void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E) +/* Check and flag operand src that may be affected */ +{ + LLI->A.Flags |= Affected (&LLI->A, E); + LLI->X.Flags |= Affected (&LLI->X, E); + LLI->Y.Flags |= Affected (&LLI->Y, E); +} + + + +void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used) +/* Check and flag operand load that may be unremovable */ +{ + /* Disallow removing the loads if the registers are used */ + if ((Used & REG_A) != 0) { + LI->A.Flags |= LI_DONT_REMOVE; + } + if ((Used & REG_X) != 0) { + LI->X.Flags |= LI_DONT_REMOVE; + } + if ((Used & REG_Y) != 0) { + LI->Y.Flags |= LI_DONT_REMOVE; + } +} + + + +unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I) +/* Track loads for a code entry. +** Return used registers. +*/ +{ + unsigned Used; + CodeEntry* E = CS_GetEntry (S, I); + CHECK (E != 0); + + /* By default */ + Used = E->Use; + + /* Whether we had a load or xfer op before or not, the newly loaded value + ** will be the real one used for the pushax/op unless it's overwritten, + ** so we can just reset the flags about it in such cases. + */ + if (E->Info & OF_LOAD) { + + LoadRegInfo* LRI = 0; + + /* Determine, which register was loaded */ + if (E->Chg & REG_A) { + LRI = &LI->A; + } else if (E->Chg & REG_X) { + LRI = &LI->X; + } else if (E->Chg & REG_Y) { + LRI = &LI->Y; + } + CHECK (LRI != 0); + + /* Remember the load */ + LRI->LoadIndex = I; + LRI->ChgIndex = I; + LRI->LoadYIndex = -1; + + /* Set load flags */ + LRI->Flags = LI_LOAD_INSN; + if (E->AM == AM65_IMM) { + /* These insns are all ok and replaceable */ + LRI->Flags |= LI_DIRECT; + } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG; + } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } else if (E->AM == AM65_ZP_INDY && + strcmp (E->Arg, "sp") == 0) { + /* A load from the stack with known offset is also ok, but in this + ** case we must reload the index register later. Please note that + ** a load indirect via other zero page locations is not ok, since + ** these locations may change between the push and the actual + ** operation. + */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y | LI_SP; + + /* Reg Y can be regarded as unused if this load is removed */ + Used &= ~REG_Y; + if (LRI == &LI->A) { + LI->Y.Flags |= LI_USED_BY_A; + } else { + LI->Y.Flags |= LI_USED_BY_X; + } + } + + /* If the load offset has a known value, we can just remember and reload + ** it into the index register later. + */ + if ((LRI->Flags & LI_CHECK_Y) != 0) { + if (RegValIsKnown (E->RI->In.RegY)) { + LRI->Offs = (unsigned char)E->RI->In.RegY; + LRI->Flags &= ~LI_CHECK_Y; + LRI->Flags |= LI_RELOAD_Y; + } else { + /* We need to check if the src of Y is changed */ + LRI->LoadYIndex = LI->Y.LoadIndex; + } + } + + /* Watch for any change of the load target */ + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + LRI->LoadEntry = CS_GetEntry (S, I); + } + + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); + } else { + LRI->LoadYEntry = 0; + } + + } else if (E->Info & OF_XFR) { + + /* Determine source and target of the transfer and handle the TSX insn */ + LoadRegInfo* Src; + LoadRegInfo* Tgt; + switch (E->OPC) { + case OP65_TAX: + Src = &LI->A; + Tgt = &LI->X; + Used &= ~REG_A; + Src->Flags |= LI_USED_BY_X; + break; + case OP65_TAY: + Src = &LI->A; + Tgt = &LI->Y; + Used &= ~REG_A; + Src->Flags |= LI_USED_BY_Y; + break; + case OP65_TXA: + Src = &LI->X; + Tgt = &LI->A; + Used &= ~REG_X; + Src->Flags |= LI_USED_BY_A; + break; + case OP65_TYA: + Src = &LI->Y; + Tgt = &LI->A; + Used &= ~REG_Y; + Src->Flags |= LI_USED_BY_A; + break; + case OP65_TSX: + ClearLoadRegInfo (&LI->X); + return Used; + case OP65_TXS: + return Used; + default: Internal ("Unknown XFR insn in TrackLoads"); + } + + /* Transfer the data */ + Tgt->LoadIndex = Src->LoadIndex; + Tgt->LoadEntry = Src->LoadEntry; + Tgt->LoadYIndex = Src->LoadYIndex; + Tgt->LoadYEntry = Src->LoadYEntry; + Tgt->ChgIndex = I; + Tgt->Offs = Src->Offs; + Tgt->Flags = Src->Flags; + + } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) { + + /* Both registers set, Y changed */ + LI->A.LoadIndex = I; + LI->A.ChgIndex = I; + LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); + LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; + + LI->X.LoadIndex = I; + LI->X.ChgIndex = I; + LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); + LI->X.Offs = (unsigned char) E->RI->In.RegY; + + /* Reg Y can be regarded as unused if this load is removed */ + Used &= ~REG_Y; + LI->Y.Flags |= LI_USED_BY_A | LI_USED_BY_X; + + } else { + HonourUseAndChg (&LI->A, REG_A, E, I); + HonourUseAndChg (&LI->X, REG_X, E, I); + HonourUseAndChg (&LI->Y, REG_Y, E, I); + } + + return Used; +} + + + +void SetDontRemoveEntryFlag (LoadRegInfo* LRI) +/* Flag the entry as non-removable according to register flags */ +{ + if (LRI->Flags & LI_DONT_REMOVE) { + if (LRI->LoadEntry != 0) { + LRI->LoadEntry->Flags |= CEF_DONT_REMOVE; + + /* If the load requires Y, then Y shouldn't be removed either */ + if (LRI->LoadYEntry != 0) { + LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE; + } + } + } +} + + + +void ResetDontRemoveEntryFlag (LoadRegInfo* LRI) +/* Unflag the entry as non-removable according to register flags */ +{ + if (LRI->LoadEntry != 0) { + LRI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; + } + + if (LRI->LoadYEntry != 0) { + LRI->LoadYEntry->Flags &= ~CEF_DONT_REMOVE; + } + + if (LRI->ChgEntry != 0) { + LRI->ChgEntry->Flags &= ~CEF_DONT_REMOVE; + } +} + + + +void SetDontRemoveEntryFlags (StackOpData* D) +/* Flag the entries as non-removable according to register flags */ +{ + SetDontRemoveEntryFlag (&D->Lhs.A); + SetDontRemoveEntryFlag (&D->Lhs.X); + SetDontRemoveEntryFlag (&D->Lhs.Y); + SetDontRemoveEntryFlag (&D->Rhs.A); + SetDontRemoveEntryFlag (&D->Rhs.X); + SetDontRemoveEntryFlag (&D->Rhs.Y); + SetDontRemoveEntryFlag (&D->Rv.A); + SetDontRemoveEntryFlag (&D->Rv.X); + SetDontRemoveEntryFlag (&D->Rv.Y); +} + + + +void ResetDontRemoveEntryFlags (StackOpData* D) +/* Unflag the entries as non-removable according to register flags */ +{ + ResetDontRemoveEntryFlag (&D->Lhs.A); + ResetDontRemoveEntryFlag (&D->Lhs.X); + ResetDontRemoveEntryFlag (&D->Lhs.Y); + ResetDontRemoveEntryFlag (&D->Rhs.A); + ResetDontRemoveEntryFlag (&D->Rhs.X); + ResetDontRemoveEntryFlag (&D->Rhs.Y); + ResetDontRemoveEntryFlag (&D->Rv.A); + ResetDontRemoveEntryFlag (&D->Rv.X); + ResetDontRemoveEntryFlag (&D->Rv.Y); +} + + + +void ResetStackOpData (StackOpData* Data) +/* Reset the given data structure */ +{ + Data->OptFunc = 0; + Data->ZPUsage = REG_NONE; + Data->ZPChanged = REG_NONE; + Data->UsedRegs = REG_NONE; + Data->RhsMultiChg = 0; + + ClearLoadInfo (&Data->Lhs); + ClearLoadInfo (&Data->Rhs); + ClearLoadInfo (&Data->Rv); + + Data->PushIndex = -1; + Data->OpIndex = -1; +} + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +void InsertEntry (StackOpData* D, CodeEntry* E, int Index) +/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will +** be adjusted by this function. +*/ +{ + /* Insert the entry into the code segment */ + CS_InsertEntry (D->Code, E, Index); + + /* Adjust register loads if necessary */ + AdjustLoadInfo (&D->Lhs, Index, 1); + AdjustLoadInfo (&D->Rhs, Index, 1); + + /* Adjust the indices if necessary */ + if (D->PushEntry && Index <= D->PushIndex) { + ++D->PushIndex; + } + if (D->OpEntry && Index <= D->OpIndex) { + ++D->OpIndex; + } +} + + + +void DelEntry (StackOpData* D, int Index) +/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be +** adjusted by this function, and PushEntry/OpEntry may get invalidated. +*/ +{ + /* Delete the entry from the code segment */ + CS_DelEntry (D->Code, Index); + + /* Adjust register loads if necessary */ + AdjustLoadInfo (&D->Lhs, Index, -1); + AdjustLoadInfo (&D->Rhs, Index, -1); + + /* Adjust the other indices if necessary */ + if (Index < D->PushIndex) { + --D->PushIndex; + } else if (Index == D->PushIndex) { + D->PushEntry = 0; + } + if (Index < D->OpIndex) { + --D->OpIndex; + } else if (Index == D->OpIndex) { + D->OpEntry = 0; + } +} + + + +void AdjustStackOffset (StackOpData* D, unsigned Offs) +/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. +** OpIndex is adjusted according to the insertions. +*/ +{ + /* Walk over all entries */ + int I = D->PushIndex + 1; + while (I < D->OpIndex) { + + CodeEntry* E = CS_GetEntry (D->Code, I); + + /* Check against some things that should not happen */ + CHECK ((E->Use & SLV_TOP) != SLV_TOP); + + /* Check if this entry does a stack access, and if so, if it's a plain + ** load from stack, since this is needed later. + */ + int Correction = 0; + if ((E->Use & SLV_IND) == SLV_IND) { + + if (E->OPC != OP65_JSR) { + /* Check against some things that should not happen */ + CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs); + CHECK (strcmp (E->Arg, "sp") == 0); + + /* We need to correct this one */ + Correction = 2; + + } else { + /* We need to correct this one */ + Correction = 1; + } + + } + + if (Correction) { + /* Get the code entry before this one. If it's a LDY, adjust the + ** value. + */ + CodeEntry* P = CS_GetPrevEntry (D->Code, I); + if (P && P->OPC == OP65_LDY && CE_IsConstImm (P) && !CE_HasLabel (E)) { + /* The Y load is just before the stack access, adjust it */ + CE_SetNumArg (P, P->Num - Offs); + } else { + /* Insert a new load instruction before the stack access */ + const char* Arg = MakeHexArg (E->RI->In.RegY - Offs); + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I++); + } + + /* If we need the value of Y later, be sure to reload it */ + unsigned R = REG_Y | (E->Chg & ~REG_A); + R = GetRegInfo (D->Code, I + 1, R) & R; + if ((R & REG_Y) != 0) { + const char* Arg = MakeHexArg (E->RI->In.RegY); + if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) { + CodeEntry* N; + if ((N = CS_GetNextEntry (D->Code, I)) != 0 && + ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { + /* The Y register is used but the load instruction loads A + ** and is followed by a branch that evaluates the zero flag. + ** This means that we cannot just insert the load insn + ** for the Y register at this place, because it would + ** destroy the Z flag. Instead place load insns at the + ** target of the branch and after it. + ** Note: There is a chance that this code won't work. The + ** jump may be a backwards jump (in which case the stack + ** offset has already been adjusted) or there may be other + ** instructions between the load and the conditional jump. + ** Currently the compiler does not generate such code, but + ** it is possible to force the optimizer into something + ** invalid by use of inline assembler. + ** Note: In reality, this route is never taken as all + ** callers of this function will just give up with + ** optimization whenever they detect a branch. + */ + + /* Add load insn after the branch */ + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+2); + + /* Add load insn before branch target */ + CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); + CHECK (J > I); /* Must not happen */ + InsertEntry (D, Y, J); + + /* Move the label to the new insn */ + CodeLabel* L = CS_GenLabel (D->Code, Y); + CS_MoveLabelRef (D->Code, N, L); + + /* Skip the next two instructions in the next round */ + I += 2; + } else { + /* This could be suboptimal but it will always work (unless stack overflows) */ + CodeEntry* X = NewCodeEntry (OP65_PHP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, 0, 0, E->LI); + InsertEntry (D, X, I+2); + X = NewCodeEntry (OP65_PLP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+3); + /* Skip the three inserted instructions in the next round */ + I += 3; + } + } else { + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+1); + /* Skip this instruction in the next round */ + ++I; + } + } + } + + /* Next entry */ + ++I; + } + + /* If we have rhs load insns that load from stack, we'll have to adjust + ** the offsets for these also. + */ + if ((D->Rhs.A.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { + D->Rhs.A.Offs -= Offs; + } + if ((D->Rhs.X.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { + D->Rhs.X.Offs -= Offs; + } +} + + + +int IsRegVar (StackOpData* D) +/* If the value pushed is that of a zeropage variable that is unchanged until Op, +** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. +** Otherwise leave D untouched and return false. +*/ +{ + CodeEntry* LoadA = D->Lhs.A.LoadEntry; + CodeEntry* LoadX = D->Lhs.X.LoadEntry; + unsigned Len; + + /* Must be unchanged till Op */ + if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT || + (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT) { + return 0; + } + + /* Must have both load insns */ + if (LoadA == 0 || LoadX == 0) { + return 0; + } + + /* Must be loads from zp */ + if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) { + return 0; + } + + /* Must be the same zp loc with high byte in X */ + Len = strlen (LoadA->Arg); + if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0 || + strcmp (LoadX->Arg + Len, "+1") != 0) { + return 0; + } + + /* Use the zero page location directly */ + D->ZPLo = LoadA->Arg; + D->ZPHi = LoadX->Arg; + return 1; +} + + + +void AddStoreLhsA (StackOpData* D) +/* Add a store to zero page after the push insn */ +{ + CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); +} + + + +void AddStoreLhsX (StackOpData* D) +/* Add a store to zero page after the push insn */ +{ + CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); +} + + + +void ReplacePushByStore (StackOpData* D) +/* Replace the call to the push subroutine by a store into the zero page +** location (actually, the push is not replaced, because we need it for +** later, but the name is still ok since the push will get removed at the +** end of each routine). +*/ +{ + /* Store the value into the zeropage instead of pushing it. Check high + ** byte first so that the store is later in A/X order. + */ + if ((D->Lhs.X.Flags & LI_DIRECT) == 0) { + AddStoreLhsX (D); + } + if ((D->Lhs.A.Flags & LI_DIRECT) == 0) { + AddStoreLhsA (D); + } +} + + + +void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI) +/* Add an op for the low byte of an operator. This function honours the +** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. +** All code is inserted at the current insertion point. +*/ +{ + CodeEntry* X; + + if ((LI->A.Flags & LI_DIRECT) != 0) { + /* Op with a variable location. If the location is on the stack, we + ** need to reload the Y register. + */ + if ((LI->A.Flags & LI_RELOAD_Y) == 0) { + + /* opc ... */ + CodeEntry* LoadA = LI->A.LoadEntry; + X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } else { + + if ((LI->A.Flags & LI_CHECK_Y) == 0) { + /* ldy #offs */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->A.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->A.LoadYEntry->AM, LI->A.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + if (LI->A.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + } + + /* In both cases, we can remove the load */ + LI->A.Flags |= LI_REMOVE; + + } else { + + /* Op with temp storage */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } +} + + + +void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) +/* Add an op for the high byte of an operator. Special cases (constant values +** or similar) have to be checked separately, the function covers only the +** generic case. Code is inserted at the insertion point. +*/ +{ + CodeEntry* X; + + if (KeepResult) { + /* pha */ + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + /* txa */ + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + if ((LI->X.Flags & LI_DIRECT) != 0) { + + if ((LI->X.Flags & LI_RELOAD_Y) == 0) { + + /* opc xxx */ + CodeEntry* LoadX = LI->X.LoadEntry; + X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } else { + + if ((LI->X.Flags & LI_CHECK_Y) == 0) { + /* ldy #const */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->X.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->X.LoadYEntry->AM, LI->X.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + if (LI->X.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->X.LoadEntry->AM, LI->X.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + } + + /* In both cases, we can remove the load */ + LI->X.Flags |= LI_REMOVE; + + } else { + /* opc zphi */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + if (KeepResult) { + /* tax */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* pla */ + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } +} + + + +void RemoveRegLoads (StackOpData* D, LoadInfo* LI) +/* Remove register load insns */ +{ + if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) { + if (LI->A.LoadIndex >= 0 && + (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.LoadIndex); + LI->A.LoadEntry = 0; + } + if (LI->A.LoadYIndex >= 0 && + (LI->A.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.LoadYIndex); + } + if (LI->A.ChgIndex >= 0 && + (LI->A.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.ChgIndex); + } + } + + if (LI->A.LoadEntry != 0 && + (LI->A.Flags & LI_RELOAD_Y) != 0 && + LI->A.LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LI->A.LoadYEntry->Flags |= CEF_DONT_REMOVE; + } + + if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) { + if (LI->X.LoadIndex >= 0 && + (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.LoadIndex); + LI->X.LoadEntry = 0; + } + if (LI->X.LoadYIndex >= 0 && + (LI->X.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.LoadYIndex); + } + if (LI->X.ChgIndex >= 0 && + (LI->X.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.ChgIndex); + } + } + + if (LI->X.LoadEntry != 0 && + (LI->X.Flags & LI_RELOAD_Y) != 0 && + LI->X.LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LI->X.LoadYEntry->Flags |= CEF_DONT_REMOVE; + } +} + + + +void RemoveRemainders (StackOpData* D) +/* Remove the code that is unnecessary after translation of the sequence */ +{ + /* Remove the register loads for lhs and rhs if nothing prevents that */ + RemoveRegLoads (D, &D->Lhs); + RemoveRegLoads (D, &D->Rhs); + + /* Remove the push and the operator routine */ + DelEntry (D, D->OpIndex); + DelEntry (D, D->PushIndex); +} + + + +static int CmpHarmless (const void* Key, const void* Entry) +/* Compare function for bsearch */ +{ + return strcmp (Key, *(const char**)Entry); +} + + + +int HarmlessCall (const CodeEntry* E, int PushedBytes) +/* Check if this is a call to a harmless subroutine that will not interrupt +** the pushax/op sequence when encountered. +*/ +{ + unsigned Use = 0, Chg = 0; + if (GetFuncInfo (E->Arg, &Use, &Chg) == FNCLS_BUILTIN) { + if ((Chg & REG_SP) != 0) { + return 0; + } + if ((Use & REG_SP) != 0 && + ((Use & (SLV_IND | SLV_TOP)) != SLV_IND || + RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < PushedBytes)) { + /* If we are using the stack, and we don't have "indirect" + ** addressing mode, or the value of Y is unknown, or less + ** than two, we cannot cope with this piece of code. Having + ** an unknown value of Y means that we cannot correct the + ** stack offset, while having an offset less than PushedBytes + ** means that the code works with the value on stack which + ** is to be removed. + */ + return 0; + } + return 1; + } else { + static const char* const Tab[] = { + "_abs", + }; + + void* R = bsearch (E->Arg, + Tab, + sizeof (Tab) / sizeof (Tab[0]), + sizeof (Tab[0]), + CmpHarmless); + return (R != 0); + } +} + + + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +const char* GetZPName (unsigned ZPLoc) +/* Get the name strings of certain known ZP Regs */ +{ + if ((ZPLoc & REG_TMP1) != 0) { + return "tmp1"; + } + if ((ZPLoc & REG_PTR1_LO) != 0) { + return "ptr1"; + } + if ((ZPLoc & REG_PTR1_HI) != 0) { + return "ptr1+1"; + } + if ((ZPLoc & REG_PTR2_LO) != 0) { + return "ptr2"; + } + if ((ZPLoc & REG_PTR2_HI) != 0) { + return "ptr2+1"; + } + if ((ZPLoc & REG_SREG_LO) != 0) { + return "sreg"; + } + if ((ZPLoc & REG_SREG_HI) != 0) { + return "sreg+1"; + } + if ((ZPLoc & REG_SAVE_LO) != 0) { + return "save"; + } + if ((ZPLoc & REG_SAVE_HI) != 0) { + return "save+1"; + } + if ((ZPLoc & REG_SP_LO) != 0) { + return "sp"; + } + if ((ZPLoc & REG_SP_HI) != 0) { + return "sp+1"; + } + + return 0; +} + +unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type) +/* Find a ZP loc for storing the backup and fill in the info. +** The allowed types are specified with the Type parameter. +** For convenience, all types are aloowed if none is specified. +** Return the type of the found loc. +*/ +{ + unsigned SizeType = Type & BU_SIZE_MASK; + Type &= BU_TYPE_MASK; + if (Type == 0) { + Type = BU_TYPE_MASK; + } + + if (SizeType == BU_B8 && (Type & BU_REG) != 0 && (B->ZPUsage & REG_Y) == 0) { + /* Use the Y Reg only */ + B->Type = BU_REG | SizeType; + B->Where = REG_Y; + B->ZPUsage |= REG_Y; + return B->Type; + } + + if (SizeType == BU_B8 && (Type & BU_ZP) != 0) { + /* For now we only check for tmp1 and sreg */ + if ((B->ZPUsage & REG_TMP1) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_TMP1; + B->ZPUsage |= REG_TMP1; + return B->Type; + } + if ((B->ZPUsage & REG_SREG_LO) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_SREG_LO; + B->ZPUsage |= REG_SREG_LO; + return B->Type; + } + if ((B->ZPUsage & REG_SREG_HI) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_SREG_HI; + B->ZPUsage |= REG_SREG_HI; + return B->Type; + } + } + + if (SizeType == BU_B16 && (Type & BU_ZP) != 0) { + /* For now we only check for ptr1, sreg and ptr2 */ + if ((B->ZPUsage & REG_PTR1) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_PTR1; + B->ZPUsage |= REG_PTR1; + return B->Type; + } + if ((B->ZPUsage & REG_SREG) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_SREG; + B->ZPUsage |= REG_SREG; + return B->Type; + } + if ((B->ZPUsage & REG_PTR2) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_PTR2; + B->ZPUsage |= REG_PTR2; + return B->Type; + } + } + + if (SizeType == BU_B24 && (Type & BU_ZP) != 0) { + /* For now we only check for certain combinations of + ** tmp1 + (ptr1, sreg or ptr2). + */ + if ((B->ZPUsage & (REG_TMP1 | REG_PTR1)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_PTR1; + B->ZPUsage |= REG_TMP1 | REG_PTR1; + return B->Type; + } + if ((B->ZPUsage & (REG_TMP1 | REG_SREG)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_SREG; + B->ZPUsage |= REG_TMP1 | REG_SREG; + return B->Type; + } + if ((B->ZPUsage & (REG_TMP1 | REG_PTR2)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_PTR2; + B->ZPUsage |= REG_TMP1 | REG_PTR2; + return B->Type; + } + } + + if (SizeType < BU_B32 && (Type & BU_SP6502) != 0) { + /* Even for BU_B24, we just push/pop all 3 of AXY */ + B->Type = BU_SP6502 | BU_B16; + B->Where = 0; + return B->Type; + } + + if (SizeType != BU_B24 && SizeType <= BU_B32 && (Type & BU_SP) != 0) { + /* We may also use pusha/popa, pushax/popax and pusheax/popeax */ + B->Type = BU_SP | SizeType; + B->Where = 0; + return B->Type; + } + + /* No available */ + return BU_UNKNOWN; +} + + + +void AdjustEntryIndices (Collection* Indices, int Index, int Change) +/* Adjust a load register info struct after deleting or inserting successive +** entries with a given index. +*/ +{ + int I; + int* IndexPtr; + + if (Change > 0) { + /* Insertion */ + for (I = 0; I < (int)CollCount (Indices); ++I) { + IndexPtr = CollAtUnchecked (Indices, I); + if (Index <= *IndexPtr) { + *IndexPtr += Change; + } + } + } else if (Change < 0) { + /* Deletion */ + for (I = 0; I < (int)CollCount (Indices); ++I) { + IndexPtr = CollAtUnchecked (Indices, I); + if (Index <= *IndexPtr + Change) { + *IndexPtr += Change; + } else if (Index <= *IndexPtr) { + /* Has been removed */ + *IndexPtr = -1; + /*CollDelete (Indices, I);*/ + --I; + } + } + } +} + + + +void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices) +/* Delete an entry and adjust Indices if necessary */ +{ + CS_DelEntry (S, Idx); + AdjustEntryIndices (Indices, Idx, -1); +} + + + +void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices) +/* Delete entries and adjust Indices if necessary */ +{ + CS_DelEntries (S, Idx, Count); + AdjustEntryIndices (Indices, Idx, -Count); +} + + + +void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices) +/* Remove flagged register load insns */ +{ + if ((LRI->Flags & LI_REMOVE) == LI_REMOVE) { + if (LRI->LoadIndex >= 0 && + (LRI->LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->LoadIndex, Indices); + LRI->LoadEntry = 0; + } + if (LRI->LoadYIndex >= 0 && + (LRI->LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->LoadYIndex, Indices); + } + if (LRI->ChgIndex >= 0 && + (LRI->ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->ChgIndex, Indices); + } + } + + if (LRI->LoadEntry != 0 && + (LRI->Flags & LI_RELOAD_Y) != 0 && + LRI->LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE; + } +} + + +void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices) +/* Remove flagged load insns */ +{ + RemoveFlaggedRegLoads (S, &LI->A, Indices); + RemoveFlaggedRegLoads (S, &LI->X, Indices); + RemoveFlaggedRegLoads (S, &LI->Y, Indices); +} + + + +static int BackupAAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of A Before or After the specified index Idx depending on the After param */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegA; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_REG: + if ((B->Where & REG_X) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } else if ((B->Where & REG_Y) != 0) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_STA, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of X before or after the specified index Idx depending on the param After */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegX)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegX; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_STX, AM65_ZP, GetZPName(B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + default: + + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of Y before or after the specified index Idx depending on the param After */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegY)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegY; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName(B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + default: + + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupAXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of AX Before or After the specified index Idx depending on the After param */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA) && RegValIsKnown (E->RI->In.RegX)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B16; + B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8); + + } else { + FindAvailableBackupLoc (B, BU_B16); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + SB_AppendStr (&Arg, GetZPName (B->Where)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +static int BackupAXYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of AXY before or after the specified index Idx depending on the param After. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && + RegValIsKnown (E->RI->In.RegA) && + RegValIsKnown (E->RI->In.RegX) && + RegValIsKnown (E->RI->In.RegY)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B24; + B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8) | (E->RI->In.RegY << 16); + + } else { + FindAvailableBackupLoc (B, BU_B24); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + CHECK ((B->Where & REG_TMP1) != 0); + SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI); + CS_InsertEntry(S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_AY) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of A Before the specified index Idx */ +{ + return BackupAAt (S, B, Idx, Indices, 0); +} + + + +int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of X before the specified index Idx */ +{ + return BackupXAt (S, B, Idx, Indices, 0); +} + + + +int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of Y before the specified index Idx */ +{ + return BackupYAt (S, B, Idx, Indices, 0); +} + + + +int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AX before the specified index Idx */ +{ + return BackupAXAt (S, B, Idx, Indices, 0); +} + + + +int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AXY before the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + return BackupAXYAt (S, B, Idx, Indices, 0); +} + + + +int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of A after the specified index Idx */ +{ + return BackupAAt (S, B, Idx, Indices, 1); +} + + + +int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of X after the specified index Idx */ +{ + return BackupXAt (S, B, Idx, Indices, 1); +} + + + +int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of Y after the specified index Idx */ +{ + return BackupYAt (S, B, Idx, Indices, 1); +} + + + +int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AX after the specified index Idx */ +{ + return BackupAXAt (S, B, Idx, Indices, 1); +} + + + +int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AXY after the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + return BackupAXYAt (S, B, Idx, Indices, 1); +} + + + +int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of Y before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_X) != 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_Y) != 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDA, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of X before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_A) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_Y) != 0) { + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDX, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of Y before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_A) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_X) != 0) { + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of AX before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + StrBuf Arg; + int OldIdx = Idx; + + SB_Init (&Arg); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_REG: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_ZP: + SB_AppendStr (&Arg, GetZPName (B->Where)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + SB_Done (&Arg); + + return 1; +} + + + +int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of AXY before the specified index Idx. +** This only allows restore from compacted AXY backup for now. +*/ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg ((B->Imm >> 16) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_ZP: + CHECK ((B->Where & REG_TMP1) != 0); + SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + default: + /* Unable to restorep */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices) +/* Backup the content of the opc arg of the entry E after the specified index Idx. +** Reg A/Y will be used to transfer the content from a memory location to another +** regardless of whether it is in use. +*/ +{ + CodeEntry* X; + int OldIdx = Idx; + unsigned ArgSize; + unsigned Use, Chg; + StrBuf SrcArg; + StrBuf DstArg; + + SB_Init (&SrcArg); + SB_Init (&DstArg); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA) { + /* Get size of the arg */ + if ((E->Info & OF_LBRA) != 0 || strcmp (E->Arg, "ldaxysp") == 0) { + ArgSize = BU_B16; + } else { + ArgSize = BU_B8; + } + + if (E->AM == AM65_IMM && CE_HasNumArg (E)) { + /* Just memorize the value */ + B->Type = BU_IMM | ArgSize; + B->Imm = E->Num; + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx); + + /* Done */ + return 1; + + } + + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* We only recognize opc with an arg for now */ + FindAvailableBackupLoc (B, ArgSize); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + SB_AppendStr (&DstArg, GetZPName (B->Where)); + SB_Terminate (&DstArg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize == BU_B16) { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + SB_AppendStr (&DstArg, "+1"); + SB_Terminate (&DstArg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_REG: + CHECK (ArgSize == BU_B8 && B->Where == REG_Y); + if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize == BU_B16) { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_SP: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize != BU_B16) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + if ((B->ZPUsage & REG_X) == 0) { + if (E->AM == AM65_ZP) { + X = NewCodeEntry (OP65_LDX, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + } else { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + } + break; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx); + + /* Done */ + return 1; + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + ArgSize = BU_B8; + return BackupAAfter (S, B, Idx, Indices); + } else if (Use == REG_AX) { + ArgSize = BU_B16; + return BackupAXAfter (S, B, Idx, Indices); + } else if (Use == REG_AXY) { + /* This is actually a 16-bit word plus a 8-bit byte */ + ArgSize = BU_B24; + return BackupAXYAfter (S, B, Idx, Indices); + } + + /* We don't recognize other usage patterns for now */ + } + } + + SB_Done (&SrcArg); + SB_Done (&DstArg); + + /* Unable to do backup */ + return 0; +} + +static int LoadAAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into A the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + + /* For other function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_X) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_Y) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_A) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +static int LoadXAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into X the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + + /* ldx does support AM65_ZPY and AM65_ABSY */ + if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + } else { + X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_Y) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_X) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +static int LoadYAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into Y the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else { + X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_X) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_Y) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into A the same arg according to LoadRegInfo at Idx */ +{ + return LoadAAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into X the same arg according to LoadRegInfo at Idx */ +{ + return LoadXAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into Y the same arg according to LoadRegInfo at Idx */ +{ + return LoadYAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into A the same arg according to LoadRegInfo after Idx */ +{ + return LoadAAt (S, Idx, LRI, Indices, 1); +} + + + +int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into X the same arg according to LoadRegInfo after Idx */ +{ + return LoadXAt (S, Idx, LRI, Indices, 1); +} + + + +int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into Y the same arg according to LoadRegInfo after Idx */ +{ + return LoadYAt (S, Idx, LRI, Indices, 1); +} + + + +unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last) +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last). +** The code block must be basic without any jump backwards. +*/ +{ + CodeEntry* X; + unsigned ZPAccessed = 0; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + ZPAccessed |= X->Use | X->Chg; + } + + return ZPAccessed; +} + + + +unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg) +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last) in output parameters Use and Chg. +** Return what ZP regs are used before changed in this range. +** The code block must be basic without any jump backwards. +*/ +{ + CodeEntry* X; + unsigned U = 0; + unsigned C = 0; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* Clear the output flags first */ + if (Use != 0) { + *Use = 0; + } + if (Chg != 0) { + *Chg = 0; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if (Use != 0) { + *Use |= X->Use; + } + if (Chg != 0) { + *Chg |= X->Chg; + } + /* Used before changed */ + U |= ~C & X->Use; + C |= X->Chg; + } + + return U; +} + + + +int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E) +/* Check if the loading the opc arg gives the same result everywhere between (First, Last). +** The code block in the range must be basic without any jump backwards. +** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + unsigned CheckedFlags = LI_SRC_CHG; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Just bail out as if the src would change right away */ + return 0; + } + + /* If there's no need to check */ + if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + return 1; + } + + /* This always checks Y */ + if ((LRI.Flags & (LI_CHECK_Y | LI_RELOAD_Y)) != 0) { + LRI.Flags |= LI_CHECK_Y; + LRI.Flags &= ~LI_RELOAD_Y; + CheckedFlags |= LI_Y_CHG; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((Affected (&LRI, X) & CheckedFlags) != 0) { + return 0; + } + } + + /* No change found */ + return 1; +} + + + +int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E) +/* Find the first possible spot where the loaded arg of E might be changed in +** the range (First, Last). The code block in the range must be basic without +** any jump backwards. +** Return the index of the found entry, or Last if not found. +** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + unsigned CheckedFlags = LI_SRC_CHG; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) == 0) { + /* Just bail out as if the src would change right away */ + return First + 1; + } + + /* If there's no need to check */ + if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y)) == 0) { + return Last; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((Affected (&LRI, X) & CheckedFlags) != 0) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY) +/* Find the last index where the arg of E might be used or changed in the range (First, Last). +** ReloadY indicates whether Y is supposed to be reloaded. +** The code block in the range must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + unsigned CheckedFlags = LI_SRC_USE | LI_SRC_CHG; + int Found = First; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* Whether Y is to be reloaded */ + if (ReloadY) { + /* Always reload Y */ + if ((LRI.Flags & LI_CHECK_Y) != 0) { + LRI.Flags |= LI_RELOAD_Y; + } + } else if ((LRI.Flags & LI_RELOAD_Y) != 0) { + /* Always check Y */ + LRI.Flags |= LI_CHECK_Y; + LRI.Flags &= ~LI_RELOAD_Y; + } + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Just bail out as if the src would change everywhere */ + return First < Last ? Last - 1 : First; + } + + if ((LRI.Flags & LI_CHECK_Y) != 0) { + CheckedFlags |= LI_Y_SRC_USE | LI_Y_SRC_CHG; + } + + if ((LRI.Flags & LI_RELOAD_Y) != 0) { + CheckedFlags |= LI_Y_USE; + } else if ((LRI.Flags & LI_CHECK_Y) != 0) { + CheckedFlags |= LI_Y_CHG; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((Affected (&LRI, X) & CheckedFlags) != 0) { + Found = First; + } + } + + /* Result */ + return Found; +} + + + +int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ +{ + CodeEntry* X; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Chg & what) != 0) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ +{ + CodeEntry* X; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Use & what) != 0) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ +{ + CodeEntry* X; + int Found = First; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Chg & what) != 0) { + Found = First; + } + } + + return Found; +} + + + +int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ +{ + CodeEntry* X; + int Found = First; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Use & what) != 0) { + Found = First; + } + } + + return Found; +} diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h new file mode 100644 index 000000000..140b11236 --- /dev/null +++ b/src/cc65/codeoptutil.h @@ -0,0 +1,469 @@ +/*****************************************************************************/ +/* */ +/* codeoptutil.h */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CODEOPTUTIL_H +#define CODEOPTUTIL_H + + + +/* cc65 */ +#include "codeent.h" +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Load tracking data */ +/*****************************************************************************/ + + + +/* LoadRegInfo flags set by DirectOp */ +typedef enum { + LI_NONE = 0x00, + LI_DIRECT = 0x01, /* Direct op may be used */ + LI_RELOAD_Y = 0x02, /* Reload index register Y */ + LI_REMOVE = 0x04, /* Load may be removed */ + LI_DONT_REMOVE = 0x08, /* Load may not be removed */ + LI_CHECK_ARG = 0x10, /* Load src might be modified later */ + LI_CHECK_Y = 0x20, /* Indexed load src might be modified later */ + LI_SRC_USE = 0x40, /* src of Opc argument is possibly used */ + LI_SRC_CHG = 0x80, /* src of Opc argument is possibly modified */ + LI_Y_SRC_USE = 0x0100, /* src of Opc addressing Y is possibly used */ + LI_Y_SRC_CHG = 0x0200, /* src of Opc addressing Y is possibly modified */ + LI_Y_USE = 0x0400, /* Opc addressing Y is possibly used */ + LI_Y_CHG = 0x0800, /* Opc addressing Y is possibly modified */ + LI_USED_BY_A = 0x1000, /* Content used by RegA */ + LI_USED_BY_X = 0x2000, /* Content used by RegX */ + LI_USED_BY_Y = 0x4000, /* Content used by RegY */ + LI_SP = 0x8000, /* Content on stack */ + LI_LOAD_INSN = 0x010000, /* Is a load insn */ +} LI_FLAGS; + +/* Structure that tells us how to load the lhs values */ +typedef struct LoadRegInfo LoadRegInfo; +struct LoadRegInfo { + LI_FLAGS Flags; /* Tells us how to load */ + int LoadIndex; /* Index of load insn, -1 if invalid */ + CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ + int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ + CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ + int ChgIndex; /* Index of last change */ + CodeEntry* ChgEntry; /* The actual change entry */ + int Offs; /* Stack offset if data is on stack */ +}; + +/* Now combined for both registers */ +typedef struct LoadInfo LoadInfo; +struct LoadInfo { + LoadRegInfo A; /* Info for A register */ + LoadRegInfo X; /* Info for X register */ + LoadRegInfo Y; /* Info for Y register */ +}; + +/* Structure forward decl */ +typedef struct StackOpData StackOpData; + +/* Structure that holds the needed data */ +struct StackOpData { + CodeSeg* Code; /* Pointer to code segment */ + unsigned Flags; /* Flags to remember things */ + + /* Pointer to optimizer subfunction description */ + const void* OptFunc; + + /* ZP register usage inside the sequence */ + unsigned ZPUsage; + unsigned ZPChanged; + + /* Freedom of registers inside the sequence */ + unsigned UsedRegs; /* Registers used */ + + /* Whether the rhs is changed multiple times */ + int RhsMultiChg; + + /* Register load information for lhs, rhs and rv */ + LoadInfo Lhs; + LoadInfo Rhs; + LoadInfo Rv; + + /* Several indices of insns in the code segment */ + int PushIndex; /* Index of call to pushax in codeseg */ + int OpIndex; /* Index of actual operation */ + + /* Pointers to insns in the code segment */ + CodeEntry* PrevEntry; /* Entry before the call to pushax */ + CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ + CodeEntry* OpEntry; /* Pointer to entry with op */ + CodeEntry* NextEntry; /* Entry after the op */ + + const char* ZPLo; /* Lo byte of zero page loc to use */ + const char* ZPHi; /* Hi byte of zero page loc to use */ + unsigned IP; /* Insertion point used by some routines */ +}; + + + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +void ClearLoadRegInfo (LoadRegInfo* LRI); +/* Clear a LoadRegInfo struct */ + +void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From); +/* Copy a LoadRegInfo struct */ + +void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S); +/* Prepare a LoadRegInfo struct for use */ + +void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change); +/* Adjust a load register info struct after deleting or inserting an entry +** with a given index +*/ + +void ClearLoadInfo (LoadInfo* LI); +/* Clear a LoadInfo struct */ + +void CopyLoadInfo (LoadInfo* To, LoadInfo* From); +/* Copy a LoadInfo struct */ + +void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S); +/* Prepare a LoadInfo struct for use */ + +void AdjustLoadInfo (LoadInfo* LI, int Index, int Change); +/* Adjust a load info struct after deleting entry with a given index */ + +RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg); +/* Get RegInfo of the last insn entry that changed the reg */ + +void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E); +/* Set the load src flags and remember to check for load src change if necessary */ + +void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E); +/* Check and flag operand src that may be affected */ + +void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used); +/* Check and flag operand load that may be unremovable */ + +unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I); +/* Track loads for a code entry. +** Return used registers. +*/ + +void SetDontRemoveEntryFlag (LoadRegInfo* LRI); +/* Flag the entry as non-removable according to register flags */ + +void ResetDontRemoveEntryFlag (LoadRegInfo* LRI); +/* Unflag the entry as non-removable according to register flags */ + +void SetDontRemoveEntryFlags (StackOpData* D); +/* Flag the entries as non-removable according to register flags */ + +void ResetDontRemoveEntryFlags (StackOpData* D); +/* Unflag the entries as non-removable according to register flags */ + +void ResetStackOpData (StackOpData* Data); +/* Reset the given data structure */ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +void InsertEntry (StackOpData* D, CodeEntry* E, int Index); +/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will +** be adjusted by this function. +*/ + +void DelEntry (StackOpData* D, int Index); +/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be +** adjusted by this function, and PushEntry/OpEntry may get invalidated. +*/ + +void AdjustStackOffset (StackOpData* D, unsigned Offs); +/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. +** OpIndex is adjusted according to the insertions. +*/ + +int IsRegVar (StackOpData* D); +/* If the value pushed is that of a zeropage variable that is unchanged until Op, +** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. +** Otherwise leave D untouched and return false. +*/ + +void AddStoreLhsA (StackOpData* D); +/* Add a store to zero page after the push insn */ + +void AddStoreLhsX (StackOpData* D); +/* Add a store to zero page after the push insn */ + +void ReplacePushByStore (StackOpData* D); +/* Replace the call to the push subroutine by a store into the zero page +** location (actually, the push is not replaced, because we need it for +** later, but the name is still ok since the push will get removed at the +** end of each routine). +*/ + +void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI); +/* Add an op for the low byte of an operator. This function honours the +** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. +** All code is inserted at the current insertion point. +*/ + +void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult); +/* Add an op for the high byte of an operator. Special cases (constant values +** or similar) have to be checked separately, the function covers only the +** generic case. Code is inserted at the insertion point. +*/ + +void RemoveRegLoads (StackOpData* D, LoadInfo* LI); +/* Remove register load insns */ + + +void RemoveRemainders (StackOpData* D); +/* Remove the code that is unnecessary after translation of the sequence */ + +int HarmlessCall (const CodeEntry* E, int PushedBytes); +/* Check if this is a call to a harmless subroutine that will not interrupt +** the pushax/op sequence when encountered. +*/ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +/* Backup location types */ +#define BU_UNKNOWN 0x00000000U /* Unknown */ +#define BU_IMM 0x00000000U /* Immediate */ +#define BU_REG 0x01000000U /* In register */ +#define BU_ZP 0x02000000U /* On ZP */ +#define BU_SP6502 0x04000000U /* On 6502 stack */ +#define BU_SP 0x08000000U /* On CC65 stack */ +#define BU_B8 0x00000000U /* Size of 8-bit */ +#define BU_B16 0x10000000U /* Size of 16-bit */ +#define BU_B24 0x20000000U /* Size of 24-bit */ +#define BU_B32 0x30000000U /* Size of 32-bit */ +#define BU_TYPE_MASK 0x0F000000U /* Type mask */ +#define BU_SIZE_MASK 0xF0000000U /* Size mask */ + +typedef struct { + unsigned Type; /* Backup location type and size */ + unsigned ZPUsage; /* ZP unusable for backup */ + union { + unsigned Where; /* Backup location */ + unsigned Imm; /* Backed-up value */ + unsigned char* Bytes; /* Pointer to backed-up value */ + }; +} BackupInfo; + + + +const char* GetZPName (unsigned ZPLoc); +/* Get the name strings of certain known ZP Regs */ + +unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type); +/* Find a ZP loc for storing the backup and fill in the info. +** The allowed types are specified with the Type parameter. +** For convenience, all types are aloowed if none is specified. +** Return the type of the found loc. +*/ + +void AdjustEntryIndices (Collection* Indices, int Index, int Change); +/* Adjust a load register info struct after deleting or inserting successive +** entries with a given index. +*/ + +void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices); +/* Delete an entry and adjust Indices if necessary */ + +void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices); +/* Delete entries and adjust Indices if necessary */ + +void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices); +/* Remove flagged register load insns */ + +void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices); +/* Remove flagged load insns */ + +int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of A before the specified index Idx */ + +int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of X before the specified index Idx */ + +int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of Y before the specified index Idx */ + +int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AX before the specified index Idx */ + +int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AXY before the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ + +int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of A after the specified index Idx */ + +int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of X after the specified index Idx */ + +int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of Y after the specified index Idx */ + +int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AX after the specified index Idx */ + +int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AXY after the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ + +int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of Y before the specified index Idx */ + +int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of X before the specified index Idx */ + +int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of Y before the specified index Idx */ + +int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of AX before the specified index Idx */ + +int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of AXY before the specified index Idx. +** This only allows restore from compacted AXY backup for now. +*/ + +int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices); +/* Backup the content of the opc arg of the entry E after the specified index Idx. +** Reg A/Y will be used to transfer the content from a memory location to another +** regardless of whether it is in use. +*/ + +int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into A the same arg according to LoadRegInfo at Idx */ + +int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into X the same arg according to LoadRegInfo at Idx */ + +int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into Y the same arg according to LoadRegInfo at Idx */ + +int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into A the same arg according to LoadRegInfo after Idx */ + +int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into X the same arg according to LoadRegInfo after Idx */ + +int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into Y the same arg according to LoadRegInfo after Idx */ + +unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last); +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last). +** The code block must be basic without any jump backwards. +*/ + +unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg); +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last) in output parameters Use and Chg. +** Return what ZP regs are used before changed in this range. +** The code block must be basic without any jump backwards. +*/ + +int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E); +/* Check if the loading the opc arg gives the same result everywhere between (First, Last). +** The code block in the range must be basic without any jump backwards. +** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set. +*/ + +int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E); +/* Find the first possible spot where the loaded arg of E might be changed in +** the range (First, Last). The code block in the range must be basic without +** any jump backwards. +** Return the index of the found entry, or Last if not found. +** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set. +*/ + +int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY); +/* Find the last index where the arg of E might be used or changed in the range (First, Last). +** ReloadY indicates whether Y is supposed to be reloaded. +** The code block in the range must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ + +int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ + +int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ + +int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ + +int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or First if not found. +*/ + +/* End of codeoptutil.h */ + +#endif diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index f2e0faf15..e621147ab 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -183,6 +183,37 @@ static void CS_RemoveLabelFromHash (CodeSeg* S, CodeLabel* L) +static CodeLabel* PickRefLab (CodeEntry* E) +/* Pick a reference label and move it to index 0 in E. */ +{ + unsigned I; + unsigned LabelCount = CE_GetLabelCount (E); + CHECK (LabelCount > 0); + /* Use either the first one as reference label, or a label with a Ref that has no JumpTo. + ** This is a hack to partially work around #1211. Refs with no JumpTo are labels used + ** in data segments. (They are not tracked.) If a data segment is the only reference, + ** the label will be pruned away, but the data reference will remain, causing linking to fail. + */ + CodeLabel* L0 = CE_GetLabel (E, 0); + for (I = 1; I < LabelCount; ++I) { + unsigned J; + CodeLabel* L = CE_GetLabel (E, I); + unsigned RefCount = CL_GetRefCount (L); + for (J = 0; J < RefCount; ++J) { + CodeEntry* EJ = CL_GetRef (L, J); + if (EJ->JumpTo == NULL) { + /* Move it to the beginning since it's simpler to handle the removal this way. */ + CE_ReplaceLabel (E, L, 0); + CE_ReplaceLabel (E, L0, I); + return L; + } + } + } + return L0; +} + + + /*****************************************************************************/ /* Functions for parsing instructions */ /*****************************************************************************/ @@ -251,6 +282,8 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) char Reg; CodeEntry* E; CodeLabel* Label; + const char* ArgBase = Arg; + int IsLabel = 0; /* Read the first token and skip white space after it */ L = SkipSpace (ReadToken (L, " \t:", Mnemo, sizeof (Mnemo))); @@ -372,7 +405,7 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) if ((OPC->Info & OF_BRA) != 0) { /* Branch */ AM = AM65_BRA; - } else if (GetZPInfo(Arg) != 0) { + } else if (IsZPArg (Arg)) { AM = AM65_ZP; } else { /* Check for subroutine call to local label */ @@ -393,12 +426,15 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) Reg = toupper (*L); L = SkipSpace (L+1); if (Reg == 'X') { - if (GetZPInfo(Arg) != 0) { + if (IsZPArg (Arg)) { AM = AM65_ZPX; } else { AM = AM65_ABSX; } } else if (Reg == 'Y') { + /* On 6502 only LDX and STX support AM65_ZPY. + ** We just play it simple and safe for now. + */ AM = AM65_ABSY; } else { Error ("ASM code error: syntax error"); @@ -414,32 +450,44 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) } - /* If the instruction is a branch, check for the label and generate it - ** if it does not exist. This may lead to unused labels (if the label + /* We do now have the addressing mode in AM. Allocate a new CodeEntry + ** structure and half-initialize it. We'll set the argument and the label + ** later. + */ + E = NewCodeEntry (OPC->OPC, AM, Arg, 0, LI); + + /* If the instruction is a branch or accessing memory data, check if for + ** the argument could refer to a label. If it does but the label does not + ** exist yet, generate it. This may lead to unused labels (if the label ** is actually an external one) which are removed by the CS_MergeLabels ** function later. */ - Label = 0; - if (AM == AM65_BRA) { + if ((E->Info & OF_CALL) == 0 && + (E->ArgInfo & AIF_HAS_NAME) != 0) { + ArgBase = E->ArgBase; + IsLabel = (E->ArgInfo & AIF_LOCAL) != 0; + } + + if (AM == AM65_BRA || IsLabel) { /* Generate the hash over the label, then search for the label */ - unsigned Hash = HashStr (Arg) % CS_LABEL_HASH_SIZE; - Label = CS_FindLabel (S, Arg, Hash); + unsigned Hash = HashStr (ArgBase) % CS_LABEL_HASH_SIZE; + Label = CS_FindLabel (S, ArgBase, Hash); /* If we don't have the label, it's a forward ref - create it unless ** it's an external function. */ - if (Label == 0 && (OPC->OPC != OP65_JMP || IsLocalLabelName (Arg)) ) { + if (Label == 0 && (OPC->OPC != OP65_JMP || IsLabel)) { /* Generate a new label */ - Label = CS_NewCodeLabel (S, Arg, Hash); + Label = CS_NewCodeLabel (S, ArgBase, Hash); + } + + if (Label != 0) { + /* Assign the jump */ + CL_AddRef (Label, E); } } - /* We do now have the addressing mode in AM. Allocate a new CodeEntry - ** structure and initialize it. - */ - E = NewCodeEntry (OPC->OPC, AM, Arg, Label, LI); - /* Return the new code entry */ return E; } @@ -935,8 +983,10 @@ void CS_MergeLabels (CodeSeg* S) continue; } - /* We have at least one label. Use the first one as reference label. */ - RefLab = CE_GetLabel (E, 0); + /* Pick a label to keep, all references will be moved to this one, and the other labels + ** will be deleted. PickRefLab moves this to index 0. + */ + RefLab = PickRefLab (E); /* Walk through the remaining labels and change references to these ** labels to a reference to the one and only label. Delete the labels @@ -1048,8 +1098,13 @@ void CS_MoveLabelRef (CodeSeg* S, struct CodeEntry* E, CodeLabel* L) /* Be sure that code entry references a label */ PRECONDITION (OldLabel != 0); - /* Remove the reference to our label */ - CS_RemoveLabelRef (S, E); + /* Delete the entry from the label */ + CollDeleteItem (&OldLabel->JumpFrom, E); + + /* If there are no more references, delete the label */ + if (CollCount (&OldLabel->JumpFrom) == 0) { + CS_DelLabel (S, OldLabel); + } /* Use the new label */ CL_AddRef (L, E); @@ -1468,6 +1523,7 @@ void CS_GenRegInfo (CodeSeg* S) /* On entry, the register contents are unknown */ RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); CurrentRegs = &Regs; /* Walk over all insns and note just the changes from one insn to the @@ -1502,6 +1558,7 @@ void CS_GenRegInfo (CodeSeg* S) Regs = J->RI->Out2; } else { RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); } Entry = 1; } else { @@ -1521,6 +1578,7 @@ void CS_GenRegInfo (CodeSeg* S) */ Done = 0; RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); break; } if (J->RI->Out2.RegA != Regs.RegA) { @@ -1541,6 +1599,9 @@ void CS_GenRegInfo (CodeSeg* S) if (J->RI->Out2.Tmp1 != Regs.Tmp1) { Regs.Tmp1 = UNKNOWN_REGVAL; } + unsigned PF = J->RI->Out2.PFlags ^ Regs.PFlags; + Regs.PFlags |= ((PF >> 8) | PF | (PF << 8)) & UNKNOWN_PFVAL_ALL; + Regs.ZNRegs &= J->RI->Out2.ZNRegs; ++Entry; } diff --git a/src/cc65/compile.c b/src/cc65/compile.c index ef66d38d2..94dfc3ffb 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -37,6 +37,7 @@ #include <time.h> /* common */ +#include "addrsize.h" #include "debugflag.h" #include "segnames.h" #include "version.h" @@ -52,6 +53,7 @@ #include "declare.h" #include "error.h" #include "expr.h" +#include "funcdesc.h" #include "function.h" #include "global.h" #include "input.h" @@ -61,6 +63,7 @@ #include "pragma.h" #include "preproc.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" @@ -76,6 +79,7 @@ static void Parse (void) { int comma; SymEntry* Entry; + FuncDesc* FuncDef = 0; /* Go... */ NextToken (); @@ -108,11 +112,17 @@ static void Parse (void) continue; } + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + /* Read variable defs and functions */ ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); /* Don't accept illegal storage classes */ - if ((Spec.StorageClass & SC_TYPE) == 0) { + if ((Spec.StorageClass & SC_TYPEMASK) == 0) { if ((Spec.StorageClass & SC_AUTO) != 0 || (Spec.StorageClass & SC_REGISTER) != 0) { Error ("Illegal storage class"); @@ -132,14 +142,10 @@ static void Parse (void) comma = 0; while (1) { - Declaration Decl; + Declaration Decl; /* Read the next declaration */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); - if (Decl.Ident[0] == '\0') { - NextToken (); - break; - } /* Check if we must reserve storage for the variable. We do this, ** @@ -150,30 +156,38 @@ static void Parse (void) ** ** This means that "extern int i;" will not get storage allocated. */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && - (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF && - ((Spec.Flags & DS_DEF_STORAGE) != 0 || - (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || - ((Decl.StorageClass & SC_EXTERN) != 0 && - CurTok.Tok == TOK_ASSIGN))) { - - /* We will allocate storage */ - Decl.StorageClass |= SC_STORAGE; + if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + if ((Spec.Flags & DS_DEF_STORAGE) != 0 || + (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || + ((Decl.StorageClass & SC_EXTERN) != 0 && + CurTok.Tok == TOK_ASSIGN)) { + /* We will allocate storage */ + Decl.StorageClass |= SC_STORAGE; + } else { + /* It's a declaration */ + Decl.StorageClass |= SC_DECL; + } } /* If this is a function declarator that is not followed by a comma - ** or semicolon, it must be followed by a function body. If this is - ** the case, convert an empty parameter list into one accepting no - ** parameters (same as void) as required by the standard. + ** or semicolon, it must be followed by a function body. */ - if ((Decl.StorageClass & SC_FUNC) != 0 && - (CurTok.Tok != TOK_COMMA) && - (CurTok.Tok != TOK_SEMI)) { + if ((Decl.StorageClass & SC_FUNC) != 0) { + if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { + /* A definition */ + Decl.StorageClass |= SC_DEF; - FuncDesc* D = GetFuncDesc (Decl.Type); - - if (D->Flags & FD_EMPTY) { - D->Flags = (D->Flags & ~FD_EMPTY) | FD_VOID_PARAM; + /* Convert an empty parameter list into one accepting no + ** parameters (same as void) as required by the standard. + */ + FuncDef = GetFuncDesc (Decl.Type); + if (FuncDef->Flags & FD_EMPTY) { + FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM; + } + } else { + /* Just a declaration */ + Decl.StorageClass |= SC_DECL; } } @@ -238,12 +252,11 @@ static void Parse (void) /* 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) { + } else if (Size == 0 && SymIsDef (Entry)) { /* Size is unknown. Is it an array? */ if (!IsTypeArray (Decl.Type)) { Error ("Variable '%s' has unknown size", Decl.Ident); } - Entry->Flags &= ~(SC_STORAGE | SC_DEF); } else { /* A global (including static) uninitialized variable is ** only a tentative definition. For example, this is valid: @@ -262,6 +275,26 @@ static void Parse (void) Entry->Name, Entry->V.BssName); } Entry->V.BssName = xstrdup (bssName); + + /* This is to make the automatical zeropage setting of the symbol + ** work right. + */ + g_usebss (); + } + } + + /* Make the symbol zeropage according to the segment address size */ + if ((Entry->Flags & SC_EXTERN) != 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"); + } + } } } @@ -285,15 +318,11 @@ static void Parse (void) /* Prototype only */ NextToken (); } else { - - /* Function body. Check for duplicate function definitions */ - if (SymIsDef (Entry)) { - Error ("Body for function '%s' has already been defined", - Entry->Name); - } - /* Parse the function body */ - NewFunc (Entry); + NewFunc (Entry, FuncDef); + + /* Make sure we aren't omitting any work */ + CheckDeferredOpAllDone (); } } @@ -369,12 +398,20 @@ void Compile (const char* FileName) /* DefineNumericMacro ("__STDC__", 1); <- not now */ DefineNumericMacro ("__STDC_HOSTED__", 1); + InitDeferredOps (); + /* Create the base lexical level */ EnterGlobalLevel (); /* Create the global code and data segments */ CreateGlobalSegments (); + /* There shouldn't be needs for local labels outside a function, but the + ** current code generator still tries to get some at times even though the + ** code were ill-formed. So just set it up with the global segment list. + */ + UseLabelPoolFromSegments (GS); + /* Initialize the literal pool */ InitLiteralPool (); @@ -401,11 +438,61 @@ void Compile (const char* FileName) } else { + /* Used for emitting externals */ + SymEntry* Entry; + /* Ok, start the ball rolling... */ Parse (); + /* Reset the BSS segment name to its default; so that the below strcmp() + ** will work as expected, at the beginning of the list of variables + */ + SetSegName (SEG_BSS, SEGNAME_BSS); + + /* Walk over all global symbols and generate code for uninitialized + ** global variables. + */ + 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); + unsigned Size = SizeOf (Entry->Type); + if (Size == 0 && IsTypeArray (Entry->Type)) { + if (GetElementCount (Entry->Type) == UNSPECIFIED) { + /* Assume array size of 1 */ + SetElementCount (Entry->Type, 1); + Size = SizeOf (Entry->Type); + Warning ("Incomplete array '%s[]' assumed to have one element", Entry->Name); + } + + Sym = GetSymType (GetElementType (Entry->Type)); + } + + /* For non-ESU types, Size != 0 */ + if (Size != 0 || (Sym != 0 && SymIsDef (Sym))) { + /* Set the segment name only when it changes */ + if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { + SetSegName (SEG_BSS, Entry->V.BssName); + g_segname (SEG_BSS); + } + g_usebss (); + g_defgloblabel (Entry->Name); + g_res (Size); + + /* Mark as defined; so that it will be exported, not imported */ + Entry->Flags |= SC_DEF; + } else { + /* Tentative declared variable is still of incomplete type */ + Error ("Definition of '%s' has type '%s' that is never completed", + Entry->Name, + GetFullTypeName (Entry->Type)); + } + } + } } + DoneDeferredOps (); + if (Debug) { PrintMacroStats (stdout); } @@ -417,38 +504,22 @@ void Compile (const char* FileName) void FinishCompile (void) -/* Emit literals, externals, debug info, do cleanup and optimizations */ +/* Emit literals, debug info, do cleanup and optimizations */ { SymEntry* Entry; - /* Reset the BSS segment name to its default; so that the below strcmp() - ** will work as expected, at the beginning of the list of variables - */ - SetSegName (SEG_BSS, SEGNAME_BSS); - - /* Walk over all global symbols: - ** - for functions, do clean-up and optimizations - ** - generate code for uninitialized global variables + /* Walk over all global symbols and do clean-up and optimizations for + ** functions. */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { if (SymIsOutputFunc (Entry)) { + /* Continue with previous label numbers */ + UseLabelPoolFromSegments (Entry->V.F.Seg); + /* Function which is defined and referenced or extern */ MoveLiteralPool (Entry->V.F.LitPool); CS_MergeLabels (Entry->V.F.Seg->Code); RunOpt (Entry->V.F.Seg->Code); - } else if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { - /* Assembly definition of uninitialized global variable */ - - /* Set the segment name only when it changes */ - if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { - SetSegName (SEG_BSS, Entry->V.BssName); - g_segname (SEG_BSS); - } - g_usebss (); - g_defgloblabel (Entry->Name); - g_res (SizeOf (Entry->Type)); - /* Mark as defined; so that it will be exported, not imported */ - Entry->Flags |= SC_DEF; } } diff --git a/src/cc65/compile.h b/src/cc65/compile.h index 2d15c8200..7af14ef7e 100644 --- a/src/cc65/compile.h +++ b/src/cc65/compile.h @@ -48,7 +48,7 @@ void Compile (const char* FileName); /* Top level compile routine. Will setup things and call the parser. */ void FinishCompile (void); -/* Emit literals, externals, do cleanup and optimizations */ +/* Emit literals, debug info, do cleanup and optimizations */ diff --git a/src/cc65/coptadd.c b/src/cc65/coptadd.c index 07bd2bf98..bc67f7a74 100644 --- a/src/cc65/coptadd.c +++ b/src/cc65/coptadd.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2001-2005, Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index dbc71bcd5..fda23ae0a 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -176,14 +176,14 @@ static int IsImmCmp16 (CodeEntry** L) { return (L[0]->OPC == OP65_CPX && L[0]->AM == AM65_IMM && - (L[0]->Flags & CEF_NUMARG) != 0 && + CE_HasNumArg (L[0]) && !CE_HasLabel (L[0]) && (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) && L[1]->JumpTo != 0 && !CE_HasLabel (L[1]) && L[2]->OPC == OP65_CMP && L[2]->AM == AM65_IMM && - (L[2]->Flags & CEF_NUMARG) != 0 && + CE_HasNumArg (L[2]) && (L[3]->Info & OF_CBRA) != 0 && L[3]->JumpTo != 0 && (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo)); @@ -448,11 +448,7 @@ unsigned OptCmp3 (CodeSeg* S) Delete = 1; break; - case CMP_UGT: - case CMP_UGE: - case CMP_ULT: - case CMP_ULE: - case CMP_INV: + default: /* Leave it alone */ break; } @@ -948,7 +944,9 @@ unsigned OptCmp9 (CodeSeg* S) if (L[0]->OPC == OP65_SBC && CS_GetEntries (S, L+1, I+1, 4) && (L[1]->OPC == OP65_BVC || - L[1]->OPC == OP65_BVS) && + L[1]->OPC == OP65_BVS || + L[1]->OPC == OP65_JVC || + L[1]->OPC == OP65_JVS) && L[1]->JumpTo != 0 && L[1]->JumpTo->Owner == L[3] && L[2]->OPC == OP65_EOR && diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 04deb0119..a080cfb20 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -124,46 +124,6 @@ static int MemAccess (CodeSeg* S, unsigned From, unsigned To, const CodeEntry* N -static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) -/* Get the branch distance between the two entries and return it. The distance -** will be negative for backward jumps and positive for forward jumps. -*/ -{ - /* Get the index of the branch target */ - unsigned TI = CS_GetEntryIndex (S, To); - - /* Determine the branch distance */ - int Distance = 0; - if (TI >= From) { - /* Forward branch, do not count the current insn */ - unsigned J = From+1; - while (J < TI) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance += N->Size; - } - } else { - /* Backward branch */ - unsigned J = TI; - while (J < From) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance -= N->Size; - } - } - - /* Return the calculated distance */ - return Distance; -} - - - -static int IsShortDist (int Distance) -/* Return true if the given distance is a short branch distance */ -{ - return (Distance >= -125 && Distance <= 125); -} - - - static short ZPRegVal (unsigned short Use, const RegContents* RC) /* Return the contents of the given zeropage register */ { @@ -184,838 +144,6 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) -static short RegVal (unsigned short Use, const RegContents* RC) -/* Return the contents of the given register */ -{ - if ((Use & REG_A) != 0) { - return RC->RegA; - } else if ((Use & REG_X) != 0) { - return RC->RegX; - } else if ((Use & REG_Y) != 0) { - return RC->RegY; - } else { - return ZPRegVal (Use, RC); - } -} - - - -/*****************************************************************************/ -/* Replace jumps to RTS by RTS */ -/*****************************************************************************/ - - - -unsigned OptRTSJumps1 (CodeSeg* S) -/* Replace jumps to RTS by RTS */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an unconditional branch to a local target */ - if ((E->Info & OF_UBRA) != 0 && - E->JumpTo != 0 && - E->JumpTo->Owner->OPC == OP65_RTS) { - - /* Insert an RTS instruction */ - CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptRTSJumps2 (CodeSeg* S) -/* Replace long conditional jumps to RTS or to a final target */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S) - 1) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an conditional branch to a local target */ - if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ - (E->Info & OF_LBRA) != 0 && /* Long branch */ - E->JumpTo != 0) { /* Local label */ - - - /* Get the jump target and the next entry. There's always a next - ** entry, because we don't cover the last entry in the loop. - */ - CodeEntry* X = 0; - CodeEntry* T = E->JumpTo->Owner; - CodeEntry* N = CS_GetNextEntry (S, I); - - /* Check if it's a jump to an RTS insn */ - if (T->OPC == OP65_RTS) { - - /* It's a jump to RTS. Create a conditional branch around an - ** RTS insn. - */ - X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); - - } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { - - /* It's a jump to a label outside the function. Create a - ** conditional branch around a jump to the external label. - */ - X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); - - } - - /* If we have a replacement insn, insert it */ - if (X) { - - CodeLabel* LN; - opc_t NewBranch; - - /* Insert the new insn */ - CS_InsertEntry (S, X, I+1); - - /* Create a conditional branch with the inverse condition - ** around the replacement insn - */ - - /* Get the new branch opcode */ - NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); - - /* Get the label attached to N, create a new one if needed */ - LN = CS_GenLabel (S, N); - - /* Generate the branch */ - X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the long branch */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Remove dead jumps */ -/*****************************************************************************/ - - - -unsigned OptDeadJumps (CodeSeg* S) -/* Remove dead jumps (jumps to the next instruction) */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a branch, if it has a local target, and if the target - ** is the next instruction. - */ - if (E->AM == AM65_BRA && - E->JumpTo && - E->JumpTo->Owner == CS_GetNextEntry (S, I)) { - - /* Delete the dead jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Remove dead code */ -/*****************************************************************************/ - - - -unsigned OptDeadCode (CodeSeg* S) -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* LN; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an unconditional branch, and if the next entry has - ** no labels attached, or if the label is just used so that the insn - ** can jump to itself. - */ - if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ - (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ - (!CE_HasLabel (N) || /* Don't has a label */ - ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ - (LN = N->JumpTo) != 0 && /* Jumps to known label */ - LN->Owner == N && /* Attached to insn */ - CL_GetRefCount (LN) == 1))) { /* Only reference */ - - /* Delete the next entry */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump cascades */ -/*****************************************************************************/ - - - -unsigned OptJumpCascades (CodeSeg* S) -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* OldLabel; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check: - ** - if it's a branch, - ** - if it has a jump label, - ** - if this jump label is not attached to the instruction itself, - ** - if the target instruction is itself a branch, - ** - if either the first branch is unconditional or the target of - ** the second branch is internal to the function. - ** The latter condition will avoid conditional branches to targets - ** outside of the function (usually incspx), which won't simplify the - ** code, since conditional far branches are emulated by a short branch - ** around a jump. - */ - if ((E->Info & OF_BRA) != 0 && - (OldLabel = E->JumpTo) != 0 && - (N = OldLabel->Owner) != E && - (N->Info & OF_BRA) != 0 && - ((E->Info & OF_CBRA) == 0 || - N->JumpTo != 0)) { - - /* Check if we can use the final target label. That is the case, - ** if the target branch is an absolute branch; or, if it is a - ** conditional branch checking the same condition as the first one. - */ - if ((N->Info & OF_UBRA) != 0 || - ((E->Info & OF_CBRA) != 0 && - GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { - - /* This is a jump cascade and we may jump to the final target, - ** provided that the other insn does not jump to itself. If - ** this is the case, we can also jump to ourselves, otherwise - ** insert a jump to the new instruction and remove the old one. - */ - CodeEntry* X; - CodeLabel* LN = N->JumpTo; - - if (LN != 0 && LN->Owner == N) { - - /* We found a jump to a jump to itself. Replace our jump - ** by a jump to itself. - */ - CodeLabel* LE = CS_GenLabel (S, E); - X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); - - } else { - - /* Jump to the final jump target */ - X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); - - } - - /* Insert it behind E */ - CS_InsertEntry (S, X, I+1); - - /* Remove E */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Check if both are conditional branches, and the condition of - ** the second is the inverse of that of the first. In this case, - ** the second branch will never be taken, and we may jump directly - ** to the instruction behind this one. - */ - } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { - - CodeEntry* X; /* Instruction behind N */ - CodeLabel* LX; /* Label attached to X */ - - /* Get the branch conditions of both branches */ - bc_t BC1 = GetBranchCond (E->OPC); - bc_t BC2 = GetBranchCond (N->OPC); - - /* Check the branch conditions */ - if (BC1 != GetInverseCond (BC2)) { - /* Condition not met */ - goto NextEntry; - } - - /* We may jump behind this conditional branch. Get the - ** pointer to the next instruction - */ - if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { - /* N is the last entry, bail out */ - goto NextEntry; - } - - /* Get the label attached to X, create a new one if needed */ - LX = CS_GenLabel (S, X); - - /* Move the reference from E to the new label */ - CS_MoveLabelRef (S, E, LX); - - /* Remember, we had changes */ - ++Changes; - } - } - -NextEntry: - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jsr/rts */ -/*****************************************************************************/ - - - -unsigned OptRTS (CodeSeg* S) -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a subroutine call and if the following insn is RTS */ - if (E->OPC == OP65_JSR && - (N = CS_GetNextEntry (S, I)) != 0 && - N->OPC == OP65_RTS) { - - /* Change the jsr to a jmp and use the additional info for a jump */ - E->AM = AM65_BRA; - CE_ReplaceOPC (E, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump targets */ -/*****************************************************************************/ - - - -unsigned OptJumpTarget1 (CodeSeg* S) -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ -{ - unsigned Changes = 0; - CodeEntry* E1; /* Entry 1 */ - CodeEntry* E2; /* Entry 2 */ - CodeEntry* T1; /* Jump target entry 1 */ - CodeLabel* TL1; /* Target label 1 */ - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - E2 = CS_GetNextEntry (S, I); - - /* Check if we have a jump or branch without a label attached, and - ** a jump target, which is not attached to the jump itself - */ - if (E2 != 0 && - (E2->Info & OF_UBRA) != 0 && - !CE_HasLabel (E2) && - E2->JumpTo && - E2->JumpTo->Owner != E2) { - - /* Get the entry preceeding the branch target */ - T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); - if (T1 == 0) { - /* There is no such entry */ - goto NextEntry; - } - - /* The entry preceeding the branch target may not be the branch - ** insn. - */ - if (T1 == E2) { - goto NextEntry; - } - - /* Get the entry preceeding the jump */ - E1 = CS_GetEntry (S, I); - - /* Check if both preceeding instructions are identical */ - if (!CodeEntriesAreEqual (E1, T1)) { - /* Not equal, try next */ - goto NextEntry; - } - - /* Get the label for the instruction preceeding the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - TL1 = CS_GenLabel (S, T1); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E2, TL1); - - /* If the instruction preceeding the jump has labels attached, - ** move references to this label to the new label. - */ - if (CE_HasLabel (E1)) { - CS_MoveLabels (S, E1, T1); - } - - /* Remove the entry preceeding the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { -NextEntry: - /* Next entry */ - ++I; - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget2 (CodeSeg* S) -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* OP that may be skipped */ - opc_t OPC; - - /* Jump target insn, old and new */ - CodeEntry* T; - CodeEntry* N; - - /* New jump label */ - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a bcc insn */ - if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { - OPC = OP65_CLC; - } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { - OPC = OP65_SEC; - } else { - /* Not what we're looking for */ - goto NextEntry; - } - - /* Must have a jump target */ - if (E->JumpTo == 0) { - goto NextEntry; - } - - /* Get the owner insn of the jump target and check if it's the one, we - ** will skip if present. - */ - T = E->JumpTo->Owner; - if (T->OPC != OPC) { - goto NextEntry; - } - - /* Get the entry following the branch target */ - N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); - if (N == 0) { - /* There is no such entry */ - goto NextEntry; - } - - /* Get the label for the instruction following the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - L = CS_GenLabel (S, N); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E, L); - - /* Remember that we had changes */ - ++Changes; - -NextEntry: - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget3 (CodeSeg* S) -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a load insn with a label and the next insn is not - ** a conditional branch that needs the flags from the load. - */ - if ((E->Info & OF_LOAD) != 0 && - CE_IsConstImm (E) && - CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { - - unsigned J; - int K; - - /* New jump label */ - CodeLabel* LN = 0; - - /* Walk over all insn that jump here */ - for (J = 0; J < CE_GetLabelCount (E); ++J) { - - /* Get the label */ - CodeLabel* L = CE_GetLabel (E, J); - - /* Loop over all insn that reference this label. Since we may - ** eventually remove a reference in the loop, we must loop - ** from end down to start. - */ - for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { - - /* Get the entry that jumps here */ - CodeEntry* Jump = CL_GetRef (L, K); - - /* Get the register info from this insn */ - short Val = RegVal (E->Chg, &Jump->RI->Out2); - - /* Check if the outgoing value is the one thats's loaded */ - if (Val == (unsigned char) E->Num) { - - /* OK, skip the insn. First, generate a label for the - ** next insn after E. - */ - if (LN == 0) { - LN = CS_GenLabel (S, N); - } - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, Jump, LN); - - /* Remember that we had changes */ - ++Changes; - } - } - } - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize conditional branches */ -/*****************************************************************************/ - - - -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** -** - If an immediate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -** - If the conditional branch is always taken because of the register load, -** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a register load */ - if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ - E->AM == AM65_IMM && /* ..with immidiate addressing */ - (E->Flags & CEF_NUMARG) != 0 && /* ..and a numeric argument. */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ - !CE_HasLabel (N)) { /* ..and does not have a label */ - - /* Get the branch condition */ - bc_t BC = GetBranchCond (N->OPC); - - /* Check the argument against the branch condition */ - if ((BC == BC_EQ && E->Num != 0) || - (BC == BC_NE && E->Num == 0) || - (BC == BC_PL && (E->Num & 0x80) != 0) || - (BC == BC_MI && (E->Num & 0x80) == 0)) { - - /* Remove the conditional branch */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } else if ((BC == BC_EQ && E->Num == 0) || - (BC == BC_NE && E->Num != 0) || - (BC == BC_PL && (E->Num & 0x80) == 0) || - (BC == BC_MI && (E->Num & 0x80) != 0)) { - - /* The branch is always taken, replace it by a jump */ - CE_ReplaceOPC (N, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - } - - } - - if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ - (L = E->JumpTo) != 0 && /* ..referencing a local label */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ - !CE_HasLabel (N) && /* ..has no label attached */ - L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ - - /* Replace the jump by a conditional branch with the inverse branch - ** condition than the branch around it. - */ - CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); - - /* Remove the conditional branch */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptCondBranches2 (CodeSeg* S) -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry flag. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a rol insn with A in accu and a branch follows */ - if (E->OPC == OP65_ROL && - E->AM == AM65_ACC && - E->RI->In.RegA == 0 && - !CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !RegAUsed (S, I+1)) { - - /* Replace the branch condition */ - switch (GetBranchCond (N->OPC)) { - case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; - case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; - default: Internal ("Unknown branch condition in OptCondBranches2"); - } - - /* Delete the rol insn */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Remove unused loads and stores */ /*****************************************************************************/ @@ -1132,6 +260,70 @@ unsigned OptUnusedStores (CodeSeg* S) +unsigned OptLoad3 (CodeSeg* S) +/* Remove repeated loads from one and the same memory location */ +{ + unsigned Changes = 0; + CodeEntry* Load = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Forget a preceeding load if we have a label */ + if (Load && CE_HasLabel (E)) { + Load = 0; + } + + /* Check if this insn is a load */ + if (E->Info & OF_LOAD) { + + CodeEntry* N; + + /* If we had a preceeding load that is identical, remove this one. + ** If it is not identical, or we didn't have one, remember it. + */ + if (Load != 0 && + E->OPC == Load->OPC && + E->AM == Load->AM && + ((E->Arg == 0 && Load->Arg == 0) || + strcmp (E->Arg, Load->Arg) == 0) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_CBRA) == 0) { + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Next insn */ + continue; + + } else { + + Load = E; + + } + + } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { + /* Forget the first load on occurance of any insn we don't like */ + Load = 0; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptDupLoads (CodeSeg* S) /* Remove loads of registers where the value loaded is already in the register. */ { @@ -1147,8 +339,9 @@ unsigned OptDupLoads (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Assume we won't delete the entry */ + /* Assume we won't delete or replace the entry */ int Delete = 0; + opc_t NewOPC = OP65_INVALID; /* Get a pointer to the input registers of the insn */ const RegContents* In = &E->RI->In; @@ -1218,7 +411,7 @@ unsigned OptDupLoads (CodeSeg* S) E->AM != AM65_ABSY && E->AM != AM65_ZPY) { /* Use the A register instead */ - CE_ReplaceOPC (E, OP65_STA); + NewOPC = OP65_STA; } break; @@ -1242,11 +435,11 @@ unsigned OptDupLoads (CodeSeg* S) */ } else if (RegValIsKnown (In->RegY)) { if (In->RegY == In->RegA) { - CE_ReplaceOPC (E, OP65_STA); + NewOPC = OP65_STA; } else if (In->RegY == In->RegX && E->AM != AM65_ABSX && E->AM != AM65_ZPX) { - CE_ReplaceOPC (E, OP65_STX); + NewOPC = OP65_STX; } } break; @@ -1319,6 +512,14 @@ unsigned OptDupLoads (CodeSeg* S) } else { + if (NewOPC != OP65_INVALID) { + /* Replace the opcode */ + CE_ReplaceOPC (E, NewOPC); + + /* Remember, we had changes */ + ++Changes; + } + /* Next entry */ ++I; @@ -1471,7 +672,7 @@ unsigned OptTransfers2 (CodeSeg* S) (N = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (N) && (N->Info & OF_XFR) != 0 && - (GetRegInfo (S, I+2, E->Chg) & E->Chg) == 0) { + (GetRegInfo (S, I+2, E->Chg & REG_ALL) & E->Chg & REG_ALL) == 0) { CodeEntry* X = 0; @@ -1595,7 +796,7 @@ unsigned OptTransfers3 (CodeSeg* S) } /* Does this insn change the target register of the transfer? */ - } else if (E->Chg & XferEntry->Chg) { + } else if (E->Chg & XferEntry->Chg & ~PSTATE_ZN) { /* We *may* add code here to remove the transfer, but I'm ** currently not sure about the consequences, so I won't @@ -1622,8 +823,9 @@ unsigned OptTransfers3 (CodeSeg* S) ** isn't used later, and we have an address mode match, we can ** replace the transfer by a store and remove the store here. */ - if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 && - (StoreEntry->AM == AM65_ABS || + if ((GetRegInfo (S, I, XferEntry->Chg & REG_ALL) & + XferEntry->Chg & REG_ALL) == 0 && + (StoreEntry->AM == AM65_ABS || StoreEntry->AM == AM65_ZP) && (StoreEntry->AM != AM65_ZP || (StoreEntry->Chg & UsedRegs) == 0) && @@ -1772,7 +974,7 @@ unsigned OptTransfers4 (CodeSeg* S) } /* Does this insn change the target register of the load? */ - } else if (E->Chg & LoadEntry->Chg) { + } else if (E->Chg & LoadEntry->Chg & ~PSTATE_ZN) { /* We *may* add code here to remove the load, but I'm ** currently not sure about the consequences, so I won't @@ -1788,9 +990,10 @@ unsigned OptTransfers4 (CodeSeg* S) ** isn't used later, and we have an address mode match, we can ** replace the transfer by a load and remove the initial load. */ - if ((GetRegInfo (S, I, LoadEntry->Chg) & LoadEntry->Chg) == 0 && - (LoadEntry->AM == AM65_ABS || - LoadEntry->AM == AM65_ZP || + if ((GetRegInfo (S, I, LoadEntry->Chg & REG_ALL) & + LoadEntry->Chg & REG_ALL) == 0 && + (LoadEntry->AM == AM65_ABS || + LoadEntry->AM == AM65_ZP || LoadEntry->AM == AM65_IMM) && !MemAccess (S, Load+1, Xfer-1, LoadEntry)) { @@ -1866,8 +1069,8 @@ unsigned OptTransfers4 (CodeSeg* S) -unsigned OptPushPop (CodeSeg* S) -/* Remove a PHA/PLA sequence were A is not used later */ +unsigned OptPushPop1 (CodeSeg* S) +/* Remove a PHA/PLA sequence were A not used later */ { unsigned Changes = 0; unsigned Push = 0; /* Index of push insn */ @@ -1996,6 +1199,95 @@ unsigned OptPushPop (CodeSeg* S) +unsigned OptPushPop2 (CodeSeg* S) +/* Remove a PHP/PLP sequence were no processor flags changed inside */ +{ + unsigned Changes = 0; + unsigned Push = 0; /* Index of push insn */ + unsigned Pop = 0; /* Index of pop insn */ + enum { + Searching, + FoundPush, + FoundPop + } State = Searching; + + /* Walk over the entries. Look for a push instruction that is followed by + ** a pop later, where the pop is not followed by an conditional branch, + ** and where the value of the A register is not used later on. + ** Look out for the following problems: + ** + ** - There may be another PHP/PLP inside the sequence: Restart it. + ** - All jumps inside the sequence must not go outside the sequence, + ** otherwise it would be too complicated to remove the PHP/PLP. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Searching: + if (E->OPC == OP65_PHP) { + /* Found start of sequence */ + Push = I; + State = FoundPush; + } + break; + + case FoundPush: + if (E->OPC == OP65_PHP) { + /* Inner push/pop, restart */ + Push = I; + } else if (E->OPC == OP65_PLP) { + /* Found a matching pop */ + Pop = I; + /* Check that the block between Push and Pop is a basic + ** block (one entry, one exit). Otherwise ignore it. + */ + if (CS_IsBasicBlock (S, Push, Pop)) { + State = FoundPop; + } else { + /* Go into searching mode again */ + State = Searching; + } + } else if ((E->Info & OF_BRA) == 0 && + (E->Info & OF_STORE) == 0 && + E->OPC != OP65_NOP && + E->OPC != OP65_TSX) { + /* Don't bother skipping dead code */ + State = Searching; + } + break; + + case FoundPop: + /* We can remove the PHP and PLP instructions */ + CS_DelEntry (S, Pop); + CS_DelEntry (S, Push); + + /* Correct I so we continue with THIS insn */ + I -= 3; + + /* Remember we had changes */ + ++Changes; + + /* Go into search mode again */ + State = Searching; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptPrecalc (CodeSeg* S) /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. @@ -2045,36 +1337,70 @@ unsigned OptPrecalc (CodeSeg* S) } break; - case OP65_EOR: - if (RegValIsKnown (Out->RegA)) { - /* Accu op zp with known contents */ - Arg = MakeHexArg (Out->RegA); - } - break; - case OP65_ADC: case OP65_SBC: - /* If this is an operation with an immediate operand of zero, - ** and the register is zero, the operation won't give us any - ** results we don't already have (including the flags), so - ** remove it. Something like this is generated as a result of - ** a compare where parts of the values are known to be zero. - ** The only situation where we need to leave things as they are - ** is when V flag is being tested in the next instruction, - ** because ADC/SBC #0 always clears it. - */ - if (In->RegA == 0 && CE_IsKnownImm (E, 0x00) && - (E = CS_GetEntry (S, I + 1)) && - E->OPC != OP65_BVC && - E->OPC != OP65_BVS ) { - /* 0-0 or 0+0 -> remove */ - CS_DelEntry (S, I); - ++Changes; + if (CE_IsKnownImm (E, 0x00)) { + /* If this is an operation with an immediate operand of zero, + ** and the Z/N flags reflect the current states of the content + ** in A, then the operation won't give us any results we don't + ** already have (including the flags) as long as the C flag is + ** set normally (cleared for ADC and set for SBC) for the + ** operation. So we can remove the operation if it is the + ** normal case or the result in A is not used later. + ** Something like this is generated as a result of a compare + ** where parts of the values are known to be zero. + ** The only situation where we need to leave things as they + ** are is when an indeterminate V flag is being tested later, + ** because ADC/SBC #0 always clears it. + */ + int CondC = PStatesAreKnown (In->PFlags, PSTATE_C) && + ((E->OPC == OP65_ADC && (In->PFlags & PFVAL_C) == 0) || + (E->OPC == OP65_SBC && (In->PFlags & PFVAL_C) != 0)); + int CondV = PStatesAreKnown (In->PFlags, PSTATE_V) && (In->PFlags & PFVAL_V) == 0; + int CondZN = (In->ZNRegs & ZNREG_A) != 0; + unsigned R = 0; + if (CondC) { + R = (CondV ? 0 : PSTATE_V) | (CondZN ? 0 : PSTATE_ZN); + } else { + R = REG_A | PSTATE_CZVN; + } + if (R != 0) { + /* Collect info on all flags in one round to save time */ + R = GetRegInfo (S, I + 1, R); + } + CondV = (CondC && CondV) || (R & PSTATE_V) == 0; + CondZN = (CondC && CondZN) || (R & PSTATE_ZN) == 0; + /* This is done last as it could change the info used by the two above */ + CondC = CondC || (R & (REG_A | PSTATE_C)) == 0; + if (CondC && CondV && CondZN) { + /* ?+0, ?-0 or result unused -> remove */ + CS_DelEntry (S, I); + ++Changes; + } + } else if (E->OPC == OP65_ADC && In->RegA == 0) { + /* 0 + arg. In this case we need only care about the C/V flags and + ** let the load set the Z/N flags properly. + */ + int CondC = PStatesAreClear (In->PFlags, PSTATE_C); + int CondV = PStatesAreClear (In->PFlags, PSTATE_V); + unsigned R = (CondC ? 0 : REG_A | PSTATE_C) | (CondC && CondV ? 0 : PSTATE_V); + if (R) { + R = GetRegInfo (S, I + 1, R); + } + CondV = (CondC && CondV) || (R & PSTATE_V) == 0; + CondC = CondC || (R & (REG_A | PSTATE_C)) == 0; + if (CondC && CondV) { + /* 0 + arg -> replace with lda arg */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } } break; case OP65_AND: - if (CE_IsKnownImm (E, 0xFF)) { + if (CE_IsKnownImm (E, 0xFF) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { /* AND with 0xFF, remove */ CS_DelEntry (S, I); ++Changes; @@ -2092,7 +1418,9 @@ unsigned OptPrecalc (CodeSeg* S) break; case OP65_ORA: - if (CE_IsKnownImm (E, 0x00)) { + if (CE_IsKnownImm (E, 0x00) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { /* ORA with zero, remove */ CS_DelEntry (S, I); ++Changes; @@ -2109,6 +1437,23 @@ unsigned OptPrecalc (CodeSeg* S) } break; + case OP65_EOR: + if (CE_IsKnownImm (E, 0x00) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { + /* EOR with zero, remove */ + CS_DelEntry (S, I); + ++Changes; + } else if (RegValIsKnown (Out->RegA)) { + /* Accu op zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } else if (In->RegA == 0) { + /* EOR but A contains 0x00 - replace by lda */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } + break; + default: break; @@ -2132,60 +1477,45 @@ unsigned OptPrecalc (CodeSeg* S) -/*****************************************************************************/ -/* Optimize branch types */ -/*****************************************************************************/ - - - -unsigned OptBranchDist (CodeSeg* S) -/* Change branches for the distance needed. */ +unsigned OptShiftBack (CodeSeg* S) +/* Remove a pair of shifts to the opposite directions if none of the bits of +** the register A or the Z/N flags modified by these shifts are used later. +*/ { unsigned Changes = 0; + CodeEntry* E; + CodeEntry* N; + unsigned CheckStates; /* Walk over the entries */ unsigned I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); + E = CS_GetEntry (S, I); - /* Check if it's a conditional branch to a local label. */ - if (E->Info & OF_CBRA) { + /* Check if it's a register load or transfer insn */ + if (E->OPC == OP65_ROL && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->OPC == OP65_LSR || + N->OPC == OP65_ROR) && + !CE_HasLabel (N)) { + CheckStates = PSTATE_ZN; + if (N->OPC == OP65_LSR && + !PStatesAreClear (E->RI->Out.PFlags, PSTATE_C)) { + CheckStates |= REG_A; + } + if ((GetRegInfo (S, I+2, CheckStates) & CheckStates) == 0) { - /* Is this a branch to a local symbol? */ - if (E->JumpTo != 0) { + /* Remove the shifts */ + CS_DelEntries (S, I, 2); - /* Check if the branch distance is short */ - int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); - - /* Make the branch short/long according to distance */ - if ((E->Info & OF_LBRA) == 0 && !IsShort) { - /* Short branch but long distance */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - } else if ((E->Info & OF_LBRA) != 0 && IsShort) { - /* Long branch but short distance */ - CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); - ++Changes; - } - - } else if ((E->Info & OF_LBRA) == 0) { - - /* Short branch to external symbol - make it long */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + /* Remember, we had changes */ ++Changes; + /* Continue with next insn */ + continue; } - - } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && - (E->Info & OF_UBRA) != 0 && - E->JumpTo != 0 && - IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { - - /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ - CE_ReplaceOPC (E, OP65_BRA); - ++Changes; } /* Next entry */ @@ -2198,96 +1528,85 @@ unsigned OptBranchDist (CodeSeg* S) } - -/*****************************************************************************/ -/* Optimize indirect loads */ -/*****************************************************************************/ - - - -unsigned OptIndLoads1 (CodeSeg* S) +unsigned OptSignExtended (CodeSeg* S) /* Change ** -** lda (zp),y +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$00 +** bpl L2 +** +** or +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$80 +** bcc/bmi L2 ** ** into +** lda xxx ; X is 0 +** bpl L2 +** dex/ldx #$FF ** -** lda (zp,x) -** -** provided that x and y are both zero. +** provided the C flag isn't used later. */ { unsigned Changes = 0; - unsigned I; + CodeEntry* L[5]; + CodeEntry* X; + unsigned CheckStates; /* Walk over the entries */ - I = 0; + unsigned I = 0; while (I < CS_GetEntryCount (S)) { /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); + L[0] = CS_GetEntry (S, I); - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZP_INDY && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { + /* Check if it's a register load or transfer insn */ + if (L[0]->OPC == OP65_LDA && + CS_GetEntries (S, L+1, I+1, 4) && + !CS_RangeHasLabel (S, I+1, 2) && + CE_GetLabelCount (L[3]) == 1 && + L[1]->JumpTo == CE_GetLabel (L[3], 0) && + (L[1]->Info & OF_CBRA) != 0 && + GetBranchCond (L[1]->OPC) == BC_PL && + RegValIsKnown (L[2]->RI->Out.RegX) && + L[2]->RI->Out.RegX == 0xFF && + L[2]->OPC != OP65_JSR && + (L[2]->Chg & REG_AXY) == REG_X) { - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); + /* We find a sign extention */ + CheckStates = PSTATE_CZN; + if (L[3]->OPC == OP65_CPX && + CE_IsConstImm (L[3]) && + (L[4]->Info & OF_CBRA) != 0 && + ((L[3]->Num == 0x00 && + GetBranchCond (L[4]->OPC) == BC_PL) || + ((L[3]->Num == 0x80 && + GetBranchCond (L[4]->OPC) == BC_CC && + GetBranchCond (L[4]->OPC) == BC_MI)))) { - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptIndLoads2 (CodeSeg* S) -/* Change -** -** lda (zp,x) -** -** into -** -** lda (zp),y -** -** provided that x and y are both zero. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZPX_IND && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { - - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; + /* Check if the processor states set by the CPX are unused later */ + if ((GetRegInfo (S, I+5, CheckStates) & CheckStates) == 0) { + + /* Change the target of the sign extention branch */ + X = NewCodeEntry (OP65_JPL, L[4]->AM, L[4]->Arg, L[4]->JumpTo, L[4]->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I+2); + + /* Remove the old conditional branch */ + CS_DelEntries (S, I+3, 2); + + /* Remember, we had changes */ + ++Changes; + + /* Continue with the current insn */ + continue; + } + } } /* Next entry */ diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 90e27d547..c7ecf4194 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -49,69 +49,15 @@ -unsigned OptRTSJumps1 (CodeSeg* S); -/* Replace jumps to RTS by RTS */ - -unsigned OptRTSJumps2 (CodeSeg* S); -/* Replace long conditional jumps to RTS */ - -unsigned OptDeadJumps (CodeSeg* S); -/* Remove dead jumps (jumps to the next instruction) */ - -unsigned OptDeadCode (CodeSeg* S); -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ - -unsigned OptJumpCascades (CodeSeg* S); -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ - -unsigned OptRTS (CodeSeg* S); -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ - -unsigned OptJumpTarget1 (CodeSeg* S); -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ - -unsigned OptJumpTarget2 (CodeSeg* S); -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ - -unsigned OptJumpTarget3 (CodeSeg* S); -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ - -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -*/ - -unsigned OptCondBranches2 (CodeSeg* S); -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry. -*/ - unsigned OptUnusedLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is not used later. */ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ +unsigned OptLoad3 (CodeSeg* S); +/* Remove repeated loads from one and the same memory location */ + unsigned OptDupLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is already in the register. */ @@ -136,39 +82,45 @@ unsigned OptTransfers4 (CodeSeg* S); ** by a load of the second register if possible. */ -unsigned OptPushPop (CodeSeg* S); -/* Remove a PHA/PLA sequence were A is not used later */ +unsigned OptPushPop1 (CodeSeg* S); +/* Remove a PHA/PLA sequence were A not used later */ + +unsigned OptPushPop2 (CodeSeg* S); +/* Remove a PHP/PLP sequence were no processor flags changed inside */ unsigned OptPrecalc (CodeSeg* S); /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. */ -unsigned OptBranchDist (CodeSeg* S); -/* Change branches for the distance needed. */ - -unsigned OptIndLoads1 (CodeSeg* S); -/* Change -** -** lda (zp),y -** -** into -** -** lda (zp,x) -** -** provided that x and y are both zero. +unsigned OptShiftBack (CodeSeg* S); +/* Remove a pair of shifts to the opposite directions if none of the bits of +** the register A or the Z/N flags modified by these shifts are used later. */ -unsigned OptIndLoads2 (CodeSeg* S); +unsigned OptSignExtended (CodeSeg* S); /* Change ** -** lda (zp,x) +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$00 +** bpl L2 +** +** or +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$80 +** bcc/bmi L2 ** ** into +** lda xxx ; X is 0 +** bpl L2 +** dex/ldx #$FF ** -** lda (zp),y -** -** provided that x and y are both zero. +** provided the C flag isn't used later. */ diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c new file mode 100644 index 000000000..dd092a5ad --- /dev/null +++ b/src/cc65/coptjmp.c @@ -0,0 +1,1009 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.c */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "cpu.h" + +/* cc65 */ +#include "codeent.h" +#include "coptjmp.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) +/* Get the branch distance between the two entries and return it. The distance +** will be negative for backward jumps and positive for forward jumps. +*/ +{ + /* Get the index of the branch target */ + unsigned TI = CS_GetEntryIndex (S, To); + + /* Determine the branch distance */ + int Distance = 0; + if (TI >= From) { + /* Forward branch, do not count the current insn */ + unsigned J = From+1; + while (J < TI) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance += N->Size; + } + } else { + /* Backward branch */ + unsigned J = TI; + while (J < From) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance -= N->Size; + } + } + + /* Return the calculated distance */ + return Distance; +} + + + +static int IsShortDist (int Distance) +/* Return true if the given distance is a short branch distance */ +{ + return (Distance >= -125 && Distance <= 125); +} + + + +static short ZPRegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given zeropage register */ +{ + if ((Use & REG_TMP1) != 0) { + return RC->Tmp1; + } else if ((Use & REG_PTR1_LO) != 0) { + return RC->Ptr1Lo; + } else if ((Use & REG_PTR1_HI) != 0) { + return RC->Ptr1Hi; + } else if ((Use & REG_SREG_LO) != 0) { + return RC->SRegLo; + } else if ((Use & REG_SREG_HI) != 0) { + return RC->SRegHi; + } else { + return UNKNOWN_REGVAL; + } +} + + + +static short RegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given register */ +{ + if ((Use & REG_A) != 0) { + return RC->RegA; + } else if ((Use & REG_X) != 0) { + return RC->RegX; + } else if ((Use & REG_Y) != 0) { + return RC->RegY; + } else { + return ZPRegVal (Use, RC); + } +} + + + +/*****************************************************************************/ +/* Optimize branch types */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S) +/* Change branches for the distance needed. */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a conditional branch to a local label. */ + if (E->Info & OF_CBRA) { + + /* Is this a branch to a local symbol? */ + if (E->JumpTo != 0) { + + /* Check if the branch distance is short */ + int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); + + /* Make the branch short/long according to distance */ + if ((E->Info & OF_LBRA) == 0 && !IsShort) { + /* Short branch but long distance */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + } else if ((E->Info & OF_LBRA) != 0 && IsShort) { + /* Long branch but short distance */ + CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); + ++Changes; + } + + } else if ((E->Info & OF_LBRA) == 0) { + + /* Short branch to external symbol - make it long */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + + } + + } else if ((CPUIsets[CPU] & (CPU_ISET_65SC02 |CPU_ISET_6502DTV)) != 0 && + (E->Info & OF_UBRA) != 0 && + E->JumpTo != 0 && + IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { + + /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ + CE_ReplaceOPC (E, OP65_BRA); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Replace jumps to RTS by RTS */ +/*****************************************************************************/ + + + +unsigned OptRTSJumps1 (CodeSeg* S) +/* Replace jumps to RTS by RTS */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch to a local target */ + if ((E->Info & OF_UBRA) != 0 && + E->JumpTo != 0 && + E->JumpTo->Owner->OPC == OP65_RTS) { + + /* Insert an RTS instruction */ + CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptRTSJumps2 (CodeSeg* S) +/* Replace long conditional jumps to RTS or to a final target */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S) - 1) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an conditional branch to a local target */ + if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ + (E->Info & OF_LBRA) != 0 && /* Long branch */ + E->JumpTo != 0) { /* Local label */ + + + /* Get the jump target and the next entry. There's always a next + ** entry, because we don't cover the last entry in the loop. + */ + CodeEntry* X = 0; + CodeEntry* T = E->JumpTo->Owner; + CodeEntry* N = CS_GetNextEntry (S, I); + + /* Check if it's a jump to an RTS insn */ + if (T->OPC == OP65_RTS) { + + /* It's a jump to RTS. Create a conditional branch around an + ** RTS insn. + */ + X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); + + } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { + + /* It's a jump to a label outside the function. Create a + ** conditional branch around a jump to the external label. + */ + X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); + + } + + /* If we have a replacement insn, insert it */ + if (X) { + + CodeLabel* LN; + opc_t NewBranch; + + /* Insert the new insn */ + CS_InsertEntry (S, X, I+1); + + /* Create a conditional branch with the inverse condition + ** around the replacement insn + */ + + /* Get the new branch opcode */ + NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); + + /* Get the label attached to N, create a new one if needed */ + LN = CS_GenLabel (S, N); + + /* Generate the branch */ + X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the long branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove dead jumps */ +/*****************************************************************************/ + + + +unsigned OptDeadJumps (CodeSeg* S) +/* Remove dead jumps (jumps to the next instruction) */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a branch, if it has a local target, and if the target + ** is the next instruction. + */ + if (E->AM == AM65_BRA && + E->JumpTo && + E->JumpTo->Owner == CS_GetNextEntry (S, I)) { + + /* Delete the dead jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove dead code */ +/*****************************************************************************/ + + + +unsigned OptDeadCode (CodeSeg* S) +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* LN; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch, and if the next entry has + ** no labels attached, or if the label is just used so that the insn + ** can jump to itself. + */ + if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ + (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ + (!CE_HasLabel (N) || /* Don't has a label */ + ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ + (LN = N->JumpTo) != 0 && /* Jumps to known label */ + LN->Owner == N && /* Attached to insn */ + CL_GetRefCount (LN) == 1))) { /* Only reference */ + + /* Delete the next entry */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump cascades */ +/*****************************************************************************/ + + + +unsigned OptJumpCascades (CodeSeg* S) +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* OldLabel; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check: + ** - if it's a branch, + ** - if it has a jump label, + ** - if this jump label is not attached to the instruction itself, + ** - if the target instruction is itself a branch, + ** - if either the first branch is unconditional or the target of + ** the second branch is internal to the function. + ** The latter condition will avoid conditional branches to targets + ** outside of the function (usually incspx), which won't simplify the + ** code, since conditional far branches are emulated by a short branch + ** around a jump. + */ + if ((E->Info & OF_BRA) != 0 && + (OldLabel = E->JumpTo) != 0 && + (N = OldLabel->Owner) != E && + (N->Info & OF_BRA) != 0 && + ((E->Info & OF_CBRA) == 0 || + N->JumpTo != 0)) { + + /* Check if we can use the final target label. That is the case, + ** if the target branch is an absolute branch; or, if it is a + ** conditional branch checking the same condition as the first one. + */ + if ((N->Info & OF_UBRA) != 0 || + ((E->Info & OF_CBRA) != 0 && + GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { + + /* This is a jump cascade and we may jump to the final target, + ** provided that the other insn does not jump to itself. If + ** this is the case, we can also jump to ourselves, otherwise + ** insert a jump to the new instruction and remove the old one. + */ + CodeEntry* X; + CodeLabel* LN = N->JumpTo; + + if (LN != 0 && LN->Owner == N) { + + /* We found a jump to a jump to itself. Replace our jump + ** by a jump to itself. + */ + CodeLabel* LE = CS_GenLabel (S, E); + X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); + + } else { + + /* Jump to the final jump target */ + X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); + + } + + /* Insert it behind E */ + CS_InsertEntry (S, X, I+1); + + /* Remove E */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Check if both are conditional branches, and the condition of + ** the second is the inverse of that of the first. In this case, + ** the second branch will never be taken, and we may jump directly + ** to the instruction behind this one. + */ + } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { + + CodeEntry* X; /* Instruction behind N */ + CodeLabel* LX; /* Label attached to X */ + + /* Get the branch conditions of both branches */ + bc_t BC1 = GetBranchCond (E->OPC); + bc_t BC2 = GetBranchCond (N->OPC); + + /* Check the branch conditions */ + if (BC1 != GetInverseCond (BC2)) { + /* Condition not met */ + goto NextEntry; + } + + /* We may jump behind this conditional branch. Get the + ** pointer to the next instruction + */ + if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { + /* N is the last entry, bail out */ + goto NextEntry; + } + + /* Get the label attached to X, create a new one if needed */ + LX = CS_GenLabel (S, X); + + /* Move the reference from E to the new label */ + CS_MoveLabelRef (S, E, LX); + + /* Remember, we had changes */ + ++Changes; + } + } + +NextEntry: + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jsr/rts */ +/*****************************************************************************/ + + + +unsigned OptRTS (CodeSeg* S) +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a subroutine call and if the following insn is RTS */ + if (E->OPC == OP65_JSR && + (N = CS_GetNextEntry (S, I)) != 0 && + N->OPC == OP65_RTS) { + + /* Change the jsr to a jmp and use the additional info for a jump */ + E->AM = AM65_BRA; + CE_ReplaceOPC (E, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump targets */ +/*****************************************************************************/ + + + +unsigned OptJumpTarget1 (CodeSeg* S) +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ +{ + unsigned Changes = 0; + CodeEntry* E1; /* Entry 1 */ + CodeEntry* E2; /* Entry 2 */ + CodeEntry* T1; /* Jump target entry 1 */ + CodeLabel* TL1; /* Target label 1 */ + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + E2 = CS_GetNextEntry (S, I); + + /* Check if we have a jump or branch without a label attached, and + ** a jump target, which is not attached to the jump itself + */ + if (E2 != 0 && + (E2->Info & OF_UBRA) != 0 && + !CE_HasLabel (E2) && + E2->JumpTo && + E2->JumpTo->Owner != E2) { + + /* Get the entry preceeding the branch target */ + T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); + if (T1 == 0) { + /* There is no such entry */ + goto NextEntry; + } + + /* The entry preceeding the branch target may not be the branch + ** insn. + */ + if (T1 == E2) { + goto NextEntry; + } + + /* Get the entry preceeding the jump */ + E1 = CS_GetEntry (S, I); + + /* Check if both preceeding instructions are identical */ + if (!CodeEntriesAreEqual (E1, T1)) { + /* Not equal, try next */ + goto NextEntry; + } + + /* Get the label for the instruction preceeding the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + TL1 = CS_GenLabel (S, T1); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E2, TL1); + + /* If the instruction preceeding the jump has labels attached, + ** move references to this label to the new label. + */ + if (CE_HasLabel (E1)) { + CS_MoveLabels (S, E1, T1); + } + + /* Remove the entry preceeding the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { +NextEntry: + /* Next entry */ + ++I; + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget2 (CodeSeg* S) +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* OP that may be skipped */ + opc_t OPC; + + /* Jump target insn, old and new */ + CodeEntry* T; + CodeEntry* N; + + /* New jump label */ + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a bcc insn */ + if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { + OPC = OP65_CLC; + } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { + OPC = OP65_SEC; + } else { + /* Not what we're looking for */ + goto NextEntry; + } + + /* Must have a jump target */ + if (E->JumpTo == 0) { + goto NextEntry; + } + + /* Get the owner insn of the jump target and check if it's the one, we + ** will skip if present. + */ + T = E->JumpTo->Owner; + if (T->OPC != OPC) { + goto NextEntry; + } + + /* Get the entry following the branch target */ + N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); + if (N == 0) { + /* There is no such entry */ + goto NextEntry; + } + + /* Get the label for the instruction following the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + L = CS_GenLabel (S, N); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E, L); + + /* Remember that we had changes */ + ++Changes; + +NextEntry: + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget3 (CodeSeg* S) +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a load insn with a label and the next insn is not + ** a conditional branch that needs the flags from the load. + */ + if ((E->Info & OF_LOAD) != 0 && + CE_IsConstImm (E) && + CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + + unsigned J; + int K; + + /* New jump label */ + CodeLabel* LN = 0; + + /* Walk over all insn that jump here */ + for (J = 0; J < CE_GetLabelCount (E); ++J) { + + /* Get the label */ + CodeLabel* L = CE_GetLabel (E, J); + + /* Loop over all insn that reference this label. Since we may + ** eventually remove a reference in the loop, we must loop + ** from end down to start. + */ + for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { + + /* Get the entry that jumps here */ + CodeEntry* Jump = CL_GetRef (L, K); + + /* Get the register info from this insn */ + short Val = RegVal (E->Chg, &Jump->RI->Out2); + + /* Check if the outgoing value is the one thats's loaded */ + if (Val == (unsigned char) E->Num) { + + /* OK, skip the insn. First, generate a label for the + ** next insn after E. + */ + if (LN == 0) { + LN = CS_GenLabel (S, N); + } + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, Jump, LN); + + /* Remember that we had changes */ + ++Changes; + } + } + } + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize conditional branches */ +/*****************************************************************************/ + + + +unsigned OptCondBranches1 (CodeSeg* S) +/* Performs several optimization steps: +** +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. +** - If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the +** inverse condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a register load */ + if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ + E->AM == AM65_IMM && /* ..with immidiate addressing */ + CE_HasNumArg (E) && /* ..and a numeric argument. */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Get the branch condition */ + bc_t BC = GetBranchCond (N->OPC); + + /* Check the argument against the branch condition */ + if ((BC == BC_EQ && E->Num != 0) || + (BC == BC_NE && E->Num == 0) || + (BC == BC_PL && (E->Num & 0x80) != 0) || + (BC == BC_MI && (E->Num & 0x80) == 0)) { + + /* Remove the conditional branch */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } else if ((BC == BC_EQ && E->Num == 0) || + (BC == BC_NE && E->Num != 0) || + (BC == BC_PL && (E->Num & 0x80) == 0) || + (BC == BC_MI && (E->Num & 0x80) != 0)) { + + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (L = E->JumpTo) != 0 && /* ..referencing a local label */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ + !CE_HasLabel (N) && /* ..has no label attached */ + L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ + + /* Replace the jump by a conditional branch with the inverse branch + ** condition than the branch around it. + */ + CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); + + /* Remove the conditional branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranches2 (CodeSeg* S) +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry flag. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a rol insn with A in accu and a branch follows */ + if (E->OPC == OP65_ROL && + E->AM == AM65_ACC && + E->RI->In.RegA == 0 && + !CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !RegAUsed (S, I+1)) { + + /* Replace the branch condition */ + switch (GetBranchCond (N->OPC)) { + case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; + case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; + default: Internal ("Unknown branch condition in OptCondBranches2"); + } + + /* Delete the rol insn */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h new file mode 100644 index 000000000..4cb7a2792 --- /dev/null +++ b/src/cc65/coptjmp.h @@ -0,0 +1,116 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.h */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTJMP_H +#define COPTJMP_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S); +/* Change branches for the distance needed. */ + +unsigned OptRTSJumps1 (CodeSeg* S); +/* Replace jumps to RTS by RTS */ + +unsigned OptRTSJumps2 (CodeSeg* S); +/* Replace long conditional jumps to RTS */ + +unsigned OptDeadJumps (CodeSeg* S); +/* Remove dead jumps (jumps to the next instruction) */ + +unsigned OptDeadCode (CodeSeg* S); +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ + +unsigned OptJumpCascades (CodeSeg* S); +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ + +unsigned OptRTS (CodeSeg* S); +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ + +unsigned OptJumpTarget1 (CodeSeg* S); +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ + +unsigned OptJumpTarget2 (CodeSeg* S); +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ + +unsigned OptJumpTarget3 (CodeSeg* S); +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ + +unsigned OptCondBranches1 (CodeSeg* S); +/* If an immidiate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +*/ + +unsigned OptCondBranches2 (CodeSeg* S); +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry. +*/ + + + +/* End of coptjmp.h */ + +#endif diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c new file mode 100644 index 000000000..523fbf17c --- /dev/null +++ b/src/cc65/coptmisc.c @@ -0,0 +1,735 @@ +/*****************************************************************************/ +/* */ +/* codemisc.c */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include <stdlib.h> + +/* common */ +#include "chartype.h" +#include "xsprintf.h" + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptmisc.h" +#include "error.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Decouple operations */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S) +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + const char* Arg; + + /* Get next entry and it's input register values */ + CodeEntry* E = CS_GetEntry (S, I); + const RegContents* In = &E->RI->In; + + /* Assume we have no replacement */ + CodeEntry* X = 0; + + /* Check the instruction */ + switch (E->OPC) { + + case OP65_DEA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA - 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX - 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY - 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA + 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX + 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY + 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_LDA: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDX: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDY: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_TAX: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TAY: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TXA: + if (E->RI->In.RegX >= 0) { + Arg = MakeHexArg (In->RegX); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TYA: + if (E->RI->In.RegY >= 0) { + Arg = MakeHexArg (In->RegY); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + default: + /* Avoid gcc warnings */ + break; + + } + + /* Insert the replacement if we have one */ + if (X) { + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads1 (CodeSeg* S) +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZP_INDY && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads2 (CodeSeg* S) +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZPX_IND && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack pointer ops */ +/*****************************************************************************/ + + + +static unsigned IsDecSP (const CodeEntry* E) +/* Check if this is an insn that decrements the stack pointer. If so, return +** the decrement. If not, return zero. +** The function expects E to be a subroutine call. +*/ +{ + if (strncmp (E->Arg, "decsp", 5) == 0) { + if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { + return (E->Arg[5] - '0'); + } + } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { + return E->RI->In.RegY; + } + + /* If we come here, it's not a decsp op */ + return 0; +} + + + +unsigned OptStackPtrOps (CodeSeg* S) +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + unsigned Dec1; + unsigned Dec2; + const CodeEntry* N; + + /* Get the next entry */ + const CodeEntry* E = CS_GetEntry (S, I); + + /* Check for decspn or subysp */ + if (E->OPC == OP65_JSR && + (Dec1 = IsDecSP (E)) > 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + (Dec2 = IsDecSP (N)) > 0 && + (Dec1 += Dec2) <= 255 && + !CE_HasLabel (N)) { + + CodeEntry* X; + char Buf[20]; + + /* We can combine the two */ + if (Dec1 <= 8) { + /* Insert a call to decsp */ + xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); + CS_InsertEntry (S, X, I+2); + } else { + /* Insert a call to subysp */ + const char* Arg = MakeHexArg (Dec1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); + CS_InsertEntry (S, X, I+2); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); + CS_InsertEntry (S, X, I+3); + } + + /* Delete the old code */ + CS_DelEntries (S, I, 2); + + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptGotoSPAdj (CodeSeg* S) +/* Optimize SP adjustment for forward 'goto' */ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[10], *X; + unsigned short adjustment; + const char* Arg; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence generated by g_lateadjustSP */ + if (L[0]->OPC == OP65_PHA && + CS_GetEntries (S, L+1, I+1, 9) && + L[1]->OPC == OP65_LDA && + L[1]->AM == AM65_ABS && + L[2]->OPC == OP65_CLC && + L[3]->OPC == OP65_ADC && + strcmp (L[3]->Arg, "sp") == 0 && + L[6]->OPC == OP65_ADC && + strcmp (L[6]->Arg, "sp+1") == 0 && + L[9]->OPC == OP65_JMP) { + adjustment = FindSPAdjustment (L[1]->Arg); + + if (adjustment == 0) { + /* No SP adjustment needed, remove the whole sequence */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 8) { + /* If adjustment is in range [-8, 0) we use decsp* calls */ + char Buf[20]; + adjustment = 65536 - adjustment; + xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 255) { + /* For range [-255, -8) we have ldy #, jsr subysp */ + adjustment = 65536 - adjustment; + Arg = MakeHexArg (adjustment); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment > 255) { + /* For ranges [-32768, 255) and (255, 32767) the only modification + ** is to replace the absolute with immediate addressing + */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 1); + Arg = MakeHexArg (adjustment >> 8); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); + CS_InsertEntry (S, X, I + 6); + + /* Delete the old code */ + CS_DelEntry (S, I + 2); + CS_DelEntry (S, I + 6); + } + else if (adjustment > 8) { + /* For range (8, 255] we have ldy #, jsr addysp */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else { + /* If adjustment is in range (0, 8] we use incsp* calls */ + char Buf[20]; + xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + Changes++; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack load ops */ +/*****************************************************************************/ + + + +unsigned OptLoad1 (CodeSeg* S) +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (E, "ldaxysp") && + RegValIsKnown (E->RI->In.RegY) && + !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Reload the Y register */ + const char* Arg = MakeHexArg (E->RI->In.RegY - 1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Load from stack */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptLoad2 (CodeSeg* S) +/* Replace calls to ldaxysp by inline code */ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (L[0], "ldaxysp")) { + + CodeEntry* X; + + /* Followed by sta abs/stx abs? */ + if (CS_GetEntries (S, L+1, I+1, 2) && + L[1]->OPC == OP65_STA && + L[2]->OPC == OP65_STX && + (L[1]->Arg == 0 || + L[2]->Arg == 0 || + strcmp (L[1]->Arg, L[2]->Arg) != 0) && + !CS_RangeHasLabel (S, I+1, 2) && + !RegXUsed (S, I+3)) { + + /* A/X are stored into memory somewhere and X is not used + ** later + */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); + CS_InsertEntry (S, X, I+4); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+5); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+6); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I+7); + + /* Now remove the call to the subroutine and the sta/stx */ + CS_DelEntries (S, I, 3); + + } else { + + /* Standard replacement */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+1); + + /* tax */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+4); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + } + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h new file mode 100644 index 000000000..89242351c --- /dev/null +++ b/src/cc65/coptmisc.h @@ -0,0 +1,113 @@ +/*****************************************************************************/ +/* */ +/* codemisc.h */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTMISC_H +#define COPTMISC_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S); +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ + +unsigned OptIndLoads1 (CodeSeg* S); +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ + +unsigned OptIndLoads2 (CodeSeg* S); +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ + +unsigned OptStackPtrOps (CodeSeg* S); +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ + +unsigned OptGotoSPAdj (CodeSeg* S); +/* Optimize SP adjustment for forward 'goto' */ + +unsigned OptLoad1 (CodeSeg* S); +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ + +unsigned OptLoad2 (CodeSeg* S); +/* Replace calls to ldaxysp by inline code */ + + +/* End of coptmisc.h */ + +#endif diff --git a/src/cc65/coptneg.c b/src/cc65/coptneg.c index 03b39eb42..27171c68d 100644 --- a/src/cc65/coptneg.c +++ b/src/cc65/coptneg.c @@ -68,15 +68,15 @@ unsigned OptBNegA1 (CodeSeg* S) CodeEntry* E = CS_GetEntry (S, I); /* Check for a ldx */ - if (E->OPC == OP65_LDX && - E->AM == AM65_IMM && - (E->Flags & CEF_NUMARG) != 0 && - E->Num == 0 && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_LDA && - (L[0]->Use & REG_X) == 0 && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnega") && + if (E->OPC == OP65_LDX && + E->AM == AM65_IMM && + CE_HasNumArg (E) && + E->Num == 0 && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_LDA && + (L[0]->Use & REG_X) == 0 && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnega") && !CE_HasLabel (L[1])) { /* Remove the ldx instruction */ @@ -480,7 +480,7 @@ unsigned OptNegAX2 (CodeSeg* S) ** eor #$FF ** clc ** adc #$01 -** bne L1 +** bcc L1 ** inx ** L1: ** @@ -528,8 +528,8 @@ unsigned OptNegAX2 (CodeSeg* S) /* Get the label attached to the insn following the call */ L = CS_GenLabel (S, P); - /* bne L */ - X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, E->LI); + /* bcc L */ + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+5); /* inx */ diff --git a/src/cc65/coptneg.h b/src/cc65/coptneg.h index f549fc553..844d8b886 100644 --- a/src/cc65/coptneg.h +++ b/src/cc65/coptneg.h @@ -155,7 +155,7 @@ unsigned OptNegAX2 (CodeSeg* S); ** eor #$FF ** clc ** adc #$01 -** bne L1 +** bcc L1 ** inx ** L1: ** diff --git a/src/cc65/coptptrload.c b/src/cc65/coptptrload.c index 0534a1fa2..046e65d79 100644 --- a/src/cc65/coptptrload.c +++ b/src/cc65/coptptrload.c @@ -988,7 +988,7 @@ unsigned OptPtrLoad12 (CodeSeg* S) L[4]->OPC == OP65_CLC && L[5]->OPC == OP65_ADC && CE_IsKnownImm (L[5], 1) && - L[6]->OPC == OP65_BCC && + (L[6]->OPC == OP65_BCC || L[6]->OPC == OP65_JCC) && L[6]->JumpTo != 0 && L[6]->JumpTo->Owner == L[8] && L[7]->OPC == OP65_INX && diff --git a/src/cc65/coptptrload.h b/src/cc65/coptptrload.h index 84d7cc19f..d4e0e2ed4 100644 --- a/src/cc65/coptptrload.h +++ b/src/cc65/coptptrload.h @@ -233,7 +233,7 @@ unsigned OptPtrLoad11 (CodeSeg* S); */ unsigned OptPtrLoad12 (CodeSeg* S); -/* Search for the sequence: +/* Search for the sequence: ** ** lda regbank+n ** ldx regbank+n+1 diff --git a/src/cc65/coptpush.c b/src/cc65/coptpush.c index 5c3daffca..34d794c64 100644 --- a/src/cc65/coptpush.c +++ b/src/cc65/coptpush.c @@ -56,12 +56,14 @@ unsigned OptPush1 (CodeSeg* S) ** ** ldy #xx+2 ** jsr pushwysp +** ldy #$00 ; present if later code expects Y = 0 ** -** saving 3 bytes and several cycles. +** saving several cycles. */ { unsigned I; unsigned Changes = 0; + unsigned R; /* Walk over the entries */ I = 0; @@ -79,7 +81,7 @@ unsigned OptPush1 (CodeSeg* S) (L[1] = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (L[1]) && CE_IsCallTo (L[1], "pushax") && - !RegAXUsed (S, I+2)) { + ((R = (GetRegInfo (S, I+2, REG_AXY))) & REG_AX) == 0) { /* Insert new code behind the pushax */ const char* Arg; @@ -94,12 +96,25 @@ unsigned OptPush1 (CodeSeg* S) X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushwysp", 0, L[1]->LI); CS_InsertEntry (S, X, I+3); + /* pushax sets Y = 0 and following code might rely on this */ + if ((R & REG_Y) != 0) { + /* ldy #0 */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (0), 0, L[1]->LI); + CS_InsertEntry (S, X, I+4); + } + /* Delete the old code */ CS_DelEntries (S, I, 2); /* Remember, we had changes */ ++Changes; + /* Skip the handled lines */ + if ((R & REG_Y) == 0) { + ++I; + } else { + I += 2; + } } /* Next entry */ diff --git a/src/cc65/coptpush.h b/src/cc65/coptpush.h index 0d637e824..aa5548517 100644 --- a/src/cc65/coptpush.h +++ b/src/cc65/coptpush.h @@ -52,16 +52,16 @@ unsigned OptPush1 (CodeSeg* S); /* Given a sequence ** -** ldy #xx ** jsr ldaxysp ** jsr pushax ** -** If a/x are not used later, replace that by +** If a/x are not used later, and Y is known, replace that by ** ** ldy #xx+2 ** jsr pushwysp +** ldy #$00 ; present if later code expects Y = 0 ** -** saving 3 bytes and several cycles. +** saving several cycles. */ unsigned OptPush2 (CodeSeg* S); diff --git a/src/cc65/coptshift.c b/src/cc65/coptshift.c index 92210ebb5..aef3f64bc 100644 --- a/src/cc65/coptshift.c +++ b/src/cc65/coptshift.c @@ -341,7 +341,10 @@ unsigned OptShift2 (CodeSeg* S) L[0] = CS_GetEntry (S, I); /* Check for the sequence */ - if ((L[0]->OPC == OP65_BPL || L[0]->OPC == OP65_BCC) && + if ((L[0]->OPC == OP65_BPL || + L[0]->OPC == OP65_BCC || + L[0]->OPC == OP65_JPL || + L[0]->OPC == OP65_JCC) && L[0]->JumpTo != 0 && CS_GetEntries (S, L+1, I+1, 3) && L[1]->OPC == OP65_DEX && diff --git a/src/cc65/coptsize.c b/src/cc65/coptsize.c index 5c23e637c..a5da4736d 100644 --- a/src/cc65/coptsize.c +++ b/src/cc65/coptsize.c @@ -76,730 +76,730 @@ static const CallDesc CallTable [] = { { "addeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "addeq0sp" },{ "laddeq", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeq1" },{ "laddeq", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeqa" },{ "laddeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeq0sp" },{ "ldaxidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldaxi" },{ "ldaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldax0sp" },{ "ldeaxidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldeaxi" },{ "ldeaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldeax0sp" },{ "leaaxsp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "leaa0sp" },{ "lsubeq", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeq1" },{ "lsubeq", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeqa" },{ "lsubeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeq0sp" },{ "pusha", { - /* A X Y SRegLo */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc0" },{ "pusha", { - /* A X Y SRegLo */ - 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc1" },{ "pusha", { - /* A X Y SRegLo */ - 2, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 2, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc2" },{ "pushax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "push0" },{ "pushax", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push1" },{ "pushax", { - /* A X Y SRegLo */ - 2, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 2, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push2" },{ "pushax", { - /* A X Y SRegLo */ - 3, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 3, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push3" },{ "pushax", { - /* A X Y SRegLo */ - 4, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 4, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push4" },{ "pushax", { - /* A X Y SRegLo */ - 5, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 5, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push5" },{ "pushax", { - /* A X Y SRegLo */ - 6, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 6, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push6" },{ "pushax", { - /* A X Y SRegLo */ - 7, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 7, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push7" },{ "pushax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pusha0" },{ "pushax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0xFF, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0xFF, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushaFF" },{ "pushaysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pusha0sp" },{ "pusheax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushl0" },{ "pusheax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "push0ax" },{ "pushwidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushw" },{ "pushwysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushw0sp" },{ "staxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "stax0sp" },{ "steaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "steax0sp" },{ "subeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "subeq0sp" },{ "tosaddax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosadda0" },{ "tosaddeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosadd0ax" },{ "tosandax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosanda0" },{ "tosandeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosand0ax" },{ "tosdivax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosdiva0" },{ "tosdiveax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosdiv0ax" },{ "toseqax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toseq00" },{ "toseqax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toseqa0" },{ "tosgeax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosge00" },{ "tosgeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgea0" },{ "tosgtax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgt00" },{ "tosgtax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgta0" },{ "tosicmp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosicmp0" },{ "tosleax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosle00" },{ "tosleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslea0" },{ "tosltax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslt00" },{ "tosltax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslta0" },{ "tosmodax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmoda0" },{ "tosmodeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmod0ax" },{ "tosmulax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmula0" },{ "tosmuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmul0ax" },{ "tosneax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosnea0" },{ "tosorax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosora0" },{ "tosoreax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosor0ax" },{ "tosrsubax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosrsuba0" },{ "tosrsubeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosrsub0ax" },{ "tossubax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tossuba0" },{ "tossubeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tossub0ax" },{ "tosudivax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosudiva0" },{ "tosudiveax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosudiv0ax" },{ "tosugeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosugea0" },{ "tosugtax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosugta0" },{ "tosuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosulea0" },{ "tosultax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosulta0" },{ "tosumodax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumoda0" },{ "tosumodeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumod0ax" },{ "tosumulax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumula0" },{ "tosumuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumul0ax" },{ "tosxorax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosxora0" },{ "tosxoreax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosxor0ax" diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index b14b23195..08f6c820e 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -41,53 +41,12 @@ /* cc65 */ #include "codeent.h" #include "codeinfo.h" +#include "codeoptutil.h" #include "coptstop.h" #include "error.h" -/*****************************************************************************/ -/* Load tracking data */ -/*****************************************************************************/ - - - -/* LoadRegInfo flags set by DirectOp */ -typedef enum { - LI_NONE = 0x00, - LI_DIRECT = 0x01, /* Direct op may be used */ - LI_RELOAD_Y = 0x02, /* Reload index register Y */ - LI_REMOVE = 0x04, /* Load may be removed */ - LI_DONT_REMOVE = 0x08, /* Load may not be removed */ - LI_MAYBE_DIRECT = 0x10, /* Load src might be modified later */ - LI_SRC_CHG = 0x20, /* Load src is possibly modified */ - LI_LOAD_INSN = 0x40, /* Has a load insn */ - LI_USED_BY_A = 0x100, /* Content used by RegA */ - LI_USED_BY_X = 0x200, /* Content used by RegX */ - LI_USED_BY_Y = 0x400, /* Content used by RegY */ -} LI_FLAGS; - -/* Structure that tells us how to load the lhs values */ -typedef struct LoadRegInfo LoadRegInfo; -struct LoadRegInfo { - LI_FLAGS Flags; /* Tells us how to load */ - int LoadIndex; /* Index of load insn, -1 if invalid */ - CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ - int XferIndex; /* Index of transfer insn */ - CodeEntry* XferEntry; /* The actual transfer entry */ - int Offs; /* Stack offset if data is on stack */ -}; - -/* Now combined for both registers */ -typedef struct LoadInfo LoadInfo; -struct LoadInfo { - LoadRegInfo A; /* Info for A register */ - LoadRegInfo X; /* Info for X register */ - LoadRegInfo Y; /* Info for Y register */ -}; - - - /*****************************************************************************/ /* Data */ /*****************************************************************************/ @@ -117,9 +76,6 @@ typedef enum { OP_RHS_REMOVE_DIRECT = 0xC000, /* RHS must be directly removable */ } OP_FLAGS; -/* Structure forward decl */ -typedef struct StackOpData StackOpData; - /* Structure that describes an optimizer subfunction for a specific op */ typedef unsigned (*OptFunc) (StackOpData* D); typedef struct OptFuncDesc OptFuncDesc; @@ -130,380 +86,6 @@ struct OptFuncDesc { OP_FLAGS Flags; /* Flags */ }; -/* Structure that holds the needed data */ -struct StackOpData { - CodeSeg* Code; /* Pointer to code segment */ - unsigned Flags; /* Flags to remember things */ - - /* Pointer to optimizer subfunction description */ - const OptFuncDesc* OptFunc; - - /* ZP register usage inside the sequence */ - unsigned ZPUsage; - unsigned ZPChanged; - - /* Freedom of registers inside the sequence */ - unsigned UsedRegs; /* Registers used */ - - /* Register load information for lhs and rhs */ - LoadInfo Lhs; - LoadInfo Rhs; - - /* Several indices of insns in the code segment */ - int PushIndex; /* Index of call to pushax in codeseg */ - int OpIndex; /* Index of actual operation */ - - /* Pointers to insns in the code segment */ - CodeEntry* PrevEntry; /* Entry before the call to pushax */ - CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ - CodeEntry* OpEntry; /* Pointer to entry with op */ - CodeEntry* NextEntry; /* Entry after the op */ - - const char* ZPLo; /* Lo byte of zero page loc to use */ - const char* ZPHi; /* Hi byte of zero page loc to use */ - unsigned IP; /* Insertion point used by some routines */ -}; - - - -/*****************************************************************************/ -/* Load tracking code */ -/*****************************************************************************/ - - - -static void ClearLoadRegInfo (LoadRegInfo* RI) -/* Clear a LoadRegInfo struct */ -{ - RI->Flags = LI_NONE; - RI->LoadIndex = -1; - RI->LoadEntry = 0; - RI->XferIndex = -1; - RI->XferEntry = 0; - RI->Offs = 0; -} - - - -static void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) -/* Copy a LoadRegInfo struct */ -{ - To->Flags = From->Flags; - To->LoadIndex = From->LoadIndex; - To->LoadEntry = From->LoadEntry; - To->XferIndex = From->XferIndex; - To->XferEntry = From->XferEntry; - To->Offs = From->Offs; -} - - - -static void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S) -/* Prepare a LoadRegInfo struct for use */ -{ - /* Get the entries */ - if (RI->LoadIndex >= 0) { - RI->LoadEntry = CS_GetEntry (S, RI->LoadIndex); - } else { - RI->LoadEntry = 0; - } - if (RI->XferIndex >= 0) { - RI->XferEntry = CS_GetEntry (S, RI->XferIndex); - } else { - RI->XferEntry = 0; - } - /* Load from src not modified before op can be treated as direct */ - if ((RI->Flags & (LI_MAYBE_DIRECT | LI_SRC_CHG)) == LI_MAYBE_DIRECT) { - RI->Flags |= LI_DIRECT; - } -} - - - -static void ClearLoadInfo (LoadInfo* LI) -/* Clear a LoadInfo struct */ -{ - ClearLoadRegInfo (&LI->A); - ClearLoadRegInfo (&LI->X); - ClearLoadRegInfo (&LI->Y); -} - - - -static void CopyLoadInfo (LoadInfo* To, LoadInfo* From) -/* Copy a LoadInfo struct */ -{ - CopyLoadRegInfo (&To->A, &From->A); - CopyLoadRegInfo (&To->X, &From->X); - CopyLoadRegInfo (&To->Y, &From->Y); -} - - - -static void AdjustLoadRegInfo (LoadRegInfo* RI, int Index, int Change) -/* Adjust a load register info struct after deleting or inserting an entry -** with a given index -*/ -{ - CHECK (abs (Change) == 1); - if (Change < 0) { - /* Deletion */ - if (Index < RI->LoadIndex) { - --RI->LoadIndex; - } else if (Index == RI->LoadIndex) { - /* Has been removed */ - RI->LoadIndex = -1; - RI->LoadEntry = 0; - } - if (Index < RI->XferIndex) { - --RI->XferIndex; - } else if (Index == RI->XferIndex) { - /* Has been removed */ - RI->XferIndex = -1; - RI->XferEntry = 0; - } - } else { - /* Insertion */ - if (Index <= RI->LoadIndex) { - ++RI->LoadIndex; - } - if (Index <= RI->XferIndex) { - ++RI->XferIndex; - } - } -} - - - -static void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S) -/* Prepare a LoadInfo struct for use */ -{ - /* Get the entries */ - FinalizeLoadRegInfo (&LI->A, S); - FinalizeLoadRegInfo (&LI->X, S); - FinalizeLoadRegInfo (&LI->Y, S); -} - - - -static void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) -/* Adjust a load info struct after deleting entry with a given index */ -{ - AdjustLoadRegInfo (&LI->A, Index, Change); - AdjustLoadRegInfo (&LI->X, Index, Change); - AdjustLoadRegInfo (&LI->Y, Index, Change); -} - - - -static int Affected (LoadRegInfo* RI, const CodeEntry* E) -/* Check if the load src may be modified between the pushax and op */ -{ - fncls_t fncls; - unsigned short Use; - unsigned short Chg; - - if (RI->Flags & LI_MAYBE_DIRECT) { - if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { - return 0; - } - CHECK (RI->LoadEntry != 0); - - if (E->OPC == OP65_JSR) { - /* Try to know about the function */ - fncls = GetFuncInfo (E->Arg, &Use, &Chg); - if ((RI->LoadEntry->Use & Chg & REG_ALL) == 0 && - fncls == FNCLS_BUILTIN) { - /* Builtin functions are known to be harmless */ - return 0; - } - /* Otherwise play it safe */ - return 1; - } else if (E->OPC == OP65_DEC || E->OPC == OP65_INC || - E->OPC == OP65_ASL || E->OPC == OP65_LSR || - E->OPC == OP65_ROL || E->OPC == OP65_ROR || - E->OPC == OP65_TRB || E->OPC == OP65_TSB || - E->OPC == OP65_STA || E->OPC == OP65_STX || E->OPC == OP65_STY) { - if ((E->AM == AM65_ABS || E->AM == AM65_ZP) && - strcmp (RI->LoadEntry->Arg, E->Arg) != 0) { - return 0; - } - /* We could've check further for more cases where the load target isn't modified, - ** But for now let's save the trouble and just play it safe. */ - return 1; - } - } - return 0; -} - - - -static void HonourUseAndChg (LoadRegInfo* RI, unsigned Reg, const CodeEntry* E, int I) -/* Honour use and change flags for an instruction */ -{ - if ((E->Chg & Reg) != 0) { - /* Remember this as an indirect load */ - ClearLoadRegInfo (RI); - RI->LoadIndex = I; - RI->XferIndex = -1; - RI->Flags = 0; - } else if (Affected (RI, E)) { - RI->Flags |= LI_SRC_CHG; - } -} - - - -static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) -/* Track loads for a code entry. -** Return used registers. -*/ -{ - unsigned Used; - CodeEntry* E = CS_GetEntry (S, I); - CHECK (E != 0); - - /* By default */ - Used = E->Use; - - /* Whether we had a load or xfer op before or not, the newly loaded value - ** will be the real one used for the pushax/op unless it's overwritten, - ** so we can just reset the flags about it in such cases. - */ - if (E->Info & OF_LOAD) { - - LoadRegInfo* RI = 0; - - /* Determine, which register was loaded */ - if (E->Chg & REG_A) { - RI = &LI->A; - } else if (E->Chg & REG_X) { - RI = &LI->X; - } else if (E->Chg & REG_Y) { - RI = &LI->Y; - } - CHECK (RI != 0); - - /* Remember the load */ - RI->LoadIndex = I; - RI->XferIndex = -1; - - /* Set load flags */ - RI->Flags = LI_LOAD_INSN; - if (E->AM == AM65_IMM) { - /* These insns are all ok and replaceable */ - RI->Flags |= LI_DIRECT; - } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { - /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_MAYBE_DIRECT; - /* Watch for any change of the load target */ - RI->LoadEntry = CS_GetEntry (S, I); - } else if (E->AM == AM65_ZP_INDY && - RegValIsKnown (E->RI->In.RegY) && - strcmp (E->Arg, "sp") == 0) { - /* A load from the stack with known offset is also ok, but in this - ** case we must reload the index register later. Please note that - ** a load indirect via other zero page locations is not ok, since - ** these locations may change between the push and the actual - ** operation. - */ - RI->Offs = (unsigned char) E->RI->In.RegY; - RI->Flags |= (LI_DIRECT | LI_RELOAD_Y); - - /* Reg Y can be regarded as unused if this load is removed */ - Used &= ~REG_Y; - LI->Y.Flags |= LI_USED_BY_A; - } - - /* Watch for any change of the load target */ - if ((RI->Flags & LI_MAYBE_DIRECT) != 0) { - RI->LoadEntry = CS_GetEntry (S, I); - } - - } else if (E->Info & OF_XFR) { - - /* Determine source and target of the transfer and handle the TSX insn */ - LoadRegInfo* Src; - LoadRegInfo* Tgt; - switch (E->OPC) { - case OP65_TAX: - Src = &LI->A; - Tgt = &LI->X; - Used &= ~REG_A; - Src->Flags |= LI_USED_BY_X; - break; - case OP65_TAY: - Src = &LI->A; - Tgt = &LI->Y; - Used &= ~REG_A; - Src->Flags |= LI_USED_BY_Y; - break; - case OP65_TXA: - Src = &LI->X; - Tgt = &LI->A; - Used &= ~REG_X; - Src->Flags |= LI_USED_BY_A; - break; - case OP65_TYA: - Src = &LI->Y; - Tgt = &LI->A; - Used &= ~REG_Y; - Src->Flags |= LI_USED_BY_A; - break; - case OP65_TSX: - ClearLoadRegInfo (&LI->X); - return Used; - case OP65_TXS: - return Used; - default: Internal ("Unknown XFR insn in TrackLoads"); - } - - /* Transfer the data */ - Tgt->LoadIndex = Src->LoadIndex; - Tgt->LoadEntry = Src->LoadEntry; - Tgt->XferIndex = I; - Tgt->Offs = Src->Offs; - Tgt->Flags = Src->Flags; - - } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) { - - /* Both registers set, Y changed */ - LI->A.LoadIndex = I; - LI->A.XferIndex = -1; - LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y); - LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; - - LI->X.LoadIndex = I; - LI->X.XferIndex = -1; - LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y); - LI->X.Offs = (unsigned char) E->RI->In.RegY; - - /* Reg Y can be regarded as unused if this load is removed */ - Used &= ~REG_Y; - LI->Y.Flags |= LI_USED_BY_A | LI_USED_BY_X; - - } else { - HonourUseAndChg (&LI->A, REG_A, E, I); - HonourUseAndChg (&LI->X, REG_X, E, I); - HonourUseAndChg (&LI->Y, REG_Y, E, I); - - /* The other operand may be affected too */ - if (LLI != 0) { - if (Affected (&LLI->A, E)) { - LLI->A.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->X, E)) { - LLI->X.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->Y, E)) { - LLI->Y.Flags |= LI_SRC_CHG; - } - } - } - - return Used; -} - /*****************************************************************************/ @@ -512,385 +94,49 @@ static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) -static void InsertEntry (StackOpData* D, CodeEntry* E, int Index) -/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will -** be adjusted by this function. -*/ +static int SameRegAValue (StackOpData* D) +/* Check if Rhs Reg A == Lhs Reg A */ { - /* Insert the entry into the code segment */ - CS_InsertEntry (D->Code, E, Index); + RegInfo* LRI = GetLastChangedRegInfo (D, &D->Lhs.A); + RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.A); - /* Adjust register loads if necessary */ - AdjustLoadInfo (&D->Lhs, Index, 1); - AdjustLoadInfo (&D->Rhs, Index, 1); + /* RHS can have a -1 ChgIndex only if it is carried over from LHS */ + if (RRI == 0 || + (D->Rhs.A.ChgIndex >= 0 && + D->Rhs.A.ChgIndex == D->Lhs.A.ChgIndex) || + (LRI != 0 && + RegValIsKnown (LRI->Out.RegA) && + RegValIsKnown (RRI->Out.RegA) && + (LRI->Out.RegA & 0xFF) == (RRI->Out.RegA & 0xFF))) { - /* Adjust the indices if necessary */ - if (D->PushEntry && Index <= D->PushIndex) { - ++D->PushIndex; - } - if (D->OpEntry && Index <= D->OpIndex) { - ++D->OpIndex; + return 1; } + + return 0; + } -static void DelEntry (StackOpData* D, int Index) -/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be -** adjusted by this function, and PushEntry/OpEntry may get invalidated. -*/ +static int SameRegXValue (StackOpData* D) +/* Check if Rhs Reg X == Lhs Reg X */ { - /* Delete the entry from the code segment */ - CS_DelEntry (D->Code, Index); + RegInfo* LRI = GetLastChangedRegInfo (D, &D->Lhs.X); + RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.X); - /* Adjust register loads if necessary */ - AdjustLoadInfo (&D->Lhs, Index, -1); - AdjustLoadInfo (&D->Rhs, Index, -1); + if (RRI == 0 || + (D->Rhs.X.ChgIndex >= 0 && + D->Rhs.X.ChgIndex == D->Lhs.X.ChgIndex) || + (LRI != 0 && + RegValIsKnown (LRI->Out.RegX) && + RegValIsKnown (RRI->Out.RegX) && + (LRI->Out.RegX & 0xFF) == (RRI->Out.RegX & 0xFF))) { - /* Adjust the other indices if necessary */ - if (Index < D->PushIndex) { - --D->PushIndex; - } else if (Index == D->PushIndex) { - D->PushEntry = 0; - } - if (Index < D->OpIndex) { - --D->OpIndex; - } else if (Index == D->OpIndex) { - D->OpEntry = 0; - } -} - - - -static void AdjustStackOffset (StackOpData* D, unsigned Offs) -/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. -** OpIndex is adjusted according to the insertions. -*/ -{ - /* Walk over all entries */ - int I = D->PushIndex + 1; - while (I < D->OpIndex) { - - CodeEntry* E = CS_GetEntry (D->Code, I); - - /* Check if this entry does a stack access, and if so, if it's a plain - ** load from stack, since this is needed later. - */ - int Correction = 0; - if ((E->Use & REG_SP) != 0) { - - /* Check for some things that should not happen */ - CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); - CHECK (strcmp (E->Arg, "sp") == 0); - /* We need to correct this one */ - Correction = (E->OPC == OP65_LDA)? 2 : 1; - - } else if (CE_IsCallTo (E, "ldaxysp")) { - /* We need to correct this one */ - Correction = 1; - } - - if (Correction) { - /* Get the code entry before this one. If it's a LDY, adjust the - ** value. - */ - CodeEntry* P = CS_GetPrevEntry (D->Code, I); - if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { - /* The Y load is just before the stack access, adjust it */ - CE_SetNumArg (P, P->Num - Offs); - } else { - /* Insert a new load instruction before the stack access */ - const char* Arg = MakeHexArg (E->RI->In.RegY - Offs); - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I++); - } - - /* If we need the value of Y later, be sure to reload it */ - if (RegYUsed (D->Code, I+1)) { - CodeEntry* N; - const char* Arg = MakeHexArg (E->RI->In.RegY); - if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && - ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { - /* The Y register is used but the load instruction loads A - ** and is followed by a branch that evaluates the zero flag. - ** This means that we cannot just insert the load insn - ** for the Y register at this place, because it would - ** destroy the Z flag. Instead place load insns at the - ** target of the branch and after it. - ** Note: There is a chance that this code won't work. The - ** jump may be a backwards jump (in which case the stack - ** offset has already been adjusted) or there may be other - ** instructions between the load and the conditional jump. - ** Currently the compiler does not generate such code, but - ** it is possible to force the optimizer into something - ** invalid by use of inline assembler. - */ - - /* Add load insn after the branch */ - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+2); - - /* Add load insn before branch target */ - CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); - CHECK (J > I); /* Must not happen */ - InsertEntry (D, Y, J); - - /* Move the label to the new insn */ - CodeLabel* L = CS_GenLabel (D->Code, Y); - CS_MoveLabelRef (D->Code, N, L); - } else { - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+1); - /* Skip this instruction in the next round */ - ++I; - } - } - } - - /* Next entry */ - ++I; + return 1; } - /* If we have rhs load insns that load from stack, we'll have to adjust - ** the offsets for these also. - */ - if (D->Rhs.A.Flags & LI_RELOAD_Y) { - D->Rhs.A.Offs -= Offs; - } - if (D->Rhs.X.Flags & LI_RELOAD_Y) { - D->Rhs.X.Offs -= Offs; - } -} + return 0; - - -static void AddStoreLhsA (StackOpData* D) -/* Add a store to zero page after the push insn */ -{ - CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); - InsertEntry (D, X, D->PushIndex+1); -} - - - -static void AddStoreLhsX (StackOpData* D) -/* Add a store to zero page after the push insn */ -{ - CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI); - InsertEntry (D, X, D->PushIndex+1); -} - - - -static void ReplacePushByStore (StackOpData* D) -/* Replace the call to the push subroutine by a store into the zero page -** location (actually, the push is not replaced, because we need it for -** later, but the name is still ok since the push will get removed at the -** end of each routine). -*/ -{ - /* Store the value into the zeropage instead of pushing it. Check high - ** byte first so that the store is later in A/X order. - */ - if ((D->Lhs.X.Flags & LI_DIRECT) == 0) { - AddStoreLhsX (D); - } - if ((D->Lhs.A.Flags & LI_DIRECT) == 0) { - AddStoreLhsA (D); - } -} - - - -static void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI) -/* Add an op for the low byte of an operator. This function honours the -** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. -** All code is inserted at the current insertion point. -*/ -{ - CodeEntry* X; - - if ((LI->A.Flags & LI_DIRECT) != 0) { - /* Op with a variable location. If the location is on the stack, we - ** need to reload the Y register. - */ - if ((LI->A.Flags & LI_RELOAD_Y) == 0) { - - /* opc ... */ - CodeEntry* LoadA = LI->A.LoadEntry; - X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } else { - - /* ldy #offs */ - const char* Arg = MakeHexArg (LI->A.Offs); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } - - /* In both cases, we can remove the load */ - LI->A.Flags |= LI_REMOVE; - - } else { - - /* Op with temp storage */ - X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } -} - - - -static void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) -/* Add an op for the high byte of an operator. Special cases (constant values -** or similar) have to be checked separately, the function covers only the -** generic case. Code is inserted at the insertion point. -*/ -{ - CodeEntry* X; - - if (KeepResult) { - /* pha */ - X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } - - /* txa */ - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - if ((LI->X.Flags & LI_DIRECT) != 0) { - - if ((LI->X.Flags & LI_RELOAD_Y) == 0) { - - /* opc xxx */ - CodeEntry* LoadX = LI->X.LoadEntry; - X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } else { - - /* ldy #const */ - const char* Arg = MakeHexArg (LI->X.Offs); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } - - /* In both cases, we can remove the load */ - LI->X.Flags |= LI_REMOVE; - - } else { - /* opc zphi */ - X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } - - if (KeepResult) { - /* tax */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - /* pla */ - X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } -} - - - -static void RemoveRegLoads (StackOpData* D, LoadInfo* LI) -/* Remove register load insns */ -{ - /* Both registers may be loaded with one insn, but DelEntry will in this - ** case clear the other one. - */ - if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) { - if (LI->A.LoadIndex >= 0 && - (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->A.LoadIndex); - } - if (LI->A.XferIndex >= 0 && - (LI->A.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->A.XferIndex); - } - } - if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) { - if (LI->X.LoadIndex >= 0 && - (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->X.LoadIndex); - } - if (LI->X.XferIndex >= 0 && - (LI->X.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->X.XferIndex); - } - } -} - - - -static void RemoveRemainders (StackOpData* D) -/* Remove the code that is unnecessary after translation of the sequence */ -{ - /* Remove the register loads for lhs and rhs if nothing prevents that */ - RemoveRegLoads (D, &D->Lhs); - RemoveRegLoads (D, &D->Rhs); - - /* Remove the push and the operator routine */ - DelEntry (D, D->OpIndex); - DelEntry (D, D->PushIndex); -} - - - -static int IsRegVar (StackOpData* D) -/* If the value pushed is that of a zeropage variable that is unchanged until Op, -** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. -** Otherwise leave D untouched and return false. -*/ -{ - CodeEntry* LoadA = D->Lhs.A.LoadEntry; - CodeEntry* LoadX = D->Lhs.X.LoadEntry; - unsigned Len; - - /* Must be unchanged till Op */ - if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT || - (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT) { - return 0; - } - - /* Must have both load insns */ - if (LoadA == 0 || LoadX == 0) { - return 0; - } - - /* Must be loads from zp */ - if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) { - return 0; - } - - /* Must be the same zp loc with high byte in X */ - Len = strlen (LoadA->Arg); - if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0 || - strcmp (LoadX->Arg + Len, "+1") != 0) { - return 0; - } - - /* Use the zero page location directly */ - D->ZPLo = LoadA->Arg; - D->ZPHi = LoadX->Arg; - return 1; } @@ -943,7 +189,8 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer D->Lhs.A.Flags |= LI_REMOVE; } else if ((D->Rhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT && - (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) { + (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT && + D->RhsMultiChg == 0) { CodeEntry* LoadX = D->Rhs.X.LoadEntry; CodeEntry* LoadA = D->Rhs.A.LoadEntry; @@ -1040,7 +287,7 @@ static unsigned Opt_tosshift (StackOpData* D, const char* Name) /* ldx */ X = NewCodeEntry (OP65_LDX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - + /* Lhs load entries can be removed if not used later */ D->Lhs.X.Flags |= LI_REMOVE; D->Lhs.A.Flags |= LI_REMOVE; @@ -1206,6 +453,7 @@ static unsigned Opt_staxspidx (StackOpData* D) /* Optimize the staxspidx sequence */ { CodeEntry* X; + const char* Arg = 0; /* Check if we're using a register variable */ if (!IsRegVar (D)) { @@ -1222,7 +470,7 @@ static unsigned Opt_staxspidx (StackOpData* D) if (RegValIsKnown (D->OpEntry->RI->In.RegY)) { /* Value of Y is known */ - const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegY + 1); + Arg = MakeHexArg (D->OpEntry->RI->In.RegY + 1); X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); } else { X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI); @@ -1231,7 +479,7 @@ static unsigned Opt_staxspidx (StackOpData* D) if (RegValIsKnown (D->OpEntry->RI->In.RegX)) { /* Value of X is known */ - const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX); + Arg = MakeHexArg (D->OpEntry->RI->In.RegX); X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); } else { /* Value unknown */ @@ -1246,7 +494,12 @@ static unsigned Opt_staxspidx (StackOpData* D) /* If we remove staxspidx, we must restore the Y register to what the ** function would return. */ - X = NewCodeEntry (OP65_LDY, AM65_IMM, "$00", 0, D->OpEntry->LI); + if (RegValIsKnown (D->OpEntry->RI->In.RegY)) { + Arg = MakeHexArg (D->OpEntry->RI->In.RegY); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); + } else { + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, D->OpEntry->LI); + } InsertEntry (D, X, D->OpIndex+5); /* Remove the push and the call to the staxspidx function */ @@ -1846,12 +1099,235 @@ static unsigned Opt_tosxorax (StackOpData* D) +/*****************************************************************************/ +/* Optimization functions when hi-bytes can be ignored */ +/*****************************************************************************/ + + + +static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) +/* Optimize the TOS compare sequence with a bool transformer */ +{ + CodeEntry* X; + cmp_t Cond; + + D->IP = D->OpIndex + 1; + + if (!D->RhsMultiChg && + (D->Rhs.A.Flags & LI_DIRECT) != 0 && + (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + + /* cmp */ + AddOpLow (D, OP65_CMP, &D->Rhs); + + /* Rhs low-byte load must be removed and hi-byte load may be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_REMOVE; + + } else if ((D->Lhs.A.Flags & LI_DIRECT) != 0) { + /* If the lhs is direct (but not stack relative), encode compares with lhs, + ** effectively reversing the order (which doesn't matter for == and !=). + */ + Cond = FindBoolCmpCond (BoolTransformer); + Cond = GetRevertedCond (Cond); + BoolTransformer = GetBoolTransformer (Cond); + + /* This shouldn't fail */ + CHECK (BoolTransformer); + + /* cmp */ + AddOpLow (D, OP65_CMP, &D->Lhs); + + /* Lhs load entries can be removed if not used later */ + D->Lhs.X.Flags |= LI_REMOVE; + D->Lhs.A.Flags |= LI_REMOVE; + + } else { + /* We'll do reverse-compare */ + Cond = FindBoolCmpCond (BoolTransformer); + Cond = GetRevertedCond (Cond); + BoolTransformer = GetBoolTransformer (Cond); + + /* This shouldn't fail */ + CHECK (BoolTransformer); + + /* Save lhs into zeropage */ + AddStoreLhsA (D); + + /* cmp */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } + + /* Create a call to the boolean transformer function. This is needed for all + ** variants. + */ + X = NewCodeEntry (OP65_JSR, AM65_ABS, BoolTransformer, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Remove the push and the call to the TOS function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_a_toseq (StackOpData* D) +/* Optimize the toseqax sequence */ +{ + return Opt_a_toscmpbool (D, "booleq"); +} + + + +static unsigned Opt_a_tosicmp (StackOpData* D) +/* Replace tosicmp with CMP */ +{ + CodeEntry* X; + RegInfo* RI; + const char* Arg; + + if (!SameRegAValue (D)) { + /* Because of SameRegAValue */ + CHECK (D->Rhs.A.ChgIndex >= 0); + + /* Store LHS in ZP and reload it before op */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex + 1); + X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->OpIndex); + + D->IP = D->OpIndex + 1; + + if ((D->Rhs.A.Flags & LI_DIRECT) == 0) { + /* 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); + + /* Cmp with stored RHS */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) { + /* Cmp directly with RHS src */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + /* ldy #offs */ + if ((D->Rhs.A.Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (D->Rhs.A.Offs), 0, D->OpEntry->LI); + } else { + X = NewCodeEntry (OP65_LDY, D->Rhs.A.LoadYEntry->AM, D->Rhs.A.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + /* cmp src,y OR cmp (sp),y */ + if (D->Rhs.A.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OP65_CMP, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + } + + /* RHS may be removed */ + D->Rhs.A.Flags |= LI_REMOVE; + D->Rhs.X.Flags |= LI_REMOVE; + } + + /* Fix up the N/V flags: N = ~C, V = 0 */ + Arg = MakeHexArg (0); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_SBC, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + Arg = MakeHexArg (0x01); + X = NewCodeEntry (OP65_ORA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* jeq L1 */ + CodeLabel* Label = CS_GenLabel (D->Code, CS_GetEntry (D->Code, D->IP)); + X = NewCodeEntry (OP65_JEQ, AM65_BRA, Label->Name, Label, X->LI); + InsertEntry (D, X, D->IP-3); + + } else { + /* Just clear A,Z,N; and set C */ + Arg = MakeHexArg (0); + if ((RI = GetLastChangedRegInfo (D, &D->Lhs.A)) != 0 && + RegValIsKnown (RI->Out.RegA) && + (RI->Out.RegA & 0xFF) == 0) { + + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 1); + } else { + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 1); + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 2); + } + } + + /* Remove the push and the call to the operator function */ + RemoveRemainders (D); + + return 1; +} + + + +static unsigned Opt_a_tosne (StackOpData* D) +/* Optimize the tosneax sequence */ +{ + return Opt_a_toscmpbool (D, "boolne"); +} + + + +static unsigned Opt_a_tosuge (StackOpData* D) +/* Optimize the tosgeax and tosugeax sequences */ +{ + return Opt_a_toscmpbool (D, "booluge"); +} + + + +static unsigned Opt_a_tosugt (StackOpData* D) +/* Optimize the tosgtax and tosugtax sequences */ +{ + return Opt_a_toscmpbool (D, "boolugt"); +} + + + +static unsigned Opt_a_tosule (StackOpData* D) +/* Optimize the tosleax and tosuleax sequences */ +{ + return Opt_a_toscmpbool (D, "boolule"); +} + + + +static unsigned Opt_a_tosult (StackOpData* D) +/* Optimize the tosltax and tosultax sequences */ +{ + return Opt_a_toscmpbool (D, "boolult"); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ +/* 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 }, { "staspidx", Opt_staspidx, REG_NONE, OP_NONE }, @@ -1874,7 +1350,22 @@ static const OptFuncDesc FuncTable[] = { { "tosultax", Opt_tosultax, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosxorax", Opt_tosxorax, REG_NONE, OP_NONE }, }; -#define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0])) + +static const OptFuncDesc FuncRegATable[] = { + { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, + { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, + { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, + { "tosicmp", Opt_a_tosicmp, REG_NONE, OP_NONE }, + { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, + { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, + { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, + { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, + { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, + { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, + { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, +}; + +#define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) @@ -1886,98 +1377,12 @@ static int CmpFunc (const void* Key, const void* Func) -static const OptFuncDesc* FindFunc (const char* Name) +static const OptFuncDesc* FindFunc (const OptFuncDesc FuncTable[], size_t Count, const char* Name) /* Find the function with the given name. Return a pointer to the table entry ** or NULL if the function was not found. */ { - return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc); -} - - - -static int CmpHarmless (const void* Key, const void* Entry) -/* Compare function for bsearch */ -{ - return strcmp (Key, *(const char**)Entry); -} - - - -static int HarmlessCall (const char* Name) -/* Check if this is a call to a harmless subroutine that will not interrupt -** the pushax/op sequence when encountered. -*/ -{ - static const char* const Tab[] = { - "aslax1", - "aslax2", - "aslax3", - "aslax4", - "aslaxy", - "asrax1", - "asrax2", - "asrax3", - "asrax4", - "asraxy", - "bnegax", - "complax", - "decax1", - "decax2", - "decax3", - "decax4", - "decax5", - "decax6", - "decax7", - "decax8", - "decaxy", - "incax1", - "incax2", - "incax3", - "incax4", - "incax5", - "incax6", - "incax7", - "incax8", - "incaxy", - "ldaxidx", - "ldaxysp", - "negax", - "shlax1", - "shlax2", - "shlax3", - "shlax4", - "shlaxy", - "shrax1", - "shrax2", - "shrax3", - "shrax4", - "shraxy", - }; - - void* R = bsearch (Name, - Tab, - sizeof (Tab) / sizeof (Tab[0]), - sizeof (Tab[0]), - CmpHarmless); - return (R != 0); -} - - - -static void ResetStackOpData (StackOpData* Data) -/* Reset the given data structure */ -{ - Data->OptFunc = 0; - Data->ZPUsage = REG_NONE; - Data->ZPChanged = REG_NONE; - Data->UsedRegs = REG_NONE; - - ClearLoadInfo (&Data->Lhs); - ClearLoadInfo (&Data->Rhs); - - Data->PushIndex = -1; - Data->OpIndex = -1; + return bsearch (Name, FuncTable, Count, sizeof(OptFuncDesc), CmpFunc); } @@ -1985,7 +1390,7 @@ static void ResetStackOpData (StackOpData* Data) static int PreCondOk (StackOpData* D) /* Check if the preconditions for a call to the optimizer subfunction are ** satisfied. As a side effect, this function will also choose the zero page -** register to use. +** register to use for temporary storage. */ { LoadInfo* Lhs; @@ -2000,7 +1405,8 @@ static int PreCondOk (StackOpData* D) int Passed = 0; /* Check the flags */ - unsigned UnusedRegs = D->OptFunc->UnusedRegs; + const OptFuncDesc* Desc = D->OptFunc; + unsigned UnusedRegs = Desc->UnusedRegs; if (UnusedRegs != REG_NONE && (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) { /* Cannot optimize */ @@ -2011,15 +1417,15 @@ static int PreCondOk (StackOpData* D) LoVal = D->OpEntry->RI->In.RegA; HiVal = D->OpEntry->RI->In.RegX; /* Check normally first, then interchange A/X and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { - if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 && + if ((Desc->Flags & OP_A_KNOWN) != 0 && RegValIsUnknown (LoVal)) { /* Cannot optimize */ break; } - if ((D->OptFunc->Flags & OP_X_ZERO) != 0 && + if ((Desc->Flags & OP_X_ZERO) != 0 && HiVal != 0) { /* Cannot optimize */ break; @@ -2040,7 +1446,7 @@ static int PreCondOk (StackOpData* D) Lhs = &D->Lhs; Rhs = &D->Rhs; /* Check normally first, then interchange LHS/RHS and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { LhsLo = &Lhs->A; @@ -2051,47 +1457,206 @@ static int PreCondOk (StackOpData* D) ** so we don't need to check twice for now. */ - if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) { + if ((Desc->Flags & OP_LHS_LOAD) != 0) { if ((LhsLo->Flags & LhsHi->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_LHS_LOAD_DIRECT) != 0) { if ((LhsLo->Flags & LhsHi->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) { + if ((Desc->Flags & OP_RHS_LOAD) != 0) { if ((RhsLo->Flags & RhsHi->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_RHS_LOAD_DIRECT) != 0) { if ((RhsLo->Flags & RhsHi->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_LHS_REMOVE) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE) != 0) { /* Check if the load entries cannot be removed */ if ((LhsLo->LoadEntry != 0 && (LhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0) || (LhsHi->LoadEntry != 0 && (LhsHi->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_REMOVE) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE) != 0) { if ((RhsLo->LoadEntry != 0 && (RhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0) || (RhsHi->LoadEntry != 0 && (RhsHi->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } + if (D->RhsMultiChg && (Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + Passed = 1; + } while (0); + + /* Interchange LHS/RHS for next round */ + Lhs = &D->Rhs; + Rhs = &D->Lhs; + } + if (!Passed) { + /* Cannot optimize */ + return 0; + } + + /* Determine the zero page locations to use. We've tracked the used + ** ZP locations, so try to find some for us that are unused. + */ + if ((D->ZPUsage & REG_PTR1) == REG_NONE) { + D->ZPLo = "ptr1"; + D->ZPHi = "ptr1+1"; + } else if ((D->ZPUsage & REG_SREG) == REG_NONE) { + D->ZPLo = "sreg"; + D->ZPHi = "sreg+1"; + } else if ((D->ZPUsage & REG_PTR2) == REG_NONE) { + D->ZPLo = "ptr2"; + D->ZPHi = "ptr2+1"; + } else { + /* No registers available */ + return 0; + } + + /* Determine if we have a basic block */ + return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex); +} + + + +static int RegAPreCondOk (StackOpData* D) +/* Check if the preconditions for a call to the RegA-only optimizer subfunction +** are satisfied. As a side effect, this function will also choose the zero page +** register to use for temporary storage. +*/ +{ + LoadInfo* Lhs; + LoadInfo* Rhs; + LoadRegInfo* LhsLo; + LoadRegInfo* RhsLo; + short LhsLoVal, LhsHiVal; + short RhsLoVal, RhsHiVal; + int I; + int Passed = 0; + + /* Check the flags */ + const OptFuncDesc* Desc = D->OptFunc; + unsigned UnusedRegs = Desc->UnusedRegs; + if (UnusedRegs != REG_NONE && + (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) { + /* Cannot optimize */ + return 0; + } + + Passed = 0; + LhsLoVal = D->PushEntry->RI->In.RegA; + LhsHiVal = D->PushEntry->RI->In.RegX; + RhsLoVal = D->OpEntry->RI->In.RegA; + RhsHiVal = D->OpEntry->RI->In.RegX; + /* Check normally first, then interchange A/X and check again if necessary */ + for (I = (Desc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + + do { + if (LhsHiVal != RhsHiVal) { + /* Cannot optimize */ + break; + } + if ((Desc->Flags & OP_A_KNOWN) != 0 && + RegValIsUnknown (LhsLoVal)) { + /* Cannot optimize */ + break; + } + if ((Desc->Flags & OP_X_ZERO) != 0 && + LhsHiVal != 0) { + /* Cannot optimize */ + break; + } + Passed = 1; + } while (0); + + /* Suppress warning about unused assignment in GCC */ + (void)RhsLoVal; + + /* Interchange A/X */ + LhsLoVal = D->PushEntry->RI->In.RegX; + LhsHiVal = D->PushEntry->RI->In.RegA; + RhsLoVal = D->OpEntry->RI->In.RegX; + RhsHiVal = D->OpEntry->RI->In.RegA; + } + if (!Passed) { + /* Cannot optimize */ + return 0; + } + + Passed = 0; + Lhs = &D->Lhs; + Rhs = &D->Rhs; + /* Check normally first, then interchange LHS/RHS and check again if necessary */ + for (I = (Desc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + + do { + LhsLo = &Lhs->A; + RhsLo = &Rhs->A; + /* Currently we have only LHS/RHS checks with identical requirements for A/X, + ** so we don't need to check twice for now. + */ + + if ((Desc->Flags & OP_LHS_LOAD) != 0) { + if ((LhsLo->Flags & LI_LOAD_INSN) == 0) { + /* Cannot optimize */ + break; + } else if ((Desc->Flags & OP_LHS_LOAD_DIRECT) != 0) { + if ((LhsLo->Flags & LI_DIRECT) == 0) { + /* Cannot optimize */ + break; + } + } + } + if ((Desc->Flags & OP_RHS_LOAD) != 0) { + if ((RhsLo->Flags & LI_LOAD_INSN) == 0) { + /* Cannot optimize */ + break; + } else if ((Desc->Flags & OP_RHS_LOAD_DIRECT) != 0) { + if ((RhsLo->Flags & LI_DIRECT) == 0) { + /* Cannot optimize */ + break; + } + } + } + if ((Desc->Flags & OP_LHS_REMOVE) != 0) { + /* Check if the load entries cannot be removed */ + if ((LhsLo->LoadEntry != 0 && (LhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { + if ((Desc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + } + } + if ((Desc->Flags & OP_RHS_REMOVE) != 0) { + if ((RhsLo->LoadEntry != 0 && (RhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { + if ((Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + } + } + if (D->RhsMultiChg && (Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } Passed = 1; } while (0); @@ -2133,61 +1698,18 @@ static int PreCondOk (StackOpData* D) -static void SetDontRemoveEntryFlag (LoadRegInfo* RI) -/* Flag the entry as non-removable according to register flags */ -{ - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags |= CEF_DONT_REMOVE; - } - } -} - - - -static void ResetDontRemoveEntryFlag (LoadRegInfo* RI) -/* Unflag the entry as non-removable according to register flags */ -{ - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; - } - } -} - - - -static void SetDontRemoveEntryFlags (StackOpData* D) -/* Flag the entries as non-removable according to register flags */ -{ - SetDontRemoveEntryFlag (&D->Lhs.A); - SetDontRemoveEntryFlag (&D->Lhs.X); - SetDontRemoveEntryFlag (&D->Rhs.A); - SetDontRemoveEntryFlag (&D->Rhs.X); -} - - - -static void ResetDontRemoveEntryFlags (StackOpData* D) -/* Unflag the entries as non-removable according to register flags */ -{ - ResetDontRemoveEntryFlag (&D->Lhs.A); - ResetDontRemoveEntryFlag (&D->Lhs.X); - ResetDontRemoveEntryFlag (&D->Rhs.A); - ResetDontRemoveEntryFlag (&D->Rhs.X); -} - - - unsigned OptStackOps (CodeSeg* S) /* Optimize operations that take operands via the stack */ { - unsigned Changes = 0; /* Number of changes in one run */ - StackOpData Data; - int I; - int OldEntryCount; /* Old number of entries */ - unsigned Used; /* What registers would be used */ - unsigned PushedRegs; /* Track if the same regs are used after the push */ + unsigned Changes = 0; /* Number of changes in one run */ + StackOpData Data; + int I; + int OldEntryCount; /* Old number of entries */ + unsigned Used; /* What registers would be used */ + unsigned PushedRegs = 0; /* Track if the same regs are used after the push */ + int RhsAChgIndex; /* Track if rhs is changed more than once */ + int RhsXChgIndex; /* Track if rhs is changed more than once */ + int IsRegAOptFunc = 0; /* Whether to use the RegA-only optimizations */ enum { Initialize, @@ -2214,6 +1736,8 @@ unsigned OptStackOps (CodeSeg* S) ** ** Since we need a zero page register later, do also check the ** intermediate code for zero page use. + ** When hibytes of both oprands are equal, we may have more specialized + ** optimization for the op. */ I = 0; while (I < (int)CS_GetEntryCount (S)) { @@ -2235,28 +1759,19 @@ unsigned OptStackOps (CodeSeg* S) */ if (CE_HasLabel (E)) { /* Currently we don't track across branches. - ** Remember this as an indirect load. + ** Treat this as a change to all regs. */ ClearLoadInfo (&Data.Lhs); - Data.Lhs.A.LoadIndex = I; - Data.Lhs.X.LoadIndex = I; - Data.Lhs.Y.LoadIndex = I; + Data.Lhs.A.ChgIndex = I; + Data.Lhs.X.ChgIndex = I; + Data.Lhs.Y.ChgIndex = I; } if (CE_IsCallTo (E, "pushax")) { - /* Disallow removing the loads if the registers are used */ - if (Data.UsedRegs & REG_A) { - Data.Lhs.A.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_X) { - Data.Lhs.X.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_Y) { - Data.Lhs.Y.Flags |= LI_DONT_REMOVE; - } + /* Disallow removing Lhs loads if the registers are used */ + SetIfOperandLoadUnremovable (&Data.Lhs, Data.UsedRegs); - /* The LHS regs are also used as the default RHS until changed */ - PushedRegs = REG_AXY; - Data.UsedRegs = REG_AXY; + /* The Lhs regs are also used as the default Rhs until changed */ + PushedRegs = REG_AXY; CopyLoadInfo (&Data.Rhs, &Data.Lhs); Data.PushIndex = I; @@ -2264,7 +1779,7 @@ unsigned OptStackOps (CodeSeg* S) State = FoundPush; } else { /* Track load insns */ - Used = TrackLoads (&Data.Lhs, 0, Data.Code, I); + Used = TrackLoads (&Data.Lhs, S, I); Data.UsedRegs &= ~E->Chg; Data.UsedRegs |= Used; } @@ -2277,49 +1792,46 @@ unsigned OptStackOps (CodeSeg* S) */ if (CE_HasLabel (E)) { /* Currently we don't track across branches. - ** Remember this as an indirect load. + ** Treat this as a change to all regs. */ ClearLoadInfo (&Data.Rhs); - Data.Rhs.A.LoadIndex = I; - Data.Rhs.X.LoadIndex = I; - Data.Rhs.Y.LoadIndex = I; + Data.Rhs.A.ChgIndex = I; + Data.Rhs.X.ChgIndex = I; + Data.Rhs.Y.ChgIndex = I; } if (E->OPC == OP65_JSR) { - /* Subroutine call: Check if this is one of the functions, ** we're going to replace. */ - Data.OptFunc = FindFunc (E->Arg); + if (SameRegXValue (&Data)) { + Data.OptFunc = FindFunc (FuncRegATable, FUNC_COUNT (FuncRegATable), E->Arg); + IsRegAOptFunc = 1; + } + if (Data.OptFunc == 0) { + Data.OptFunc = FindFunc (FuncTable, FUNC_COUNT (FuncTable), E->Arg); + IsRegAOptFunc = 0; + } if (Data.OptFunc) { - /* Disallow removing the loads if the registers are used */ - if (Data.UsedRegs & REG_A) { - Data.Rhs.A.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_X) { - Data.Rhs.X.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_Y) { - Data.Rhs.Y.Flags |= LI_DONT_REMOVE; - } + /* Disallow removing Rhs loads if the registers are used */ + SetIfOperandLoadUnremovable (&Data.Rhs, Data.UsedRegs); + /* Remember the op index and go on */ Data.OpIndex = I; Data.OpEntry = E; State = FoundOp; break; - } else if (!HarmlessCall (E->Arg)) { - /* A call to an unkown subroutine: We need to start - ** over after the last pushax. Note: This will also - ** happen if we encounter a call to pushax! + } else if (!HarmlessCall (E, 2)) { + /* The call might use or change the content that we are + ** going to access later via the stack pointer. In any + ** case, we need to start over after the last pushax. + ** Note: This will also happen if we encounter a call + ** to pushax! */ I = Data.PushIndex; State = Initialize; break; } - - } else if ((E->Use & REG_SP) != 0 && - (E->AM != AM65_ZP_INDY || - RegValIsUnknown (E->RI->In.RegY) || - E->RI->In.RegY < 2)) { + } else if (((E->Chg | E->Use) & REG_SP) != 0) { /* If we are using the stack, and we don't have "indirect Y" ** addressing mode, or the value of Y is unknown, or less @@ -2329,14 +1841,27 @@ unsigned OptStackOps (CodeSeg* S) ** that the code works with the value on stack which is to ** be removed. */ - I = Data.PushIndex; - State = Initialize; - break; + if (E->AM == AM65_ZPX_IND || + ((E->Chg | E->Use) & SLV_IND) == 0 || + (RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < 2)) { + I = Data.PushIndex; + State = Initialize; + break; + } } + /* Memorize the old rhs load indices before refreshing them */ + RhsAChgIndex = Data.Rhs.A.ChgIndex; + RhsXChgIndex = Data.Rhs.X.ChgIndex; + + /* Keep tracking Lhs src if necessary */ + SetIfOperandSrcAffected (&Data.Lhs, E); + /* Track register usage */ - Used = TrackLoads (&Data.Rhs, &Data.Lhs, Data.Code, I); + Used = TrackLoads (&Data.Rhs, S, I); + Data.ZPUsage |= (E->Use | E->Chg); /* The changes could depend on the use */ Data.UsedRegs &= ~E->Chg; @@ -2346,18 +1871,20 @@ unsigned OptStackOps (CodeSeg* S) /* Check if any parts of Lhs are used again before overwritten */ if (PushedRegs != 0) { if ((PushedRegs & E->Use) != 0) { - if ((PushedRegs & E->Use & REG_A) != 0) { - Data.Lhs.A.Flags |= LI_DONT_REMOVE; - } - if ((PushedRegs & E->Use & REG_X) != 0) { - Data.Lhs.X.Flags |= LI_DONT_REMOVE; - } - if ((PushedRegs & E->Use & REG_Y) != 0) { - Data.Lhs.Y.Flags |= LI_DONT_REMOVE; - } + SetIfOperandLoadUnremovable (&Data.Lhs, PushedRegs & E->Use); } PushedRegs &= ~E->Chg; } + /* Check if rhs is changed again after the push */ + if ((RhsAChgIndex != Data.Lhs.A.ChgIndex && + RhsAChgIndex != Data.Rhs.A.ChgIndex) || + (RhsXChgIndex != Data.Lhs.X.ChgIndex && + RhsXChgIndex != Data.Rhs.X.ChgIndex)) { + /* This will disable those sub-opts that require removing + ** the rhs as they can't handle such cases correctly. + */ + Data.RhsMultiChg = 1; + } break; case FoundOp: @@ -2394,11 +1921,11 @@ unsigned OptStackOps (CodeSeg* S) SetDontRemoveEntryFlags (&Data); /* Check the preconditions. If they aren't ok, reset the insn - ** pointer to the pushax and start over. We will loose part of + ** pointer to the pushax and start over. We will lose part of ** load tracking but at least a/x has probably lost between ** pushax and here and will be tracked again when restarting. */ - if (!PreCondOk (&Data)) { + if (IsRegAOptFunc ? !RegAPreCondOk (&Data) : !PreCondOk (&Data)) { /* Unflag entries that can't be removed */ ResetDontRemoveEntryFlags (&Data); I = Data.PushIndex; @@ -2424,7 +1951,8 @@ unsigned OptStackOps (CodeSeg* S) CS_GenRegInfo (S); /* Call the optimizer function */ - Changes += Data.OptFunc->Func (&Data); + const OptFuncDesc* Desc = Data.OptFunc; + Changes += Desc->Func (&Data); /* Unflag entries that can't be removed */ ResetDontRemoveEntryFlags (&Data); diff --git a/src/cc65/coptsub.c b/src/cc65/coptsub.c index 3d75c1f72..08e65fe1d 100644 --- a/src/cc65/coptsub.c +++ b/src/cc65/coptsub.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2001-2006, Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/copttest.c b/src/cc65/copttest.c index 5628a42c3..f2d55244f 100644 --- a/src/cc65/copttest.c +++ b/src/cc65/copttest.c @@ -153,7 +153,7 @@ unsigned OptTest2 (CodeSeg* S) (L[2]->Info & OF_FBRA) != 0 && L[1]->AM == L[0]->AM && strcmp (L[0]->Arg, L[1]->Arg) == 0 && - (GetRegInfo (S, I+2, L[1]->Chg) & L[1]->Chg) == 0) { + (GetRegInfo (S, I+2, L[1]->Chg & ~PSTATE_ZN) & L[1]->Chg & ~PSTATE_ZN) == 0) { /* Remove the load */ CS_DelEntry (S, I+1); diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2d54316cd..e5d3f8d96 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -59,16 +59,24 @@ /* Predefined type strings */ -Type type_schar[] = { TYPE(T_SCHAR), TYPE(T_END) }; -Type type_uchar[] = { TYPE(T_UCHAR), TYPE(T_END) }; -Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; -Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; -Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; -Type type_ulong[] = { TYPE(T_ULONG), TYPE(T_END) }; -Type type_void[] = { TYPE(T_VOID), TYPE(T_END) }; -Type type_size_t[] = { TYPE(T_SIZE_T), TYPE(T_END) }; -Type type_float[] = { TYPE(T_FLOAT), TYPE(T_END) }; -Type type_double[] = { TYPE(T_DOUBLE), TYPE(T_END) }; +const Type type_char[] = { TYPE(T_CHAR), TYPE(T_END) }; +const Type type_schar[] = { TYPE(T_SCHAR), TYPE(T_END) }; +const Type type_uchar[] = { TYPE(T_UCHAR), TYPE(T_END) }; +const Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; +const Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; +const Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; +const Type type_ulong[] = { TYPE(T_ULONG), TYPE(T_END) }; +const Type type_bool[] = { TYPE(T_INT), TYPE(T_END) }; +const Type type_void[] = { TYPE(T_VOID), TYPE(T_END) }; +const Type type_size_t[] = { TYPE(T_SIZE_T), TYPE(T_END) }; +const Type type_float[] = { TYPE(T_FLOAT), TYPE(T_END) }; +const Type type_double[] = { TYPE(T_DOUBLE), TYPE(T_END) }; + +/* More predefined type strings */ +const Type type_char_p[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) }; +const Type type_c_char_p[] = { TYPE(T_PTR), TYPE(T_C_CHAR), TYPE(T_END) }; +const Type type_void_p[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) }; +const Type type_c_void_p[] = { TYPE(T_PTR), TYPE(T_C_VOID), TYPE(T_END) }; @@ -78,6 +86,297 @@ Type type_double[] = { TYPE(T_DOUBLE), TYPE(T_END) }; +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. +*/ +{ + struct StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + if (IsTypeArray (T)) { + + long Count = GetElementCount (T); + if (!SB_IsEmpty (East)) { + if (Count > 0) { + SB_Printf (&Buf, "[%ld]", Count); + } else { + SB_Printf (&Buf, "[]"); + } + SB_Append (East, &Buf); + SB_Terminate (East); + + } else { + if (Count > 0) { + SB_Printf (East, "[%ld]", Count); + } else { + SB_Printf (East, "[]"); + } + + if (!SB_IsEmpty (West)) { + /* Add parentheses to West */ + SB_Printf (&Buf, "(%s)", SB_GetConstBuf (West)); + SB_Copy (West, &Buf); + SB_Terminate (West); + } + } + + /* Get element type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else if (IsTypeFunc (T)) { + + FuncDesc* D = GetFuncDesc (T); + struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + + /* First argument */ + SymEntry* Param = D->SymTab->SymHead; + unsigned I; + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (&ParamList, "void"); + } + } else { + if (D->ParamCount > 0) { + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); + } + } + SB_Terminate (&ParamList); + + /* Join the existing West and East together */ + if (!SB_IsEmpty (East)) { + SB_Append (West, East); + SB_Terminate (West); + SB_Clear (East); + } + + if (SB_IsEmpty (West)) { + /* Just use the param list */ + SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList)); + } else { + /* Append the param list to the existing West */ + SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList)); + SB_Printf (West, "%s", SB_GetConstBuf (&Buf)); + } + SB_Done (&ParamList); + + /* Return type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else if (IsTypePtr (T)) { + + int QualCount = 0; + + SB_Printf (&Buf, "*"); + + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) { + QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR); + } + + if (!SB_IsEmpty (West)) { + if (QualCount > 0) { + SB_AppendChar (&Buf, ' '); + } + SB_Append (&Buf, West); + } + + SB_Copy (West, &Buf); + SB_Terminate (West); + + /* Get indirection type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else { + + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != 0) { + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR) > 0) { + SB_AppendChar (&Buf, ' '); + } + } + + if (!IsTypeBitField (T)) { + SB_AppendStr (&Buf, GetSymTypeName (T)); + } else { + SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + } + + if (!SB_IsEmpty (West)) { + SB_AppendChar (&Buf, ' '); + SB_Append (&Buf, West); + } + + SB_Copy (West, &Buf); + SB_Terminate (West); + } + + SB_Done (&Buf); + return West; +} + + + +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 */ +{ + struct StrBuf East = AUTO_STRBUF_INITIALIZER; + GetFullTypeNameWestEast (S, &East, T); + + /* Join West and East */ + SB_Append (S, &East); + SB_Terminate (S); + SB_Done (&East); + + return S; +} + + + +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. +*/ +{ + int Count = 0; + + Qual &= T_MASK_QUAL & ~IgnoredQual; + if (Qual & T_QUAL_CONST) { + if (!SB_IsEmpty (S)) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "const"); + ++Count; + } + if (Qual & T_QUAL_VOLATILE) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "volatile"); + ++Count; + } + if (Qual & T_QUAL_RESTRICT) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "restrict"); + ++Count; + } + if (Qual & T_QUAL_NEAR) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__near__"); + ++Count; + } + if (Qual & T_QUAL_FAR) { + SB_AppendStr (S, "__far__"); + ++Count; + } + if (Qual & T_QUAL_FASTCALL) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__fastcall__"); + ++Count; + } + if (Qual & T_QUAL_CDECL) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__cdecl__"); + ++Count; + } + + if (Count > 0) { + SB_Terminate (S); + } + + return Count; +} + + + unsigned TypeLen (const Type* T) /* Return the length of the type string */ { @@ -146,14 +445,6 @@ int SignExtendChar (int C) -TypeCode GetDefaultChar (void) -/* Return the default char type (signed/unsigned) depending on the settings */ -{ - return IS_Get (&SignedChars)? T_SCHAR : T_UCHAR; -} - - - Type* GetCharArrayType (unsigned Len) /* Return the type for a char array of the given length */ { @@ -163,7 +454,7 @@ Type* GetCharArrayType (unsigned Len) /* Fill the type string */ T[0].C = T_ARRAY; T[0].A.L = Len; /* Array length is in the L attribute */ - T[1].C = GetDefaultChar (); + T[1].C = T_CHAR; T[2].C = T_END; /* Return the new type */ @@ -182,13 +473,13 @@ Type* GetImplicitFuncType (void) Type* T = TypeAlloc (3); /* func/returns int/terminator */ /* Prepare the function descriptor */ - F->Flags = FD_EMPTY | FD_VARIADIC; + F->Flags = FD_EMPTY; F->SymTab = &EmptySymTab; F->TagTab = &EmptySymTab; /* Fill the type string */ T[0].C = T_FUNC | CodeAddrSizeQualifier (); - T[0].A.P = F; + T[0].A.F = F; T[1].C = T_INT; T[2].C = T_END; @@ -198,187 +489,176 @@ Type* GetImplicitFuncType (void) -Type* PointerTo (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. +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. */ { - /* 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; -} - - - -static TypeCode PrintTypeComp (FILE* F, TypeCode C, TypeCode Mask, const char* Name) -/* Check for a specific component of the type. If it is there, print the -** name and remove it. Return the type with the component removed. -*/ -{ - if ((C & Mask) == Mask) { - fprintf (F, "%s ", Name); - C &= ~Mask; + if (SizeOf (Type) == 0) { + Internal ("Incomplete type used in GetIntegerTypeMin"); } - return C; -} - - -void PrintType (FILE* F, const Type* T) -/* Output translation of type array. */ -{ - /* Walk over the type string */ - while (T->C != T_END) { - - /* Get the type code */ - TypeCode C = T->C; - - /* Print any qualifiers */ - C = PrintTypeComp (F, C, T_QUAL_CONST, "const"); - C = PrintTypeComp (F, C, T_QUAL_VOLATILE, "volatile"); - C = PrintTypeComp (F, C, T_QUAL_RESTRICT, "restrict"); - C = PrintTypeComp (F, C, T_QUAL_NEAR, "__near__"); - C = PrintTypeComp (F, C, T_QUAL_FAR, "__far__"); - C = PrintTypeComp (F, C, T_QUAL_FASTCALL, "__fastcall__"); - C = PrintTypeComp (F, C, T_QUAL_CDECL, "__cdecl__"); - - /* Signedness. Omit the signedness specifier for long and int */ - if ((C & T_MASK_TYPE) != T_TYPE_INT && (C & T_MASK_TYPE) != T_TYPE_LONG) { - C = PrintTypeComp (F, C, T_SIGN_SIGNED, "signed"); - } - C = PrintTypeComp (F, C, T_SIGN_UNSIGNED, "unsigned"); - - /* Now check the real type */ - switch (C & T_MASK_TYPE) { - case T_TYPE_CHAR: - fprintf (F, "char"); - break; - case T_TYPE_SHORT: - fprintf (F, "short"); - break; - case T_TYPE_INT: - fprintf (F, "int"); - break; - case T_TYPE_LONG: - fprintf (F, "long"); - break; - case T_TYPE_LONGLONG: - fprintf (F, "long long"); - break; - case T_TYPE_FLOAT: - fprintf (F, "float"); - break; - case T_TYPE_DOUBLE: - fprintf (F, "double"); - break; - case T_TYPE_VOID: - fprintf (F, "void"); - break; - case T_TYPE_STRUCT: - fprintf (F, "struct %s", ((SymEntry*) T->A.P)->Name); - break; - case T_TYPE_UNION: - fprintf (F, "union %s", ((SymEntry*) T->A.P)->Name); - break; - case T_TYPE_ARRAY: - /* Recursive call */ - PrintType (F, T + 1); - if (T->A.L == UNSPECIFIED) { - fprintf (F, " []"); - } else { - fprintf (F, " [%ld]", T->A.L); - } - return; - case T_TYPE_PTR: - /* Recursive call */ - PrintType (F, T + 1); - fprintf (F, " *"); - return; - case T_TYPE_FUNC: - fprintf (F, "function returning "); - break; - default: - fprintf (F, "unknown type: %04lX", T->C); - } - - /* Next element */ - ++T; - } -} - - - -void PrintFuncSig (FILE* F, const char* Name, Type* T) -/* Print a function signature. */ -{ - /* Get the function descriptor */ - const FuncDesc* D = GetFuncDesc (T); - - /* Print a comment with the function signature */ - PrintType (F, GetFuncReturn (T)); - if (IsQualNear (T)) { - fprintf (F, " __near__"); - } - if (IsQualFar (T)) { - fprintf (F, " __far__"); - } - if (IsQualFastcall (T)) { - fprintf (F, " __fastcall__"); - } - if (IsQualCDecl (T)) { - fprintf (F, " __cdecl__"); - } - fprintf (F, " %s (", Name); - - /* Parameters */ - if (D->Flags & FD_VOID_PARAM) { - fprintf (F, "void"); + 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 { - unsigned I; - SymEntry* E = D->SymTab->SymHead; - for (I = 0; I < D->ParamCount; ++I) { - if (I > 0) { - fprintf (F, ", "); - } - if (SymIsRegVar (E)) { - fprintf (F, "register "); - } - PrintType (F, E->Type); - E = E->NextSym; + 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 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. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + 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; } } - /* End of parameter list */ - fprintf (F, ")"); + return Type; } -void PrintRawType (FILE* F, const Type* T) -/* Print a type string in raw format (for debugging) */ +TypeCode GetUnderlyingTypeCode (const Type* Type) +/* Get the type code of the unqualified underlying type of TCode. +** Return UnqualifiedType (TCode) if TCode is not scalar. +*/ { - while (T->C != T_END) { - fprintf (F, "%04lX ", T->C); - ++T; + 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. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + 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; } - fprintf (F, "\n"); -} - - -int TypeHasAttr (const Type* T) -/* Return true if the given type has attribute data */ -{ - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); + return Underlying; } @@ -386,7 +666,7 @@ int TypeHasAttr (const Type* T) unsigned SizeOf (const Type* T) /* Compute size of object represented by type array. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -395,8 +675,9 @@ unsigned SizeOf (const Type* T) 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. */ + ** 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 */ @@ -423,9 +704,6 @@ unsigned SizeOf (const Type* T) case T_ULONGLONG: return SIZEOF_LONGLONG; - case T_ENUM: - return SIZEOF_INT; - case T_FLOAT: return SIZEOF_FLOAT; @@ -434,7 +712,7 @@ unsigned SizeOf (const Type* T) case T_STRUCT: case T_UNION: - return ((SymEntry*) T->A.P)->V.S.Size; + return T->A.S->V.S.Size; case T_ARRAY: if (T->A.L == UNSPECIFIED) { @@ -444,7 +722,12 @@ unsigned SizeOf (const Type* T) 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; @@ -473,7 +756,7 @@ unsigned CheckedSizeOf (const Type* T) { unsigned Size = SizeOf (T); if (Size == 0) { - Error ("Size of data type is unknown"); + Error ("Size of type '%s' is unknown", GetFullTypeName (T)); Size = SIZEOF_CHAR; /* Don't return zero */ } return Size; @@ -489,7 +772,7 @@ unsigned CheckedPSizeOf (const Type* T) { unsigned Size = PSizeOf (T); if (Size == 0) { - Error ("Size of data type is unknown"); + Error ("Size of type '%s' is unknown", GetFullTypeName (T + 1)); Size = SIZEOF_CHAR; /* Don't return zero */ } return Size; @@ -500,7 +783,9 @@ unsigned CheckedPSizeOf (const Type* T) unsigned TypeOf (const Type* T) /* Get the code generator base type of the object */ { - switch (UnqualifiedType (T->C)) { + unsigned NewType; + + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -510,7 +795,6 @@ unsigned TypeOf (const Type* T) case T_SHORT: case T_INT: - case T_ENUM: return CF_INT; case T_USHORT: @@ -531,13 +815,24 @@ unsigned TypeOf (const Type* T) return CF_FLOAT; case T_FUNC: - return (((FuncDesc*) T->A.P)->Flags & FD_VARIADIC)? 0 : CF_FIXARGC; + /* 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; @@ -546,7 +841,20 @@ unsigned TypeOf (const Type* T) -Type* Indirect (Type* T) +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. */ @@ -560,22 +868,399 @@ Type* Indirect (Type* T) -Type* ArrayToPtr (Type* T) +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 PointerTo (GetElementType (T)); + 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* 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)) { + /* The standard rule is OK for now as we don't support bit-fields with widths > 16. + */ + 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 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 +** variable parameter list. +** Check fails if the type is not a function or a pointer to function. */ { - FuncDesc* F = GetFuncDesc (T); - return (F->Flags & FD_VARIADIC) != 0; + 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)); } @@ -592,7 +1277,7 @@ FuncDesc* GetFuncDesc (const Type* T) CHECK (IsClassFunc (T)); /* Get the function descriptor from the type attributes */ - return T->A.P; + return T->A.F; } @@ -609,12 +1294,12 @@ void SetFuncDesc (Type* T, FuncDesc* F) CHECK (IsClassFunc (T)); /* Set the function descriptor */ - T->A.P = F; + T->A.F = F; } -Type* GetFuncReturn (Type* T) +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) { @@ -631,6 +1316,37 @@ Type* GetFuncReturn (Type* T) +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). @@ -653,7 +1369,7 @@ void SetElementCount (Type* T, long Count) -Type* GetElementType (Type* T) +const Type* GetElementType (const Type* T) /* Return the element type of the given array type. */ { CHECK (IsTypeArray (T)); @@ -662,7 +1378,7 @@ Type* GetElementType (Type* T) -Type* GetBaseElementType (Type* T) +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. @@ -676,63 +1392,26 @@ Type* GetBaseElementType (Type* T) -SymEntry* GetSymEntry (const Type* T) -/* Return a SymEntry pointer from a type */ +struct SymEntry* GetESUSymEntry (const Type* T) +/* Return a SymEntry pointer from an enum/struct/union type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Return the attribute */ - return T->A.P; + return T->A.S; } -void SetSymEntry (Type* T, SymEntry* S) -/* Set the SymEntry pointer for a type */ +void SetESUSymEntry (Type* T, struct SymEntry* S) +/* Set the SymEntry pointer for an enum/struct/union type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Set the attribute */ - T->A.P = S; -} - - - -Type* IntPromotion (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)); - - /* An integer can represent all values from either signed or unsigned char, - ** so convert chars to int and leave all other types alone. - */ - if (IsTypeChar (T)) { - return type_int; - } else { - return T; - } -} - - - -Type* PtrConversion (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 PointerTo (T); - } else if (IsTypeArray (T)) { - return ArrayToPtr (T); - } else { - return T; - } + T->A.S = S; } @@ -754,3 +1433,101 @@ TypeCode AddrSizeQualifier (unsigned AddrSize) } } + + + +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 */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); + SB_Done (&Buf); +} + + + +void PrintFuncSig (FILE* F, const char* Name, const Type* T) +/* Print a function signature */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + StrBuf East = AUTO_STRBUF_INITIALIZER; + StrBuf West = AUTO_STRBUF_INITIALIZER; + + /* Get the function descriptor used in definition */ + const FuncDesc* D = GetFuncDefinitionDesc (T); + + /* Get the parameter list string. Start from the first parameter */ + SymEntry* Param = D->SymTab->SymHead; + unsigned I; + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!HasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (&ParamList, "void"); + } + } else { + if (D->ParamCount > 0) { + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); + } + } + SB_Terminate (&ParamList); + + /* Get the function qualifiers */ + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { + /* Append a space between the qualifiers and the name */ + SB_AppendChar (&Buf, ' '); + } + SB_Terminate (&Buf); + + /* Get the signature string without the return type */ + SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); + SB_Done (&Buf); + SB_Done (&ParamList); + + /* Complete with the return type */ + GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + SB_Append (&West, &East); + SB_Terminate (&West); + + /* Output */ + fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&East); + SB_Done (&West); +} + + + +void PrintRawType (FILE* F, const Type* T) +/* Print a type string in raw hex format (for debugging) */ +{ + while (T->C != T_END) { + fprintf (F, "%04lX ", T->C); + ++T; + } + fprintf (F, "\n"); +} diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 92b3d0122..e36d7c82e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -51,11 +51,21 @@ /*****************************************************************************/ -/* Data */ +/* Forward declarations */ /*****************************************************************************/ +struct StrBuf; +struct SymEntry; + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + /* Basic data types */ enum { @@ -68,65 +78,71 @@ enum { T_TYPE_INT = 0x000003, T_TYPE_LONG = 0x000004, T_TYPE_LONGLONG = 0x000005, - T_TYPE_ENUM = 0x000006, - T_TYPE_FLOAT = 0x000007, - T_TYPE_DOUBLE = 0x000008, - T_TYPE_VOID = 0x000009, - T_TYPE_STRUCT = 0x00000A, - T_TYPE_UNION = 0x00000B, - T_TYPE_ARRAY = 0x00000C, - T_TYPE_PTR = 0x00000D, - T_TYPE_FUNC = 0x00000E, - T_MASK_TYPE = 0x00000F, + 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, /* Type classes */ T_CLASS_NONE = 0x000000, - T_CLASS_INT = 0x000010, - T_CLASS_FLOAT = 0x000020, - T_CLASS_PTR = 0x000030, - T_CLASS_STRUCT = 0x000040, - T_CLASS_FUNC = 0x000050, - T_MASK_CLASS = 0x000070, + T_CLASS_INT = 0x000020, + T_CLASS_FLOAT = 0x000040, + T_CLASS_PTR = 0x000060, + T_CLASS_STRUCT = 0x000080, + T_CLASS_FUNC = 0x0000A0, + T_MASK_CLASS = 0x0000E0, /* Type signedness */ T_SIGN_NONE = 0x000000, - T_SIGN_UNSIGNED = 0x000080, - T_SIGN_SIGNED = 0x000100, - T_MASK_SIGN = 0x000180, + T_SIGN_UNSIGNED = 0x000100, + T_SIGN_SIGNED = 0x000200, + T_MASK_SIGN = 0x000300, /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_SHORT = 0x000200, - T_SIZE_LONG = 0x000400, - T_SIZE_LONGLONG = 0x000600, - T_MASK_SIZE = 0x000600, + T_SIZE_CHAR = 0x001000, + T_SIZE_SHORT = 0x002000, + T_SIZE_INT = 0x003000, + T_SIZE_LONG = 0x004000, + T_SIZE_LONGLONG = 0x005000, + T_MASK_SIZE = 0x00F000, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x000800, - T_QUAL_VOLATILE = 0x001000, - T_QUAL_RESTRICT = 0x002000, - T_QUAL_NEAR = 0x004000, - T_QUAL_FAR = 0x008000, + T_QUAL_CONST = 0x010000, + T_QUAL_VOLATILE = 0x020000, + T_QUAL_RESTRICT = 0x040000, + T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, + T_QUAL_NEAR = 0x080000, + T_QUAL_FAR = 0x100000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x010000, - T_QUAL_CDECL = 0x020000, + T_QUAL_FASTCALL = 0x200000, + T_QUAL_CDECL = 0x400000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x03F800, + T_MASK_QUAL = 0x7F0000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, - T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + 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_NONE, - T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + 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_SIGNED | T_SIZE_NONE, + 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, @@ -136,6 +152,10 @@ enum { 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, + /* More types for convenience */ + T_C_CHAR = T_CHAR | T_QUAL_CONST, + T_C_VOID = T_VOID | T_QUAL_CONST, + /* Aliases */ T_SIZE_T = T_UINT, }; @@ -148,11 +168,16 @@ typedef unsigned long TypeCode; /* Type entry */ typedef struct Type Type; struct Type { - TypeCode C; /* Code for this entry */ + TypeCode C; /* Code for this entry */ union { - void* P; /* Arbitrary attribute pointer */ - long L; /* Numeric attribute value */ - unsigned long U; /* Dito, unsigned */ + struct FuncDesc* F; /* Function description pointer */ + struct SymEntry* S; /* Enum/struct/union tag symbol entry pointer */ + long L; /* Numeric attribute value */ + unsigned long U; /* Dito, unsigned */ + struct { + unsigned Offs; /* Bit offset into storage unit */ + unsigned Width; /* Width in bits */ + } B; /* Data for bit fields */ } A; /* Type attribute if necessary */ }; @@ -187,19 +212,24 @@ struct Type { #define PTR_BITS (8 * SIZEOF_PTR) /* Predefined type strings */ -extern Type type_schar[]; -extern Type type_uchar[]; -extern Type type_int[]; -extern Type type_uint[]; -extern Type type_long[]; -extern Type type_ulong[]; -extern Type type_void[]; -extern Type type_size_t[]; -extern Type type_float[]; -extern Type type_double[]; +extern const Type type_char[]; +extern const Type type_schar[]; +extern const Type type_uchar[]; +extern const Type type_int[]; +extern const Type type_uint[]; +extern const Type type_long[]; +extern const Type type_ulong[]; +extern const Type type_bool[]; +extern const Type type_void[]; +extern const Type type_size_t[]; +extern const Type type_float[]; +extern const Type type_double[]; -/* Forward for the SymEntry struct */ -struct SymEntry; +/* More predefined type strings */ +extern const Type type_char_p[]; +extern const Type type_c_char_p[]; +extern const Type type_void_p[]; +extern const Type type_c_void_p[]; @@ -209,6 +239,23 @@ struct SymEntry; +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 */ @@ -229,41 +276,24 @@ void TypeFree (Type* T); int SignExtendChar (int C); /* Do correct sign extension of a character */ -TypeCode GetDefaultChar (void); -/* Return the default char type (signed/unsigned) depending on the settings */ - 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 */ -Type* PointerTo (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. +const Type* GetStructReplacementType (const Type* SType); +/* Get a replacement type for passing a struct/union in the primary register */ + +long GetIntegerTypeMin (const Type* Type); +/* Get the smallest possible value of the integer type. +** The type must have a known size. */ -void PrintType (FILE* F, const Type* T); -/* Output translation of type array. */ - -void PrintRawType (FILE* F, const Type* T); -/* Print a type string in raw format (for debugging) */ - -void PrintFuncSig (FILE* F, const char* Name, Type* T); -/* Print a function signature. */ - -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 +unsigned long GetIntegerTypeMax (const Type* Type); +/* Get the largest possible value of the integer type. +** The type must have a known size. +*/ #if defined(HAVE_INLINE) INLINE TypeCode UnqualifiedType (TypeCode T) @@ -275,6 +305,14 @@ INLINE TypeCode UnqualifiedType (TypeCode T) # 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. +*/ + unsigned SizeOf (const Type* T); /* Compute size of object represented by type array. */ @@ -295,132 +333,277 @@ unsigned CheckedPSizeOf (const Type* T); unsigned TypeOf (const Type* T); /* Get the code generator base type of the object */ -Type* Indirect (Type* T); +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. */ -Type* ArrayToPtr (Type* T); +Type* IndirectModifiable (Type* T); +/* Do one indirection for the given type, that is, return the type where the +** given type points to. +*/ + +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. +*/ + +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. +*/ + +Type* ArrayToPtr (const Type* T); /* Convert an array to a pointer to it's first element */ +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. +*/ + +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. +*/ + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst); +/* Perform the usual arithmetic conversions for binary operators. */ + +const Type* SignedType (const Type* T); +/* Get signed counterpart of the integral type */ + +const Type* UnsignedType (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. +*/ + #if defined(HAVE_INLINE) -INLINE TypeCode GetType (const Type* T) +INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ { return (T->C & T_MASK_TYPE); } #else -# define GetType(T) ((T)->C & T_MASK_TYPE) +# define GetRawType(T) ((T)->C & T_MASK_TYPE) #endif #if defined(HAVE_INLINE) INLINE int IsTypeChar (const Type* T) -/* Return true if this is a character type */ +/* Return true if this is a char type */ { - return (GetType (T) == T_TYPE_CHAR); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); } #else -# define IsTypeChar(T) (GetType (T) == T_TYPE_CHAR) +# define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeShort (const Type* T) +/* Return true if this is a short type (signed or unsigned) */ +{ + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT); +} +#else +# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_INT); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT); } #else -# define IsTypeInt(T) (GetType (T) == T_TYPE_INT) +# define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeLong (const Type* T) -/* Return true if this is a long type (signed or unsigned) */ +/* Return true if this is a long int type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_LONG); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); } #else -# define IsTypeLong(T) (GetType (T) == T_TYPE_LONG) +# define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_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). +*/ +{ + return (GetRawType (T) == T_TYPE_CHAR); +} +#else +# define IsClassChar(T) (GetRawType (T) == T_TYPE_CHAR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeChar (const Type* T) +/* Return true if this is a char raw type (including signed/unsigned) */ +{ + return (GetRawType (T) == T_TYPE_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) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFloat (const Type* T) /* Return true if this is a float type */ { - return (GetType (T) == T_TYPE_FLOAT); + return (GetRawType (T) == T_TYPE_FLOAT); } #else -# define IsTypeFloat(T) (GetType (T) == T_TYPE_FLOAT) +# define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeDouble (const Type* T) /* Return true if this is a double type */ { - return (GetType (T) == T_TYPE_DOUBLE); + return (GetRawType (T) == T_TYPE_DOUBLE); } #else -# define IsTypeDouble(T) (GetType (T) == T_TYPE_DOUBLE) +# define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE) #endif #if defined(HAVE_INLINE) INLINE int IsTypePtr (const Type* T) /* Return true if this is a pointer type */ { - return (GetType (T) == T_TYPE_PTR); + return (GetRawType (T) == T_TYPE_PTR); } #else -# define IsTypePtr(T) (GetType (T) == T_TYPE_PTR) +# define IsTypePtr(T) (GetRawType (T) == T_TYPE_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); +} +#else +# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_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); +} +#else +# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == 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); +} +#else +# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeBitField (const Type* T) +/* Return true if this is a bit-field (either signed or unsigned) */ +{ + return IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T); +} +#else +# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T)) #endif #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ { - return (GetType (T) == T_TYPE_STRUCT); + return (GetRawType (T) == T_TYPE_STRUCT); } #else -# define IsTypeStruct(T) (GetType (T) == T_TYPE_STRUCT) +# define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnion (const Type* T) /* Return true if this is a union type */ { - return (GetType (T) == T_TYPE_UNION); + return (GetRawType (T) == T_TYPE_UNION); } #else -# define IsTypeUnion(T) (GetType (T) == T_TYPE_UNION) +# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION) #endif #if defined(HAVE_INLINE) INLINE int IsTypeArray (const Type* T) /* Return true if this is an array type */ { - return (GetType (T) == T_TYPE_ARRAY); + return (GetRawType (T) == T_TYPE_ARRAY); } #else -# define IsTypeArray(T) (GetType (T) == T_TYPE_ARRAY) +# define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY) #endif #if defined(HAVE_INLINE) INLINE int IsTypeVoid (const Type* T) /* Return true if this is a void type */ { - return (GetType (T) == T_TYPE_VOID); + return (GetRawType (T) == T_TYPE_VOID); } #else -# define IsTypeVoid(T) (GetType (T) == T_TYPE_VOID) +# define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFunc (const Type* T) /* Return true if this is a function class */ { - return (GetType (T) == T_TYPE_FUNC); + return (GetRawType (T) == T_TYPE_FUNC); } #else -# define IsTypeFunc(T) (GetType (T) == T_TYPE_FUNC) +# define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC) #endif #if defined(HAVE_INLINE) @@ -475,7 +658,7 @@ INLINE int IsClassPtr (const Type* T) #if defined(HAVE_INLINE) INLINE int IsClassStruct (const Type* T) -/* Return true if this is a struct type */ +/* Return true if this is a struct or union type */ { return (GetClass (T) == T_CLASS_STRUCT); } @@ -493,14 +676,73 @@ INLINE int IsClassFunc (const Type* T) # define IsClassFunc(T) (GetClass (T) == T_CLASS_FUNC) #endif +int IsClassObject (const Type* T); +/* Return true if this is a fully described object type */ + +int IsClassIncomplete (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 IsClassBasic (const Type* T); +/* Return true if this is a char, integer or floating type */ + +int IsClassScalar (const Type* T); +/* Return true if this is an arithmetic or pointer type */ + +int IsClassDerived (const Type* T); +/* Return true if this is an array, struct, union, function or pointer type */ + +int IsClassAggregate (const Type* T); +/* Return true if this is an array or struct type */ + +int IsRelationType (const Type* T); +/* Return true if this is an arithmetic, array or pointer type */ + +int IsCastType (const Type* T); +/* Return true if this type can be used for casting */ + +int IsESUType (const Type* T); +/* Return true if this is an enum/struct/union type */ + +int IsIncompleteESUType (const Type* T); +/* Return true if this is an incomplete ESU type */ + +int IsEmptiableObjectType (const Type* T); +/* Return true if this is a struct/union/void type that can have zero size */ + +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 GetSignedness (const Type* T) -/* Get the sign of a type */ +INLINE TypeCode GetRawSignedness (const Type* T) +/* Get the raw signedness of a type */ { - return (T->C & T_MASK_SIGN); + return ((T)->C & T_MASK_SIGN); } #else -# define GetSignedness(T) ((T)->C & T_MASK_SIGN) +# 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 + +#if defined(HAVE_INLINE) +INLINE int IsRawSignUnsigned (const Type* T) +/* Return true if this is an unsigned raw type */ +{ + return (GetRawSignedness (T) == T_SIGN_UNSIGNED); +} +#else +# define IsRawSignUnsigned(T) (GetRawSignedness (T) == T_SIGN_UNSIGNED) #endif #if defined(HAVE_INLINE) @@ -513,6 +755,16 @@ INLINE int IsSignUnsigned (const Type* T) # define IsSignUnsigned(T) (GetSignedness (T) == T_SIGN_UNSIGNED) #endif +#if defined(HAVE_INLINE) +INLINE int IsRawSignSigned (const Type* T) +/* Return true if this is a signed raw type */ +{ + return (GetRawSignedness (T) == T_SIGN_SIGNED); +} +#else +# define IsRawSignSigned(T) (GetRawSignedness (T) == T_SIGN_SIGNED) +#endif + #if defined(HAVE_INLINE) INLINE int IsSignSigned (const Type* T) /* Return true if this is a signed type */ @@ -523,6 +775,26 @@ 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 */ @@ -615,18 +887,15 @@ INLINE int IsQualCConv (const Type* T) int IsVariadicFunc (const Type* T) attribute ((const)); /* Return true if this is a function type or pointer to function type with -** variable parameter list +** variable parameter list. +** Check fails if the type is not a function or a pointer to function. */ -#if defined(HAVE_INLINE) -INLINE TypeCode GetSizeModifier (const Type* T) -/* Get the size modifier of a type */ -{ - return (T->C & T_MASK_SIZE); -} -#else -# define GetSizeModifier(T) ((T)->C & T_MASK_SIZE) -#endif +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 */ @@ -634,9 +903,15 @@ FuncDesc* GetFuncDesc (const Type* T) attribute ((const)); void SetFuncDesc (Type* T, FuncDesc* F); /* Set the FuncDesc pointer in a function or pointer-to-function type */ -Type* GetFuncReturn (Type* T) attribute ((const)); +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). @@ -647,31 +922,20 @@ void SetElementCount (Type* T, long Count); ** array type). */ -Type* GetElementType (Type* T); +const Type* GetElementType (const Type* T); /* Return the element type of the given array type. */ -Type* GetBaseElementType (Type* T); +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* GetSymEntry (const Type* T) attribute ((const)); -/* Return a SymEntry pointer from a type */ +struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const)); +/* Return a SymEntry pointer from an enum/struct/union type */ -void SetSymEntry (Type* T, struct SymEntry* S); -/* Set the SymEntry pointer for a type */ - -Type* IntPromotion (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. -*/ - -Type* PtrConversion (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. -*/ +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 */ @@ -696,6 +960,28 @@ 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 + +void PrintType (FILE* F, const Type* T); +/* Print fulle name of the type */ + +void PrintFuncSig (FILE* F, const char* Name, const Type* T); +/* Print a function signature */ + +void PrintRawType (FILE* F, const Type* T); +/* Print a type string in raw hex format (for debugging) */ + /* End of datatype.h */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index d7940c93b..c1346e872 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -41,6 +41,7 @@ /* common */ #include "addrsize.h" #include "mmodel.h" +#include "shift.h" #include "xmalloc.h" /* cc65 */ @@ -58,6 +59,7 @@ #include "pragma.h" #include "scanner.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "wrappedcall.h" #include "typeconv.h" @@ -86,10 +88,11 @@ struct StructInitData { -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, + int* SignednessSpecified); /* Parse a type specifier */ -static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers); +static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers); /* Parse initialization of variables. Return the number of data bytes. */ @@ -251,18 +254,21 @@ static void OptionalInt (void) -static void OptionalSigned (void) +static void OptionalSigned (int* SignednessSpecified) /* Eat an optional "signed" token */ { if (CurTok.Tok == TOK_SIGNED) { /* Skip it */ NextToken (); + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } } } -static void InitDeclSpec (DeclSpec* D) +void InitDeclSpec (DeclSpec* D) /* Initialize the DeclSpec struct for use */ { D->StorageClass = 0; @@ -409,6 +415,79 @@ 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 */ +{ + Type* T = DataType; + + while (T->C != T_END) { + if (IsTypeArray (T)) { + ++T; + if (IsIncompleteESUType (T)) { + /* We cannot have an array of incomplete elements */ + Error ("Array of incomplete element type '%s'", GetFullTypeName (T)); + } else if (SizeOf (T) == 0) { + /* If the array is multi-dimensional, try to get the true + ** element type. + */ + if (IsTypeArray (T)) { + continue; + } + /* We could support certain 0-size element types as an extension */ + if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { + Error ("Array of 0-size element type '%s'", GetFullTypeName (T)); + } + } + } else { + ++T; + } + } +} + + + static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) /* Parse a storage class */ { @@ -416,64 +495,122 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) D->Flags &= ~DS_DEF_STORAGE; /* Check the storage class given */ - switch (CurTok.Tok) { - - case TOK_EXTERN: - D->StorageClass = SC_EXTERN | SC_STATIC; - NextToken (); - break; - - case TOK_STATIC: - D->StorageClass = SC_STATIC; - NextToken (); - break; - - case TOK_REGISTER: - D->StorageClass = SC_REGISTER | SC_STATIC; - NextToken (); - break; - - case TOK_AUTO: - D->StorageClass = SC_AUTO; - NextToken (); - break; - - case TOK_TYPEDEF: - D->StorageClass = SC_TYPEDEF; - NextToken (); - break; - - default: - /* No storage class given, use default */ - D->Flags |= DS_DEF_STORAGE; - D->StorageClass = DefStorage; - break; + 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 void ParseEnumDecl (void) -/* Process an enum declaration . */ +static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags) +/* Handle an enum, struct or union forward decl */ { - int EnumVal; - ident Ident; - - /* Accept forward definitions */ - if (CurTok.Tok != TOK_LCURLY) { - return; + /* 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) { + if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { + Entry = AddStructSym (Name, Flags, 0, 0, DSFlags); + } else { + Entry = AddEnumSym (Name, Flags, 0, 0, DSFlags); + } + } else if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { + /* Already defined, but not the same type class */ + Error ("Symbol '%s' is already different kind", Name); } + return Entry; +} + + + +static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) +/* GitHub #1093 - We use unsigned types to save spaces whenever possible. +** If both the signed and unsigned integer types of the same minimum size +** capable of representing all values of the enum, we prefer the unsigned +** one. +** Return 0 if impossible to represent Min and Max as the same integer type. +*/ +{ + const Type* Underlying = type_int; /* default type */ + + /* Change the underlying type if necessary */ + if (Min < 0 || Signed) { + /* We can't use unsigned types if there are any negative values */ + if (Max > (unsigned long)INT32_MAX) { + /* No way to represent both Min and Max as the same integer type */ + Underlying = 0; + } else if (Min < INT16_MIN || Max > (unsigned long)INT16_MAX) { + Underlying = type_long; + } else if (Min < INT8_MIN || Max > (unsigned long)INT8_MAX) { + Underlying = type_int; + } else { + Underlying = type_schar; + } + } else { + if (Max > UINT16_MAX) { + Underlying = type_ulong; + } else if (Max > UINT8_MAX) { + Underlying = type_uint; + } else { + Underlying = type_uchar; + } + } + + return Underlying; +} + + + +static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) +/* Process an enum declaration */ +{ + SymTable* FieldTab; + long EnumVal; + int IsSigned; + int IsIncremented; + ident Ident; + long MinConstant = 0; + unsigned long MaxConstant = 0; + const Type* NewType = 0; /* new member type */ + const Type* MemberType = type_int; /* default member type */ + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; + + + if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward definition */ + return ESUForwardDecl (Name, SC_ENUM, DSFlags); + } + + /* Add a forward declaration for the enum tag in the current lexical level */ + AddEnumSym (Name, 0, 0, 0, DSFlags); /* Skip the opening curly brace */ NextToken (); /* Read the enum tags */ - EnumVal = 0; + EnumVal = -1L; while (CurTok.Tok != TOK_RCURLY) { /* We expect an identifier */ if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected"); + Error ("Identifier expected for enumerator declarator"); + /* Avoid excessive errors */ + NextToken (); continue; } @@ -483,57 +620,178 @@ static void ParseEnumDecl (void) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; + NextToken (); - ConstAbsIntExpr (hie1, &Expr); - EnumVal = Expr.IVal; + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); + EnumVal = Expr.IVal; + MemberType = Expr.Type; + IsSigned = IsSignSigned (MemberType); + IsIncremented = 0; + + } else { + + /* Defaulted with the same signedness as the previous member's */ + IsSigned = IsSignSigned (MemberType) && + (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); + + /* Enumerate. Signed integer overflow is UB but unsigned integers + ** are guaranteed to wrap around. + */ + EnumVal = (long)((unsigned long)EnumVal + 1UL); + + 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)); + } + + IsIncremented = 1; } - /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUM, EnumVal++); + /* Track down the min/max values and evaluate the type of EnumVal + ** using GetEnumeratorType in a tricky way. + */ + if (!IsSigned || EnumVal >= 0) { + if ((unsigned long)EnumVal > MaxConstant) { + MaxConstant = (unsigned long)EnumVal; + } + NewType = GetEnumeratorType (0, EnumVal, IsSigned); + } else { + if (EnumVal < MinConstant) { + MinConstant = EnumVal; + } + NewType = GetEnumeratorType (EnumVal, 0, 1); + } + + /* GetEnumeratorType above should never fail, but just in case */ + if (NewType == 0) { + Internal ("Unexpected failure with GetEnumeratorType: %lx", EnumVal); + NewType = type_ulong; + } else if (SizeOf (NewType) < SizeOf (type_int)) { + /* Integer constants are not shorter than int */ + NewType = type_int; + } + + /* Warn if the incremented value exceeds the range of the previous + ** type. + */ + if (IsIncremented && + EnumVal >= 0 && + NewType->C != UnqualifiedType (MemberType->C)) { + /* The possible overflow here can only be when EnumVal > 0 */ + Warning ("Enumerator '%s' (value = %lu) is of type '%s'", + Ident, + (unsigned long)EnumVal, + GetBasicTypeName (NewType)); + } + + /* Warn if the value exceeds range of 'int' in standard mode */ + if (IS_Get (&Standard) != STD_CC65 && NewType->C != T_INT) { + if (!IsSigned || EnumVal >= 0) { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %lu) is too large", + Ident, + (unsigned long)EnumVal); + } else { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %ld) is too small", + Ident, + EnumVal); + } + } + + /* Add an entry of the enumerator to the symbol table */ + AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); + + /* Use this type for following members */ + MemberType = NewType; /* Check for end of definition */ - if (CurTok.Tok != TOK_COMMA) + if (CurTok.Tok != TOK_COMMA) { break; + } NextToken (); } ConsumeRCurly (); + + /* Check if there have been any members. Error if none */ + if (NewType == 0) { + Error ("Empty enum is invalid"); + } + + /* This evaluates the underlying type of the whole enum */ + MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); + if (MemberType == 0) { + /* It is very likely that the program is wrong */ + Error ("Enumeration values cannot be represented all as 'long'\n" + "\tMin enumerator value = %ld, Max enumerator value = %lu", + MinConstant, MaxConstant); + + /* Avoid more errors */ + MemberType = type_long; + } + + FieldTab = GetSymTab (); + + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + return AddEnumSym (Name, Flags, MemberType, FieldTab, DSFlags); } -static int ParseFieldWidth (Declaration* Decl) +static int ParseFieldWidth (Declaration* D) /* Parse an optional field width. Returns -1 if no field width is specified, ** otherwise the width of the field. */ { - ExprDesc Expr; - if (CurTok.Tok != TOK_COLON) { /* No bit-field declaration */ return -1; } + if (!IsClassInt (D->Type)) { + /* Only integer types may be used for bit-fields */ + Error ("Bit-field has invalid type '%s', must be integral", + GetBasicTypeName (D->Type)); + + /* Avoid a diagnostic storm by giving the bit-field the widest valid + ** signed type, and continuing to parse. + */ + D->Type[0].C = T_INT; + } + + /* TODO: This can be relaxed to be any integral type, but + ** ParseStructInit currently supports only up to int. + */ + if (SizeOf (D->Type) > SizeOf (type_uint)) { + /* Only int-sized or smaller types may be used for bit-fields, for now */ + Error ("cc65 currently supports only char-sized and int-sized bit-field types"); + + /* Avoid a diagnostic storm */ + D->Type[0].C = T_INT; + } + /* Read the width */ NextToken (); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); + if (Expr.IVal < 0) { Error ("Negative width in bit-field"); return -1; } - if (Expr.IVal > (int) INT_BITS) { + if (Expr.IVal > (long)(SizeOf (D->Type) * CHAR_BITS)) { Error ("Width of bit-field exceeds its type"); return -1; } - if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { + if (Expr.IVal == 0 && D->Ident[0] != '\0') { Error ("Zero width for named bit-field"); return -1; } - if (!IsTypeInt (Decl->Type)) { - /* Only integer types may be used for bit-fields */ - Error ("Bit-field has invalid type"); - return -1; - } /* Return the field width */ return (int) Expr.IVal; @@ -541,34 +799,41 @@ static int ParseFieldWidth (Declaration* Decl) -static SymEntry* StructOrUnionForwardDecl (const char* Name, unsigned Type) -/* Handle a struct or union forward decl */ +static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) +/* Pad the current struct with an anonymous bit-field aligned to the next byte. +** Return how many bits are used to pad. +*/ { - /* Try to find a struct/union with the given name. If there is none, - ** insert a forward declaration into the current lexical level. + /* MSVC complains about unary negation of unsigned, + ** so it has been rewritten as subtraction. */ - SymEntry* Entry = FindTagSym (Name); - if (Entry == 0) { - Entry = AddStructSym (Name, Type, 0, 0); - } else if ((Entry->Flags & SC_TYPEMASK) != Type) { - /* Already defined, but no struct */ - Error ("Symbol '%s' is already different kind", Name); - } - return Entry; + unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; + + /* We need an anonymous name */ + ident Ident; + AnonName (Ident, "bit-field"); + + /* Add an anonymous bit-field that aligns to the next + ** byte. + */ + AddBitField (Ident, type_uchar, StructSize, BitOffs, PaddingBits, + /*SignednessSpecified=*/1); + + return PaddingBits; } -static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) -/* Copy fields from an anon union/struct into the current lexical level. The -** function returns the size of the embedded struct/union. +static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon) +/* Create alias fields from an anon union/struct in the current lexical level. +** The function returns the count of created aliases. */ { - /* Get the pointer to the symbol table entry of the anon struct */ - SymEntry* Entry = GetSymEntry (Decl->Type); + unsigned Count = 0; + SymEntry* Alias; - /* Get the size of the anon struct */ - unsigned Size = Entry->V.S.Size; + /* 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. @@ -576,7 +841,7 @@ static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) SymTable* Tab = Entry->V.S.SymTab; if (Tab == 0) { /* Incomplete definition - has been flagged before */ - return Size; + return 0; } /* Get a pointer to the list of symbols. Then walk the list adding copies @@ -585,10 +850,13 @@ static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) Entry = Tab->SymHead; while (Entry) { - /* Enter a copy of this symbol adjusting the offset. We will just - ** reuse the type string here. - */ - AddLocalSym (Entry->Name, Entry->Type, SC_STRUCTFIELD, Offs + Entry->V.Offs); + /* 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; + ++Count; + } /* Currently, there can not be any attributes, but if there will be ** some in the future, we want to know this. @@ -599,13 +867,13 @@ static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) Entry = Entry->NextSym; } - /* Return the size of the embedded struct */ - return Size; + /* Return the count of created aliases */ + return Count; } -static SymEntry* ParseUnionDecl (const char* Name) +static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) /* Parse a union declaration. */ { @@ -613,15 +881,21 @@ static SymEntry* ParseUnionDecl (const char* Name) unsigned FieldSize; int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; + SymEntry* UnionTagEntry; + SymEntry* Entry; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_UNION); + /* Just a forward declaration */ + return ESUForwardDecl (Name, SC_UNION, DSFlags); } - /* Add a forward declaration for the struct in the current lexical level */ - AddStructSym (Name, SC_UNION, 0, 0); + /* Add a forward declaration for the union tag in the current lexical level */ + UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0, DSFlags); + + UnionTagEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -635,8 +909,9 @@ static SymEntry* ParseUnionDecl (const char* Name) /* Get the type of the entry */ DeclSpec Spec; + int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { @@ -663,29 +938,48 @@ static SymEntry* ParseUnionDecl (const char* Name) /* This is an anonymous struct or union. Copy the fields ** into the current level. */ - FieldSize = CopyAnonStructFields (&Decl, 0); - if (FieldSize > UnionSize) { - UnionSize = FieldSize; - } - + AnonFieldName (Decl.Ident, "field", UnionTagEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); } - goto NextMember; + } + + /* Check for incomplete types including 'void' */ + if (IsClassIncomplete (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); } /* Handle sizes */ - FieldSize = CheckedSizeOf (Decl.Type); + FieldSize = SizeOf (Decl.Type); if (FieldSize > UnionSize) { UnionSize = FieldSize; } /* Add a field entry to the table. */ if (FieldWidth > 0) { - AddBitField (Decl.Ident, 0, 0, FieldWidth); - } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + /* For a union, allocate space for the type specified by the + ** bit-field. + */ + AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, + SignednessSpecified); + } else if (Decl.Ident[0] != '\0') { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + if (IsAnonName (Decl.Ident)) { + Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Entry); + } + + /* 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; + Flags |= SC_HAVEFAM; + } + } } NextMember: if (CurTok.Tok != TOK_COMMA) { @@ -700,16 +994,26 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { NextToken (); /* Remember the symbol table and leave the struct level */ - FieldTab = GetSymTab (); + FieldTab = GetFieldSymTab (); LeaveStructLevel (); + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + /* Empty union is not supported now */ + if (UnionSize == 0) { + Error ("Empty union type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_UNION, UnionSize, FieldTab); + return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags); } -static SymEntry* ParseStructDecl (const char* Name) +static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) /* Parse a struct declaration. */ { @@ -718,15 +1022,21 @@ static SymEntry* ParseStructDecl (const char* Name) unsigned BitOffs; /* Bit offset for bit-fields */ int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; + SymEntry* StructTagEntry; + SymEntry* Entry; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_STRUCT); + /* Just a forward declaration */ + return ESUForwardDecl (Name, SC_STRUCT, DSFlags); } - /* Add a forward declaration for the struct in the current lexical level */ - AddStructSym (Name, SC_STRUCT, 0, 0); + /* Add a forward declaration for the struct tag in the current lexical level */ + StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0, DSFlags); + + StructTagEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -742,14 +1052,21 @@ static SymEntry* ParseStructDecl (const char* Name) /* Get the type of the entry */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + + int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { Declaration Decl; - ident Ident; /* If we had a flexible array member before, no other fields can ** follow. @@ -766,23 +1083,20 @@ static SymEntry* ParseStructDecl (const char* Name) FieldWidth = ParseFieldWidth (&Decl); /* If this is not a bit field, or the bit field is too large for - ** the remainder of the current member, or we have a bit field + ** the remainder of the allocated unit, or we have a bit field ** with width zero, align the struct to the next member by adding ** a member with an anonymous name. */ if (BitOffs > 0) { - if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { - - /* We need an anonymous name */ - AnonName (Ident, "bit-field"); - + if (FieldWidth <= 0 || + (BitOffs + FieldWidth) > CHAR_BITS * SizeOf (Decl.Type)) { /* Add an anonymous bit-field that aligns to the next - ** storage unit. + ** byte. */ - AddBitField (Ident, StructSize, BitOffs, INT_BITS - BitOffs); + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); /* No bits left */ - StructSize += SIZEOF_INT; + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; BitOffs = 0; } } @@ -803,6 +1117,8 @@ static SymEntry* ParseStructDecl (const char* Name) Error ("Flexible array member cannot be first struct field"); } FlexibleMember = 1; + Flags |= SC_HAVEFAM; + /* Assume zero for size calculations */ SetElementCount (Decl.Type, FLEXIBLE); } @@ -818,19 +1134,24 @@ static SymEntry* ParseStructDecl (const char* Name) /* This is an anonymous struct or union. Copy the ** fields into the current level. */ - StructSize += CopyAnonStructFields (&Decl, StructSize); - + AnonFieldName (Decl.Ident, "field", StructTagEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); } - goto NextMember; } else { /* A bit-field without a name will get an anonymous one */ AnonName (Decl.Ident, "bit-field"); } } + /* Check for incomplete types including 'void' */ + if (IsClassIncomplete (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } + /* Add a field entry to the table */ if (FieldWidth > 0) { /* Full bytes have already been added to the StructSize, @@ -839,16 +1160,31 @@ static SymEntry* ParseStructDecl (const char* Name) ** bit-field as a char type in expressions. */ CHECK (BitOffs < CHAR_BITS); - AddBitField (Decl.Ident, StructSize, BitOffs, FieldWidth); + AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, + FieldWidth, SignednessSpecified); BitOffs += FieldWidth; - CHECK (BitOffs <= INT_BITS); + CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); /* Add any full bytes to the struct size. */ StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; - } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + } else if (Decl.Ident[0] != '\0') { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + if (IsAnonName (Decl.Ident)) { + Entry->V.A.ANumber = StructTagEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Entry); + } + + /* 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; + Flags |= SC_HAVEFAM; + } + } + if (!FlexibleMember) { - StructSize += CheckedSizeOf (Decl.Type); + StructSize += SizeOf (Decl.Type); } } @@ -860,30 +1196,51 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { ConsumeSemi (); } - /* If we have bits from bit-fields left, add them to the size. */ if (BitOffs > 0) { - StructSize += ((BitOffs + CHAR_BITS - 1) / CHAR_BITS); + /* If we have bits from bit-fields left, pad the struct to next byte */ + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); + + /* No bits left */ + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; } /* Skip the closing brace */ NextToken (); /* Remember the symbol table and leave the struct level */ - FieldTab = GetSymTab (); + FieldTab = GetFieldSymTab (); LeaveStructLevel (); + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + /* Empty struct is not supported now */ + if (StructSize == 0) { + Error ("Empty struct type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_STRUCT, StructSize, FieldTab); + return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags); } -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) -/* Parse a type specifier */ +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, + 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; + if (SignednessSpecified != NULL) { + *SignednessSpecified = 0; + } + /* Assume we have an explicit type */ D->Flags &= ~DS_DEF_TYPE; @@ -902,19 +1259,22 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_CHAR: NextToken (); - D->Type[0].C = GetDefaultChar(); + D->Type[0].C = T_CHAR; D->Type[1].C = T_END; break; case TOK_LONG: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); OptionalInt (); D->Type[0].C = T_ULONG; D->Type[1].C = T_END; } else { - OptionalSigned (); + OptionalSigned (SignednessSpecified); OptionalInt (); D->Type[0].C = T_LONG; D->Type[1].C = T_END; @@ -924,12 +1284,15 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_SHORT: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); OptionalInt (); D->Type[0].C = T_USHORT; D->Type[1].C = T_END; } else { - OptionalSigned (); + OptionalSigned (SignednessSpecified); OptionalInt (); D->Type[0].C = T_SHORT; D->Type[1].C = T_END; @@ -943,6 +1306,9 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) break; case TOK_SIGNED: + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); switch (CurTok.Tok) { @@ -978,6 +1344,9 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) break; case TOK_UNSIGNED: + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); switch (CurTok.Tok) { @@ -1036,10 +1405,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); + Entry = ParseUnionDecl (Ident, &D->Flags); /* Encode the union entry into the type */ D->Type[0].C = T_UNION; - SetSymEntry (D->Type, Entry); + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -1055,47 +1424,69 @@ 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); + Entry = ParseStructDecl (Ident, &D->Flags); /* Encode the struct entry into the type */ D->Type[0].C = T_STRUCT; - SetSymEntry (D->Type, Entry); + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; case TOK_ENUM: NextToken (); - if (CurTok.Tok != TOK_LCURLY) { - /* Named enum */ - if (CurTok.Tok == TOK_IDENT) { - /* Find an entry with this name */ - Entry = FindTagSym (CurTok.Ident); - if (Entry) { - if (SymIsLocal (Entry) && (Entry->Flags & SC_ENUM) == 0) { - Error ("Symbol '%s' is already different kind", Entry->Name); - } - } else { - /* Insert entry into table ### */ - } - /* Skip the identifier */ - NextToken (); - } else { + /* Named enum */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else { + if (CurTok.Tok != TOK_LCURLY) { Error ("Identifier expected"); + } else { + AnonName (Ident, "enum"); } } /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - ParseEnumDecl (); - D->Type[0].C = T_INT; + Entry = ParseEnumDecl (Ident, &D->Flags); + /* Encode the enum entry into the type */ + D->Type[0].C |= T_ENUM; + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; + /* The signedness of enums is determined by the type, so say this is specified to avoid + ** the int -> unsigned int handling for plain int bit-fields in AddBitField. + */ + if (SignednessSpecified) { + *SignednessSpecified = 1; + } break; case TOK_IDENT: - Entry = FindSym (CurTok.Ident); - if (Entry && SymIsTypeDef (Entry)) { - /* It's a typedef */ - NextToken (); - TypeCopy (D->Type, Entry->Type); + /* This could be a label */ + if (NextTok.Tok != TOK_COLON) { + Entry = FindSym (CurTok.Ident); + if (Entry && SymIsTypeDef (Entry)) { + /* It's a typedef */ + NextToken (); + TypeCopy (D->Type, Entry->Type); + /* If it's a typedef, we should actually use whether the signedness was + ** specified on the typedef, but that information has been lost. Treat the + ** signedness as being specified to work around the ICE in #1267. + ** Unforunately, this will cause plain int bit-fields defined via typedefs + ** to be treated as signed rather than unsigned. + */ + if (SignednessSpecified) { + *SignednessSpecified = 1; + } + break; + } + } else { + /* This is a label. Use the default type flag to end the loop + ** in DeclareLocals. The type code used here doesn't matter as + ** long as it has no qualifiers. + */ + D->Flags |= DS_DEF_TYPE; + D->Type[0].C = T_QUAL_NONE; + D->Type[1].C = T_END; break; } /* FALL THROUGH */ @@ -1119,14 +1510,28 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) -static Type* ParamTypeCvt (Type* T) -/* If T is an array, convert it to a pointer else do nothing. Return the -** resulting type. +static const Type* ParamTypeCvt (Type* T) +/* If T is an array or a function, convert it to a pointer else do nothing. +** Return the resulting type. */ { + Type* Tmp = 0; + if (IsTypeArray (T)) { - T->C = T_PTR; + Tmp = ArrayToPtr (T); + } else if (IsTypeFunc (T)) { + Tmp = NewPointerTo (T); } + + if (Tmp != 0) { + /* Do several fixes on qualifiers */ + FixQualifiers (Tmp); + + /* Replace the type */ + TypeCopy (T, Tmp); + TypeFree (Tmp); + } + return T; } @@ -1197,6 +1602,13 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Read the parameter */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + + /* Warn about new local type declaration */ + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + Warning ("'%s' will be invisible out of this function", + GetFullTypeName (Spec.Type)); + } + if (Decl.Ident[0] != '\0') { /* We have a name given. Search for the symbol */ @@ -1264,6 +1676,12 @@ static void ParseAnsiParamList (FuncDesc* F) Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } + /* Warn about new local type declaration */ + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + Warning ("'%s' will be invisible out of this function", + GetFullTypeName (Spec.Type)); + } + /* Allow parameters without a name, but remember if we had some to ** eventually print an error message later. */ @@ -1318,10 +1736,9 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) /* Parse the argument list of a function. */ { - unsigned Offs; SymEntry* Sym; SymEntry* WrappedCall; - unsigned char WrappedCallData; + unsigned int WrappedCallData; /* Create a new function descriptor */ FuncDesc* F = NewFuncDesc (); @@ -1365,22 +1782,10 @@ static FuncDesc* ParseFuncDecl (void) */ F->LastParam = GetSymTab()->SymTail; - /* Assign offsets. If the function has a variable parameter list, - ** there's one additional byte (the arg size). + /* It is allowed to use incomplete types in function prototypes, so we + ** won't always get to know the parameter sizes here and may do that later. */ - Offs = (F->Flags & FD_VARIADIC)? 1 : 0; - Sym = F->LastParam; - while (Sym) { - unsigned Size = CheckedSizeOf (Sym->Type); - if (SymIsRegVar (Sym)) { - Sym->V.R.SaveOffs = Offs; - } else { - Sym->V.Offs = Offs; - } - Offs += Size; - F->ParamSize += Size; - Sym = Sym->PrevSym; - } + F->Flags |= FD_INCOMPLETE_PARAM; /* Leave the lexical level remembering the symbol tables */ RememberFunctionLevel (F); @@ -1416,7 +1821,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) NextToken (); /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT); + Qualifiers |= OptionalQualifiers (T_QUAL_CVR); /* Parse the type that the pointer points to */ Declarator (Spec, D, Mode); @@ -1476,7 +1881,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Was there a previous entry? If so, copy WrappedCall info from it */ PrevEntry = FindGlobalSym (D->Ident); if (PrevEntry && PrevEntry->Flags & SC_FUNC) { - FuncDesc* D = PrevEntry->V.F.Func; + FuncDesc* D = GetFuncDesc (PrevEntry->Type); if (D->WrappedCall && !F->WrappedCall) { F->WrappedCall = D->WrappedCall; F->WrappedCallData = D->WrappedCallData; @@ -1486,7 +1891,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Add the function type. Be sure to bounds check the type buffer */ NeedTypeSpace (D, 1); D->Type[D->Index].C = T_FUNC | Qualifiers; - D->Type[D->Index].A.P = F; + D->Type[D->Index].A.F = F; ++D->Index; /* Qualifiers now used */ @@ -1507,8 +1912,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { - ExprDesc Expr; - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); if (Expr.IVal <= 0) { if (D->Ident[0] != '\0') { Error ("Size of array '%s' is invalid", D->Ident); @@ -1562,7 +1966,7 @@ Type* ParseType (Type* T) /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL); /* Parse additional declarators */ ParseDecl (&Spec, &Decl, DM_NO_IDENT); @@ -1579,6 +1983,9 @@ Type* ParseType (Type* T) void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Parse a variable, type or function declaration */ { + /* Used to check if we have any errors during parsing this */ + unsigned PrevErrorCount = ErrorCount; + /* Initialize the Declaration struct */ InitDeclaration (D); @@ -1595,6 +2002,9 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Do several fixes on qualifiers */ FixQualifiers (D->Type); + /* Check if the data type consists of any arrays of forbidden types */ + CheckArrayElementType (D->Type); + /* If we have a function, add a special storage class */ if (IsTypeFunc (D->Type)) { D->StorageClass |= SC_FUNC; @@ -1607,7 +2017,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) if (IsTypeFunc (D->Type) || IsTypeFuncPtr (D->Type)) { /* A function. Check the return type */ - Type* RetType = GetFuncReturn (D->Type); + Type* RetType = GetFuncReturnModifiable (D->Type); /* Functions may not return functions or arrays */ if (IsTypeFunc (RetType)) { @@ -1619,7 +2029,7 @@ 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 (GetType (RetType) == T_TYPE_VOID) { + if (GetRawType (RetType) == T_TYPE_VOID) { /* A qualified void type is always an error */ Error ("function definition has qualified void return type"); } else { @@ -1656,8 +2066,8 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) } } - /* Check the size of the generated type */ if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type)) { + /* Check the size of the generated type */ unsigned Size = SizeOf (D->Type); if (Size >= 0x10000) { if (D->Ident[0] != '\0') { @@ -1668,6 +2078,15 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) } } + if (PrevErrorCount != ErrorCount) { + /* Make the declaration fictitious if is is not parsed correctly */ + D->StorageClass |= SC_FICTITIOUS; + + if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + /* Use a fictitious name for the identifier if it is missing */ + AnonName (D->Ident, "global"); + } + } } @@ -1687,7 +2106,7 @@ void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType) ParseStorageClass (D, DefStorage); /* Parse the type specifiers passing any initial type qualifiers */ - ParseTypeSpec (D, DefType, Qualifiers); + ParseTypeSpec (D, DefType, Qualifiers, NULL); } @@ -1705,18 +2124,20 @@ void CheckEmptyDecl (const DeclSpec* D) -static void SkipInitializer (unsigned BracesExpected) +static void SkipInitializer (int BracesExpected) /* Skip the remainder of an initializer in case of errors. Try to be somewhat ** smart so we don't have too many following errors. */ { - while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected > 0) { + while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) { switch (CurTok.Tok) { case TOK_RCURLY: --BracesExpected; break; case TOK_LCURLY: ++BracesExpected; break; default: break; } - NextToken (); + if (BracesExpected >= 0) { + NextToken (); + } } } @@ -1748,6 +2169,7 @@ static void ClosingCurlyBraces (unsigned BracesExpected) */ { while (BracesExpected) { + /* TODO: Skip all excess initializers until next closing curly brace */ if (CurTok.Tok == TOK_RCURLY) { NextToken (); } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) { @@ -1768,9 +2190,14 @@ static void DefineData (ExprDesc* Expr) { switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: + /* Immediate numeric value with no storage */ + g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + break; + case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_defdata (TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + /* Absolute numeric address */ + g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: @@ -1779,11 +2206,15 @@ static void DefineData (ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + /* Static variable */ g_defdata (CF_STATIC, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal in the literal pool */ + g_defdata (CF_LITERAL, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable. Taking the address is usually not ** allowed. @@ -1794,6 +2225,11 @@ static void DefineData (ExprDesc* Expr) g_defdata (CF_REGVAR, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label location */ + g_defdata (CF_CODE, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: case E_LOC_PRIMARY: case E_LOC_EXPR: @@ -1807,7 +2243,7 @@ static void DefineData (ExprDesc* Expr) -static void OutputBitFieldData (StructInitData* SI) +static void DefineBitFieldData (StructInitData* SI) /* Output bit field data */ { /* Ignore if we have no data */ @@ -1830,7 +2266,18 @@ static void OutputBitFieldData (StructInitData* SI) -static void ParseScalarInitInternal (Type* T, ExprDesc* ED) +static void DefineStrData (Literal* Lit, unsigned Count) +{ + /* Translate into target charset */ + TranslateLiteral (Lit); + + /* Output the data */ + g_defbytes (GetLiteralStr (Lit), Count); +} + + + +static ExprDesc ParseScalarInitInternal (const Type* T) /* Parse initializaton for scalar data types. This function will not output the ** data but return it in ED. */ @@ -1846,46 +2293,51 @@ static void ParseScalarInitInternal (Type* T, ExprDesc* ED) } /* Get the expression and convert it to the target type */ - ConstExpr (hie1, ED); - TypeConversion (ED, T); + ExprDesc ED = NoCodeConstExpr (hie1); + TypeConversion (&ED, T); /* Close eventually opening braces */ ClosingCurlyBraces (BraceCount); + + return ED; } -static unsigned ParseScalarInit (Type* T) +static unsigned ParseScalarInit (const Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { - ExprDesc ED; - /* Parse initialization */ - ParseScalarInitInternal (T, &ED); + ExprDesc ED = ParseScalarInitInternal (T); /* Output the data */ DefineData (&ED); + /* Do this anyways for safety */ + DoDeferred (SQP_KEEP_NONE, &ED); + /* Done */ return SizeOf (T); } -static unsigned ParsePointerInit (Type* T) +static unsigned ParsePointerInit (const Type* T) /* Parse initializaton for pointer data types. Return the number of data bytes. */ { /* Optional opening brace */ unsigned BraceCount = OpeningCurlyBraces (0); /* Expression */ - ExprDesc ED; - ConstExpr (hie1, &ED); + ExprDesc ED = NoCodeConstExpr (hie1); TypeConversion (&ED, T); /* Output the data */ DefineData (&ED); + /* Do this anyways for safety */ + DoDeferred (SQP_KEEP_NONE, &ED); + /* Close eventually opening braces */ ClosingCurlyBraces (BraceCount); @@ -1895,18 +2347,19 @@ static unsigned ParsePointerInit (Type* T) -static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) +static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initializaton for arrays. Return the number of data bytes. */ { int Count; + int HasCurly = 0; /* Get the array data */ - Type* ElementType = GetElementType (T); - unsigned ElementSize = CheckedSizeOf (ElementType); + Type* ElementType = IndirectModifiable (T); + unsigned ElementSize = SizeOf (ElementType); long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsTypeChar (ElementType) && + if (IsClassChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { @@ -1922,9 +2375,6 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) NextToken (); } - /* Translate into target charset */ - TranslateLiteral (CurTok.SVal); - /* If the array is one too small for the string literal, omit the ** trailing zero. */ @@ -1937,7 +2387,7 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) } /* Output the data */ - g_defbytes (GetLiteralStr (CurTok.SVal), Count); + DefineStrData (CurTok.SVal, Count); /* Skip the string */ NextToken (); @@ -1951,8 +2401,12 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) } else { - /* Curly brace */ - ConsumeLCurly (); + /* Arrays can be initialized without a pair of curly braces */ + if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { + /* Consume the opening curly brace */ + HasCurly = ConsumeLCurly (); + *Braces += HasCurly; + } /* Initialize the array members */ Count = 0; @@ -1961,67 +2415,86 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) ** an array (because the size of each element may differ ** otherwise). */ - ParseInitInternal (ElementType, 0); + ParseInitInternal (ElementType, Braces, 0); ++Count; if (CurTok.Tok != TOK_COMMA) break; NextToken (); } - /* Closing curly braces */ - ConsumeRCurly (); + if (HasCurly) { + /* Closing curly braces */ + ConsumeRCurly (); + } + } + + /* Size of 'void' elements are determined after initialization */ + if (ElementSize == 0) { + ElementSize = SizeOf (ElementType); } if (ElementCount == UNSPECIFIED) { /* Number of elements determined by initializer */ SetElementCount (T, Count); ElementCount = Count; - } else if (ElementCount == FLEXIBLE && AllowFlexibleMembers) { - /* In non ANSI mode, allow initialization of flexible array - ** members. - */ - ElementCount = Count; + } else if (ElementCount == FLEXIBLE) { + if (AllowFlexibleMembers) { + /* In non ANSI mode, allow initialization of flexible array + ** members. + */ + ElementCount = Count; + } else { + /* Forbid */ + Error ("Initializing flexible array member is forbidden"); + ElementCount = Count; + } } else if (Count < ElementCount) { g_zerobytes ((ElementCount - Count) * ElementSize); - } else if (Count > ElementCount) { - Error ("Too many initializers"); + } else if (Count > ElementCount && HasCurly) { + Error ("Excess elements in array initializer"); } return ElementCount * ElementSize; } -static unsigned ParseStructInit (Type* T, 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* Entry; + SymEntry* Sym; SymTable* Tab; StructInitData SI; + int HasCurly = 0; + int SkipComma = 0; - /* Consume the opening curly brace */ - ConsumeLCurly (); + /* Fields can be initialized without a pair of curly braces */ + if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { + /* Consume the opening curly brace */ + HasCurly = ConsumeLCurly (); + *Braces += HasCurly; + } /* Get a pointer to the struct entry from the type */ - Entry = GetSymEntry (T); + Sym = GetESUSymEntry (T); /* Get the size of the struct from the symbol table entry */ - SI.Size = Entry->V.S.Size; + SI.Size = Sym->V.S.Size; /* Check if this struct definition has a field table. If it doesn't, it ** is an incomplete definition. */ - Tab = Entry->V.S.SymTab; + Tab = Sym->V.S.SymTab; if (Tab == 0) { Error ("Cannot initialize variables with incomplete type"); /* Try error recovery */ - SkipInitializer (1); + SkipInitializer (HasCurly); /* Nothing initialized */ return 0; } /* Get a pointer to the list of symbols */ - Entry = Tab->SymHead; + Sym = Tab->SymHead; /* Initialize fields */ SI.Offs = 0; @@ -2029,66 +2502,106 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) SI.ValBits = 0; while (CurTok.Tok != TOK_RCURLY) { - /* */ - if (Entry == 0) { - Error ("Too many initializers"); - SkipInitializer (1); + /* Check for excess elements */ + if (Sym == 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 */ + NextToken (); + break; + } + + if (HasCurly) { + Error ("Excess elements in %s initializer", GetBasicTypeName (T)); + SkipInitializer (HasCurly); + } return SI.Offs; } - /* Parse initialization of one field. Bit-fields need a special - ** handling. - */ - if (SymIsBitField (Entry)) { + /* Check for special members that don't consume the initializer */ + if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) { + /* Just skip */ + goto NextMember; + } + /* This may be an anonymous bit-field, in which case it doesn't + ** have an initializer. + */ + if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) { + /* Account for the data and output it if we have at least a full + ** word. We may have more if there was storage unit overlap, for + ** example two consecutive 10 bit fields. These will be packed + ** into 3 bytes. + */ + SI.ValBits += Sym->Type->A.B.Width; + /* TODO: Generalize this so any type can be used. */ + CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); + while (SI.ValBits >= CHAR_BITS) { + DefineBitFieldData (&SI); + } + /* Avoid consuming the comma if any */ + goto NextMember; + } + + /* Skip comma this round */ + if (SkipComma) { + NextToken (); + SkipComma = 0; + } + + if (SymIsBitField (Sym)) { + + /* Parse initialization of one field. Bit-fields need a special + ** handling. + */ ExprDesc ED; + ED_Init (&ED); unsigned Val; unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Entry->V.B.BitWidth) - 1U; + unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; /* Safety ... */ - CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == - SI.Offs * CHAR_BITS + SI.ValBits); + CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == + SI.Offs * CHAR_BITS + SI.ValBits); - /* This may be an anonymous bit-field, in which case it doesn't - ** have an initializer. + /* Read the data, check for a constant integer, do a range check */ + ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); + if (!ED_IsConstAbsInt (&ED)) { + Error ("Constant initializer expected"); + ED_MakeConstAbsInt (&ED, 1); + } + + /* Truncate the initializer value to the width of the bit-field and check if we lost + ** any useful bits. */ - if (IsAnonName (Entry->Name)) { - /* Account for the data and output it if we have at least a - ** full word. We may have more if there was storage unit - ** overlap, for example two consecutive 10 bit fields. - ** These will be packed into 3 bytes. - */ - SI.ValBits += Entry->V.B.BitWidth; - CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); - while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + Val = (unsigned) ED.IVal & Mask; + if (IsSignUnsigned (Sym->Type)) { + if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { + Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %ld to %u", + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, ED.IVal, Val); } - goto NextMember; } else { - /* Read the data, check for a constant integer, do a range - ** check. - */ - ParseScalarInitInternal (type_uint, &ED); - if (!ED_IsConstAbsInt (&ED)) { - Error ("Constant initializer expected"); - ED_MakeConstAbsInt (&ED, 1); + /* Sign extend back to full width of host long. */ + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; + long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); + if (ED.IVal != RestoredVal) { + Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " + "changes value from %ld to %ld", + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, ED.IVal, RestoredVal); } - if (ED.IVal > (long) Mask) { - Warning ("Truncating value in bit-field initializer"); - ED.IVal &= (long) Mask; - } - Val = (unsigned) ED.IVal; } /* Add the value to the currently stored bit-field value */ - Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; + Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Entry->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* Make sure unsigned is big enough to hold the value, 22 bits. ** This is 22 bits because the most we can have is 7 bits left ** over from the previous OutputBitField call, plus 15 bits @@ -2096,22 +2609,24 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) ** aligned, so will have padding before it. */ CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); + /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); } } else { /* Standard member. We should never have stuff from a - ** bit-field left + ** bit-field left because an anonymous member was added + ** for padding by ParseStructDecl. */ 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 (Entry->Type, AllowFlexibleMembers && Entry->NextSym == 0); + SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0); } /* More initializers? */ @@ -2119,26 +2634,28 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) break; } - /* Skip the comma */ - NextToken (); + /* Skip the comma next round */ + SkipComma = 1; NextMember: /* Next member. For unions, only the first one can be initialized */ if (IsTypeUnion (T)) { /* Union */ - Entry = 0; + Sym = 0; } else { /* Struct */ - Entry = Entry->NextSym; + Sym = Sym->NextSym; } } - /* Consume the closing curly brace */ - ConsumeRCurly (); + if (HasCurly) { + /* Consume the closing curly brace */ + ConsumeRCurly (); + } /* If we have data from a bit-field left, output it now */ CHECK (SI.ValBits < CHAR_BITS); - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); /* If there are struct fields left, reserve additional storage */ if (SI.Offs < SI.Size) { @@ -2160,7 +2677,6 @@ static unsigned ParseVoidInit (Type* T) ** Return the number of bytes initialized. */ { - ExprDesc Expr; unsigned Size; /* Opening brace */ @@ -2169,8 +2685,8 @@ static unsigned ParseVoidInit (Type* T) /* Allow an arbitrary list of values */ Size = 0; do { - ConstExpr (hie1, &Expr); - switch (UnqualifiedType (Expr.Type[0].C)) { + ExprDesc Expr = NoCodeConstExpr (hie1); + switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -2223,7 +2739,11 @@ static unsigned ParseVoidInit (Type* T) ConsumeRCurly (); /* Number of bytes determined by initializer */ - T->A.U = Size; + if (T->A.U != 0 && T->A.U != Size) { + Error ("'void' array initialized with elements of variant sizes"); + } else { + T->A.U = Size; + } /* Return the number of bytes initialized */ return Size; @@ -2231,10 +2751,10 @@ static unsigned ParseVoidInit (Type* T) -static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) +static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: case T_UCHAR: @@ -2252,11 +2772,17 @@ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) return ParsePointerInit (T); case T_ARRAY: - return ParseArrayInit (T, AllowFlexibleMembers); + return ParseArrayInit (T, Braces, AllowFlexibleMembers); case T_STRUCT: case T_UNION: - return ParseStructInit (T, AllowFlexibleMembers); + return ParseStructInit (T, Braces, AllowFlexibleMembers); + + case T_ENUM: + /* Incomplete enum type must have already raised errors. + ** Just proceed to consume the value. + */ + return ParseScalarInit (T); case T_VOID: if (IS_Get (&Standard) == STD_CC65) { @@ -2277,10 +2803,13 @@ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) unsigned ParseInit (Type* T) /* Parse initialization of variables. Return the number of data bytes. */ { + /* Current curly braces layers */ + int Braces = 0; + /* Parse the initialization. Flexible array members can only be initialized ** in cc65 mode. */ - unsigned Size = ParseInitInternal (T, IS_Get (&Standard) == STD_CC65); + unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65); /* The initialization may not generate code on global level, because code ** outside function scope will never get executed. diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 117ac14a6..3293a0dcb 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -57,6 +57,9 @@ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ #define DS_DEF_TYPE 0x0002U /* Default type used */ #define DS_EXTRA_TYPE 0x0004U /* Extra type declared */ +#define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ +#define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ +#define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) /* Result of ParseDeclSpec */ typedef struct DeclSpec DeclSpec; @@ -93,6 +96,9 @@ typedef enum { +void InitDeclSpec (DeclSpec* D); +/* Initialize the DeclSpec struct for use */ + Type* ParseType (Type* Type); /* Parse a complete type specification */ diff --git a/src/cc65/error.c b/src/cc65/error.c index 858a80826..f0e023969 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -38,7 +38,9 @@ #include <stdarg.h> /* common */ +#include "coll.h" #include "print.h" +#include "strbuf.h" /* cc65 */ #include "global.h" @@ -66,12 +68,17 @@ IntStack WarningsAreErrors = INTSTACK(0); /* Treat warnings as errors */ /* Warn about: */ IntStack WarnConstComparison= INTSTACK(1); /* - constant comparison results */ IntStack WarnNoEffect = INTSTACK(1); /* - statements without an effect */ +IntStack WarnPointerSign = INTSTACK(1); /* - pointer conversion to pointer differing in signedness */ +IntStack WarnPointerTypes = INTSTACK(1); /* - pointer conversion to incompatible pointer type */ IntStack WarnRemapZero = INTSTACK(1); /* - remapping character code zero */ -IntStack WarnStructParam = INTSTACK(1); /* - structs passed by val */ +IntStack WarnReturnType = INTSTACK(1); /* - control reaches end of non-void function */ +IntStack WarnStructParam = INTSTACK(0); /* - structs passed by val */ IntStack WarnUnknownPragma = INTSTACK(1); /* - unknown #pragmas */ +IntStack WarnUnreachableCode= INTSTACK(1); /* - unreachable code */ IntStack WarnUnusedLabel = INTSTACK(1); /* - unused labels */ IntStack WarnUnusedParam = INTSTACK(1); /* - unused parameters */ IntStack WarnUnusedVar = INTSTACK(1); /* - unused variables */ +IntStack WarnUnusedFunc = INTSTACK(1); /* - unused functions */ /* Map the name of a warning to the intstack that holds its state */ typedef struct WarnMapEntry WarnMapEntry; @@ -84,14 +91,21 @@ static WarnMapEntry WarnMap[] = { { &WarnConstComparison, "const-comparison" }, { &WarningsAreErrors, "error" }, { &WarnNoEffect, "no-effect" }, + { &WarnPointerSign, "pointer-sign" }, + { &WarnPointerTypes, "pointer-types" }, { &WarnRemapZero, "remap-zero" }, + { &WarnReturnType, "return-type" }, { &WarnStructParam, "struct-param" }, { &WarnUnknownPragma, "unknown-pragma" }, + { &WarnUnreachableCode, "unreachable-code" }, + { &WarnUnusedFunc, "unused-func" }, { &WarnUnusedLabel, "unused-label" }, { &WarnUnusedParam, "unused-param" }, { &WarnUnusedVar, "unused-var" }, }; +Collection DiagnosticStrBufs; + /*****************************************************************************/ @@ -115,7 +129,7 @@ void Fatal (const char* Format, ...) LineNum = GetCurrentLine (); } - fprintf (stderr, "%s(%u): Fatal: ", FileName, LineNum); + fprintf (stderr, "%s:%u: Fatal: ", FileName, LineNum); va_start (ap, Format); vfprintf (stderr, Format, ap); @@ -145,7 +159,7 @@ void Internal (const char* Format, ...) LineNum = GetCurrentLine (); } - fprintf (stderr, "%s(%u): Internal compiler error:\n", + fprintf (stderr, "%s:%u: Internal compiler error:\n", FileName, LineNum); va_start (ap, Format); @@ -172,7 +186,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*/ { - fprintf (stderr, "%s(%u): Error: ", Filename, LineNo); + fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -180,7 +194,7 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } ++ErrorCount; - if (ErrorCount > 10) { + if (ErrorCount > 20) { Fatal ("Too many errors"); } } @@ -236,7 +250,7 @@ static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, } else if (IS_Get (&WarnEnable)) { - fprintf (stderr, "%s(%u): Warning: ", Filename, LineNo); + fprintf (stderr, "%s:%u: Warning: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -321,5 +335,53 @@ void ListWarnings (FILE* F) void ErrorReport (void) /* Report errors (called at end of compile) */ { - Print (stdout, 1, "%u errors, %u warnings\n", ErrorCount, WarningCount); + unsigned int V = (ErrorCount != 0 ? 0 : 1); + Print (stdout, V, "%u errors and %u warnings generated.\n", ErrorCount, WarningCount); +} + + + +/*****************************************************************************/ +/* Tracked StrBufs */ +/*****************************************************************************/ + + + +void InitDiagnosticStrBufs (void) +/* Init tracking string buffers used for diagnostics */ +{ + InitCollection (&DiagnosticStrBufs); +} + + + +void DoneDiagnosticStrBufs (void) +/* Done with tracked string buffers used for diagnostics */ +{ + ClearDiagnosticStrBufs (); + DoneCollection (&DiagnosticStrBufs); +} + + + +void ClearDiagnosticStrBufs (void) +/* Free all tracked string buffers */ +{ + unsigned I; + + for (I = 0; I < CollCount (&DiagnosticStrBufs); ++I) { + SB_Done (CollAtUnchecked (&DiagnosticStrBufs, I)); + } + + CollDeleteAll (&DiagnosticStrBufs); +} + + + +struct StrBuf* NewDiagnosticStrBuf (void) +/* Get a new tracked string buffer */ +{ + StrBuf *Buf = NewStrBuf (); + CollAppend (&DiagnosticStrBufs, Buf); + return Buf; } diff --git a/src/cc65/error.h b/src/cc65/error.h index 97ee09591..c4420c434 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -64,13 +64,21 @@ extern IntStack WarnEnable; /* Enable warnings */ extern IntStack WarningsAreErrors; /* Treat warnings as errors */ /* Warn about: */ extern IntStack WarnConstComparison; /* - constant comparison results */ +extern IntStack WarnPointerSign; /* - pointer conversion to pointer differing in signedness */ +extern IntStack WarnPointerTypes; /* - pointer conversion to incompatible pointer type */ extern IntStack WarnNoEffect; /* - statements without an effect */ extern IntStack WarnRemapZero; /* - remapping character code zero */ +extern IntStack WarnReturnType; /* - control reaches end of non-void function */ extern IntStack WarnStructParam; /* - structs passed by val */ extern IntStack WarnUnknownPragma; /* - unknown #pragmas */ +extern IntStack WarnUnreachableCode; /* - unreachable code */ extern IntStack WarnUnusedLabel; /* - unused labels */ extern IntStack WarnUnusedParam; /* - unused parameters */ extern IntStack WarnUnusedVar; /* - unused variables */ +extern IntStack WarnUnusedFunc; /* - unused functions */ + +/* Forward */ +struct StrBuf; @@ -115,6 +123,18 @@ void ListWarnings (FILE* F); void ErrorReport (void); /* Report errors (called at end of compile) */ +void InitDiagnosticStrBufs (void); +/* Init tracking string buffers used for diagnostics */ + +void DoneDiagnosticStrBufs (void); +/* Done with tracked string buffers used for diagnostics */ + +void ClearDiagnosticStrBufs (void); +/* Free all tracked string buffers */ + +struct StrBuf* NewDiagnosticStrBuf (void); +/* Get a new tracked string buffer */ + /* End of error.h */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 84f11189e..3b9307a37 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1,7 +1,7 @@ /* expr.c ** ** 1998-06-21, Ullrich von Bassewitz -** 2020-01-25, Greg King +** 2020-11-20, Greg King */ @@ -47,13 +47,8 @@ -/* Generator attributes */ -#define GEN_NOPUSH 0x01 /* Don't push lhs */ -#define GEN_COMM 0x02 /* Operator is commutative */ -#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ - /* Map a generator function and its attributes to a token */ -typedef struct { +typedef struct GenDesc { token_t Tok; /* Token to map to */ unsigned Flags; /* Flags for generator function */ void (*Func) (unsigned, unsigned long); /* Generator func */ @@ -73,24 +68,38 @@ static GenDesc GenOASGN = { TOK_OR_ASSIGN, GEN_NOPUSH, g_or }; +/*****************************************************************************/ +/* Forward declarations */ +/*****************************************************************************/ + + + +static void parseadd (ExprDesc* Expr, int DoArrayRef); +static void PostInc (ExprDesc* Expr); +static void PostDec (ExprDesc* Expr); + + + /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ -static unsigned GlobalModeFlags (const ExprDesc* Expr) +unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: return CF_IMM; case E_LOC_ABS: return CF_ABSOLUTE; case E_LOC_GLOBAL: return CF_EXTERNAL; case E_LOC_STATIC: return CF_STATIC; case E_LOC_REGISTER: return CF_REGVAR; - case E_LOC_STACK: return CF_NONE; - case E_LOC_PRIMARY: return CF_NONE; - case E_LOC_EXPR: return CF_NONE; - case E_LOC_LITERAL: return CF_STATIC; /* Same as static */ + case E_LOC_STACK: return CF_STACK; + case E_LOC_PRIMARY: return CF_PRIMARY; + case E_LOC_EXPR: return CF_EXPR; + case E_LOC_LITERAL: return CF_LITERAL; + case E_LOC_CODE: return CF_CODE; default: Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); /* NOTREACHED */ @@ -139,32 +148,7 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) -static Type* promoteint (Type* lhst, Type* rhst) -/* In an expression with two ints, return the type of the result */ -{ - /* Rules for integer types: - ** - If one of the values is a long, the result is long. - ** - If one of the values is unsigned, the result is also unsigned. - ** - Otherwise the result is an int. - */ - if (IsTypeLong (lhst) || IsTypeLong (rhst)) { - if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) { - return type_ulong; - } else { - return type_long; - } - } else { - if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) { - return type_uint; - } else { - return type_int; - } - } -} - - - -static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) +static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) /* Adjust the two values for a binary operation. lhs is expected on stack or ** to be constant, rhs is expected to be in the primary register or constant. ** The function will put the type of the result into lhs and return the @@ -178,26 +162,36 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) unsigned flags; /* Get the type strings */ - Type* lhst = lhs->Type; - Type* rhst = rhs->Type; + const Type* lhst = lhs->Type; + const Type* rhst = rhs->Type; /* Generate type adjustment code if needed */ ltype = TypeOf (lhst); - if (ED_IsLocAbs (lhs)) { + if (ED_IsConstAbsInt (lhs) && ltype == CF_INT && lhs->IVal >= 0 && lhs->IVal < 256) { + /* If the lhs is a int constant that fits in an unsigned char, use unsigned char. + ** g_typeadjust will either promote this to int or unsigned int as appropriate + ** based on the other operand. See comment in hie_internal. + */ + ltype = CF_CHAR | CF_UNSIGNED; + } + if (ED_IsLocNone (lhs)) { ltype |= CF_CONST; } if (NoPush) { /* Value is in primary register*/ - ltype |= CF_REG; + ltype |= CF_PRIMARY; } rtype = TypeOf (rhst); - if (ED_IsLocAbs (rhs)) { + if (ED_IsConstAbsInt (rhs) && rtype == CF_INT && rhs->IVal >= 0 && rhs->IVal < 256) { + rtype = CF_CHAR | CF_UNSIGNED; + } + if (ED_IsLocNone (rhs)) { rtype |= CF_CONST; } flags = g_typeadjust (ltype, rtype); /* Set the type of the result */ - lhs->Type = promoteint (lhst, rhst); + lhs->Type = ArithmeticConvert (lhst, rhst); /* Return the code generator flags */ return flags; @@ -205,6 +199,42 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) +void LimitExprValue (ExprDesc* Expr) +/* Limit the constant value of the expression to the range of its type */ +{ + switch (GetUnderlyingTypeCode (Expr->Type)) { + case T_INT: + case T_SHORT: + Expr->IVal = (int16_t)Expr->IVal; + break; + + case T_UINT: + case T_USHORT: + case T_PTR: + case T_ARRAY: + Expr->IVal = (uint16_t)Expr->IVal; + break; + + case T_LONG: + case T_ULONG: + /* No need to do anything */ + break; + + case T_SCHAR: + Expr->IVal = (int8_t)Expr->IVal; + break; + + case T_UCHAR: + Expr->IVal = (uint8_t)Expr->IVal; + break; + + default: + Internal ("hie_internal: constant result type %s\n", GetFullTypeName (Expr->Type)); + } +} + + + static const GenDesc* FindGen (token_t Tok, const GenDesc* Table) /* Find a token in a generator table */ { @@ -243,6 +273,23 @@ static int TypeSpecAhead (void) +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)) { + Error ("Cannot apply 'sizeof' to incomplete type '%s'", GetFullTypeName (T)); + } + } + return Size; +} + + + void PushAddr (const ExprDesc* Expr) /* If the expression contains an address that was somehow evaluated, ** push this address on the stack. This is a helper function for all @@ -259,13 +306,15 @@ void PushAddr (const ExprDesc* Expr) -static void WarnConstCompareResult (void) +static void WarnConstCompareResult (const ExprDesc* Expr) /* If the result of a comparison is constant, this is suspicious when not in ** preprocessor mode. */ { - if (!Preprocessing && IS_Get (&WarnConstComparison) != 0) { - Warning ("Result of comparison is constant"); + if (!Preprocessing && + !ED_NeedsConst (Expr) && + IS_Get (&WarnConstComparison) != 0) { + Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); } } @@ -277,45 +326,370 @@ static void WarnConstCompareResult (void) -static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) -/* Parse a function parameter list, and pass the parameters to the called -** function. Depending on several criteria, this may be done by just pushing -** each parameter separately, or creating the parameter frame once, and then -** storing into this frame. -** The function returns the size of the parameters pushed. +typedef enum { + DOT_INC, + DOT_DEC, +} DeferredOpType; + + +typedef struct { + ExprDesc Expr; + DeferredOpType OpType; +} DeferredOp; + +Collection DeferredOps; + + + +void InitDeferredOps (void) +/* Init the collection for storing deferred ops */ +{ + InitCollection (&DeferredOps); +} + + + +void DoneDeferredOps (void) +/* Deinit the collection for storing deferred ops */ +{ + DoneCollection (&DeferredOps); +} + + + +static void DeferInc (const ExprDesc* Expr) +/* Defer the post-inc and put it in a queue */ +{ + if (ED_IsUneval (Expr)) { + return; + } + DeferredOp* Op = xmalloc (sizeof (DeferredOp)); + memcpy (&Op->Expr, Expr, sizeof (ExprDesc)); + Op->OpType = DOT_INC; + CollAppend (&DeferredOps, Op); +} + + + +static void DeferDec (const ExprDesc* Expr) +/* Defer the post-dec and put it in a queue */ +{ + if (ED_IsUneval (Expr)) { + return; + } + DeferredOp* Op = xmalloc (sizeof (DeferredOp)); + memcpy (&Op->Expr, Expr, sizeof (ExprDesc)); + Op->OpType = DOT_DEC; + CollAppend (&DeferredOps, Op); +} + + + +static void DoInc (ExprDesc* Expr, unsigned KeepResult) +/* Do increment */ +{ + unsigned Flags; + long Val; + + /* Get the increment value in bytes */ + Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; + + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, Val, KeepResult); + return; + } + + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } + + if (KeepResult == OA_NEED_OLD) { + + Flags |= CF_FORCECHAR; + + /* Push the address if needed */ + PushAddr (Expr); + + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); + + /* Do the increment */ + g_inc (Flags | CF_CONST, Val); + + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_addeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_addeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_inc (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_addeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoInc(): 0x%04X", ED_GetLoc (Expr)); + } + + } +} + + + +static void DoDec (ExprDesc* Expr, unsigned KeepResult) +/* Do decrement */ +{ + unsigned Flags; + long Val; + + /* Get the decrement value in bytes */ + Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; + + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, -Val, KeepResult); + return; + } + + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } + + if (KeepResult == OA_NEED_OLD) { + + Flags |= CF_FORCECHAR; + + /* Push the address if needed */ + PushAddr (Expr); + + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); + + /* Do the decrement */ + g_dec (Flags | CF_CONST, Val); + + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_subeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_subeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_dec (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_subeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoDec(): 0x%04X", ED_GetLoc (Expr)); + } + + } +} + + + +int GetDeferredOpCount (void) +/* Return how many deferred operations are still waiting in the queque */ +{ + return (int)CollCount (&DeferredOps); +} + + + +void CheckDeferredOpAllDone (void) +/* Check if all deferred operations are done at sequence points. +** Die off if check fails. */ { - ExprDesc Expr; + if (GetDeferredOpCount () > 0) { + Internal ("Code generation messed up: missing operations past sequence points."); + } +} + + + +void DoDeferred (unsigned Flags, ExprDesc* Expr) +/* Do deferred operations such as post-inc/dec at sequence points */ +{ + int I; + unsigned Size = 0; + int Count = GetDeferredOpCount (); + + /* Nothing to be done */ + if (Count <= 0) { + return; + } + + /* Backup some regs/processor flags around the inc/dec */ + if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + /* Sufficient to add a pair of PHP/PLP for all cases */ + AddCodeLine ("php"); + } + + /* Backup the content of EAX around the inc/dec */ + if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { + /* Get the size */ + Size = CheckedSizeOf (Expr->Type); + + if (Size < 2) { + AddCodeLine ("pha"); + } else if (Size < 3) { + AddCodeLine ("sta regsave"); + AddCodeLine ("stx regsave+1"); + } else { + AddCodeLine ("jsr saveeax"); + } + } + + for (I = 0; I < Count; ++I) { + DeferredOp* Op = CollAtUnchecked (&DeferredOps, I); + switch (Op->OpType) { + + case DOT_INC: + DoInc (&Op->Expr, OA_NEED_NONE); + break; + + case DOT_DEC: + DoDec (&Op->Expr, OA_NEED_NONE); + break; + } + xfree (&Op->Expr); + } + CollDeleteAll (&DeferredOps); + + /* Restore the content of EAX around the inc/dec */ + if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { + if (Size < 2) { + AddCodeLine ("pla"); + } else if (Size < 3) { + AddCodeLine ("lda regsave"); + AddCodeLine ("ldx regsave+1"); + } else { + AddCodeLine ("jsr resteax"); + } + } + + /* Restore the regs/processor flags around the inc/dec */ + if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + /* Sufficient to pop the processor flags */ + AddCodeLine ("plp"); + } +} + + + +static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) +/* Parse the argument list of the called function and pass the arguments to it. +** Depending on several criteria, this may be done by just pushing into each +** parameter separately, or creating the parameter frame once and then storing +** arguments into this frame one by one. +** The function returns the size of the arguments pushed in bytes. +*/ +{ + ExprDesc Expr; /* Initialize variables */ SymEntry* Param = 0; /* Keep gcc silent */ - unsigned ParamSize = 0; /* Size of parameters pushed */ - unsigned ParamCount = 0; /* Number of parameters pushed */ + unsigned PushedSize = 0; /* Size of arguments pushed */ + unsigned PushedCount = 0; /* Number of arguments pushed */ unsigned FrameSize = 0; /* Size of parameter frame */ - unsigned FrameParams = 0; /* Number of params in frame */ + unsigned FrameParams = 0; /* Number of parameters in frame */ int FrameOffs = 0; /* Offset into parameter frame */ int Ellipsis = 0; /* Function is variadic */ + /* Make sure the size of all parameters are known */ + int ParamComplete = F_CheckParamList (Func, 1); + /* As an optimization, we may allocate the complete parameter frame at - ** once instead of pushing each parameter as it comes. We may do that, + ** once instead of pushing into each parameter as it comes. We may do that, ** if... ** ** - optimizations that increase code size are enabled (allocating the ** stack frame at once gives usually larger code). - ** - we have more than one parameter to push (don't count the last param - ** for __fastcall__ functions). + ** - we have more than one parameter to push into (don't count the last + ** parameter for __fastcall__ functions). ** ** The FrameSize variable will contain a value > 0 if storing into a frame ** (instead of pushing) is enabled. ** */ - if (IS_Get (&CodeSizeFactor) >= 200) { - + if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) { /* Calculate the number and size of the parameters */ FrameParams = Func->ParamCount; FrameSize = Func->ParamSize; if (FrameParams > 0 && IsFastcall) { - /* Last parameter is not pushed */ + /* Last parameter is not pushed into */ FrameSize -= CheckedSizeOf (Func->LastParam->Type); --FrameParams; } @@ -332,25 +706,29 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } } - /* Parse the actual parameter list */ + /* Parse the actual argument list */ while (CurTok.Tok != TOK_RPAREN) { + unsigned Flags; /* Code generator flags, not expression flags */ - unsigned Flags; + ED_Init (&Expr); + + /* This way, the info of the last parameter won't be cleared */ + Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; /* Count arguments */ - ++ParamCount; + ++PushedCount; /* Fetch the pointer to the next argument, check for too many args */ - if (ParamCount <= Func->ParamCount) { + if (PushedCount <= Func->ParamCount) { /* Beware: If there are parameters with identical names, they ** cannot go into the same symbol table, which means that, in this ** case of errorneous input, the number of nodes in the symbol - ** table and ParamCount are NOT equal. We have to handle this case + ** table and PushedCount are NOT equal. We have to handle this case ** below to avoid segmentation violations. Since we know that this ** problem can only occur if there is more than one parameter, ** we will just use the last one. */ - if (ParamCount == 1) { + if (PushedCount == 1) { /* First argument */ Param = Func->SymTab->SymHead; } else if (Param->NextSym != 0) { @@ -370,62 +748,74 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) Ellipsis = 1; } - /* Evaluate the parameter expression */ + /* Evaluate the argument expression */ hie1 (&Expr); - /* If we don't have an argument spec., accept anything; otherwise, - ** convert the actual argument to the type needed. - */ - Flags = CF_NONE; - if (!Ellipsis) { - - /* Convert the argument to the parameter type if needed */ - TypeConversion (&Expr, Param->Type); - - /* If we have a prototype, chars may be pushed as chars */ - Flags |= CF_FORCECHAR; - - } else { - - /* No prototype available. Convert array to "pointer to first - ** element", and function to "pointer to function". + /* Skip to the next parameter if there are any incomplete types */ + if (ParamComplete) { + /* If we don't have an argument spec., accept anything; otherwise, + ** convert the actual argument to the type needed. */ - Expr.Type = PtrConversion (Expr.Type); + Flags = CF_NONE; + if (!Ellipsis) { - } + /* Convert the argument to the parameter type if needed */ + TypeConversion (&Expr, Param->Type); - /* Load the value into the primary if it is not already there */ - LoadExpr (Flags, &Expr); + /* If we have a prototype, chars may be pushed as chars */ + Flags |= CF_FORCECHAR; - /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); - - /* If this is a fastcall function, don't push the last argument */ - if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { - unsigned ArgSize = sizeofarg (Flags); - - if (FrameSize > 0) { - /* We have the space already allocated, store in the frame. - ** Because of invalid type conversions (that have produced an - ** error before), we can end up here with a non-aligned stack - ** frame. Since no output will be generated anyway, handle - ** these cases gracefully instead of doing a CHECK. - */ - if (FrameSize >= ArgSize) { - FrameSize -= ArgSize; - } else { - FrameSize = 0; - } - FrameOffs -= ArgSize; - /* Store */ - g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); } else { - /* Push the argument */ - g_push (Flags, Expr.IVal); + + /* No prototype available. Convert array to "pointer to first + ** element", and function to "pointer to function". + */ + Expr.Type = PtrConversion (Expr.Type); + } - /* Calculate total parameter size */ - ParamSize += ArgSize; + /* Handle struct/union specially */ + if (IsClassStruct (Expr.Type)) { + /* Use the replacement type */ + Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); + } else { + /* Load the value into the primary if it is not already there */ + LoadExpr (CF_NONE, &Expr); + + /* Use the type of the argument for the push */ + Flags |= TypeOf (Expr.Type); + } + + /* If this is a fastcall function, don't push the last argument */ + if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { + unsigned ArgSize = sizeofarg (Flags); + + if (FrameSize > 0) { + /* We have the space already allocated, store in the frame. + ** Because of invalid type conversions (that have produced an + ** error before), we can end up here with a non-aligned stack + ** frame. Since no output will be generated anyway, handle + ** these cases gracefully instead of doing a CHECK. + */ + if (FrameSize >= ArgSize) { + FrameSize -= ArgSize; + } else { + FrameSize = 0; + } + FrameOffs -= ArgSize; + /* Store */ + g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); + } else { + /* Push the argument */ + g_push (Flags, Expr.IVal); + } + + /* Calculate total parameter size */ + PushedSize += ArgSize; + } } /* Check for end of argument list */ @@ -439,22 +829,29 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) Error ("Argument expected after comma"); break; } + + DoDeferred (SQP_KEEP_NONE, &Expr); } - /* Check if we had enough parameters */ - if (ParamCount < Func->ParamCount) { + /* Append last deferred inc/dec before the function is called. + ** The last parameter needs to be preserved if it is passed in AX/EAX Regs. + */ + DoDeferred (IsFastcall && PushedCount > 0 ? SQP_KEEP_EAX : SQP_KEEP_NONE, &Expr); + + /* Check if we had enough arguments */ + if (PushedCount < Func->ParamCount) { Error ("Too few arguments in function call"); } - /* The function returns the size of all parameters pushed onto the stack. - ** However, if there are parameters missing (which is an error, and was + /* The function returns the size of all arguments pushed onto the stack. + ** However, if there are parameters missed (which is an error, and was ** flagged by the compiler), AND a stack frame was preallocated above, ** we would loose track of the stackpointer, and generate an internal error ** later. So we correct the value by the parameters that should have been - ** pushed, to avoid an internal compiler error. Since an error was + ** pushed into, to avoid an internal compiler error. Since an error was ** generated before, no code will be output anyway. */ - return ParamSize + FrameSize; + return PushedSize + FrameSize; } @@ -464,11 +861,12 @@ static void FunctionCall (ExprDesc* Expr) { FuncDesc* Func; /* Function descriptor */ int IsFuncPtr; /* Flag */ - unsigned ParamSize; /* Number of parameter bytes */ + unsigned ArgSize; /* Number of arguments bytes */ CodeMark Mark; int PtrOffs = 0; /* Offset of function pointer on stack */ - int IsFastcall = 0; /* True if it's a fast-call function */ + int IsFastcall = 0; /* True if we are fast-calling the function */ int PtrOnStack = 0; /* True if a pointer copy is on stack */ + const Type* ReturnType; /* Skip the left paren */ NextToken (); @@ -485,11 +883,8 @@ static void FunctionCall (ExprDesc* Expr) ** parameter count is zero. Handle K & R functions as though there are ** parameters. */ - IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && - (Func->ParamCount > 0 || (Func->Flags & FD_EMPTY)) && - (AutoCDecl ? - IsQualFastcall (Expr->Type + 1) : - !IsQualCDecl (Expr->Type + 1)); + IsFastcall = (Func->ParamCount > 0 || (Func->Flags & FD_EMPTY) != 0) && + IsFastcallFunc (Expr->Type + 1); /* Things may be difficult, depending on where the function pointer ** resides. If the function pointer is an expression of some sort @@ -499,14 +894,14 @@ static void FunctionCall (ExprDesc* Expr) ** For fastcall functions we do also need to place a copy of the ** pointer on stack, since we cannot use a/x. */ - PtrOnStack = IsFastcall || !ED_IsConst (Expr); + PtrOnStack = IsFastcall || !ED_IsConstAddr (Expr); if (PtrOnStack) { /* Not a global or local variable, or a fastcall function. Load ** the pointer into the primary and mark it as an expression. */ LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); /* Remember the code position */ GetCodePos (&Mark); @@ -524,7 +919,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Check for known standard functions and inline them */ - if (Expr->Name != 0) { + if (Expr->Name != 0 && !ED_IsUneval (Expr)) { int StdFunc = FindStdFunc ((const char*) Expr->Name); if (StdFunc >= 0) { /* Inline this function */ @@ -534,14 +929,12 @@ static void FunctionCall (ExprDesc* Expr) } /* If we didn't inline the function, get fastcall info */ - IsFastcall = (Func->Flags & FD_VARIADIC) == 0 && - (AutoCDecl ? - IsQualFastcall (Expr->Type) : - !IsQualCDecl (Expr->Type)); + IsFastcall = (Func->ParamCount > 0 || (Func->Flags & FD_EMPTY) != 0) && + IsFastcallFunc (Expr->Type); } - /* Parse the parameter list */ - ParamSize = FunctionParamList (Func, IsFastcall); + /* Parse the argument list and pass them to the called function */ + ArgSize = FunctionArgList (Func, IsFastcall, Expr); /* We need the closing paren here */ ConsumeRParen (); @@ -560,11 +953,11 @@ static void FunctionCall (ExprDesc* Expr) /* Not a fastcall function - we may use the primary */ if (PtrOnStack) { - /* If we have no parameters, the pointer is still in the + /* If we have no arguments, the pointer is still in the ** primary. Remove the code to push it and correct the ** stack pointer. */ - if (ParamSize == 0) { + if (ArgSize == 0) { RemoveCode (&Mark); PtrOnStack = 0; } else { @@ -577,7 +970,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Call the function */ - g_callind (TypeOf (Expr->Type+1), ParamSize, PtrOffs); + g_callind (FuncTypeOf (Expr->Type+1), ArgSize, PtrOffs); } else { @@ -586,7 +979,7 @@ static void FunctionCall (ExprDesc* Expr) ** Since fastcall functions may never be variadic, we can use the ** index register for this purpose. */ - g_callind (CF_LOCAL, ParamSize, PtrOffs); + g_callind (CF_STACK, ArgSize, PtrOffs); } /* If we have a pointer on stack, remove it */ @@ -605,9 +998,16 @@ static void FunctionCall (ExprDesc* Expr) char tmp[64]; StrBuf S = AUTO_STRBUF_INITIALIZER; - /* Store the WrappedCall data in tmp4 */ - sprintf(tmp, "ldy #%u", Func->WrappedCallData); - SB_AppendStr (&S, tmp); + if (Func->WrappedCallData == WRAPPED_CALL_USE_BANK) { + /* Store the bank attribute in tmp4 */ + SB_AppendStr (&S, "ldy #<.bank(_"); + SB_AppendStr (&S, (const char*) Expr->Name); + SB_AppendChar (&S, ')'); + } else { + /* Store the WrappedCall data in tmp4 */ + sprintf(tmp, "ldy #%u", Func->WrappedCallData); + SB_AppendStr (&S, tmp); + } g_asmcode (&S); SB_Clear(&S); @@ -638,16 +1038,28 @@ static void FunctionCall (ExprDesc* Expr) SB_Done (&S); - g_call (TypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize); + g_call (FuncTypeOf (Expr->Type), Func->WrappedCall->Name, ArgSize); } else { - g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize); + g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ArgSize); } } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + ED_FinalizeRValLoad (Expr); + ReturnType = GetFuncReturn (Expr->Type); + + /* Handle struct/union specially */ + if (IsClassStruct (ReturnType)) { + /* If there is no replacement type, then it is just the address */ + if (ReturnType == GetStructReplacementType (ReturnType)) { + /* Dereference it */ + ED_IndExpr (Expr); + ED_MarkExprAsRVal (Expr); + } + } + + Expr->Type = ReturnType; } @@ -657,23 +1069,20 @@ static void Primary (ExprDesc* E) { SymEntry* Sym; - /* Initialize fields in the expression stucture */ - ED_Init (E); - /* Character and integer constants. */ if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { - E->IVal = CurTok.IVal; - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; - E->Type = CurTok.Type; + 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->FVal = CurTok.FVal; - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; - E->Type = CurTok.Type; + E->V.FVal = CurTok.FVal; + E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; NextToken (); return; } @@ -707,6 +1116,8 @@ static void Primary (ExprDesc* E) return; } + unsigned Flags = E->Flags & E_MASK_KEEP_MAKE; + switch (CurTok.Tok) { case TOK_BOOL_AND: @@ -716,9 +1127,9 @@ static void Primary (ExprDesc* E) NextToken (); Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND); /* output its label */ - E->Flags = E_RTYPE_RVAL | E_LOC_STATIC; + E->Flags = E_RTYPE_RVAL | E_LOC_CODE | E_ADDRESS_OF; E->Name = Entry->V.L.Label; - E->Type = PointerTo (type_void); + E->Type = NewPointerTo (type_void); NextToken (); } else { Error ("Computed gotos are a C extension, not supported with this --standard"); @@ -738,12 +1149,12 @@ static void Primary (ExprDesc* E) /* Check for illegal symbol types */ CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL); - if (Sym->Flags & SC_TYPE) { + if (Sym->Flags & SC_ESUTYPEMASK) { /* 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; + E->Flags |= E_LOC_STACK | E_RTYPE_LVAL; + E->Type = type_int; return; } @@ -756,7 +1167,7 @@ static void Primary (ExprDesc* E) /* Check for legal symbol types */ if ((Sym->Flags & SC_CONST) == SC_CONST) { /* Enum or some other numeric constant */ - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->IVal = Sym->V.ConstVal; } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { /* Function */ @@ -782,7 +1193,7 @@ static void Primary (ExprDesc* E) E->Name = Sym->V.R.RegOffs; } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { /* Static variable */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { + if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; E->Name = (uintptr_t) Sym->Name; } else { @@ -797,12 +1208,12 @@ static void Primary (ExprDesc* E) /* We've made all variables lvalues above. However, this is ** not always correct: An array is actually the address of its - ** first element, which is a rvalue, and a function is a + ** first element, which is an rvalue, and a function is an ** rvalue, too, because we cannot store anything in a function. ** So fix the flags depending on the type. */ if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) { - ED_MakeRVal (E); + ED_AddrExpr (E); } } else { @@ -814,16 +1225,16 @@ static void Primary (ExprDesc* E) /* IDENT is either an auto-declared function or an undefined variable. */ if (CurTok.Tok == TOK_LPAREN) { - /* C99 doesn't allow calls to undefined functions, so + /* C99 doesn't allow calls to undeclared functions, so ** generate an error and otherwise a warning. Declare a ** function returning int. For that purpose, prepare a ** function signature for a function having an empty param ** list and returning int. */ if (IS_Get (&Standard) >= STD_C99) { - Error ("Call to undefined function '%s'", Ident); + Error ("Call to undeclared function '%s'", Ident); } else { - Warning ("Call to undefined function '%s'", Ident); + Warning ("Call to undeclared function '%s'", Ident); } Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC); E->Type = Sym->Type; @@ -843,9 +1254,13 @@ static void Primary (ExprDesc* E) case TOK_SCONST: case TOK_WCSCONST: /* String literal */ - E->LVal = UseLiteral (CurTok.SVal); + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + E->V.LVal = UseLiteral (CurTok.SVal); + } else { + E->V.LVal = CurTok.SVal; + } E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); - E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL; + E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; E->IVal = 0; E->Name = GetLiteralLabel (CurTok.SVal); NextToken (); @@ -854,7 +1269,7 @@ static void Primary (ExprDesc* E) case TOK_ASM: /* ASM statement */ AsmStatement (); - E->Flags = E_LOC_EXPR | E_RTYPE_RVAL; + E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED; E->Type = type_void; break; @@ -883,310 +1298,58 @@ static void Primary (ExprDesc* E) /* Illegal primary. Be sure to skip the token to avoid endless ** error loops. */ - Error ("Expression expected"); - NextToken (); + if (CurTok.Tok == TOK_LCURLY) { + /* Statement block */ + NextToken (); + Error ("Expression expected"); + E->Flags |= E_EVAL_MAYBE_UNUSED; + hie0 (E); + if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + } + break; + } else { + /* Let's see if this is a C99-style declaration */ + DeclSpec Spec; + InitDeclSpec (&Spec); + ParseDeclSpec (&Spec, -1, T_QUAL_NONE); + + if (Spec.Type->C != T_END) { + + Error ("Mixed declarations and code are not supported in cc65"); + while (CurTok.Tok != TOK_SEMI) { + Declaration Decl; + + /* Parse one declaration */ + ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + if (CurTok.Tok == TOK_ASSIGN) { + NextToken (); + ParseInit (Decl.Type); + } + if (CurTok.Tok == TOK_COMMA) { + NextToken (); + } else { + break; + } + } + } else { + Error ("Expression expected"); + E->Flags |= E_EVAL_MAYBE_UNUSED; + NextToken (); + } + } ED_MakeConstAbsInt (E, 1); break; } -} - - -static void ArrayRef (ExprDesc* Expr) -/* Handle an array reference. This function needs a rewrite. */ -{ - int ConstBaseAddr; - ExprDesc Subscript; - CodeMark Mark1; - CodeMark Mark2; - TypeCode Qualifiers; - Type* ElementType; - Type* tptr1; - - - /* Skip the bracket */ - NextToken (); - - /* Get the type of left side */ - tptr1 = Expr->Type; - - /* We can apply a special treatment for arrays that have a const base - ** address. This is true for most arrays and will produce a lot better - ** code. Check if this is a const base address. - */ - ConstBaseAddr = ED_IsRVal (Expr) && - (ED_IsLocConst (Expr) || ED_IsLocStack (Expr)); - - /* If we have a constant base, we delay the address fetch */ - GetCodePos (&Mark1); - if (!ConstBaseAddr) { - /* Get a pointer to the array into the primary */ - LoadExpr (CF_NONE, Expr); - - /* Get the array pointer on stack. Do not push more than 16 - ** bit, even if this value is greater, since we cannot handle - ** other than 16bit stuff when doing indexing. - */ - GetCodePos (&Mark2); - g_push (CF_PTR, 0); - } - - /* TOS now contains ptr to array elements. Get the subscript. */ - MarkedExprWithCheck (hie0, &Subscript); - - /* Check the types of array and subscript. We can either have a - ** pointer/array to the left, in which case the subscript must be of an - ** integer type, or we have an integer to the left, in which case the - ** subscript must be a pointer/array. - ** Since we do the necessary checking here, we can rely later on the - ** correct types. - */ - Qualifiers = T_QUAL_NONE; - if (IsClassPtr (Expr->Type)) { - if (!IsClassInt (Subscript.Type)) { - Error ("Array subscript is not an integer"); - /* To avoid any compiler errors, make the expression a valid int */ - ED_MakeConstAbsInt (&Subscript, 0); - } - if (IsTypeArray (Expr->Type)) { - Qualifiers = GetQualifier (Expr->Type); - } - ElementType = Indirect (Expr->Type); - } else if (IsClassInt (Expr->Type)) { - if (!IsClassPtr (Subscript.Type)) { - Error ("Subscripted value is neither array nor pointer"); - /* To avoid compiler errors, make the subscript a char[] at - ** address 0. - */ - ED_MakeConstAbs (&Subscript, 0, GetCharArrayType (1)); - } else if (IsTypeArray (Subscript.Type)) { - Qualifiers = GetQualifier (Subscript.Type); - } - ElementType = Indirect (Subscript.Type); - } else { - Error ("Cannot subscript"); - /* To avoid compiler errors, fake both the array and the subscript, so - ** we can just proceed. - */ - ED_MakeConstAbs (Expr, 0, GetCharArrayType (1)); - ED_MakeConstAbsInt (&Subscript, 0); - ElementType = Indirect (Expr->Type); - } - - /* The element type has the combined qualifiers from itself and the array, - ** it is a member of (if any). - */ - if (GetQualifier (ElementType) != (GetQualifier (ElementType) | Qualifiers)) { - ElementType = TypeDup (ElementType); - ElementType->C |= Qualifiers; - } - - /* If the subscript is a bit-field, load it and make it an rvalue */ - if (ED_IsBitField (&Subscript)) { - LoadExpr (CF_NONE, &Subscript); - ED_MakeRValExpr (&Subscript); - } - - /* Check if the subscript is constant absolute value */ - if (ED_IsConstAbs (&Subscript) && ED_CodeRangeIsEmpty (&Subscript)) { - - /* The array subscript is a numeric constant. If we had pushed the - ** array base address onto the stack before, we can remove this value, - ** since we can generate expression+offset. - */ - if (!ConstBaseAddr) { - RemoveCode (&Mark2); - } else { - /* Get an array pointer into the primary */ - LoadExpr (CF_NONE, Expr); - } - - if (IsClassPtr (Expr->Type)) { - - /* Lhs is pointer/array. Scale the subscript value according to - ** the element size. - */ - Subscript.IVal *= CheckedSizeOf (ElementType); - - /* Remove the address load code */ - RemoveCode (&Mark1); - - /* In case of an array, we can adjust the offset of the expression - ** already in Expr. If the base address was a constant, we can even - ** remove the code that loaded the address into the primary. - */ - if (IsTypeArray (Expr->Type)) { - - /* Adjust the offset */ - Expr->IVal += Subscript.IVal; - - } else { - - /* It's a pointer, so we do have to load it into the primary - ** first (if it's not already there). - */ - if (ConstBaseAddr || ED_IsLVal (Expr)) { - LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); - } - - /* Use the offset */ - Expr->IVal = Subscript.IVal; - } - - } else { - - /* Scale the rhs value according to the element type */ - g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType)); - - /* Add the subscript. Since arrays are indexed by integers, - ** we will ignore the true type of the subscript here and - ** use always an int. #### Use offset but beware of LoadExpr! - */ - g_inc (CF_INT | CF_CONST, Subscript.IVal); - - } - - } else { - - /* Array subscript is not constant. Load it into the primary */ - GetCodePos (&Mark2); - LoadExpr (CF_NONE, &Subscript); - - /* Do scaling */ - if (IsClassPtr (Expr->Type)) { - - /* Indexing is based on unsigneds, so we will just use the integer - ** portion of the index (which is in (e)ax, so there's no further - ** action required). - */ - g_scale (CF_INT, CheckedSizeOf (ElementType)); - - } else { - - /* Get the int value on top. If we come here, we're sure, both - ** values are 16 bit (the first one was truncated if necessary - ** and the second one is a pointer). Note: If ConstBaseAddr is - ** true, we don't have a value on stack, so to "swap" both, just - ** push the subscript. - */ - if (ConstBaseAddr) { - g_push (CF_INT, 0); - LoadExpr (CF_NONE, Expr); - ConstBaseAddr = 0; - } else { - g_swap (CF_INT); - } - - /* Scale it */ - g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType)); - - } - - /* The offset is now in the primary register. It we didn't have a - ** constant base address for the lhs, the lhs address is already - ** on stack, and we must add the offset. If the base address was - ** constant, we call special functions to add the address to the - ** offset value. - */ - if (!ConstBaseAddr) { - - /* The array base address is on stack and the subscript is in the - ** primary. Add both. - */ - g_add (CF_INT, 0); - - } else { - - /* The subscript is in the primary, and the array base address is - ** in Expr. If the subscript has itself a constant address, it is - ** often a better idea to reverse again the order of the - ** evaluation. This will generate better code if the subscript is - ** a byte sized variable. But beware: This is only possible if the - ** subscript was not scaled, that is, if this was a byte array - ** or pointer. - */ - if ((ED_IsLocConst (&Subscript) || ED_IsLocStack (&Subscript)) && - CheckedSizeOf (ElementType) == SIZEOF_CHAR) { - - unsigned Flags; - - /* Reverse the order of evaluation */ - if (CheckedSizeOf (Subscript.Type) == SIZEOF_CHAR) { - Flags = CF_CHAR; - } else { - Flags = CF_INT; - } - RemoveCode (&Mark2); - - /* Get a pointer to the array into the primary. */ - LoadExpr (CF_NONE, Expr); - - /* Add the variable */ - if (ED_IsLocStack (&Subscript)) { - g_addlocal (Flags, Subscript.IVal); - } else { - Flags |= GlobalModeFlags (&Subscript); - g_addstatic (Flags, Subscript.Name, Subscript.IVal); - } - } else { - - if (ED_IsLocAbs (Expr)) { - /* Constant numeric address. Just add it */ - g_inc (CF_INT, Expr->IVal); - } else if (ED_IsLocStack (Expr)) { - /* Base address is a local variable address */ - if (IsTypeArray (Expr->Type)) { - g_addaddr_local (CF_INT, Expr->IVal); - } else { - g_addlocal (CF_PTR, Expr->IVal); - } - } else { - /* Base address is a static variable address */ - unsigned Flags = CF_INT | GlobalModeFlags (Expr); - if (ED_IsRVal (Expr)) { - /* Add the address of the location */ - g_addaddr_static (Flags, Expr->Name, Expr->IVal); - } else { - /* Add the contents of the location */ - g_addstatic (Flags, Expr->Name, Expr->IVal); - } - } - } - - - } - - /* The result is an expression in the primary */ - ED_MakeRValExpr (Expr); - - } - - /* Result is of element type */ - Expr->Type = ElementType; - - /* An array element is actually a variable. So the rules for variables - ** with respect to the reference type apply: If it's an array, it is - ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue, - ** but an array cannot contain functions). - */ - if (IsTypeArray (Expr->Type)) { - ED_MakeRVal (Expr); - } else { - ED_MakeLVal (Expr); - } - - /* Consume the closing bracket */ - ConsumeRBrack (); + E->Flags |= Flags; } static void StructRef (ExprDesc* Expr) -/* Process struct field after . or ->. */ +/* Process struct/union field after . or ->. */ { - ident Ident; - SymEntry* Field; Type* FinalType; TypeCode Q; @@ -1199,78 +1362,109 @@ static void StructRef (ExprDesc* Expr) return; } - /* Get the symbol table entry and check for a struct field */ - strcpy (Ident, CurTok.Ident); + /* Get the symbol table entry and check for a struct/union field */ NextToken (); - Field = FindStructField (Expr->Type, Ident); - if (Field == 0) { - Error ("Struct/union has no field named '%s'", Ident); + const SymEntry Field = FindStructField (Expr->Type, CurTok.Ident); + if (Field.Type == 0) { + Error ("No field named '%s' found in '%s'", CurTok.Ident, GetFullTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; } - /* If we have a struct pointer that is an lvalue and not already in the - ** primary, load it now. - */ - if (ED_IsLVal (Expr) && IsTypePtr (Expr->Type)) { + if (IsTypePtr (Expr->Type)) { - /* Load into the primary */ + /* pointer->field */ + if (!ED_IsQuasiConst (Expr) && !ED_IsLocPrimary (Expr)) { + /* If we have a non-const struct/union pointer that is not in the + ** primary yet, load its content now to get the base address. + */ + LoadExpr (CF_NONE, Expr); + ED_FinalizeRValLoad (Expr); + } + /* Dereference the address expression */ + ED_IndExpr (Expr); + + } else if (ED_IsRVal (Expr) && + ED_IsLocPrimary (Expr) && + Expr->Type == GetStructReplacementType (Expr->Type)) { + + /* A struct/union is usually an lvalue. If not, it is a struct/union + ** passed in the primary register, which is usually the result returned + ** from a function. However, it is possible that this rvalue is the + ** result of certain kind of operations on an lvalue such as assignment, + ** and there are no reasons to disallow such use cases. So we just rely + ** on the check upon function returns to catch the unsupported cases and + ** dereference the rvalue address of the struct/union here all the time. + */ + ED_IndExpr (Expr); + + } else if (!ED_IsLocQuasiConst (Expr) && !ED_IsLocPrimaryOrExpr (Expr)) { + /* Load the base address into the primary (and use it as a reference + ** later) if it's not quasi-const or in the primary already. + */ LoadExpr (CF_NONE, Expr); - - /* Make it an lvalue expression */ - ED_MakeLValExpr (Expr); } - /* The type is the type of the field plus any qualifiers from the struct */ + /* Clear the tested flag set during loading */ + ED_MarkAsUntested (Expr); + + /* The type is the field type plus any qualifiers from the struct/union */ if (IsClassStruct (Expr->Type)) { Q = GetQualifier (Expr->Type); } else { Q = GetQualifier (Indirect (Expr->Type)); } - if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) { - FinalType = Field->Type; + if (GetQualifier (Field.Type) == (GetQualifier (Field.Type) | Q)) { + FinalType = Field.Type; } else { - FinalType = TypeDup (Field->Type); + FinalType = TypeDup (Field.Type); FinalType->C |= Q; } - /* A struct is usually an lvalue. If not, it is a struct in the primary - ** register. - */ - if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) { + if (ED_IsRVal (Expr) && ED_IsLocPrimary (Expr) && !IsTypePtr (Expr->Type)) { unsigned Flags = 0; unsigned BitOffs; /* Get the size of the type */ - unsigned Size = SizeOf (Expr->Type); + unsigned StructSize = SizeOf (Expr->Type); + unsigned FieldSize = SizeOf (Field.Type); /* Safety check */ - CHECK (Field->V.Offs + Size <= SIZEOF_LONG); + CHECK (Field.V.Offs + FieldSize <= StructSize); - /* The type of the operation depends on the type of the struct */ - switch (Size) { - case 1: Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; break; - case 2: Flags = CF_INT | CF_UNSIGNED | CF_CONST; break; - case 3: /* FALLTHROUGH */ - case 4: Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break; - default: Internal ("Invalid struct size: %u", Size); break; + /* The type of the operation depends on the type of the struct/union */ + switch (StructSize) { + case 1: + Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; + break; + case 2: + Flags = CF_INT | CF_UNSIGNED | CF_CONST; + break; + case 3: + /* FALLTHROUGH */ + case 4: + Flags = CF_LONG | CF_UNSIGNED | CF_CONST; + break; + default: + Internal ("Invalid '%s' size: %u", GetFullTypeName (Expr->Type), StructSize); + break; } /* Generate a shift to get the field in the proper position in the ** primary. For bit fields, mask the value. */ - BitOffs = Field->V.Offs * CHAR_BITS; - if (SymIsBitField (Field)) { - BitOffs += Field->V.B.BitOffs; + BitOffs = Field.V.Offs * CHAR_BITS; + if (SymIsBitField (&Field)) { + BitOffs += Field.Type->A.B.Offs; g_asr (Flags, BitOffs); /* Mask the value. This is unnecessary if the shift executed above ** moved only zeroes into the value. */ - if (BitOffs + Field->V.B.BitWidth != Size * CHAR_BITS) { + if (BitOffs + Field.Type->A.B.Width != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, - (0x0001U << Field->V.B.BitWidth) - 1U); + (0x0001U << Field.Type->A.B.Width) - 1U); } } else { g_asr (Flags, BitOffs); @@ -1281,29 +1475,23 @@ static void StructRef (ExprDesc* Expr) } else { - /* Set the struct field offset */ - Expr->IVal += Field->V.Offs; + /* Set the struct/union field offset */ + Expr->IVal += Field.V.Offs; /* Use the new type */ Expr->Type = FinalType; - /* An struct member is actually a variable. So the rules for variables - ** with respect to the reference type apply: If it's an array, it is - ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue, - ** but a struct field cannot be a function). + /* The usual rules for variables with respect to the reference types + ** apply to struct/union fields as well: If a field is an array, it is + ** virtually an rvalue address, otherwise it's an lvalue reference. (A + ** function would also be an rvalue address, but a struct/union cannot + ** contain functions). */ if (IsTypeArray (Expr->Type)) { - ED_MakeRVal (Expr); - } else { - ED_MakeLVal (Expr); + ED_AddrExpr (Expr); } - /* Make the expression a bit field if necessary */ - if (SymIsBitField (Field)) { - ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth); - } } - } @@ -1318,14 +1506,15 @@ static void hie11 (ExprDesc *Expr) Primary (Expr); /* Check for a rhs */ - while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || + 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: /* Array reference */ - ArrayRef (Expr); + parseadd (Expr, 1); break; case TOK_LPAREN: @@ -1346,7 +1535,7 @@ static void hie11 (ExprDesc *Expr) case TOK_DOT: if (!IsClassStruct (Expr->Type)) { - Error ("Struct expected"); + Error ("Struct or union expected"); } StructRef (Expr); break; @@ -1357,11 +1546,19 @@ static void hie11 (ExprDesc *Expr) Expr->Type = ArrayToPtr (Expr->Type); } if (!IsClassPtr (Expr->Type) || !IsClassStruct (Indirect (Expr->Type))) { - Error ("Struct pointer expected"); + Error ("Struct pointer or union pointer expected"); } StructRef (Expr); break; + case TOK_INC: + PostInc (Expr); + break; + + case TOK_DEC: + PostDec (Expr); + break; + default: Internal ("Invalid token in hie11: %d", CurTok.Tok); @@ -1391,23 +1588,18 @@ void Store (ExprDesc* Expr, const Type* StoreType) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_putstatic (Flags, Expr->IVal, 0); break; case E_LOC_GLOBAL: - /* Global variable */ - g_putstatic (Flags, Expr->Name, Expr->IVal); - break; - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_putstatic (Flags, Expr->Name, Expr->IVal); - break; - case E_LOC_REGISTER: - /* Register variable */ + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ g_putstatic (Flags, Expr->Name, Expr->IVal); break; @@ -1421,10 +1613,14 @@ void Store (ExprDesc* Expr, const Type* StoreType) break; case E_LOC_EXPR: - /* An expression in the primary register */ + /* An expression referenced in the primary register */ g_putind (Flags, Expr->IVal); break; + case E_LOC_NONE: + /* We may get here as a result of previous compiler errors */ + break; + default: Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); } @@ -1438,9 +1634,6 @@ void Store (ExprDesc* Expr, const Type* StoreType) static void PreInc (ExprDesc* Expr) /* Handle the preincrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1456,57 +1649,11 @@ static void PreInc (ExprDesc* Expr) Error ("Increment of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - /* Global variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_REGISTER: - /* Register variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the increment */ + DoInc (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1514,9 +1661,6 @@ static void PreInc (ExprDesc* Expr) static void PreDec (ExprDesc* Expr) /* Handle the predecrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1532,57 +1676,11 @@ static void PreDec (ExprDesc* Expr) Error ("Decrement of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - /* Global variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_REGISTER: - /* Register variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the decrement */ + DoDec (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1608,37 +1706,36 @@ static void PostInc (ExprDesc* Expr) /* Get the data type */ Flags = TypeOf (Expr->Type); + /* We are allowed by the C standard to defer the inc operation until after + ** the expression is used, so that we don't need to save and reload + ** the original value. + */ + /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); - AddCodeLine ("inc %s", ED_GetLabelName(Expr, 0)); + AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0)); } else { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); + /* Do the increment */ + DoInc (Expr, OA_NEED_OLD); - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); } else { - g_inc (Flags | CF_CONST | CF_FORCECHAR, 1); + + /* Defer the increment until after the value of this expression is used */ + DeferInc (Expr); + + /* Just return */ + return; } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); } /* The result is always an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1650,7 +1747,7 @@ static void PostDec (ExprDesc* Expr) NextToken (); - /* The expression to increment must be an lvalue */ + /* The expression to decrement must be an lvalue */ if (!ED_IsLVal (Expr)) { Error ("Invalid lvalue"); return; @@ -1665,36 +1762,30 @@ static void PostDec (ExprDesc* Expr) Flags = TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); - AddCodeLine ("dec %s", ED_GetLabelName(Expr, 0)); + AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0)); } else { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); + /* Do the decrement */ + DoDec (Expr, OA_NEED_OLD); - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); } else { - g_dec (Flags | CF_CONST | CF_FORCECHAR, 1); + + /* Defer the decrement until after the value of this expression is used */ + DeferDec (Expr); + + /* Just return */ + return; } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); } /* The result is always an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1702,8 +1793,6 @@ static void PostDec (ExprDesc* Expr) static void UnaryOp (ExprDesc* Expr) /* Handle unary -/+ and ~ */ { - unsigned Flags; - /* Remember the operator token and skip it */ token_t Tok = CurTok.Tok; NextToken (); @@ -1717,20 +1806,33 @@ static void UnaryOp (ExprDesc* Expr) ED_MakeConstAbsInt (Expr, 1); } - /* Check for a constant expression */ + /* Check for a constant numeric expression */ if (ED_IsConstAbs (Expr)) { - /* Value is constant */ + /* Value is numeric */ switch (Tok) { case TOK_MINUS: Expr->IVal = -Expr->IVal; break; case TOK_PLUS: break; case TOK_COMP: Expr->IVal = ~Expr->IVal; break; default: Internal ("Unexpected token: %d", Tok); } + + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr); + } else { + unsigned Flags; + /* Value is not constant */ LoadExpr (CF_NONE, Expr); - /* Get the type of the expression */ + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + TypeConversion (Expr, Expr->Type); + + /* Get code generation flags */ Flags = TypeOf (Expr->Type); /* Handle the operation */ @@ -1741,8 +1843,8 @@ static void UnaryOp (ExprDesc* Expr) default: Internal ("Unexpected token: %d", Tok); } - /* The result is a rvalue in the primary */ - ED_MakeRValExpr (Expr); + /* The result is an rvalue in the primary */ + ED_FinalizeRValLoad (Expr); } } @@ -1771,26 +1873,28 @@ void hie10 (ExprDesc* Expr) case TOK_BOOL_NOT: NextToken (); - if (evalexpr (CF_NONE, hie10, Expr) == 0) { - /* Constant expression */ + BoolExpr (hie10, Expr); + if (ED_IsConstAbs (Expr)) { + /* Constant numeric expression */ Expr->IVal = !Expr->IVal; + } else if (ED_IsAddrExpr (Expr)) { + /* Address != NULL, so !Address == 0 */ + ED_MakeConstBool (Expr, 0); } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, Expr); g_bneg (TypeOf (Expr->Type)); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* bneg will set cc */ } + /* The result type is always boolean */ + Expr->Type = type_bool; break; case TOK_STAR: NextToken (); ExprWithCheck (hie10, Expr); - if (ED_IsLVal (Expr) || !(ED_IsLocConst (Expr) || ED_IsLocStack (Expr))) { - /* Not a const, load it into the primary and make it a - ** calculated value. - */ - LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); - } + /* If the expression is already a pointer to function, the ** additional dereferencing operator must be ignored. A function ** itself is represented as "pointer to function", so any number @@ -1799,8 +1903,16 @@ void hie10 (ExprDesc* Expr) */ if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) { /* Expression not storable */ - ED_MakeRVal (Expr); + ED_MarkExprAsRVal (Expr); } else { + if (!ED_IsQuasiConstAddr (Expr)) { + /* Not a constant address, load the pointer into the primary + ** and make it a calculated value. + */ + LoadExpr (CF_NONE, Expr); + ED_FinalizeRValLoad (Expr); + } + if (IsClassPtr (Expr->Type)) { Expr->Type = Indirect (Expr->Type); } else { @@ -1810,8 +1922,8 @@ void hie10 (ExprDesc* Expr) ** address -- it already is the location of the first element. */ if (!IsTypeArray (Expr->Type)) { - /* The * operator yields an lvalue */ - ED_MakeLVal (Expr); + /* The * operator yields an lvalue reference */ + ED_IndExpr (Expr); } } break; @@ -1820,20 +1932,23 @@ void hie10 (ExprDesc* Expr) NextToken (); ExprWithCheck (hie10, Expr); /* The & operator may be applied to any lvalue, and it may be - ** applied to functions, even if they're no lvalues. + ** applied to functions and arrays, even if they're not lvalues. */ - if (ED_IsRVal (Expr) && !IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { - Error ("Illegal address"); - } else { - if (ED_IsBitField (Expr)) { - Error ("Cannot take address of bit-field"); - /* Do it anyway, just to avoid further warnings */ - Expr->Flags &= ~E_BITFIELD; + if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { + if (ED_IsRVal (Expr)) { + Error ("Illegal address"); + /* Continue anyway, just to avoid further warnings */ } - Expr->Type = PointerTo (Expr->Type); - /* The & operator yields an rvalue */ - ED_MakeRVal (Expr); + + if (IsTypeBitField (Expr->Type)) { + Error ("Cannot take address of bit-field"); + /* Continue anyway, just to avoid further warnings */ + Expr->Type = GetUnderlyingType (Expr->Type); + } + /* The & operator yields an rvalue address */ + ED_AddrExpr (Expr); } + Expr->Type = NewPointerTo (Expr->Type); break; case TOK_SIZEOF: @@ -1841,21 +1956,25 @@ void hie10 (ExprDesc* Expr) if (TypeSpecAhead ()) { Type T[MAXTYPELEN]; NextToken (); - Size = CheckedSizeOf (ParseType (T)); + Size = ExprCheckedSizeOf (ParseType (T)); ConsumeRParen (); } else { /* Remember the output queue pointer */ CodeMark Mark; GetCodePos (&Mark); - hie10 (Expr); - /* If the expression is a literal string, release it, so it - ** won't be output as data if not used elsewhere. - */ - if (ED_IsLocLiteral (Expr)) { - ReleaseLiteral (Expr->LVal); + + /* The expression shall be unevaluated */ + ExprDesc Uneval; + ED_Init (&Uneval); + ED_MarkForUneval (&Uneval); + hie10 (&Uneval); + if (IsTypeBitField (Uneval.Type)) { + Error ("Cannot apply 'sizeof' to bit-field"); + Size = 0; + } else { + /* Calculate the size */ + Size = ExprCheckedSizeOf (Uneval.Type); } - /* Calculate the size */ - Size = CheckedSizeOf (Expr->Type); /* Remove any generated code */ RemoveCode (&Mark); } @@ -1874,13 +1993,6 @@ void hie10 (ExprDesc* Expr) /* An expression */ hie11 (Expr); - /* Handle post increment */ - switch (CurTok.Tok) { - case TOK_INC: PostInc (Expr); break; - case TOK_DEC: PostDec (Expr); break; - default: break; - } - } break; } @@ -1894,7 +2006,6 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ int* UsedGen) /* Helper function */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; const GenDesc* Gen; @@ -1909,6 +2020,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ *UsedGen = 0; while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Tell the caller that we handled it's ops */ *UsedGen = 1; @@ -1965,7 +2080,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ RemoveCode (&Mark1); /* Get the type of the result */ - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Handle the op differently for signed and unsigned types */ if (IsSignSigned (Expr->Type)) { @@ -2044,7 +2159,19 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ } } + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr); + } else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) { + /* If the LHS constant is an int that fits into an unsigned char, change the + ** codegen type to unsigned char. If the RHS is also an unsigned char, then + ** g_typeadjust will return unsigned int (instead of int, which would be + ** returned without this modification). This allows more efficient operations, + ** but does not affect correctness for the same reasons explained in g_typeadjust. + */ + if (ltype == CF_INT && Expr->IVal >= 0 && Expr->IVal < 256) { + ltype = CF_CHAR | CF_UNSIGNED; + } /* The left side is constant, the right side is not, and the ** operator allows swapping the operands. We haven't pushed the @@ -2057,18 +2184,18 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ if ((Gen->Flags & GEN_NOPUSH) == 0) { g_push (ltype, 0); } else { - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } /* Determine the type of the operation result. */ type |= g_typeadjust (ltype, rtype); - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Generate code */ Gen->Func (type, Expr->IVal); - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } else { @@ -2079,6 +2206,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ unsigned rtype = TypeOf (Expr2.Type); type = 0; if (rconst) { + /* As above, but for the RHS. */ + if (rtype == CF_INT && Expr2.IVal >= 0 && Expr2.IVal < 256) { + rtype = CF_CHAR | CF_UNSIGNED; + } /* Second value is constant - check for div */ type |= CF_CONST; rtype |= CF_CONST; @@ -2089,19 +2220,19 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ } if ((Gen->Flags & GEN_NOPUSH) != 0) { RemoveCode (&Mark2); - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } } /* Determine the type of the operation result. */ type |= g_typeadjust (ltype, rtype); - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Generate code */ Gen->Func (type, Expr2.IVal); - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } } } @@ -2113,8 +2244,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ void (*hienext) (ExprDesc*)) /* Helper function for the compare operators */ { - ExprDesc Expr2; - CodeMark Mark0; CodeMark Mark1; CodeMark Mark2; const GenDesc* Gen; @@ -2123,11 +2252,14 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ int rconst; /* Operand is a constant */ - GetCodePos (&Mark0); ExprWithCheck (hienext, Expr); while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Remember the generator function */ void (*GenFunc) (unsigned, unsigned long) = Gen->Func; @@ -2137,18 +2269,18 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* If lhs is a function, convert it to pointer to function */ if (IsTypeFunc (Expr->Type)) { - Expr->Type = PointerTo (Expr->Type); + Expr->Type = NewPointerTo (Expr->Type); } /* Get the lhs on stack */ GetCodePos (&Mark1); ltype = TypeOf (Expr->Type); if (ED_IsConstAbs (Expr)) { - /* Constant value */ + /* Numeric constant value */ GetCodePos (&Mark2); g_push (ltype | CF_CONST, Expr->IVal); } else { - /* Value not constant */ + /* Value not numeric constant */ LoadExpr (CF_NONE, Expr); GetCodePos (&Mark2); g_push (ltype, 0); @@ -2159,26 +2291,31 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* If rhs is a function, convert it to pointer to function */ if (IsTypeFunc (Expr2.Type)) { - Expr2.Type = PointerTo (Expr2.Type); + Expr2.Type = NewPointerTo (Expr2.Type); } - /* Check for a constant expression */ + /* Check for a numeric constant expression */ rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)); if (!rconst) { - /* Not constant, load into the primary */ + /* Not numeric constant, load into the primary */ LoadExpr (CF_NONE, &Expr2); } + /* Check if operands have allowed types for this operation */ + if (!IsRelationType (Expr->Type) || !IsRelationType (Expr2.Type)) { + /* Output only one message even if both sides are wrong */ + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + /* Avoid further errors */ + ED_MakeConstAbsInt (Expr, 0); + ED_MakeConstAbsInt (&Expr2, 0); + } + /* Some operations aren't allowed on function pointers */ if ((Gen->Flags & GEN_NOFUNC) != 0) { - /* Output only one message even if both sides are wrong */ - if (IsTypeFuncPtr (Expr->Type)) { - Error ("Invalid left operand for relational operator"); - /* Avoid further errors */ - ED_MakeConstAbsInt (Expr, 0); - ED_MakeConstAbsInt (&Expr2, 0); - } else if (IsTypeFuncPtr (Expr2.Type)) { - Error ("Invalid right operand for relational operator"); + if ((IsTypeFuncPtr (Expr->Type) || IsTypeFuncPtr (Expr2.Type))) { + /* Output only one message even if both sides are wrong */ + Error ("Cannot use function pointers in this relation operation"); /* Avoid further errors */ ED_MakeConstAbsInt (Expr, 0); ED_MakeConstAbsInt (&Expr2, 0); @@ -2187,34 +2324,104 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Make sure, the types are compatible */ if (IsClassInt (Expr->Type)) { - if (!IsClassInt (Expr2.Type) && !(IsClassPtr(Expr2.Type) && ED_IsNullPtr(Expr))) { - Error ("Incompatible types"); + if (!IsClassInt (Expr2.Type) && !ED_IsNullPtr (Expr)) { + if (IsClassPtr (Expr2.Type)) { + TypeCompatibilityDiagnostic (Expr->Type, PtrConversion (Expr2.Type), 0, + "Comparing integer '%s' with pointer '%s'"); + } else { + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + } } } else if (IsClassPtr (Expr->Type)) { if (IsClassPtr (Expr2.Type)) { - /* Both pointers are allowed in comparison if they point to - ** the same type, or if one of them is a void pointer. - */ - Type* left = Indirect (Expr->Type); - Type* right = Indirect (Expr2.Type); - if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) { - /* Incompatible pointers */ - Error ("Incompatible types"); + /* Pointers are allowed in comparison */ + if (TypeCmp (Expr->Type, Expr2.Type).C < TC_STRICT_COMPATIBLE) { + /* Warn about distinct pointer types */ + TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0, + "Distinct pointer types comparing '%s' with '%s'"); } } else if (!ED_IsNullPtr (&Expr2)) { - Error ("Incompatible types"); + if (IsClassInt (Expr2.Type)) { + TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), Expr2.Type, 0, + "Comparing pointer type '%s' with integer type '%s'"); + } else { + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + } } } - /* Check for const operands */ - if (ED_IsConstAbs (Expr) && rconst) { + /* Check for numeric constant operands */ + if ((ED_IsAddrExpr (Expr) && ED_IsNullPtr (&Expr2)) || + (ED_IsNullPtr (Expr) && ED_IsAddrExpr (&Expr2))) { + + /* Object addresses are inequal to null pointer */ + Expr->IVal = (Tok != TOK_EQ); + if (ED_IsNullPtr (&Expr2)) { + if (Tok == TOK_LT || Tok == TOK_LE) { + Expr->IVal = 0; + } + } else { + if (Tok == TOK_GT || Tok == TOK_GE) { + Expr->IVal = 0; + } + } + + /* Get rid of unwanted flags */ + ED_MakeConstBool (Expr, Expr->IVal); + + /* The result is constant, this is suspicious when not in + ** preprocessor mode. + */ + WarnConstCompareResult (Expr); + + if (ED_CodeRangeIsEmpty (&Expr2)) { + /* Both operands are static, remove the load code */ + RemoveCode (&Mark1); + } else { + /* Drop pushed lhs */ + g_drop (sizeofarg (ltype)); + pop (ltype); + } + + } else if (ED_IsAddrExpr (Expr) && + ED_IsAddrExpr (&Expr2) && + Expr->Sym == Expr2.Sym) { + + /* Evaluate the result for static addresses */ + unsigned long Val1 = Expr->IVal; + unsigned long Val2 = Expr2.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 ("hie_compare: got token 0x%X\n", Tok); + } + + /* Get rid of unwanted flags */ + ED_MakeConstBool (Expr, Expr->IVal); /* If the result is constant, this is suspicious when not in ** preprocessor mode. */ - WarnConstCompareResult (); + WarnConstCompareResult (Expr); - /* Both operands are constant, remove the generated code */ + if (ED_CodeRangeIsEmpty (&Expr2)) { + /* Both operands are static, remove the load code */ + RemoveCode (&Mark1); + } else { + /* Drop pushed lhs */ + g_drop (sizeofarg (ltype)); + pop (ltype); + } + + } else if (ED_IsConstAbs (Expr) && rconst) { + + /* Both operands are numeric constant, remove the generated code */ RemoveCode (&Mark1); /* Determine if this is a signed or unsigned compare */ @@ -2250,11 +2457,21 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } + /* Get rid of unwanted flags */ + ED_MakeConstBool (Expr, Expr->IVal); + + /* If the result is constant, this is suspicious when not in + ** preprocessor mode. + */ + WarnConstCompareResult (Expr); + } else { /* Determine the signedness of the operands */ int LeftSigned = IsSignSigned (Expr->Type); int RightSigned = IsSignSigned (Expr2.Type); + int CmpSigned = IsClassInt (Expr->Type) && IsClassInt (Expr2.Type) && + IsSignSigned (ArithmeticConvert (Expr->Type, Expr2.Type)); /* If the right hand side is constant, and the generator function ** expects the lhs in the primary, remove the push of the primary @@ -2265,12 +2482,12 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_CONST; if ((Gen->Flags & GEN_NOPUSH) != 0) { RemoveCode (&Mark2); - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } } /* Determine the type of the operation. */ - if (IsTypeChar (Expr->Type) && rconst) { + if (IsTypeChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) { /* Left side is unsigned char, right side is constant. ** Determine the minimum and maximum values @@ -2283,20 +2500,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ LeftMin = 0; LeftMax = 255; } - /* An integer value is always represented as a signed in the - ** ExprDesc structure. This may lead to false results below, - ** if it is actually unsigned, but interpreted as signed - ** because of the representation. Fortunately, in this case, - ** the actual value doesn't matter, since it's always greater - ** than what can be represented in a char. So correct the - ** value accordingly. - */ - if (!RightSigned && Expr2.IVal < 0) { - /* Correct the value so it is an unsigned. It will then - ** anyway match one of the cases below. - */ - Expr2.IVal = LeftMax + 1; - } /* Comparing a char against a constant may have a constant ** result. Please note: It is not possible to remove the code @@ -2307,48 +2510,48 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_EQ: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, 0); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, 0); + WarnConstCompareResult (Expr); goto Done; } break; case TOK_NE: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, 1); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, 1); + WarnConstCompareResult (Expr); goto Done; } break; case TOK_LT: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, Expr2.IVal > LeftMax); + WarnConstCompareResult (Expr); goto Done; } break; case TOK_LE: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, Expr2.IVal >= LeftMax); + WarnConstCompareResult (Expr); goto Done; } break; case TOK_GE: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, Expr2.IVal <= LeftMin); + WarnConstCompareResult (Expr); goto Done; } break; case TOK_GT: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin); - WarnConstCompareResult (); + ED_MakeConstBool (Expr, Expr2.IVal < LeftMin); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2362,7 +2565,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ ** since the right side constant is in a valid range. */ flags |= (CF_CHAR | CF_FORCECHAR); - if (!LeftSigned) { + if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } @@ -2376,19 +2580,24 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ if (rconst) { flags |= CF_FORCECHAR; } - if (!LeftSigned) { + if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } } else { unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + if (CmpSigned) { + ltype &= ~CF_UNSIGNED; + rtype &= ~CF_UNSIGNED; + } flags |= g_typeadjust (ltype, rtype); } - /* If the left side is an unsigned and the right is a constant, - ** we may be able to change the compares to something more + /* If the comparison is made as unsigned types and the right is a + ** constant, we may be able to change the compares to something more ** effective. */ - if (!LeftSigned && rconst) { + if (!CmpSigned && rconst) { switch (Tok) { @@ -2441,14 +2650,14 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ GenFunc (flags, Expr2.IVal); /* The result is an rvalue in the primary */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); + + /* Condition codes are set */ + ED_TestDone (Expr); } - /* Result type is always int */ - Expr->Type = type_int; - -Done: /* Condition codes are set */ - ED_TestDone (Expr); + /* Result type is always boolean */ +Done: Expr->Type = type_bool; } } @@ -2470,59 +2679,165 @@ static void hie9 (ExprDesc *Expr) -static void parseadd (ExprDesc* Expr) -/* Parse an expression with the binary plus operator. Expr contains the -** unprocessed left hand side of the expression and will contain the -** result of the expression on return. +static void parseadd (ExprDesc* Expr, int DoArrayRef) +/* Parse an expression with the binary plus or subscript operator. Expr contains +** the unprocessed left hand side of the expression and will contain the result +** of the expression on return. If DoArrayRef is zero, this evaluates the binary +** plus operation. Otherwise, this evaluates the subscript operation. */ { ExprDesc Expr2; unsigned flags; /* Operation flags */ CodeMark Mark; /* Remember code position */ - Type* lhst; /* Type of left hand side */ - Type* rhst; /* Type of right hand side */ + const Type* lhst; /* Type of left hand side */ + const Type* rhst; /* Type of right hand side */ + int lscale; + int rscale; + int AddDone; /* No need to generate runtime code */ - /* Skip the PLUS token */ + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Skip the PLUS or opening bracket token */ NextToken (); /* Get the left hand side type, initialize operation flags */ lhst = Expr->Type; flags = 0; + lscale = rscale = 1; + AddDone = 0; - /* Check for constness on both sides */ - if (ED_IsConst (Expr)) { + /* We can only do constant expressions for: + ** - integer addition: + ** - numeric + numeric + ** - (integer)(base + offset) + numeric + ** - numeric + (integer)(base + offset) + ** - pointer offset: + ** - (pointer)numeric + numeric * scale + ** - (base + offset) + numeric * scale + ** - (pointer)numeric + (integer)(base + offset) * 1 + ** - numeric * scale + (pointer)numeric + ** - numeric * scale + (base + offset) + ** - (integer)(base + offset) * 1 + (pointer)numeric + */ + if (ED_IsQuasiConst (Expr)) { /* The left hand side is a constant of some sort. Good. Get rhs */ - ExprWithCheck (hie9, &Expr2); - if (ED_IsConstAbs (&Expr2)) { + ExprWithCheck (DoArrayRef ? hie0 : hie9, &Expr2); - /* Right hand side is a constant numeric value. Get the rhs type */ - rhst = Expr2.Type; + /* Right hand side is constant. Get the rhs type */ + rhst = Expr2.Type; + if (ED_IsQuasiConst (&Expr2)) { /* Both expressions are constants. Check for pointer arithmetic */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { /* Left is pointer, right is int, must scale rhs */ - Expr->IVal += Expr2.IVal * CheckedPSizeOf (lhst); - /* Result type is a pointer */ + rscale = CheckedPSizeOf (lhst); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; } else if (IsClassInt (lhst) && IsClassPtr (rhst)) { /* Left is int, right is pointer, must scale lhs */ - Expr->IVal = Expr->IVal * CheckedPSizeOf (rhst) + Expr2.IVal; - /* Result type is a pointer */ - Expr->Type = Expr2.Type; - } else if (IsClassInt (lhst) && IsClassInt (rhst)) { + lscale = CheckedPSizeOf (rhst); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer addition */ - Expr->IVal += Expr2.IVal; - typeadjust (Expr, &Expr2, 1); + flags = CF_INT; } else { /* OOPS */ - Error ("Invalid operands for binary operator '+'"); + AddDone = -1; + /* Avoid further errors */ + ED_MakeConstAbsInt (Expr, 0); + } + + if (!AddDone) { + /* Do constant calculation if we can */ + if (ED_IsAbs (&Expr2) && + (ED_IsAbs (Expr) || lscale == 1)) { + if (IsClassInt (lhst) && IsClassInt (rhst)) { + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); + } + Expr->IVal = Expr->IVal * lscale + Expr2.IVal * rscale; + AddDone = 1; + } else if (ED_IsAbs (Expr) && + (ED_IsAbs (&Expr2) || rscale == 1)) { + if (IsClassInt (lhst) && IsClassInt (rhst)) { + Expr2.Type = ArithmeticConvert (Expr2.Type, Expr->Type); + } + Expr2.IVal = Expr->IVal * lscale + Expr2.IVal * rscale; + /* Adjust the flags */ + Expr2.Flags |= Expr->Flags & ~E_MASK_KEEP_SUBEXPR; + /* Get the symbol and the name */ + *Expr = Expr2; + AddDone = 1; + } + } + + if (AddDone) { + /* Adjust the result for addition */ + if (!DoArrayRef) { + if (IsClassPtr (lhst)) { + /* Result type is a pointer */ + Expr->Type = lhst; + } else if (IsClassPtr (rhst)) { + /* Result type is a pointer */ + Expr->Type = rhst; + } else { + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr); + } + + /* The result is always an rvalue */ + ED_MarkExprAsRVal (Expr); + } + } else { + /* Decide the order */ + if (!ED_IsAbs (&Expr2) && rscale > 1) { + /* Rhs needs scaling but is not numeric. Load it. */ + LoadExpr (CF_NONE, &Expr2); + /* Scale rhs */ + g_scale (CF_INT, rscale); + /* Generate the code for the add */ + if (ED_IsAbs (Expr)) { + /* Numeric constant */ + g_inc (flags | CF_CONST, Expr->IVal); + } else if (ED_IsLocStack (Expr)) { + /* Local stack address */ + g_addaddr_local (flags, Expr->IVal); + } else { + /* Static address */ + g_addaddr_static (flags | GlobalModeFlags (Expr), Expr->Name, Expr->IVal); + } + } else { + /* Lhs is not numeric. Load it. */ + LoadExpr (CF_NONE, Expr); + /* Scale lhs if necessary */ + if (lscale != 1) { + g_scale (CF_INT, lscale); + } + /* Generate the code for the add */ + if (ED_IsAbs (&Expr2)) { + /* Numeric constant */ + g_inc (flags | CF_CONST, Expr2.IVal); + } else if (ED_IsLocStack (&Expr2)) { + /* Local stack address */ + g_addaddr_local (flags, Expr2.IVal); + } else { + /* Static address */ + g_addaddr_static (flags | GlobalModeFlags (&Expr2), Expr2.Name, Expr2.IVal); + } + } + + /* Result is an rvalue in primary register */ + ED_FinalizeRValLoad (Expr); } } else { - /* lhs is a constant and rhs is not constant. Load rhs into - ** the primary. + /* lhs is constant and rhs is not constant. Load rhs into the + ** primary. */ + GetCodePos (&Mark); LoadExpr (CF_NONE, &Expr2); /* Beware: The check above (for lhs) lets not only pass numeric @@ -2530,98 +2845,107 @@ static void parseadd (ExprDesc* Expr) ** with an offset. We have to check for that here. */ - /* First, get the rhs type. */ - rhst = Expr2.Type; - /* Setup flags */ - if (ED_IsLocAbs (Expr)) { + if (ED_IsAbs (Expr)) { /* A numerical constant */ flags |= CF_CONST; } else { /* Constant address label */ - flags |= GlobalModeFlags (Expr) | CF_CONSTADDR; + flags |= GlobalModeFlags (Expr); } /* Check for pointer arithmetic */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { /* Left is pointer, right is int, must scale rhs */ - g_scale (CF_INT, CheckedPSizeOf (lhst)); + rscale = CheckedPSizeOf (lhst); + g_scale (CF_INT, rscale); /* Operate on pointers, result type is a pointer */ flags |= CF_PTR; - /* Generate the code for the add */ - if (ED_GetLoc (Expr) == E_LOC_ABS) { - /* Numeric constant */ - g_inc (flags, Expr->IVal); - } else { - /* Constant address */ - g_addaddr_static (flags, Expr->Name, Expr->IVal); - } } else if (IsClassInt (lhst) && IsClassPtr (rhst)) { - /* Left is int, right is pointer, must scale lhs. */ - unsigned ScaleFactor = CheckedPSizeOf (rhst); - + lscale = CheckedPSizeOf (rhst); /* Operate on pointers, result type is a pointer */ flags |= CF_PTR; Expr->Type = Expr2.Type; - - /* Since we do already have rhs in the primary, if lhs is - ** not a numeric constant, and the scale factor is not one - ** (no scaling), we must take the long way over the stack. - */ - if (ED_IsLocAbs (Expr)) { - /* Numeric constant, scale lhs */ - Expr->IVal *= ScaleFactor; - /* Generate the code for the add */ - g_inc (flags, Expr->IVal); - } else if (ScaleFactor == 1) { - /* Constant address but no need to scale */ - g_addaddr_static (flags, Expr->Name, Expr->IVal); - } else { - /* Constant address that must be scaled */ - g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */ - g_getimmed (flags, Expr->Name, Expr->IVal); - g_scale (CF_PTR, ScaleFactor); - g_add (CF_PTR, 0); - } - } else if (IsClassInt (lhst) && IsClassInt (rhst)) { + } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer addition */ flags |= typeadjust (Expr, &Expr2, 1); - /* Generate the code for the add */ - if (ED_IsLocAbs (Expr)) { - /* Numeric constant */ - g_inc (flags, Expr->IVal); - } else { - /* Constant address */ - g_addaddr_static (flags, Expr->Name, Expr->IVal); - } } else { /* OOPS */ - Error ("Invalid operands for binary operator '+'"); - flags = CF_INT; + AddDone = -1; } - /* Result is a rvalue in primary register */ - ED_MakeRValExpr (Expr); + /* Generate the code for the add */ + if (!AddDone) { + if (ED_IsAbs (Expr) && + Expr->IVal >= 0 && + Expr->IVal * lscale < 256) { + /* Numeric constant */ + g_inc (flags, Expr->IVal * lscale); + AddDone = 1; + } + } + + if (!AddDone) { + if (ED_IsLocQuasiConst (&Expr2) && + rscale == 1 && + CheckedSizeOf (rhst) == SIZEOF_CHAR) { + /* Change the order back */ + RemoveCode (&Mark); + /* Load lhs */ + LoadExpr (CF_NONE, Expr); + /* Use new flags */ + flags = CF_CHAR | GlobalModeFlags (&Expr2); + /* Add the variable */ + if (ED_IsLocStack (&Expr2)) { + g_addlocal (flags, Expr2.IVal); + } else { + g_addstatic (flags, Expr2.Name, Expr2.IVal); + } + } else if (ED_IsAbs (Expr)) { + /* Numeric constant */ + g_inc (flags, Expr->IVal * lscale); + } else if (lscale == 1) { + if (ED_IsLocStack (Expr)) { + /* Constant address */ + g_addaddr_local (flags, Expr->IVal); + } else { + g_addaddr_static (flags, Expr->Name, Expr->IVal); + } + } else { + /* Since we do already have rhs in the primary, if lhs is + ** not a numeric constant, and the scale factor is not one + ** (no scaling), we must take the long way over the stack. + */ + g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */ + LoadExpr (CF_NONE, Expr); + g_scale (CF_PTR, lscale); + g_add (CF_PTR, 0); + } + } + + /* Result is an rvalue in primary register */ + ED_FinalizeRValLoad (Expr); } } else { /* Left hand side is not constant. Get the value onto the stack. */ - LoadExpr (CF_NONE, Expr); /* --> primary register */ + LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark); - g_push (TypeOf (Expr->Type), 0); /* --> stack */ + flags = TypeOf (Expr->Type); /* default codegen type */ + g_push (flags, 0); /* --> stack */ /* Evaluate the rhs */ - MarkedExprWithCheck (hie9, &Expr2); + MarkedExprWithCheck (DoArrayRef ? hie0 : hie9, &Expr2); + + /* Get the rhs type */ + rhst = Expr2.Type; /* Check for a constant rhs expression */ if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - /* Right hand side is a constant. Get the rhs type */ - rhst = Expr2.Type; - - /* Remove pushed value from stack */ + /* Rhs is a numeric constant. Remove pushed lhs from stack. */ RemoveCode (&Mark); /* Check for pointer arithmetic */ @@ -2636,13 +2960,12 @@ static void parseadd (ExprDesc* Expr) /* Operate on pointers, result type is a pointer */ flags = CF_PTR; Expr->Type = Expr2.Type; - } else if (IsClassInt (lhst) && IsClassInt (rhst)) { + } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer addition */ flags = typeadjust (Expr, &Expr2, 1); } else { /* OOPS */ - Error ("Invalid operands for binary operator '+'"); - flags = CF_INT; + AddDone = -1; } /* Generate code for the add */ @@ -2650,53 +2973,106 @@ static void parseadd (ExprDesc* Expr) } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); - - /* lhs and rhs are not constant. Get the rhs type. */ - rhst = Expr2.Type; - - /* Check for pointer arithmetic */ + /* Lhs and rhs are not so "numeric constant". Check for pointer arithmetic. */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { /* Left is pointer, right is int, must scale rhs */ - g_scale (CF_INT, CheckedPSizeOf (lhst)); + rscale = CheckedPSizeOf (lhst); + if (ED_IsAbs (&Expr2)) { + Expr2.IVal *= rscale; + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + } else { + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + g_scale (CF_INT, rscale); + } /* Operate on pointers, result type is a pointer */ flags = CF_PTR; } else if (IsClassInt (lhst) && IsClassPtr (rhst)) { /* Left is int, right is pointer, must scale lhs */ - g_tosint (TypeOf (lhst)); /* Make sure TOS is int */ - g_swap (CF_INT); /* Swap TOS and primary */ - g_scale (CF_INT, CheckedPSizeOf (rhst)); + lscale = CheckedPSizeOf (rhst); + if (ED_CodeRangeIsEmpty (&Expr2)) { + RemoveCode (&Mark); /* Remove pushed value from stack */ + g_scale (CF_INT, lscale); + g_push (CF_PTR, 0); /* --> stack */ + LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ + } else { + g_tosint (TypeOf (lhst)); /* Make sure TOS is int */ + LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */ + if (lscale != 1) { + g_swap (CF_INT); /* Swap TOS and primary */ + g_scale (CF_INT, CheckedPSizeOf (rhst)); + } + } /* Operate on pointers, result type is a pointer */ flags = CF_PTR; Expr->Type = Expr2.Type; - } else if (IsClassInt (lhst) && IsClassInt (rhst)) { - /* Integer addition. Note: Result is never constant. - ** Problem here is that typeadjust does not know if the - ** variable is an rvalue or lvalue, so if both operands - ** are dereferenced constant numeric addresses, typeadjust - ** thinks the operation works on constants. Removing - ** CF_CONST here means handling the symptoms, however, the - ** whole parser is such a mess that I fear to break anything - ** when trying to apply another solution. - */ - flags = typeadjust (Expr, &Expr2, 0) & ~CF_CONST; + } else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) { + /* Integer addition */ + flags = typeadjust (Expr, &Expr2, 0); + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); } else { /* OOPS */ - Error ("Invalid operands for binary operator '+'"); - flags = CF_INT; + AddDone = -1; + /* We can't just goto End as that would leave the stack unbalanced */ } - /* Generate code for the add */ - g_add (flags, 0); + /* Generate code for the add (the & is a hack here) */ + g_add (flags & ~CF_CONST, 0); } - /* Result is a rvalue in primary register */ - ED_MakeRValExpr (Expr); + /* Result is an rvalue in primary register */ + ED_FinalizeRValLoad (Expr); } - /* Condition codes not set */ + /* Deal with array ref */ + if (DoArrayRef) { + /* Check the types of array and subscript */ + if (IsClassPtr (lhst)) { + if (!IsClassInt (rhst)) { + Error ("Array subscript is not an integer"); + ED_MakeConstAbs (Expr, 0, GetCharArrayType (1)); + } + } else if (IsClassInt (lhst)) { + if (!IsClassPtr (rhst)) { + Error ("Subscripted value is neither array nor pointer"); + ED_MakeConstAbs (Expr, 0, GetCharArrayType (1)); + } + } else { + Error ("Cannot subscript"); + ED_MakeConstAbs (Expr, 0, GetCharArrayType (1)); + } + + /* The final result is usually an lvalue expression of element type + ** referenced in the primary, unless it is once again an array. We can just + ** assume the usual case first, and change it later if necessary. + */ + ED_IndExpr (Expr); + Expr->Type = Indirect (Expr->Type); + + /* An array element is actually a variable. So the rules for variables with + ** respect to the reference type apply: If it's an array, it is virtually + ** an rvalue address, otherwise it's an lvalue reference. (A function would + ** also be an rvalue address, but an array cannot contain functions). + */ + if (IsTypeArray (Expr->Type)) { + ED_AddrExpr (Expr); + } + + /* Consume the closing bracket */ + ConsumeRBrack (); + } else { + if (AddDone < 0) { + Error ("Invalid operands for binary operator '+'"); + } else { + /* Array and function types must be converted to pointer types */ + Expr->Type = PtrConversion (Expr->Type); + } + } + + /* Condition code not set */ ED_MarkAsUntested (Expr); } @@ -2710,12 +3086,15 @@ static void parsesub (ExprDesc* Expr) { ExprDesc Expr2; unsigned flags; /* Operation flags */ - Type* lhst; /* Type of left hand side */ - Type* rhst; /* Type of right hand side */ + const Type* lhst; /* Type of left hand side */ + const Type* rhst; /* Type of right hand side */ CodeMark Mark1; /* Save position of output queue */ CodeMark Mark2; /* Another position in the queue */ - int rscale; /* Scale factor for the result */ + int rscale; /* Scale factor for pointer arithmetics */ + int SubDone; /* No need to generate runtime code */ + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* lhs cannot be function or pointer to function */ if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) { @@ -2729,11 +3108,13 @@ static void parsesub (ExprDesc* Expr) /* Get the left hand side type, initialize operation flags */ lhst = Expr->Type; + flags = CF_INT; /* Default result type */ rscale = 1; /* Scale by 1, that is, don't scale */ + SubDone = 0; /* Generate runtime code by default */ /* Remember the output queue position, then bring the value onto the stack */ GetCodePos (&Mark1); - LoadExpr (CF_NONE, Expr); /* --> primary register */ + LoadExpr (CF_NONE, Expr); /* --> primary register */ GetCodePos (&Mark2); g_push (TypeOf (lhst), 0); /* --> stack */ @@ -2747,142 +3128,229 @@ static void parsesub (ExprDesc* Expr) Expr2.Type = type_uchar; } - /* Check for a constant rhs expression */ - if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + /* Get the rhs type */ + rhst = Expr2.Type; - /* The right hand side is constant. Get the rhs type. */ - rhst = Expr2.Type; + if (IsClassPtr (lhst)) { + /* We'll have to scale the result */ + rscale = PSizeOf (lhst); + /* We cannot scale by 0-size or unknown-size */ + if (rscale == 0 && (IsClassPtr (rhst) || IsClassInt (rhst))) { + TypeCompatibilityDiagnostic (lhst, rhst, + 1, "Invalid pointer types in subtraction: '%s' and '%s'"); + /* Avoid further errors */ + rscale = 1; + } + /* Generate code for pointer subtraction */ + flags = CF_PTR; + } - /* Check left hand side */ - if (ED_IsConstAbs (Expr)) { + /* We can only do constant expressions for: + ** - integer subtraction: + ** - numeric - numeric + ** - (integer)(base + offset) - numeric + ** - (integer)(same_base + offset) - (integer)(same_base + offset) + ** - pointer offset: + ** - (pointer)numeric - numeric * scale + ** - (base + offset) - numeric * scale + ** - (same_base + offset) - (integer)(same_base + offset) * 1 + ** - pointer diff: + ** - (numeric - numeric) / scale + ** - ((same_base + offset) - (same_base + offset)) / scale + ** - ((base + offset) - (pointer)numeric) / 1 + */ + if (IsClassPtr (lhst) && IsClassPtr (rhst)) { - /* Both sides are constant, remove generated code */ - RemoveCode (&Mark1); + /* Pointer Diff. We've got the scale factor and flags above */ + typecmp_t Cmp = TypeCmp (lhst, rhst); + if (Cmp.C < TC_STRICT_COMPATIBLE) { + TypeCompatibilityDiagnostic (lhst, rhst, + 1, "Incompatible pointer types in subtraction: '%s' and '%s'"); + } - /* Check for pointer arithmetic */ - if (IsClassPtr (lhst) && IsClassInt (rhst)) { - /* Left is pointer, right is int, must scale rhs */ - Expr->IVal -= Expr2.IVal * CheckedPSizeOf (lhst); - /* Operate on pointers, result type is a pointer */ - } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) { - /* Left is pointer, right is pointer, must scale result */ - if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { - Error ("Incompatible pointer types"); - } else { - Expr->IVal = (Expr->IVal - Expr2.IVal) / - CheckedPSizeOf (lhst); + /* Operate on pointers, result type is an integer */ + Expr->Type = type_int; + + /* Check for a constant rhs expression */ + if (ED_IsQuasiConst (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + /* The right hand side is constant. Check left hand side. */ + if (ED_IsQuasiConst (Expr)) { + /* We can't do all 'ptr1 - ptr2' constantly at the moment */ + if (Expr->Sym == Expr2.Sym) { + Expr->IVal = (Expr->IVal - Expr2.IVal) / rscale; + /* Get rid of unneeded flags etc. */ + ED_MakeConstAbsInt (Expr, Expr->IVal); + /* No runtime code */ + SubDone = 1; + } else if (rscale == 1 && ED_IsConstAbs (&Expr2)) { + Expr->IVal = (Expr->IVal - Expr2.IVal) / rscale; + /* No runtime code */ + SubDone = 1; } - /* Operate on pointers, result type is an integer */ - Expr->Type = type_int; + } + } + + if (!SubDone) { + /* We'll do runtime code */ + if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + /* Remove pushed value from stack */ + RemoveCode (&Mark2); + /* Do the subtraction */ + g_dec (CF_INT | CF_CONST, Expr2.IVal); + } else { + /* load into the primary */ + LoadExpr (CF_NONE, &Expr2); + /* Generate code for the sub */ + g_sub (CF_INT, 0); + } + /* We must scale the result */ + if (rscale != 1) { + g_scale (CF_INT, -rscale); + } + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); + } else { + /* Remove pushed value from stack */ + RemoveCode (&Mark1); + /* The result is always an rvalue */ + ED_MarkExprAsRVal (Expr); + } + + } else if (ED_IsQuasiConst (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + /* Right hand side is constant. Check left hand side. */ + if (ED_IsQuasiConst (Expr)) { + + /* Both sides are constant. Check for pointer arithmetic */ + if (IsClassPtr (lhst) && IsClassInt (rhst)) { + /* Pointer subtraction. We've got the scale factor and flags above */ } else if (IsClassInt (lhst) && IsClassInt (rhst)) { - /* Integer subtraction */ - typeadjust (Expr, &Expr2, 1); - Expr->IVal -= Expr2.IVal; + /* Integer subtraction. We'll adjust the types later */ } else { /* OOPS */ Error ("Invalid operands for binary operator '-'"); } - /* Result is constant, condition codes not set */ - ED_MarkAsUntested (Expr); + /* We can't make all subtraction expressions constant */ + if (ED_IsConstAbs (&Expr2)) { + Expr->IVal -= Expr2.IVal * rscale; + /* No runtime code */ + SubDone = 1; + } else if (rscale == 1 && Expr->Sym == Expr2.Sym) { + /* The result is the diff of the offsets */ + Expr->IVal -= Expr2.IVal; + /* Get rid of unneeded bases and flags etc. */ + ED_MakeConstAbs (Expr, Expr->IVal, Expr->Type); + /* No runtime code */ + SubDone = 1; + } + + if (SubDone) { + /* Remove loaded and pushed value from stack */ + RemoveCode (&Mark1); + if (IsClassInt (lhst)) { + /* Just adjust the result type */ + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); + /* And limit the calculated value to the range of it */ + LimitExprValue (Expr); + } + /* The result is always an rvalue */ + ED_MarkExprAsRVal (Expr); + } else { + if (ED_IsConstAbs (&Expr2)) { + /* Remove pushed value from stack */ + RemoveCode (&Mark2); + if (IsClassInt (lhst)) { + /* Adjust the types */ + flags = typeadjust (Expr, &Expr2, 1); + } + /* Do the subtraction */ + g_dec (flags | CF_CONST, Expr2.IVal * rscale); + } else { + if (IsClassInt (lhst)) { + /* Adjust the types */ + flags = typeadjust (Expr, &Expr2, 0); + } + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + g_scale (TypeOf (rhst), rscale); + /* Generate code for the sub (the & is a hack here) */ + g_sub (flags & ~CF_CONST, 0); + } + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); + } } else { - /* Left hand side is not constant, right hand side is. - ** Remove pushed value from stack. - */ - RemoveCode (&Mark2); - + /* Left hand side is not constant, right hand side is */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { - /* Left is pointer, right is int, must scale rhs */ - Expr2.IVal *= CheckedPSizeOf (lhst); - /* Operate on pointers, result type is a pointer */ - flags = CF_PTR; - } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) { - /* Left is pointer, right is pointer, must scale result */ - if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { - Error ("Incompatible pointer types"); - } else { - rscale = CheckedPSizeOf (lhst); - } - /* Operate on pointers, result type is an integer */ - flags = CF_PTR; - Expr->Type = type_int; + /* Pointer subtraction. We've got the scale factor and flags above */ } else if (IsClassInt (lhst) && IsClassInt (rhst)) { - /* Integer subtraction */ - flags = typeadjust (Expr, &Expr2, 1); + /* Integer subtraction. We'll adjust the types later */ } else { /* OOPS */ Error ("Invalid operands for binary operator '-'"); flags = CF_INT; } - /* Do the subtraction */ - g_dec (flags | CF_CONST, Expr2.IVal); - - /* If this was a pointer subtraction, we must scale the result */ - if (rscale != 1) { - g_scale (flags, -rscale); + if (ED_IsConstAbs (&Expr2)) { + /* Remove pushed value from stack */ + RemoveCode (&Mark2); + if (IsClassInt (lhst)) { + /* Adjust the types */ + flags = typeadjust (Expr, &Expr2, 1); + } + /* Do the subtraction */ + g_dec (flags | CF_CONST, Expr2.IVal * rscale); + } else { + if (IsClassInt (lhst)) { + /* Adjust the types */ + flags = typeadjust (Expr, &Expr2, 0); + } + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + g_scale (TypeOf (rhst), rscale); + /* Generate code for the sub (the & is a hack here) */ + g_sub (flags & ~CF_CONST, 0); } - /* Result is a rvalue in the primary register */ - ED_MakeRValExpr (Expr); - ED_MarkAsUntested (Expr); - + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); } } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); + /* We'll use the pushed lhs on stack instead of the original source */ + ED_FinalizeRValLoad (Expr); - /* Right hand side is not constant. Get the rhs type. */ - rhst = Expr2.Type; + /* Right hand side is not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); /* Check for pointer arithmetic */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { /* Left is pointer, right is int, must scale rhs */ - g_scale (CF_INT, CheckedPSizeOf (lhst)); - /* Operate on pointers, result type is a pointer */ - flags = CF_PTR; - } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) { - /* Left is pointer, right is pointer, must scale result */ - if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { - Error ("Incompatible pointer types"); - } else { - rscale = CheckedPSizeOf (lhst); - } - /* Operate on pointers, result type is an integer */ - flags = CF_PTR; - Expr->Type = type_int; + g_scale (CF_INT, rscale); } else if (IsClassInt (lhst) && IsClassInt (rhst)) { - /* Integer subtraction. If the left hand side descriptor says that - ** the lhs is const, we have to remove this mark, since this is no - ** longer true, lhs is on stack instead. - */ - if (ED_IsLocAbs (Expr)) { - ED_MakeRValExpr (Expr); - } /* Adjust operand types */ flags = typeadjust (Expr, &Expr2, 0); } else { /* OOPS */ Error ("Invalid operands for binary operator '-'"); - flags = CF_INT; } /* Generate code for the sub (the & is a hack here) */ g_sub (flags & ~CF_CONST, 0); - /* If this was a pointer subtraction, we must scale the result */ - if (rscale != 1) { - g_scale (flags, -rscale); - } - - /* Result is a rvalue in the primary register */ - ED_MakeRValExpr (Expr); - ED_MarkAsUntested (Expr); + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); } + + /* Result type is either a pointer or an integer */ + Expr->Type = PtrConversion (Expr->Type); + + /* Condition code not set */ + ED_MarkAsUntested (Expr); } @@ -2893,7 +3361,7 @@ void hie8 (ExprDesc* Expr) ExprWithCheck (hie9, Expr); while (CurTok.Tok == TOK_PLUS || CurTok.Tok == TOK_MINUS) { if (CurTok.Tok == TOK_PLUS) { - parseadd (Expr); + parseadd (Expr, 0); } else { parsesub (Expr); } @@ -2977,16 +3445,14 @@ static void hieAndPP (ExprDesc* Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - - ConstAbsIntExpr (hie2, Expr); + *Expr = NoCodeConstAbsIntExpr (hie2); while (CurTok.Tok == TOK_BOOL_AND) { /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hie2, &Expr2); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2); /* Combine the two */ Expr->IVal = (Expr->IVal && Expr2.IVal); @@ -3000,16 +3466,14 @@ static void hieOrPP (ExprDesc *Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - - ConstAbsIntExpr (hieAndPP, Expr); + *Expr = NoCodeConstAbsIntExpr (hieAndPP); while (CurTok.Tok == TOK_BOOL_OR) { /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hieAndPP, &Expr2); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP); /* Combine the two */ Expr->IVal = (Expr->IVal || Expr2.IVal); @@ -3018,61 +3482,168 @@ static void hieOrPP (ExprDesc *Expr) -static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) -/* Process "exp && exp" */ +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. +*/ { - int FalseLab; - ExprDesc Expr2; + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int HasFalseJump = 0, HasTrueJump = 0; + CodeMark Start; + /* The label that we will use for false expressions */ + int FalseLab = 0; + + /* Get lhs */ + GetCodePos (&Start); ExprWithCheck (hie2, Expr); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } + if (CurTok.Tok == TOK_BOOL_AND) { - /* Tell our caller that we're evaluating a boolean */ - *BoolOp = 1; + ExprDesc Expr2; - /* Get a label that we will use for false expressions */ - FalseLab = GetLocalLabel (); + /* Check type */ + if (!ED_IsBool (Expr)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (Expr, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (!ED_IsConstBool (Expr)) { + /* Set the test flag */ + ED_RequireTest (Expr); - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); + /* Load the value */ + LoadExpr (CF_FORCECHAR, Expr); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); + + /* Clear the test flag */ + ED_RequireNoTest (Expr); + + if (HasFalseJump == 0) { + /* Remember that the jump is used */ + HasFalseJump = 1; + /* Get a label for false expressions */ + FalseLab = GetLocalLabel (); + } + + /* Generate the jump */ + g_falsejump (CF_NONE, FalseLab); + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (ED_IsConstFalse (Expr)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } + } } - /* Load the value */ - LoadExpr (CF_FORCECHAR, Expr); - - /* Generate the jump */ - g_falsejump (CF_NONE, FalseLab); - /* Parse more boolean and's */ while (CurTok.Tok == TOK_BOOL_AND) { + ED_Init (&Expr2); + Expr2.Flags = Flags; + /* Skip the && */ NextToken (); /* Get rhs */ + GetCodePos (&Start); hie2 (&Expr2); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); } - LoadExpr (CF_FORCECHAR, &Expr2); - /* Do short circuit evaluation */ - if (CurTok.Tok == TOK_BOOL_AND) { - g_falsejump (CF_NONE, FalseLab); - } else { - /* Last expression - will evaluate to true */ - g_truejump (CF_NONE, TrueLab); + /* Check type */ + if (!ED_IsBool (&Expr2)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (&Expr2, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (!ED_IsConstBool (&Expr2)) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr2); + + /* Do short circuit evaluation */ + if (CurTok.Tok == TOK_BOOL_AND) { + if (HasFalseJump == 0) { + /* Remember that the jump is used */ + HasFalseJump = 1; + /* Get a label for false expressions */ + FalseLab = GetLocalLabel (); + } + g_falsejump (CF_NONE, FalseLab); + } else { + /* We need the true label for the last expression */ + HasTrueJump = 1; + } + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); + + if (ED_IsConstFalse (&Expr2)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The value of the expression will be false */ + ED_MakeConstBool (Expr, 0); + } + } } } - /* Define the false jump label here */ - g_defcodelabel (FalseLab); + /* Last expression */ + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (HasFalseJump || HasTrueJump) { + if (*TrueLabAllocated == 0) { + /* Get a label that we will use for true expressions */ + *TrueLab = GetLocalLabel (); + *TrueLabAllocated = 1; + } + if (!ED_IsConstAbs (&Expr2)) { + /* Will branch to true and fall to false */ + g_truejump (CF_NONE, *TrueLab); + } else { + /* Will jump away */ + g_jump (*TrueLab); + } + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* No need to test as the result will be jumped to */ + ED_TestDone (Expr); + } + } - /* The result is an rvalue in primary */ - ED_MakeRValExpr (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + if (HasFalseJump) { + /* Define the false jump label here */ + g_defcodelabel (FalseLab); + } + + /* Convert to bool */ + if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) || + ED_IsAddrExpr (Expr)) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } + + /* Tell our caller that we're evaluating a boolean */ + return 1; } + + return 0; } @@ -3080,71 +3651,156 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) static void hieOr (ExprDesc *Expr) /* Process "exp || exp". */ { - ExprDesc Expr2; - int BoolOp = 0; /* Did we have a boolean op? */ - int AndOp; /* Did we have a && operation? */ + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int AndOp; /* Did we have a && operation? */ unsigned TrueLab; /* Jump to this label if true */ - unsigned DoneLab; - - /* Get a label */ - TrueLab = GetLocalLabel (); + int HasTrueJump = 0; + CodeMark Start; /* Call the next level parser */ - hieAnd (Expr, TrueLab, &BoolOp); + GetCodePos (&Start); + AndOp = hieAnd (Expr, &TrueLab, &HasTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } /* Any boolean or's? */ if (CurTok.Tok == TOK_BOOL_OR) { - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); + /* Check type */ + if (!ED_IsBool (Expr)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (Expr, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + + if (!ED_IsConstBool (Expr)) { + /* Test the lhs if we haven't had && operators. If we had them, the + ** jump is already in place and there's no need to do the test. + */ + if (!AndOp) { + /* Set the test flag */ + ED_RequireTest (Expr); + + /* Get first expr */ + LoadExpr (CF_FORCECHAR, Expr); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); + + /* Clear the test flag */ + ED_RequireNoTest (Expr); + + if (HasTrueJump == 0) { + /* Get a label that we will use for true expressions */ + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } + + /* Jump to TrueLab if true */ + g_truejump (CF_NONE, TrueLab); + } + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (ED_IsConstTrue (Expr)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } + } } - /* Get first expr */ - LoadExpr (CF_FORCECHAR, Expr); - - /* For each expression jump to TrueLab if true. Beware: If we - ** had && operators, the jump is already in place! - */ - if (!BoolOp) { - g_truejump (CF_NONE, TrueLab); - } - - /* Remember that we had a boolean op */ - BoolOp = 1; - /* while there's more expr */ while (CurTok.Tok == TOK_BOOL_OR) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags = Flags; + /* skip the || */ NextToken (); - /* Get a subexpr */ - AndOp = 0; - hieAnd (&Expr2, TrueLab, &AndOp); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); + /* Get rhs subexpression */ + GetCodePos (&Start); + AndOp = hieAnd (&Expr2, &TrueLab, &HasTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); } - LoadExpr (CF_FORCECHAR, &Expr2); - /* If there is more to come, add shortcut boolean eval. */ - g_truejump (CF_NONE, TrueLab); + /* Check type */ + if (!ED_IsBool (&Expr2)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (&Expr2, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + + if (!ED_IsConstBool (&Expr2)) { + /* If there is more to come, add shortcut boolean eval */ + if (!AndOp) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr2); + + if (HasTrueJump == 0) { + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } + g_truejump (CF_NONE, TrueLab); + } + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); + + if (ED_IsConstTrue (&Expr2)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The result is always true */ + ED_MakeConstBool (Expr, 1); + } + } + } } - /* The result is an rvalue in primary */ - ED_MakeRValExpr (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + /* Convert to bool */ + if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) || + ED_IsAddrExpr (Expr)) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } } - /* If we really had boolean ops, generate the end sequence */ - if (BoolOp) { - DoneLab = GetLocalLabel (); - g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ - g_falsejump (CF_NONE, DoneLab); - g_defcodelabel (TrueLab); - g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ - g_defcodelabel (DoneLab); + /* If we really had boolean ops, generate the end sequence if necessary */ + if (HasTrueJump) { + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + /* False case needs to jump over true case */ + unsigned DoneLab = GetLocalLabel (); + /* Load false only if the result is not true */ + g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ + g_falsejump (CF_NONE, DoneLab); + + /* Load the true value */ + g_defcodelabel (TrueLab); + g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ + g_defcodelabel (DoneLab); + } else { + /* Load the true value */ + g_defcodelabel (TrueLab); + g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ + } + + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* Condition codes are set */ + ED_TestDone (Expr); } } @@ -3153,8 +3809,9 @@ static void hieOr (ExprDesc *Expr) static void hieQuest (ExprDesc* Expr) /* Parse the ternary operator */ { - int FalseLab; - int TrueLab; + int FalseLab = 0; + int TrueLab = 0; + CodeMark SkippedBranch; CodeMark TrueCodeEnd; ExprDesc Expr2; /* Expression 2 */ ExprDesc Expr3; /* Expression 3 */ @@ -3162,7 +3819,6 @@ static void hieQuest (ExprDesc* Expr) int Expr3IsNULL; /* Expression 3 is a NULL pointer */ Type* ResultType; /* Type of result */ - /* Call the lower level eval routine */ if (Preprocessing) { ExprWithCheck (hieOrPP, Expr); @@ -3172,61 +3828,147 @@ static void hieQuest (ExprDesc* Expr) /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { + + /* The constant condition must be compile-time known as well */ + int ConstantCond = ED_IsConstBool (Expr); + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + ED_Init (&Expr2); + Expr2.Flags = Flags; + ED_Init (&Expr3); + Expr3.Flags = Flags; + NextToken (); - if (!ED_IsTested (Expr)) { - /* Condition codes not set, request a test */ - ED_MarkForTest (Expr); + + /* 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); + LoadExpr (CF_NONE, Expr); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); + + FalseLab = GetLocalLabel (); + g_falsejump (CF_NONE, FalseLab); + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (Expr->IVal == 0) { + /* Remember the current code position */ + GetCodePos (&SkippedBranch); + } } - LoadExpr (CF_NONE, Expr); - FalseLab = GetLocalLabel (); - g_falsejump (CF_NONE, FalseLab); /* Parse second expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. */ - ExprWithCheck (hie1, &Expr2); + ExprWithCheck (hie0, &Expr2); Expr2IsNULL = ED_IsNullPtr (&Expr2); - if (!IsTypeVoid (Expr2.Type)) { + if (!IsTypeVoid (Expr2.Type) && + ED_YetToLoad (&Expr2) && + (!ConstantCond || !ED_IsConst (&Expr2))) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr2); - ED_MakeRValExpr (&Expr2); - Expr2.Type = PtrConversion (Expr2.Type); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, &Expr2); + + ED_FinalizeRValLoad (&Expr2); + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); } + Expr2.Type = PtrConversion (Expr2.Type); - /* Remember the current code position */ - GetCodePos (&TrueCodeEnd); + if (!ConstantCond) { + /* Remember the current code position */ + GetCodePos (&TrueCodeEnd); - /* Jump around the evaluation of the third expression */ - TrueLab = GetLocalLabel (); - ConsumeColon (); - g_jump (TrueLab); + /* Jump around the evaluation of the third expression */ + TrueLab = GetLocalLabel (); - /* Jump here if the first expression was false */ - g_defcodelabel (FalseLab); + 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(); + } /* Parse third expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. */ ExprWithCheck (hie1, &Expr3); Expr3IsNULL = ED_IsNullPtr (&Expr3); - if (!IsTypeVoid (Expr3.Type)) { + if (!IsTypeVoid (Expr3.Type) && + ED_YetToLoad (&Expr3) && + (!ConstantCond || !ED_IsConst (&Expr3))) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr3); - ED_MakeRValExpr (&Expr3); - Expr3.Type = PtrConversion (Expr3.Type); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, &Expr3); + + ED_FinalizeRValLoad (&Expr3); + } else { + /* Constant boolean 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); } /* Check if any conversions are needed, if so, do them. ** Conversion rules for ?: expression are: ** - if both expressions are int expressions, default promotion ** rules for ints apply. - ** - if both expressions are pointers of the same type, the - ** result of the expression is of this type. + ** - if both expressions have the same structure, union or void type, + ** the result has the same type. + ** - if both expressions are pointers to compatible types (possibly + ** qualified differently), the result of the expression is an + ** appropriately qualified version of the composite type. + ** - if one of the expressions is a pointer and the other is a + ** pointer to (possibly qualified) void, the resulting type is a + ** pointer to appropriately qualified void. ** - if one of the expressions is a pointer and the other is - ** a zero constant, the resulting type is that of the pointer - ** type. - ** - if both expressions are void expressions, the result is of - ** type void. + ** a null pointer constant, the resulting type is that of the + ** pointer type. ** - all other cases are flagged by an error. */ if (IsClassInt (Expr2.Type) && IsClassInt (Expr3.Type)) { @@ -3236,7 +3978,7 @@ static void hieQuest (ExprDesc* Expr) /* Get common type */ - ResultType = promoteint (Expr2.Type, Expr3.Type); + ResultType = TypeDup (ArithmeticConvert (Expr2.Type, Expr3.Type)); /* Convert the third expression to this type if needed */ TypeConversion (&Expr3, ResultType); @@ -3248,296 +3990,75 @@ static void hieQuest (ExprDesc* Expr) TypeConversion (&Expr2, ResultType); GetCodePos (&CvtCodeEnd); - /* If we had conversion code, move it to the right place */ - if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) { - MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd); + if (!ConstantCond) { + /* If we had conversion code, move it to the right place */ + if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) { + MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd); + } } } else if (IsClassPtr (Expr2.Type) && IsClassPtr (Expr3.Type)) { - /* Must point to same type */ - if (TypeCmp (Indirect (Expr2.Type), Indirect (Expr3.Type)) < TC_EQUAL) { - Error ("Incompatible pointer types"); - } - /* Result has the common type */ - ResultType = Expr2.Type; - } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) { - /* Result type is pointer, no cast needed */ - ResultType = Expr2.Type; - } else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) { - /* Result type is pointer, no cast needed */ - ResultType = Expr3.Type; - } else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) { - /* Result type is void */ - ResultType = Expr3.Type; - } else { - Error ("Incompatible types"); - ResultType = Expr2.Type; /* Doesn't matter here */ - } - - /* Define the final label */ - g_defcodelabel (TrueLab); - - /* Setup the target expression */ - ED_MakeRValExpr (Expr); - Expr->Type = ResultType; - } -} - - - -static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) -/* Process "op=" operators. */ -{ - ExprDesc Expr2; - unsigned flags; - CodeMark Mark; - int MustScale; - - /* op= can only be used with lvalues */ - if (!ED_IsLVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand type"); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator token */ - NextToken (); - - /* Determine the type of the lhs */ - flags = TypeOf (Expr->Type); - MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type); - - /* Get the lhs address on stack (if needed) */ - PushAddr (Expr); - - /* Fetch the lhs into the primary register if needed */ - LoadExpr (CF_NONE, Expr); - - /* Bring the lhs on stack */ - GetCodePos (&Mark); - g_push (flags, 0); - - /* Evaluate the rhs */ - MarkedExprWithCheck (hie1, &Expr2); - - /* The rhs must be an integer (or a float, but we don't support that yet */ - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right 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. - */ - } - - /* Check for a constant expression */ - if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - /* The resulting value is a constant. If the generator has the NOPUSH - ** flag set, don't push the lhs. - */ - if (Gen->Flags & GEN_NOPUSH) { - RemoveCode (&Mark); - } - if (MustScale) { - /* lhs is a pointer, scale rhs */ - Expr2.IVal *= CheckedSizeOf (Expr->Type+1); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Special handling for add and sub - some sort of a hack, but short code */ - if (Gen->Func == g_add) { - g_inc (flags | CF_CONST, Expr2.IVal); - } 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 one of the two is 'void *', the result type is a pointer to + ** appropriately qualified void. + */ + if (IsTypeVoid (Indirect (Expr2.Type))) { + ResultType = NewPointerTo (Indirect (Expr2.Type)); + ResultType[1].C |= GetQualifier (Indirect (Expr3.Type)); + } else if (IsTypeVoid (Indirect (Expr3.Type))) { + ResultType = NewPointerTo (Indirect (Expr3.Type)); + ResultType[1].C |= GetQualifier (Indirect (Expr2.Type)); + } else { + /* Must point to compatible types */ + if (TypeCmp (Expr2.Type, Expr3.Type).C < TC_VOID_PTR) { + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, + 1, "Incompatible pointer types in ternary: '%s' and '%s'"); + /* Avoid further errors */ + ResultType = NewPointerTo (type_void); + } else { + /* Result has the composite type */ + ResultType = TypeDup (Expr2.Type); + TypeComposition (ResultType, Expr3.Type); } } - Gen->Func (flags | CF_CONST, Expr2.IVal); - } - } else { - - /* rhs is not constant. Load into the primary */ - LoadExpr (CF_NONE, &Expr2); - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + } else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) { + /* Result type is pointer, no cast needed */ + ResultType = TypeDup (Expr2.Type); + } else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) { + /* Result type is pointer, no cast needed */ + ResultType = TypeDup (Expr3.Type); + } else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) { + /* Result type is void */ + ResultType = TypeDup (type_void); + } else { + if (IsClassStruct (Expr2.Type) && IsClassStruct (Expr3.Type) && + TypeCmp (Expr2.Type, Expr3.Type).C == TC_IDENTICAL) { + /* Result type is struct/union */ + ResultType = TypeDup (Expr2.Type); + } else { + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, + "Incompatible types in ternary '%s' with '%s'"); + ResultType = TypeDup (Expr2.Type); /* Doesn't matter here */ + } } - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; + if (!ConstantCond) { + /* Define the final label */ + g_defcodelabel (TrueLab); + /* Set up the result expression type */ + ED_FinalizeRValLoad (Expr); + /* Restore the original evaluation flags */ + Expr->Flags = (Expr->Flags & ~E_MASK_KEEP_RESULT) | Flags; + } else { + if (Expr->IVal != 0) { + *Expr = Expr2; + } else { + *Expr = Expr3; + } } - /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0); + /* Setup the target expression */ + Expr->Type = ResultType; } - Store (Expr, 0); - ED_MakeRValExpr (Expr); -} - - - -static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) -/* Process the += and -= operators */ -{ - ExprDesc Expr2; - unsigned lflags; - unsigned rflags; - int MustScale; - - - /* We're currently only able to handle some adressing modes */ - if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { - /* Use generic routine */ - opeq (Gen, Expr, Op); - return; - } - - /* We must have an lvalue */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand type"); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator */ - NextToken (); - - /* Check if we have a pointer expression and must scale rhs */ - MustScale = IsTypePtr (Expr->Type); - - /* Initialize the code generator flags */ - lflags = 0; - rflags = 0; - - /* Evaluate the rhs. We expect an integer here, since float is not - ** supported - */ - hie1 (&Expr2); - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right 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. - */ - } - - /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; - - if (ED_IsConstAbs (&Expr2)) { - /* The resulting value is a constant */ - rflags |= CF_CONST; - lflags |= CF_CONST; - - /* Scale it */ - if (MustScale) { - Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); - } - } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); - - /* Convert the type of the rhs to that of the lhs */ - g_typecast (lflags, rflags & ~CF_FORCECHAR); - - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); - } - } - - /* Output apropriate code depending on the location */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute: numeric address or const */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_GLOBAL: - /* Global variable */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_REGISTER: - /* Register variable */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_STACK: - /* Value on the stack */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); - } else { - g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); - } - break; - - default: - Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); - } - - /* Expression is a rvalue in the primary now */ - ED_MakeRValExpr (Expr); } @@ -3549,47 +4070,47 @@ void hie1 (ExprDesc* Expr) switch (CurTok.Tok) { case TOK_ASSIGN: - Assignment (Expr); + OpAssign (0, Expr, "="); break; case TOK_PLUS_ASSIGN: - addsubeq (&GenPASGN, Expr, "+="); + OpAddSubAssign (&GenPASGN, Expr, "+="); break; case TOK_MINUS_ASSIGN: - addsubeq (&GenSASGN, Expr, "-="); + OpAddSubAssign (&GenSASGN, Expr, "-="); break; case TOK_MUL_ASSIGN: - opeq (&GenMASGN, Expr, "*="); + OpAssign (&GenMASGN, Expr, "*="); break; case TOK_DIV_ASSIGN: - opeq (&GenDASGN, Expr, "/="); + OpAssign (&GenDASGN, Expr, "/="); break; case TOK_MOD_ASSIGN: - opeq (&GenMOASGN, Expr, "%="); + OpAssign (&GenMOASGN, Expr, "%="); break; case TOK_SHL_ASSIGN: - opeq (&GenSLASGN, Expr, "<<="); + OpAssign (&GenSLASGN, Expr, "<<="); break; case TOK_SHR_ASSIGN: - opeq (&GenSRASGN, Expr, ">>="); + OpAssign (&GenSRASGN, Expr, ">>="); break; case TOK_AND_ASSIGN: - opeq (&GenAASGN, Expr, "&="); + OpAssign (&GenAASGN, Expr, "&="); break; case TOK_XOR_ASSIGN: - opeq (&GenXOASGN, Expr, "^="); + OpAssign (&GenXOASGN, Expr, "^="); break; case TOK_OR_ASSIGN: - opeq (&GenOASGN, Expr, "|="); + OpAssign (&GenOASGN, Expr, "|="); break; default: @@ -3602,8 +4123,36 @@ void hie1 (ExprDesc* Expr) void hie0 (ExprDesc *Expr) /* Parse comma operator. */ { + unsigned Flags = Expr->Flags & E_MASK_KEEP_MAKE; + unsigned PrevErrorCount = ErrorCount; + CodeMark Start, End; + + /* Remember the current code position */ + GetCodePos (&Start); + hie1 (Expr); while (CurTok.Tok == TOK_COMMA) { + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, Expr); + + /* If the expression didn't generate code or isn't cast to type void, + ** emit a warning. + */ + GetCodePos (&End); + if (!ED_MayHaveNoEffect (Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + + PrevErrorCount = ErrorCount; + /* Remember the current code position */ + GetCodePos (&Start); + + /* Reset the expression */ + ED_Init (Expr); + Expr->Flags = Flags; NextToken (); hie1 (Expr); } @@ -3611,51 +4160,26 @@ void hie0 (ExprDesc *Expr) -int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is a -** constant, 0 is returned and the value is put in the Expr struct. If the -** result is not constant, LoadExpr is called to bring the value into the -** primary register and 1 is returned. -*/ -{ - /* Evaluate */ - ExprWithCheck (Func, Expr); - - /* Check for a constant expression */ - if (ED_IsConstAbs (Expr)) { - /* Constant expression */ - return 0; - } else { - /* Not constant, load into the primary */ - LoadExpr (Flags, Expr); - return 1; - } -} - - - void Expression0 (ExprDesc* Expr) -/* Evaluate an expression via hie0 and put the result into the primary register */ -{ - ExprWithCheck (hie0, Expr); - LoadExpr (CF_NONE, Expr); -} - - - -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. +/* Evaluate an expression via hie0 and put the result into the primary register. +** The expression is completely evaluated and all side effects complete. */ { - ExprWithCheck (Func, Expr); - if (!ED_IsConst (Expr)) { - Error ("Constant expression expected"); - /* To avoid any compiler errors, make the expression a valid const */ - ED_MakeConstAbsInt (Expr, 1); + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + /* Only check further after the expression is evaluated */ + ExprWithCheck (hie0, Expr); + + if ((Expr->Flags & Flags & E_MASK_EVAL) != (Flags & E_MASK_EVAL)) { + Internal ("Expression flags tampered: %08X", Flags); } + + if (ED_YetToLoad (Expr)) { + LoadExpr (CF_NONE, Expr); + } + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, Expr); } @@ -3669,25 +4193,56 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) { ExprWithCheck (Func, Expr); if (!ED_IsBool (Expr)) { - Error ("Boolean expression expected"); + Error ("Scalar expression expected"); /* To avoid any compiler errors, make the expression a valid int */ - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstBool (Expr, 1); } } -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)) +/* Get an expression evaluated via the given function. If the result is not a +** constant expression without runtime code generated, a diagnostic will be +** printed, and the value is replaced by a constant one to make sure there are +** no internal errors that result from this input error. */ { - ExprWithCheck (Func, Expr); - if (!ED_IsConstAbsInt (Expr)) { + ExprDesc Expr; + ED_Init (&Expr); + + Expr.Flags |= E_EVAL_C_CONST; + MarkedExprWithCheck (Func, &Expr); + if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { + Error ("Constant expression expected"); + /* To avoid any compiler errors, make the expression a valid const */ + ED_MakeConstAbsInt (&Expr, 1); + } + + /* Return by value */ + return Expr; +} + + + +ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)) +/* Get an expression evaluated via the given function. If the result is not a +** constant numeric integer value without runtime code generated, a diagnostic +** will be printed, and the value is replaced by a constant one to make sure +** there are no internal errors that result from this input error. +*/ +{ + ExprDesc Expr; + ED_Init (&Expr); + + Expr.Flags |= E_EVAL_C_CONST; + MarkedExprWithCheck (Func, &Expr); + if (!ED_IsConstAbsInt (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { Error ("Constant integer expression expected"); /* To avoid any compiler errors, make the expression a valid const */ - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstAbsInt (&Expr, 1); } + + /* Return by value */ + return Expr; } diff --git a/src/cc65/expr.h b/src/cc65/expr.h index a6c183cbd..841edcb62 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -17,12 +17,33 @@ +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +#define SQP_KEEP_NONE 0x00 +#define SQP_KEEP_TEST 0x01U +#define SQP_KEEP_EAX 0x02U +#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ + +/* Generator attributes */ +#define GEN_NOPUSH 0x01 /* Don't push lhs */ +#define GEN_COMM 0x02 /* Operator is commutative */ +#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ + + + /*****************************************************************************/ /* code */ /*****************************************************************************/ +unsigned GlobalModeFlags (const ExprDesc* Expr); +/* Return the addressing mode flags for the given expression */ + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ @@ -31,6 +52,9 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); ** generated code. */ +void LimitExprValue (ExprDesc* Expr); +/* Limit the constant value of the expression to the range of its type */ + void PushAddr (const ExprDesc* Expr); /* If the expression contains an address that was somehow evaluated, ** push this address on the stack. This is a helper function for all @@ -38,27 +62,32 @@ void PushAddr (const ExprDesc* Expr); ** must be saved if it's not constant, before evaluating the rhs. */ +void InitDeferredOps (void); +/* Init the collection for storing deferred ops */ + +void DoneDeferredOps (void); +/* Deinit the collection for storing deferred ops */ + +int GetDeferredOpCount (void); +/* Return how many deferred operations are still waiting in the queque */ + +void CheckDeferredOpAllDone (void); +/* Check if all deferred operations are done at sequence points. +** Die off if check fails. +*/ + +void DoDeferred (unsigned Flags, ExprDesc* Expr); +/* Do deferred operations such as post-inc/dec at sequence points */ + void Store (ExprDesc* Expr, const Type* StoreType); /* Store the primary register into the location denoted by lval. If StoreType ** is given, use this type when storing instead of lval->Type. If StoreType ** is NULL, use lval->Type instead. */ -int evalexpr (unsigned flags, void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is a -** constant, 0 is returned and the value is put in the Expr struct. If the -** result is not constant, LoadExpr is called to bring the value into the -** primary register and 1 is returned. -*/ - void Expression0 (ExprDesc* Expr); -/* Evaluate an expression via hie0 and put the result into the primary register */ - -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. +/* Evaluate an expression via hie0 and put the result into the primary register. +** The expression is completely evaluated and all side effects complete. */ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); @@ -68,11 +97,18 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); ** are no internal errors that result from this input error. */ -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)); +/* Get an expression evaluated via the given function. If the result is not a +** constant expression without runtime code generated, a diagnostic will be +** printed, and the value is replaced by a constant one to make sure there are +** no internal errors that result from this input error. +*/ + +ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)); +/* Get an expression evaluated via the given function. If the result is not a +** constant numeric integer value without runtime code generated, a diagnostic +** will be printed, and the value is replaced by a constant one to make sure +** there are no internal errors that result from this input error. */ void hie10 (ExprDesc* lval); diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 46377ac6b..3d7b7c384 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -56,26 +56,64 @@ ExprDesc* ED_Init (ExprDesc* Expr) /* Initialize an ExprDesc */ { - Expr->Sym = 0; Expr->Type = 0; - Expr->Flags = 0; + Expr->Flags = E_NEED_EAX; Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = 0; - Expr->FVal = FP_D_Make (0.0); - Expr->LVal = 0; - Expr->BitOffs = 0; - Expr->BitWidth = 0; + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) -/* Make this expression a bit field expression */ +#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->Flags |= E_BITFIELD; - Expr->BitOffs = BitOffs; - Expr->BitWidth = BitWidth; + 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) && !ED_IsLocPrimary (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; } @@ -115,8 +153,9 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) /* Generate a label depending on the location */ switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ SB_Printf (&Buf, "$%04X", (int)(Offs & 0xFFFF)); break; @@ -137,6 +176,15 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) case E_LOC_LITERAL: /* Literal in the literal pool */ + if (Offs) { + SB_Printf (&Buf, "%s%+ld", PooledLiteralLabelName (Expr->Name), Offs); + } else { + SB_Printf (&Buf, "%s", PooledLiteralLabelName (Expr->Name)); + } + break; + + case E_LOC_CODE: + /* Code label location */ if (Offs) { SB_Printf (&Buf, "%s%+ld", LocalLabelName (Expr->Name), Offs); } else { @@ -167,62 +215,195 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) -ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) -/* Make Expr an absolute const with the given value and type. */ +ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) +/* Replace Expr with an absolute const with the given value and type */ { - Expr->Sym = 0; Expr->Type = Type; - Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) -/* Make Expr a constant integer expression with the given 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_int; - Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Type = type_bool; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -ExprDesc* ED_MakeRValExpr (ExprDesc* Expr) -/* Convert Expr into a rvalue which is in the primary register without an -** offset. -*/ +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->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); - Expr->Flags |= (E_LOC_EXPR | E_RTYPE_RVAL); Expr->Name = 0; Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -ExprDesc* ED_MakeLValExpr (ExprDesc* Expr) -/* Convert Expr into a lvalue which is in the primary register without an -** offset. +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 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; +} + + + +#if !defined(HAVE_INLINE) +int ED_IsAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a numeric value or address. */ +{ + return (Expr->Flags & (E_MASK_LOC)) == (E_LOC_NONE) || + (Expr->Flags & (E_MASK_LOC|E_ADDRESS_OF)) == (E_LOC_ABS|E_ADDRESS_OF); +} +#endif + + + +#if !defined(HAVE_INLINE) +int ED_IsConstAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a constant absolute value. This can be +** a numeric constant, cast to any type. */ { - Expr->Sym = 0; - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); - Expr->Flags |= (E_LOC_EXPR | E_RTYPE_LVAL); - Expr->Name = 0; - Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); - return Expr; + return ED_IsRVal (Expr) && ED_IsAbs (Expr); +} +#endif + + + +int ED_IsConstAbsInt (const ExprDesc* Expr) +/* Return true if the expression is a constant (numeric) integer. */ +{ + return ED_IsConstAbs (Expr) && IsClassInt (Expr->Type); +} + + + +int ED_IsConstBool (const ExprDesc* Expr) +/* Return true if the expression can be constantly evaluated as a boolean. */ +{ + return ED_IsConstAbsInt (Expr) || ED_IsAddrExpr (Expr); +} + + + +int ED_IsConstTrue (const ExprDesc* Expr) +/* Return true if the constant expression can be evaluated as boolean true at +** compile time. +*/ +{ + /* Non-zero arithmetics and objects addresses are boolean true */ + return (ED_IsConstAbsInt (Expr) && Expr->IVal != 0) || + (ED_IsAddrExpr (Expr)); +} + + + +int ED_IsConstFalse (const ExprDesc* Expr) +/* Return true if the constant expression can be evaluated as boolean false at +** compile time. +*/ +{ + /* Zero arithmetics and null pointers are boolean false */ + return (ED_IsConstAbsInt (Expr) && Expr->IVal == 0) || + ED_IsNullPtr (Expr); } @@ -233,16 +414,36 @@ int ED_IsConst (const ExprDesc* Expr) ** similar. */ { - return ED_IsRVal (Expr) && (Expr->Flags & E_LOC_CONST) != 0; + return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE || ED_IsConstAddr (Expr); } -int ED_IsConstAbsInt (const ExprDesc* Expr) -/* Return true if the expression is a constant (numeric) integer. */ +int ED_IsQuasiConst (const ExprDesc* Expr) +/* Return true if the expression denotes a quasi-constant of some sort. This +** can be a numeric constant, a constant address or a stack variable address. +*/ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL) && - IsClassInt (Expr->Type); + return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE || ED_IsQuasiConstAddr (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. +*/ +{ + return ED_IsAddrExpr (Expr) && ED_IsLocConst (Expr); +} + + + +int ED_IsQuasiConstAddr (const ExprDesc* Expr) +/* Return true if the expression denotes a quasi-constant address of some sort. +** This can be a constant address or a stack variable address. +*/ +{ + return ED_IsAddrExpr (Expr) && ED_IsLocQuasiConst (Expr); } @@ -250,8 +451,8 @@ int ED_IsConstAbsInt (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == - (E_LOC_ABS|E_RTYPE_RVAL) && + return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == + (E_LOC_NONE|E_RTYPE_RVAL) && Expr->IVal == 0 && IsClassInt (Expr->Type); } @@ -259,14 +460,15 @@ int ED_IsNullPtr (const ExprDesc* Expr) int ED_IsBool (const ExprDesc* Expr) -/* Return true of the expression can be treated as a boolean, that is, it can +/* Return true if the expression can be treated as a boolean, that is, it can ** be an operand to a compare operation. */ { /* Either ints, floats, or pointers can be used in a boolean context */ return IsClassInt (Expr->Type) || IsClassFloat (Expr->Type) || - IsClassPtr (Expr->Type); + IsClassPtr (Expr->Type) || + IsClassFunc (Expr->Type); } @@ -288,11 +490,16 @@ void PrintExprDesc (FILE* F, ExprDesc* E) "Raw type: (unknown)\n"); } fprintf (F, "IVal: 0x%08lX\n", E->IVal); - fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->FVal)); + fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->V.FVal)); Flags = E->Flags; Sep = '('; fprintf (F, "Flags: 0x%04X ", Flags); + if ((Flags & E_MASK_LOC) == E_LOC_NONE) { + fprintf (F, "%cE_LOC_NONE", Sep); + Flags &= ~E_LOC_NONE; + Sep = ','; + } if (Flags & E_LOC_ABS) { fprintf (F, "%cE_LOC_ABS", Sep); Flags &= ~E_LOC_ABS; @@ -333,14 +540,9 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_LOC_LITERAL; Sep = ','; } - if (Flags & E_RTYPE_LVAL) { - fprintf (F, "%cE_RTYPE_LVAL", Sep); - Flags &= ~E_RTYPE_LVAL; - Sep = ','; - } - if (Flags & E_BITFIELD) { - fprintf (F, "%cE_BITFIELD", Sep); - Flags &= ~E_BITFIELD; + if (Flags & E_LOC_CODE) { + fprintf (F, "%cE_LOC_CODE", Sep); + Flags &= ~E_LOC_CODE; Sep = ','; } if (Flags & E_NEED_TEST) { @@ -353,6 +555,16 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_CC_SET; Sep = ','; } + if (Flags & E_RTYPE_LVAL) { + fprintf (F, "%cE_RTYPE_LVAL", Sep); + Flags &= ~E_RTYPE_LVAL; + Sep = ','; + } + if (Flags & E_ADDRESS_OF) { + fprintf (F, "%cE_ADDRESS_OF", Sep); + Flags &= ~E_ADDRESS_OF; + Sep = ','; + } if (Flags) { fprintf (F, "%c,0x%04X", Sep, Flags); Sep = ','; @@ -365,10 +577,10 @@ void PrintExprDesc (FILE* F, ExprDesc* E) -Type* ReplaceType (ExprDesc* Expr, const Type* NewType) +const Type* ReplaceType (ExprDesc* Expr, const Type* NewType) /* Replace the type of Expr by a copy of Newtype and return the old type string */ { - Type* OldType = Expr->Type; + const Type* OldType = Expr->Type; Expr->Type = TypeDup (NewType); return OldType; } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index e86534902..a1487a0bd 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -59,35 +59,136 @@ /* Defines for the flags field of the expression descriptor */ enum { - /* Location: Where is the value we're talking about? */ - E_MASK_LOC = 0x00FF, - E_LOC_ABS = 0x0001, /* Absolute: numeric address or const */ + /* Location: Where is the value we're talking about? + ** + ** Remarks: + ** - E_LOC_<else> refers to any other than E_LOC_NONE and E_LOC_PRIMARY. + ** - E_LOC_EXPR can be regarded as a generalized E_LOC_<else>. + ** - E_LOC_NONE can be regarded as E_LOC_PRIMARY + E_ADDRESS_OF unless + ** remarked otherwise (see below). + ** - An E_LOC_NONE value is not considered to be an "address". + ** - ref-load doesn't change the location, while rval-load puts into the + ** primary register a "temporary" that is the straight integer rvalue or + ** a "delegate" to the real rvalue somewhere else. + ** - ref-load doesn't change the rval/lval category of the expression, + ** while rval-load converts it to an rvalue if it wasn't. + ** - In practice, ref-load is unimplemented, and can be simulated with + ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, + ** whilst val-load is done with LoadExpr + FinalizeRValLoad. + ** + ** E_LOC_NONE -- ref-load -> + E_LOADED (int rvalue) + ** E_LOC_PRIMARY -- ref-load -> + E_LOADED (unchanged) + ** E_LOC_<else> -- ref-load -> + E_LOADED (reference lvalue) + ** + E_ADDRESS_OF -- ref-load -> + E_LOADED (address rvalue) + ** E_LOC_NONE -- val-load -> E_LOC_PRIMARY (int rvalue) + ** E_LOC_PRIMARY -- val-load -> E_LOC_PRIMARY (unchanged) + ** E_LOC_<else> -- val-load -> E_LOC_PRIMARY (rvalue/delegate) + ** + E_ADDRESS_OF -- val-load -> E_LOC_PRIMARY (address rvalue) + ** E_LOC_NONE -- take address -> (error) + ** E_LOC_PRIMARY -- take address -> + E_ADDRESS_OF (or error) + ** E_LOC_EXPR -- take address -> E_LOC_PRIMARY (address) + ** E_LOC_<else> -- take address -> + E_ADDRESS_OF (address) + ** + E_ADDRESS_OF -- take address -> (error) + ** E_LOC_NONE -- dereference -> E_LOC_ABS (lvalue reference) + ** E_LOC_PRIMARY -- dereference -> E_LOC_EXPR (lvalue reference) + ** E_LOC_<else> -- dereference -> E_LOC_EXPR (pointed-to-value, must load) + ** + E_ADDRESS_OF -- dereference -> (lvalue reference) + */ + E_MASK_LOC = 0x01FF, + E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ + E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ E_LOC_STATIC = 0x0004, /* Static variable */ E_LOC_REGISTER = 0x0008, /* Register variable */ E_LOC_STACK = 0x0010, /* Value on the stack */ - E_LOC_PRIMARY = 0x0020, /* The primary register */ - E_LOC_EXPR = 0x0040, /* An expression in the primary register */ + E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ + E_LOC_EXPR = 0x0040, /* A location that the primary register points to */ E_LOC_LITERAL = 0x0080, /* Literal in the literal pool */ + E_LOC_CODE = 0x0100, /* C code label location (&&Label) */ - /* Constant location of some sort (only if rval) */ - E_LOC_CONST = E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | - E_LOC_REGISTER | E_LOC_LITERAL, + /* Immutable location addresses (immutable bases and offsets) */ + E_LOC_CONST = E_LOC_NONE | E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | + E_LOC_REGISTER | E_LOC_LITERAL | E_LOC_CODE, - /* Reference? */ - E_MASK_RTYPE = 0x0100, + /* Not-so-immutable location addresses (stack offsets may change dynamically) */ + E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, + + /* Expression type modifiers */ + E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */ + + /* lvalue/rvalue in C language's sense */ + E_MASK_RTYPE = 0x0800, E_RTYPE_RVAL = 0x0000, - E_RTYPE_LVAL = 0x0100, + E_RTYPE_LVAL = 0x0800, - /* Bit-field? */ - E_BITFIELD = 0x0200, + /* Expression status */ + E_LOADED = 0x1000, /* Expression is loaded in primary */ + E_CC_SET = 0x2000, /* Condition codes are set */ + E_HAVE_MARKS = 0x4000, /* Code marks are valid */ - /* Test */ - E_NEED_TEST = 0x0400, /* Expression needs a test to set cc */ - E_CC_SET = 0x0800, /* Condition codes are set */ + /* Optimization hints */ + E_MASK_NEED = 0x030000, + E_NEED_EAX = 0x000000, /* Expression result needs to be loaded in Primary */ + E_NEED_NONE = 0x010000, /* Expression result is unused */ + E_NEED_TEST = 0x020000, /* Expression needs a test to set cc */ - E_HAVE_MARKS = 0x1000, /* Code marks are valid */ + /* Expression evaluation requirements. + ** Usage: (Flags & E_EVAL_<Flag>) == E_EVAL_<Flag> + ** + ** Remark: + ** - Expression result, that is the "final value" of the expression, is no + ** more than one of the effects of the whole expression. Effects other + ** than it are usually consided "side-effects" in this regard. + ** - The compiler front end cannot know things determined by the linker, + ** such as the actual address of an object with static storage. What it + ** can know is categorized as "compiler-known" here. + ** - The concept "immutable" here means that once something is determined + ** (not necessarily by the compiler), it will never change. This is not + ** the same meaning as the "constant" word in the C standard. + ** - The concept "compile-time" ( not to be confued with "compiler-known"), + ** or "static" (compared to "run-time" as in "_Static_assert" in C, not + ** to be confused with the "static" storage) here means that something + ** has no run-time behaviors, enforced by the fact that it generates no + ** target code (hence "no-code"). It is closely related to the concepts + ** above but not the same. + ** - An "unevaluated" expression is special and different from the above: + ** while it generates no code, cannot change its "value" (it actually has + ** no value), and must be completely handled by the compiler front-end, + ** it is unique in that it is not "evaluated" while the others are, and + ** the codegen routine of such an expression is usually separated from + ** the normally evaluated ones. Therefore it is treated differently from + ** the above and uses a separate flag that implies none of the above. + ** - The "maybe-unused" flag is to suppress the checking and warning on + ** expressions with no effects. It doesn't have any special meanings + ** beyond that, and is independent from the E_NEED_<flag>s. All + ** "unevaluated" expressions are flagged as "maybe-unused" just to + ** avoid unnecessary warnings. + ** + ** Relationship of some concepts: + ** - "no-code" implies "no-side-effects" + ** - "immutable" = "compiler-known" OR "no-code" + ** - "constant expression" in C = "compiler-known" AND "no-code", with minor differences + */ + E_MASK_EVAL = 0xFC0000, + E_EVAL_NONE = 0x000000, /* No requirements */ + E_EVAL_IMMUTABLE_RESULT = 0x040000, /* Expression result must be immutable */ + E_EVAL_COMPILER_KNOWN = 0x0C0000, /* Expression result must be known to the compiler */ + E_EVAL_NO_SIDE_EFFECTS = 0x100000, /* Evaluation must have no side effects */ + E_EVAL_NO_CODE = 0x340000, /* Evaluation must generate no code */ + E_EVAL_MAYBE_UNUSED = 0x400000, /* Expression result may be unused */ + E_EVAL_UNEVAL = 0xC00000, /* Expression is unevaluated */ + /* Expression result must be known to the compiler and generate no code to load */ + E_EVAL_C_CONST = E_EVAL_COMPILER_KNOWN | E_EVAL_NO_CODE, + + /* Flags to keep in subexpressions of most operations other than ternary */ + E_MASK_KEEP_SUBEXPR = E_MASK_EVAL, + + /* Flags to keep for the two result subexpressions of the ternary operation */ + E_MASK_KEEP_RESULT = E_MASK_NEED | E_MASK_EVAL, + + /* Flags to keep when using the ED_Make functions */ + E_MASK_KEEP_MAKE = E_HAVE_MARKS | E_MASK_KEEP_RESULT, }; /* Forward */ @@ -96,17 +197,15 @@ struct Literal; /* Describe the result of an expression */ typedef struct ExprDesc ExprDesc; struct ExprDesc { - struct SymEntry* Sym; /* Symbol table entry if known */ - Type* Type; /* Type array of expression */ - unsigned Flags; + const Type* Type; /* C type of the expression */ + unsigned Flags; /* Properties of the expression */ uintptr_t Name; /* Name pointer or label number */ + struct SymEntry* Sym; /* Symbol table entry if any */ long IVal; /* Integer value if expression constant */ - Double FVal; /* Floating point value */ - struct Literal* LVal; /* Literal value */ - - /* Bit field stuff */ - unsigned BitOffs; /* Bit offset for bit fields */ - unsigned BitWidth; /* Bit width for bit fields */ + union { + Double FVal; /* Floating point value */ + struct Literal* LVal; /* Literal value */ + } V; /* Start and end of generated code */ CodeMark Start; @@ -135,8 +234,18 @@ INLINE int ED_GetLoc (const ExprDesc* Expr) #endif #if defined(HAVE_INLINE) -INLINE int ED_IsLocAbs (const ExprDesc* Expr) +INLINE int ED_IsLocNone (const ExprDesc* Expr) /* Return true if the expression is an absolute value */ +{ + return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE; +} +#else +# define ED_IsLocNone(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_NONE) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsLocAbs (const ExprDesc* Expr) +/* Return true if the expression is referenced with an absolute address */ { return (Expr->Flags & E_MASK_LOC) == E_LOC_ABS; } @@ -151,7 +260,7 @@ INLINE int ED_IsLocRegister (const ExprDesc* Expr) return (Expr->Flags & E_MASK_LOC) == E_LOC_REGISTER; } #else -# define ED_IsLocRegister(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_REGISTER) +# define ED_IsLocRegister(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_REGISTER) #endif #if defined(HAVE_INLINE) @@ -187,7 +296,7 @@ INLINE int ED_IsLocExpr (const ExprDesc* Expr) #if defined(HAVE_INLINE) INLINE int ED_IsLocLiteral (const ExprDesc* Expr) /* Return true if the expression is a string from the literal pool */ -{ +{ return (Expr->Flags & E_MASK_LOC) == E_LOC_LITERAL; } #else @@ -198,73 +307,65 @@ INLINE int ED_IsLocLiteral (const ExprDesc* Expr) INLINE int ED_IsLocConst (const ExprDesc* Expr) /* Return true if the expression is a constant location of some sort */ { - return (Expr->Flags & E_LOC_CONST) != 0; + return ((Expr)->Flags & E_MASK_LOC & ~E_LOC_CONST) == 0; } #else -# define ED_IsLocConst(Expr) (((Expr)->Flags & E_LOC_CONST) != 0) +# define ED_IsLocConst(Expr) (((Expr)->Flags & E_MASK_LOC & ~E_LOC_CONST) == 0) #endif #if defined(HAVE_INLINE) -INLINE int ED_IsLVal (const ExprDesc* Expr) -/* Return true if the expression is a reference */ +INLINE int ED_IsLocQuasiConst (const ExprDesc* Expr) +/* Return true if the expression is a constant location of some sort or on the +** stack. +*/ { - return (Expr->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL; + return ED_IsLocConst (Expr) || ED_IsLocStack (Expr); } #else -# define ED_IsLVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL) +int ED_IsLocQuasiConst (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. +*/ #endif #if defined(HAVE_INLINE) -INLINE int ED_IsRVal (const ExprDesc* Expr) -/* Return true if the expression is a rvalue */ -{ - return (Expr->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL; -} -#else -# define ED_IsRVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_MakeLVal (ExprDesc* Expr) -/* Make the expression a lvalue. */ -{ - Expr->Flags |= E_RTYPE_LVAL; -} -#else -# define ED_MakeLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_MakeRVal (ExprDesc* Expr) -/* Make the expression a rvalue. */ -{ - Expr->Flags &= ~E_RTYPE_LVAL; -} -#else -# define ED_MakeRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE int ED_IsBitField (const ExprDesc* Expr) -/* Return true if the expression is a bit field */ -{ - return (Expr->Flags & E_BITFIELD) != 0; -} -#else -# define ED_IsBitField(Expr) (((Expr)->Flags & E_BITFIELD) != 0) -#endif - -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); -/* Make this expression a bit field expression */ - -#if defined(HAVE_INLINE) -INLINE void ED_MarkForTest (ExprDesc* Expr) +INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ { Expr->Flags |= E_NEED_TEST; } #else -# define ED_MarkForTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +# 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) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsPrimary (const ExprDesc* Expr) +/* Check if the expression needs to be in Primary. */ +{ + return (Expr->Flags & E_MASK_NEED) == E_NEED_EAX; +} +#else +# define ED_NeedsPrimary(Expr) (((Expr)->Flags & E_MASK_NEED) == E_NEED_EAX) #endif #if defined(HAVE_INLINE) @@ -277,15 +378,25 @@ 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 = (Expr->Flags & ~E_NEED_TEST) | E_CC_SET; + Expr->Flags |= E_CC_SET; } #else # define ED_TestDone(Expr) \ - do { (Expr)->Flags = ((Expr)->Flags & ~E_NEED_TEST) | E_CC_SET; } while (0) + do { (Expr)->Flags |= E_CC_SET; } while (0) #endif #if defined(HAVE_INLINE) @@ -308,6 +419,88 @@ INLINE void ED_MarkAsUntested (ExprDesc* Expr) # define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0) #endif +#if defined(HAVE_INLINE) +INLINE int ED_IsLoaded (const ExprDesc* Expr) +/* Check if the expression is loaded. +** NOTE: This is currently unused and not working due to code complexity. +*/ +{ + return (Expr->Flags & E_LOADED) != 0; +} +#else +# define ED_IsLoaded(Expr) (((Expr)->Flags & E_LOADED) != 0) +#endif + +int ED_YetToLoad (const ExprDesc* Expr); +/* Check if the expression is yet to be loaded somehow. */ + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsConst (const ExprDesc* Expr) +/* Check if the expression need be immutable */ +{ + return (Expr->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT; +} +#else +# 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 */ +{ + return (Expr->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL; +} +#else +# define ED_IsUneval(Expr) (((Expr)->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr) +/* Check if the expression may be present without effects */ +{ + return (Expr->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED; +} +#else +# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED) +#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. +*/ +{ + return (Expr->Flags & E_ADDRESS_OF) != 0; +} +#else +# define ED_IsAddrExpr(Expr) (((Expr)->Flags & E_ADDRESS_OF) != 0) +#endif + +#if defined(HAVE_INLINE) +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) && !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 */ @@ -325,20 +518,113 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs); ** an additional offset in Offs. */ -ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type); -/* Make Expr an absolute const with the given value and type. */ +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); -/* Make Expr a constant integer expression with the given value */ +/* Replace Expr with an constant integer with the given value */ -ExprDesc* ED_MakeRValExpr (ExprDesc* Expr); -/* Convert Expr into a rvalue which is in the primary register without an -** offset. +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 */ +{ + return ((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL; +} +#else +# define ED_IsLVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsRVal (const ExprDesc* Expr) +/* Return true if the expression is an rvalue */ +{ + return ((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL; +} +#else +# 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. */ +{ + return (Expr->Flags & (E_MASK_LOC)) == (E_LOC_NONE) || + (Expr->Flags & (E_MASK_LOC|E_ADDRESS_OF)) == (E_LOC_ABS|E_ADDRESS_OF); +} +#else +int ED_IsAbs (const ExprDesc* Expr); +/* Return true if the expression denotes a numeric value or address. */ +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsConstAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a constant absolute value. This can be +** a numeric constant, cast to any type. +*/ +{ + return ED_IsRVal (Expr) && ED_IsAbs (Expr); +} +#else +int ED_IsConstAbs (const ExprDesc* Expr); +/* Return true if the expression denotes a constant absolute value. This can be +** a numeric constant, cast to any type. +*/ +#endif + +int ED_IsConstAbsInt (const ExprDesc* Expr); +/* Return true if the expression is a constant (numeric) integer. */ + +int ED_IsConstBool (const ExprDesc* Expr); +/* Return true if the expression can be constantly evaluated as a boolean. */ + +int ED_IsConstTrue (const ExprDesc* Expr); +/* Return true if the constant expression can be evaluated as boolean true at +** compile time. */ -ExprDesc* ED_MakeLValExpr (ExprDesc* Expr); -/* Convert Expr into a lvalue which is in the primary register without an -** offset. +int ED_IsConstFalse (const ExprDesc* Expr); +/* Return true if the constant expression can be evaluated as boolean false at +** compile time. */ int ED_IsConst (const ExprDesc* Expr); @@ -347,34 +633,33 @@ int ED_IsConst (const ExprDesc* Expr); ** similar. */ -#if defined(HAVE_INLINE) -INLINE int ED_IsConstAbs (const ExprDesc* Expr) -/* Return true if the expression denotes a constant absolute value. This can be -** a numeric constant, cast to any type. +int ED_IsQuasiConst (const ExprDesc* Expr); +/* Return true if the expression denotes a quasi-constant of some sort. This +** can be a numeric constant, a constant address or a stack variable address. */ -{ - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL); -} -#else -# define ED_IsConstAbs(E) \ - (((E)->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL)) -#endif -int ED_IsConstAbsInt (const ExprDesc* Expr); -/* Return true if the expression is a constant (numeric) integer. */ +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. +*/ + +int ED_IsQuasiConstAddr (const ExprDesc* Expr); +/* Return true if the expression denotes a quasi-constant address of some sort. +** This can be a constant address or a stack variable address. +*/ int ED_IsNullPtr (const ExprDesc* Expr); /* Return true if the given expression is a NULL pointer constant */ int ED_IsBool (const ExprDesc* Expr); -/* Return true of the expression can be treated as a boolean, that is, it can -** be an operand to a compare operation. +/* Return true if the expression can be treated as a boolean, that is, it can +** be an operand to a compare operation with 0/NULL. */ void PrintExprDesc (FILE* F, ExprDesc* Expr); /* Print an ExprDesc */ -Type* ReplaceType (ExprDesc* Expr, const Type* NewType); +const Type* ReplaceType (ExprDesc* Expr, const Type* NewType); /* Replace the type of Expr by a copy of Newtype and return the old type string */ diff --git a/src/cc65/funcdesc.c b/src/cc65/funcdesc.c index 273263dec..2291b35ee 100644 --- a/src/cc65/funcdesc.c +++ b/src/cc65/funcdesc.c @@ -54,13 +54,14 @@ FuncDesc* NewFuncDesc (void) FuncDesc* F = (FuncDesc*) xmalloc (sizeof (FuncDesc)); /* Nullify the fields */ - F->Flags = 0; - F->SymTab = 0; - F->TagTab = 0; - F->ParamCount = 0; - F->ParamSize = 0; - F->LastParam = 0; - F->WrappedCall = 0; + F->Flags = 0; + F->SymTab = 0; + F->TagTab = 0; + F->ParamCount = 0; + F->ParamSize = 0; + F->LastParam = 0; + F->FuncDef = 0; + F->WrappedCall = 0; F->WrappedCallData = 0; /* Return the new struct */ diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h index a04ffb14a..e065c7602 100644 --- a/src/cc65/funcdesc.h +++ b/src/cc65/funcdesc.h @@ -45,31 +45,33 @@ /* Masks for the Flags field in FuncDesc */ -#define FD_NONE 0x0000U /* No flags */ -#define FD_EMPTY 0x0001U /* Function with empty param list */ -#define FD_VOID_PARAM 0x0002U /* Function with a void param list */ -#define FD_VARIADIC 0x0004U /* Function with variable param list */ -#define FD_OLDSTYLE 0x0010U /* Old style (K&R) function */ -#define FD_OLDSTYLE_INTRET 0x0020U /* K&R func has implicit int return */ -#define FD_UNNAMED_PARAMS 0x0040U /* Function has unnamed params */ -#define FD_CALL_WRAPPER 0x0080U /* This function is used as a wrapper */ +#define FD_NONE 0x0000U /* No flags */ +#define FD_EMPTY 0x0001U /* Function with empty param list */ +#define FD_VOID_PARAM 0x0002U /* Function with a void param list */ +#define FD_VARIADIC 0x0004U /* Function with variable param list */ +#define FD_INCOMPLETE_PARAM 0x0008U /* Function with param of unknown size */ +#define FD_OLDSTYLE 0x0010U /* Old style (K&R) function */ +#define FD_OLDSTYLE_INTRET 0x0020U /* K&R func has implicit int return */ +#define FD_UNNAMED_PARAMS 0x0040U /* Function has unnamed params */ +#define FD_CALL_WRAPPER 0x0080U /* This function is used as a wrapper */ /* Bits that must be ignored when comparing funcs */ -#define FD_IGNORE (FD_OLDSTYLE | FD_OLDSTYLE_INTRET | FD_UNNAMED_PARAMS | FD_CALL_WRAPPER) - +#define FD_IGNORE (FD_INCOMPLETE_PARAM | FD_OLDSTYLE | FD_OLDSTYLE_INTRET | FD_UNNAMED_PARAMS | FD_CALL_WRAPPER) +#define WRAPPED_CALL_USE_BANK 0x0100U /* WrappedCall uses .bank() */ /* Function descriptor */ typedef struct FuncDesc FuncDesc; struct FuncDesc { - unsigned Flags; /* Bitmapped flags FD_... */ - struct SymTable* SymTab; /* Symbol table */ - struct SymTable* TagTab; /* Symbol table for structs/enums */ - unsigned ParamCount; /* Number of parameters */ - unsigned ParamSize; /* Size of the parameters */ - struct SymEntry* LastParam; /* Pointer to last parameter */ - struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ - unsigned char WrappedCallData;/* The WrappedCall's user data */ + unsigned Flags; /* Bitmapped flags FD_... */ + struct SymTable* SymTab; /* Symbol table */ + struct SymTable* TagTab; /* Symbol table for structs/enums */ + unsigned ParamCount; /* Number of parameters */ + unsigned ParamSize; /* Size of the parameters */ + struct SymEntry* LastParam; /* Pointer to last parameter */ + struct FuncDesc* FuncDef; /* Descriptor used in definition */ + struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ + unsigned int WrappedCallData; /* The WrappedCall's user data */ }; diff --git a/src/cc65/function.c b/src/cc65/function.c index 8da084ac1..452181af9 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -72,7 +72,7 @@ Function* CurrentFunc = 0; -static Function* NewFunction (struct SymEntry* Sym) +static Function* NewFunction (struct SymEntry* Sym, FuncDesc* D) /* Create a new function activation structure and return it */ { /* Allocate a new structure */ @@ -81,9 +81,9 @@ static Function* NewFunction (struct SymEntry* Sym) /* Initialize the fields */ F->FuncEntry = Sym; F->ReturnType = GetFuncReturn (Sym->Type); - F->Desc = GetFuncDesc (Sym->Type); + F->Desc = D; F->Reserved = 0; - F->RetLab = GetLocalLabel (); + F->RetLab = 0; F->TopLevelSP = 0; F->RegOffs = RegisterSpace; F->Flags = IsTypeVoid (F->ReturnType) ? FF_VOID_RETURN : FF_NONE; @@ -105,6 +105,68 @@ static void FreeFunction (Function* F) +int F_CheckParamList (FuncDesc* D, int RequireAll) +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ +{ + unsigned I = 0; + unsigned Offs; + SymEntry* Param; + unsigned ParamSize = 0; + unsigned IncompleteCount = 0; + + /* Don't bother to check unnecessarily */ + if ((D->Flags & FD_INCOMPLETE_PARAM) == 0) { + return 1; + } + + /* Assign offsets. If the function has a variable parameter list, + ** there's one additional byte (the arg size). + */ + Offs = (D->Flags & FD_VARIADIC) ? 1 : 0; + Param = D->LastParam; + while (Param) { + unsigned Size = SizeOf (Param->Type); + if (RequireAll && IsIncompleteESUType (Param->Type)) { + if (D->Flags & FD_UNNAMED_PARAMS) { + Error ("Parameter %u has incomplete type '%s'", + D->ParamCount - I, + GetFullTypeName (Param->Type)); + } else { + Error ("Parameter '%s' has incomplete type '%s'", + Param->Name, + GetFullTypeName (Param->Type)); + } + ++IncompleteCount; + } + if (SymIsRegVar (Param)) { + Param->V.R.SaveOffs = Offs; + } else { + Param->V.Offs = Offs; + } + Offs += Size; + ParamSize += Size; + Param = Param->PrevSym; + ++I; + } + + /* If all parameters have complete types, set the total size description, + ** clear the FD_INCOMPLETE_PARAM flag and return true. + */ + if (IncompleteCount == 0) { + D->ParamSize = ParamSize; + D->Flags &= ~FD_INCOMPLETE_PARAM; + return 1; + } + + /* Otherwise return false */ + return 0; +} + + + const char* F_GetFuncName (const Function* F) /* Return the name of the current function */ { @@ -129,7 +191,7 @@ unsigned F_GetParamSize (const Function* F) -Type* F_GetReturnType (Function* F) +const Type* F_GetReturnType (Function* F) /* Get the return type for the function */ { return F->ReturnType; @@ -193,6 +255,14 @@ int F_HasOldStyleIntRet (const Function* F) +void F_SetRetLab (Function* F, unsigned NewRetLab) +/* Change the return jump label */ +{ + F->RetLab = NewRetLab; +} + + + unsigned F_GetRetLab (const Function* F) /* Return the return jump label */ { @@ -295,7 +365,7 @@ static void F_RestoreRegVars (Function* F) } /* Get the first symbol from the function symbol table */ - Sym = F->FuncEntry->V.F.Func->SymTab->SymHead; + Sym = F->Desc->SymTab->SymHead; /* Walk through all symbols checking for register variables */ while (Sym) { @@ -375,21 +445,39 @@ static void F_EmitDebugInfo (void) -void NewFunc (SymEntry* Func) +void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { + int ParamComplete; /* If all paramemters have complete types */ int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; + const Type* RType; /* Real type used for struct parameters */ + const Type* ReturnType; /* Return type */ - /* Get the function descriptor from the function entry */ - FuncDesc* D = Func->V.F.Func; + /* Remember this function descriptor used for definition */ + GetFuncDesc (Func->Type)->FuncDef = D; /* Allocate the function activation record for the function */ - CurrentFunc = NewFunction (Func); + CurrentFunc = NewFunction (Func, D); /* Reenter the lexical level */ ReenterFunctionLevel (D); + /* Check return type */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* There are already diagnostics on returning arrays or functions */ + if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { + Error ("Function has incomplete return type '%s'", + GetFullTypeName (ReturnType)); + } + } + + /* Check and set the parameter sizes. All parameter must have complete + ** types now. + */ + ParamComplete = F_CheckParamList (D, 1); + /* Check if the function header contains unnamed parameters. These are ** only allowed in cc65 mode. */ @@ -428,7 +516,7 @@ void NewFunc (SymEntry* Func) /* If cc65 extensions aren't enabled, don't allow a main function that ** doesn't return an int. */ - if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) { + if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) { Error ("'main' must always return an int"); } @@ -451,7 +539,7 @@ void NewFunc (SymEntry* Func) /* Determine if this is a main function in a C99 environment that ** returns an int. */ - if (IsTypeInt (F_GetReturnType (CurrentFunc)) && + if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } @@ -460,20 +548,23 @@ void NewFunc (SymEntry* Func) /* Allocate code and data segments for this function */ Func->V.F.Seg = PushSegments (Func); + /* Use the info in the segments for generating new local labels */ + UseLabelPoolFromSegments (Func->V.F.Seg); + + /* Set return label. This has to be done after the segments are pushed */ + F_SetRetLab (CurrentFunc, GetLocalLabel ()); + /* Allocate a new literal pool */ PushLiteralPool (Func); /* If this is a fastcall function, push the last parameter onto the stack */ - if ((D->Flags & FD_VARIADIC) == 0 && D->ParamCount > 0 && - (AutoCDecl ? - IsQualFastcall (Func->Type) : - !IsQualCDecl (Func->Type))) { + if (D->ParamCount > 0 && IsFastcallFunc (Func->Type)) { unsigned Flags; /* Generate the push */ - if (IsTypeFunc (D->LastParam->Type)) { - /* Pointer to function */ - Flags = CF_PTR; + /* Handle struct/union specially */ + if (IsClassStruct (D->LastParam->Type)) { + Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; } @@ -481,7 +572,7 @@ void NewFunc (SymEntry* Func) } /* Generate function entry code if needed */ - g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc)); + g_enter (FuncTypeOf (Func->Type), F_GetParamSize (CurrentFunc)); /* If stack checking code is requested, emit a call to the helper routine */ if (IS_Get (&CheckStack)) { @@ -491,34 +582,50 @@ void NewFunc (SymEntry* Func) /* Setup the stack */ StackPtr = 0; - /* Walk through the parameter list and allocate register variable space - ** for parameters declared as register. Generate code to swap the contents - ** of the register bank with the save area on the stack. - */ - Param = D->SymTab->SymHead; - while (Param && (Param->Flags & SC_PARAM) != 0) { + /* Emit code to handle the parameters if all of them have complete types */ + if (ParamComplete) { + /* Walk through the parameter list and allocate register variable space + ** for parameters declared as register. Generate code to swap the contents + ** of the register bank with the save area on the stack. + */ + Param = D->SymTab->SymHead; + while (Param && (Param->Flags & SC_PARAM) != 0) { - /* Check for a register variable */ - if (SymIsRegVar (Param)) { + /* Check if we need copy for struct/union type */ + RType = Param->Type; + if (IsClassStruct (RType)) { + RType = GetStructReplacementType (RType); - /* Allocate space */ - int Reg = F_AllocRegVar (CurrentFunc, Param->Type); - - /* Could we allocate a register? */ - if (Reg < 0) { - /* No register available: Convert parameter to auto */ - CvtRegVarToAuto (Param); - } else { - /* Remember the register offset */ - Param->V.R.RegOffs = Reg; - - /* Generate swap code */ - g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (Param->Type)); + /* If there is no replacement type, then it is just the address. + ** 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)); + } } - } - /* Next parameter */ - Param = Param->NextSym; + /* Check for a register variable */ + if (SymIsRegVar (Param)) { + + /* Allocate space */ + int Reg = F_AllocRegVar (CurrentFunc, RType); + + /* Could we allocate a register? */ + if (Reg < 0) { + /* No register available: Convert parameter to auto */ + CvtRegVarToAuto (Param); + } else { + /* Remember the register offset */ + Param->V.R.RegOffs = Reg; + + /* Generate swap code */ + g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); + } + } + + /* Next parameter */ + Param = Param->NextSym; + } } /* Need a starting curly brace */ @@ -537,15 +644,15 @@ void NewFunc (SymEntry* Func) /* Now process statements in this block */ while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { - Statement (0); + AnyStatement (0); } /* If this is not a void function, and not the main function in a C99 ** environment returning int, output a warning if we didn't see a return ** statement. */ - if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc) { - Warning ("Control reaches end of non-void function"); + if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc && IS_Get (&WarnReturnType)) { + Warning ("Control reaches end of non-void function [-Wreturn-type]"); } /* If this is the main function in a C99 environment returning an int, let diff --git a/src/cc65/function.h b/src/cc65/function.h index 0954322ac..f7f83cdc8 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -54,7 +54,7 @@ typedef enum { /* Structure that holds all data needed for function activation */ struct Function { struct SymEntry* FuncEntry; /* Symbol table entry */ - Type* ReturnType; /* Function return type */ + const Type* ReturnType; /* Function return type */ FuncDesc* Desc; /* Function descriptor */ int Reserved; /* Reserved local space */ unsigned RetLab; /* Return code label */ @@ -67,6 +67,9 @@ struct Function { /* Structure that holds all data needed for function activation */ typedef struct Function Function; +/* Forward declaration */ +struct FuncDesc; + /* Function activation data for current function (or NULL) */ extern Function* CurrentFunc; @@ -78,6 +81,12 @@ extern Function* CurrentFunc; +int F_CheckParamList (FuncDesc* D, int RequireAll); +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ + const char* F_GetFuncName (const Function* F); /* Return the name of the current function */ @@ -87,7 +96,7 @@ unsigned F_GetParamCount (const Function* F); unsigned F_GetParamSize (const Function* F); /* Return the parameter size for the current function */ -Type* F_GetReturnType (Function* F); +const Type* F_GetReturnType (Function* F); /* Get the return type for the function */ int F_HasVoidReturn (const Function* F); @@ -111,6 +120,9 @@ int F_IsOldStyle (const Function* F); int F_HasOldStyleIntRet (const Function* F); /* Return true if this is an old style (K&R) function with an implicit int return */ +void F_SetRetLab (Function* F, unsigned NewRetLab); +/* Change the return jump label */ + unsigned F_GetRetLab (const Function* F); /* Return the return jump label */ @@ -138,7 +150,7 @@ int F_AllocRegVar (Function* F, const Type* Type); ** bank (zero page storage). If there is no register space left, return -1. */ -void NewFunc (struct SymEntry* Func); +void NewFunc (struct SymEntry* Func, struct FuncDesc* D); /* Parse argument declarations and function body. */ diff --git a/src/cc65/global.h b/src/cc65/global.h index 4ffc84a39..b9bcf5550 100644 --- a/src/cc65/global.h +++ b/src/cc65/global.h @@ -67,7 +67,7 @@ extern IntStack EnableRegVars; /* Enable register variables */ extern IntStack AllowRegVarAddr; /* Allow taking addresses of register vars */ extern IntStack RegVarsToCallStack; /* Save reg variables on call stack */ extern IntStack StaticLocals; /* Make local variables static */ -extern IntStack SignedChars; /* Make characters signed by default */ +extern IntStack SignedChars; /* Use 'signed char' as the underlying type of 'char' */ extern IntStack CheckStack; /* Generate stack overflow checks */ extern IntStack Optimize; /* Optimize flag */ extern IntStack CodeSizeFactor; /* Size factor for generated code */ diff --git a/src/cc65/goto.c b/src/cc65/goto.c index f1fb725d2..7d3ff1a6a 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -80,6 +80,8 @@ void GotoStatement (void) CodeEntry *E; unsigned char val; + ED_Init (&desc); + NextToken (); /* arr[foo], we only support simple foo for now */ @@ -103,6 +105,9 @@ void GotoStatement (void) val = (unsigned char)CurTok.IVal; NextToken (); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &desc); + if (CPUIsets[CPU] & CPU_ISET_65SC02) { AddCodeLine ("ldx #$%02X", val * 2); AddCodeLine ("jmp (.loword(%s),x)", arr->AsmName); @@ -116,6 +121,10 @@ void GotoStatement (void) (idx = FindSym (CurTok.Ident))) { hie10 (&desc); LoadExpr (CF_NONE, &desc); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EAX, &desc); + AddCodeLine ("asl a"); if (CPUIsets[CPU] & CPU_ISET_65SC02) { diff --git a/src/cc65/hexval.h b/src/cc65/hexval.h index 0361ee0cf..68ea5e40f 100644 --- a/src/cc65/hexval.h +++ b/src/cc65/hexval.h @@ -45,7 +45,7 @@ unsigned HexVal (int C); -/* Convert a hex digit into a value. The function will emit an error for +/* Convert a hex digit into a value. The function will emit an error for ** invalid hex digits. */ diff --git a/src/cc65/ident.c b/src/cc65/ident.c index 7748095c7..dc59c4868 100644 --- a/src/cc65/ident.c +++ b/src/cc65/ident.c @@ -35,7 +35,7 @@ /* common */ #include "chartype.h" - + /* cc65 */ #include "ident.h" diff --git a/src/cc65/lineinfo.h b/src/cc65/lineinfo.h index 8dbe06846..f365b4f01 100644 --- a/src/cc65/lineinfo.h +++ b/src/cc65/lineinfo.h @@ -37,7 +37,7 @@ #define LINEINFO_H - + /* common */ #include "strbuf.h" @@ -50,7 +50,7 @@ /* Input file structure */ -struct IFile; +struct IFile; diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index eb31c7ecc..95228179d 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -99,7 +99,7 @@ static Literal* NewLiteral (const void* Buf, unsigned Len) Literal* L = xmalloc (sizeof (*L)); /* Initialize the fields */ - L->Label = GetLocalLabel (); + L->Label = GetPooledLiteralLabel (); L->RefCount = 0; L->Output = 0; SB_Init (&L->Data); @@ -130,7 +130,7 @@ static void OutputLiteral (Literal* L) TranslateLiteral (L); /* Define the label for the literal */ - g_defdatalabel (L->Label); + g_defliterallabel (L->Label); /* Output the literal data */ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)); @@ -423,12 +423,12 @@ static void OutputReadOnlyLiterals (Collection* Literals) if (C != 0) { /* This literal is part of a longer literal, merge them */ - g_aliasdatalabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L)); + g_aliasliterallabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L)); } else { /* Define the label for the literal */ - g_defdatalabel (L->Label); + g_defliterallabel (L->Label); /* Output the literal data */ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)); diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index fa37c6bbd..a742087b7 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -48,14 +48,14 @@ -static void LoadConstant (unsigned Flags, ExprDesc* Expr) -/* Load the primary register with some constant value. */ +static void LoadAddress (unsigned Flags, ExprDesc* Expr) +/* Load the primary register with some address value. */ { switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Number constant */ - g_getimmed (Flags | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + /* Numberic address */ + g_getimmed (Flags | CF_IMM | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: @@ -64,11 +64,15 @@ static void LoadConstant (unsigned Flags, ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static symbol or literal, load address */ + /* Static symbol, load address */ g_getimmed ((Flags | CF_STATIC) & ~CF_CONST, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal, load address */ + g_getimmed ((Flags | CF_LITERAL) & ~CF_CONST, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable. Taking the address is usually not ** allowed. @@ -79,41 +83,108 @@ static void LoadConstant (unsigned Flags, ExprDesc* Expr) g_getimmed ((Flags | CF_REGVAR) & ~CF_CONST, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label, load address */ + g_getimmed ((Flags | CF_CODE) & ~CF_CONST, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: g_leasp (Expr->IVal); break; + case E_LOC_EXPR: + if (Expr->IVal != 0) { + /* We have an expression in the primary plus a constant + ** offset. Adjust the value in the primary accordingly. + */ + g_inc (Flags | CF_CONST, Expr->IVal); + } + break; + default: - Internal ("Unknown constant type: %04X", Expr->Flags); + Internal ("Unknown address type: %04X", Expr->Flags); } } void LoadExpr (unsigned Flags, struct ExprDesc* Expr) -/* Load an expression into the primary register if it is not already there. */ +/* Load an expression into the primary register if it is not already there. +** Note: This function can't modify the content in Expr since there are many +** instances of the "GetCodePos + LoadExpr (maybe indirectly) + RemoveCode" +** code pattern here and there which assumes that Expr should be unchanged, +** unfortunately. +*/ { - if (ED_IsLVal (Expr)) { + if (!ED_IsAddrExpr (Expr)) { - /* Dereferenced lvalue. If this is a bit field its type is unsigned. - ** But if the field is completely contained in the lower byte, we will - ** throw away the high byte anyway and may therefore load just the - ** low byte. + /* Lvalue. If this is a bit field its type is unsigned. But if the + ** field is completely contained in the lower byte, we will throw away + ** the high byte anyway and may therefore load just the low byte. */ - if (ED_IsBitField (Expr)) { - Flags |= (Expr->BitOffs + Expr->BitWidth <= CHAR_BITS)? CF_CHAR : CF_INT; - Flags |= CF_UNSIGNED; - } else { - Flags |= TypeOf (Expr->Type); - } - if (ED_NeedsTest (Expr)) { - Flags |= CF_TEST; + int AdjustBitField = 0; + unsigned BitFieldFullWidthFlags = 0; + if ((Flags & CF_TYPEMASK) == 0) { + if (IsTypeBitField (Expr->Type)) { + unsigned EndBit = Expr->Type->A.B.Offs + Expr->Type->A.B.Width; + AdjustBitField = Expr->Type->A.B.Offs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + + /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are + ** supported. + */ + Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; + if (IsSignUnsigned (Expr->Type)) { + Flags |= CF_UNSIGNED; + } + + /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ + BitFieldFullWidthFlags = Flags; + + /* If we're adjusting, then only load a char (not an int) and do only char ops; + ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the + ** type is not CF_CHAR. + */ + if (AdjustBitField) { + /* If adjusting, then we're sign extending manually, so do everything unsigned + ** to make shifts faster. + */ + Flags |= CF_UNSIGNED | CF_FORCECHAR; + BitFieldFullWidthFlags |= CF_UNSIGNED; + } + } else { + /* If Expr is an incomplete ESY type, bail out */ + if (IsIncompleteESUType (Expr->Type)) { + return; + } + Flags |= TypeOf (Expr->Type); + } } + if (ED_YetToTest (Expr)) { + /* If we're only testing, we do not need to promote char to int. + ** CF_FORCECHAR does nothing if the type is not CF_CHAR. + */ + Flags |= CF_FORCECHAR; + + /* Setting CF_TEST will cause the load to perform optimizations and not actually load + ** all bits of the bit-field, instead just computing the condition codes. Therefore, + ** if adjustment is required, we do not set CF_TEST here, but handle it below. + */ + if (!AdjustBitField) { + Flags |= CF_TEST; + } + } + + /* Load the content of Expr */ switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: + /* Immediate number constant */ + g_getimmed (Flags | CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + break; + case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_getstatic (Flags | CF_ABSOLUTE, Expr->IVal, 0); break; @@ -123,23 +194,40 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + /* Static variable */ g_getstatic (Flags | CF_STATIC, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal in the literal pool */ + g_getstatic (Flags | CF_LITERAL, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable */ g_getstatic (Flags | CF_REGVAR, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label location */ + g_getstatic (Flags | CF_CODE, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: /* Value on the stack */ g_getlocal (Flags, Expr->IVal); break; case E_LOC_PRIMARY: - /* The primary register - just test if necessary */ + /* The primary register */ + if (Expr->IVal != 0) { + /* We have an expression in the primary plus a constant + ** offset. Adjust the value in the primary accordingly. + */ + g_inc (Flags | CF_CONST, Expr->IVal); + + /* We might want to clear the offset, but we can't */ + } if (Flags & CF_TEST) { g_test (Flags); } @@ -148,24 +236,33 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) case E_LOC_EXPR: /* Reference to address in primary with offset in Expr */ g_getind (Flags, Expr->IVal); + + /* Since the content in primary is now overwritten with the + ** dereference value, we might want to change the expression + ** loc to E_LOC_PRIMARY as well. That way we could be able to + ** call this function as many times as we want. Unfortunately, + ** we can't. + */ break; default: Internal ("Invalid location in LoadExpr: 0x%04X", ED_GetLoc (Expr)); } - /* Handle bit fields. The actual type may have been casted or - ** converted, so be sure to always use unsigned ints for the - ** operations. + /* Handle bit fields if necessary. The actual type may have been casted or converted, + ** so be sure to always use unsigned ints for the operations. */ - if (ED_IsBitField (Expr)) { - unsigned F = CF_INT | CF_UNSIGNED | CF_CONST | (Flags & CF_TEST); - /* Shift right by the bit offset */ - g_asr (F, Expr->BitOffs); - /* And by the width if the field doesn't end on an int boundary */ - if (Expr->BitOffs + Expr->BitWidth != CHAR_BITS && - Expr->BitOffs + Expr->BitWidth != INT_BITS) { - g_and (F, (0x0001U << Expr->BitWidth) - 1U); + if (AdjustBitField) { + /* We always need to do something with the low byte, so there is no opportunity + ** for optimization by skipping it. + */ + CHECK (Expr->Type->A.B.Offs < CHAR_BITS); + + if (ED_YetToTest (Expr)) { + g_testbitfield (Flags, Expr->Type->A.B.Offs, Expr->Type->A.B.Width); + } else { + g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), + Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } } @@ -173,24 +270,14 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ED_TestDone (Expr); } else { - /* An rvalue */ - if (ED_IsLocExpr (Expr)) { - if (Expr->IVal != 0) { - /* We have an expression in the primary plus a constant - ** offset. Adjust the value in the primary accordingly. - */ - Flags |= TypeOf (Expr->Type); - g_inc (Flags | CF_CONST, Expr->IVal); - } - } else { - /* Constant of some sort, load it into the primary */ - LoadConstant (Flags, Expr); - } + /* An address */ + Flags |= CF_INT | CF_UNSIGNED; + /* Constant of some sort, load it into the primary */ + LoadAddress (Flags, Expr); /* Are we testing this value? */ - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { /* Yes, force a test */ - Flags |= TypeOf (Expr->Type); g_test (Flags); ED_TestDone (Expr); } diff --git a/src/cc65/loadexpr.h b/src/cc65/loadexpr.h index 3f13311f1..c9e70e1f6 100644 --- a/src/cc65/loadexpr.h +++ b/src/cc65/loadexpr.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/locals.c b/src/cc65/locals.c index d0aab7f9c..c2e314485 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -50,6 +50,7 @@ #include "locals.h" #include "stackptr.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "typeconv.h" #include "input.h" @@ -63,31 +64,31 @@ static unsigned AllocLabel (void (*UseSeg) ()) -/* Switch to a segment, define a local label and return it */ +/* Switch to a segment, define a local data label and return it */ { - unsigned Label; + unsigned DataLabel; /* Switch to the segment */ UseSeg (); /* Define the variable label */ - Label = GetLocalLabel (); - g_defdatalabel (Label); + DataLabel = GetLocalDataLabel (); + g_defdatalabel (DataLabel); /* Return the label */ - return Label; + return DataLabel; } -static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size) -/* Reserve Size bytes of BSS storage prefixed by a local label. */ +static void AllocStorage (unsigned DataLabel, void (*UseSeg) (), unsigned Size) +/* Reserve Size bytes of BSS storage prefixed by a local data label. */ { /* Switch to the segment */ UseSeg (); /* Define the variable label */ - g_defdatalabel (Label); + g_defdatalabel (DataLabel); /* Reserve space for the data */ g_res (Size); @@ -121,8 +122,6 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -151,6 +150,9 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Parse the expression */ hie1 (&Expr); @@ -163,6 +165,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Store the value into the variable */ g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0); + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -171,7 +175,11 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } @@ -202,8 +210,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -241,6 +247,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate previously reserved local space */ F_AllocLocalSpace (CurrentFunc); @@ -260,12 +269,14 @@ static void ParseAutoDecl (Declaration* Decl) Flags |= CF_CONST; } else { LoadExpr (CF_NONE, &Expr); - ED_MakeRVal (&Expr); + ED_MarkExprAsRVal (&Expr); } /* Push the value */ g_push (Flags | TypeOf (Sym->Type), Expr.IVal); + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -294,7 +305,7 @@ static void ParseAutoDecl (Declaration* Decl) Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; /* Generate a label, but don't define it */ - DataLabel = GetLocalLabel (); + DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel); @@ -302,8 +313,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Allow assignments */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -327,6 +336,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate space for the variable */ AllocStorage (DataLabel, g_usebss, Size); @@ -340,7 +352,10 @@ static void ParseAutoDecl (Declaration* Decl) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (TypeOf (Sym->Type), DataLabel, 0); + g_putstatic (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); + + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -356,7 +371,11 @@ static void ParseAutoDecl (Declaration* Decl) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } @@ -368,7 +387,7 @@ static void ParseStaticDecl (Declaration* Decl) unsigned Size; /* Generate a label, but don't define it */ - unsigned DataLabel = GetLocalLabel (); + unsigned DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type, @@ -410,7 +429,11 @@ static void ParseStaticDecl (Declaration* Decl) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } @@ -425,13 +448,24 @@ static void ParseOneDecl (const DeclSpec* Spec) /* Read the declaration */ ParseDecl (Spec, &Decl, DM_NEED_IDENT); - /* Set the correct storage class for functions */ + /* Check if there are any non-extern storage classes set for function + ** declarations. Function can only be declared inside functions with the + ** 'extern' storage class specifier or no storage class specifier at all. + */ if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { - /* Function prototypes are always external */ - if ((Decl.StorageClass & SC_EXTERN) == 0) { - Warning ("Function must be extern"); + + /* Check if there are explicitly specified non-external storage classes */ + if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && + (Decl.StorageClass & SC_EXTERN) == 0 && + (Decl.StorageClass & SC_STORAGEMASK) != 0) { + Error ("Illegal storage class on function"); } - Decl.StorageClass |= SC_EXTERN; + + /* The default storage class could be wrong. Just clear them */ + Decl.StorageClass &= ~SC_STORAGEMASK; + + /* This is always a declaration */ + Decl.StorageClass |= SC_DECL; } /* If we don't have a name, this was flagged as an error earlier. @@ -442,13 +476,14 @@ static void ParseOneDecl (const DeclSpec* Spec) } /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_EXTERN) == 0) { + if ((Decl.StorageClass & SC_DECL) == 0 && + (Decl.StorageClass & SC_EXTERN) == 0) { Decl.StorageClass |= SC_DEF; } /* Handle anything that needs storage (no functions, no typdefs) */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && - (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + if ((Decl.StorageClass & SC_DEF) == SC_DEF && + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { /* If we have a register variable, try to allocate a register and ** convert the declaration to "auto" if this is not possible. @@ -467,13 +502,6 @@ static void ParseOneDecl (const DeclSpec* Spec) } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { /* Auto variable */ ParseAutoDecl (&Decl); - } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { - /* External identifier - may not get initialized */ - if (CurTok.Tok == TOK_ASSIGN) { - Error ("Cannot initialize externals"); - } - /* Add the external symbol to the symbol table */ - AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); @@ -483,10 +511,29 @@ static void ParseOneDecl (const DeclSpec* Spec) } else { - /* Add the symbol to the symbol table */ - AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + /* External identifier - may not get initialized */ + if (CurTok.Tok == TOK_ASSIGN) { + Error ("Cannot initialize extern variable '%s'", Decl.Ident); + /* Avoid excess errors */ + NextToken (); + ParseInit (Decl.Type); + } + } + + if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || + (Decl.StorageClass & SC_FUNC) == SC_FUNC) { + /* Add the global symbol to the local symbol table */ + AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); + } else { + /* Add the local symbol to the local symbol table */ + AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + } } + + /* Make sure we aren't missing some work */ + CheckDeferredOpAllDone (); } @@ -511,6 +558,13 @@ void DeclareLocals (void) ** declarations. */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + ParseDeclSpec (&Spec, SC_AUTO, T_INT); if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ diff --git a/src/cc65/loop.c b/src/cc65/loop.c index c15c37a79..cc8ccdec9 100644 --- a/src/cc65/loop.c +++ b/src/cc65/loop.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -39,7 +39,7 @@ /* cc65 */ #include "error.h" -#include "loop.h" +#include "loop.h" #include "stackptr.h" diff --git a/src/cc65/loop.h b/src/cc65/loop.h index fa2859f61..1174443b4 100644 --- a/src/cc65/loop.h +++ b/src/cc65/loop.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/main.c b/src/cc65/main.c index e86ae13ba..f95d89bd9 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -290,8 +290,12 @@ static void SetSys (const char* Sys) cbmsys ("__CX16__"); break; + case TGT_SYM1: + DefineNumericMacro ("__SYM1__", 1); + break; + default: - AbEnd ("Unknown target system type %d", Target); + AbEnd ("Unknown target system '%s'", Sys); } /* Initialize the translation tables for the target system */ @@ -463,7 +467,8 @@ static void OptCPU (const char* Opt, const char* Arg) /* Find the CPU from the given name */ CPU = FindCPU (Arg); if (CPU != CPU_6502 && CPU != CPU_6502X && CPU != CPU_65SC02 && - CPU != CPU_65C02 && CPU != CPU_65816 && CPU != CPU_HUC6280) { + CPU != CPU_65C02 && CPU != CPU_65816 && CPU != CPU_HUC6280 && + CPU != CPU_6502DTV) { AbEnd ("Invalid argument for %s: '%s'", Opt, Arg); } } @@ -731,7 +736,7 @@ static void OptRodataName (const char* Opt attribute ((unused)), const char* Arg static void OptSignedChars (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) -/* Make default characters signed */ +/* Use 'signed char' as the underlying type of 'char' */ { IS_Set (&SignedChars, 1); } @@ -897,6 +902,9 @@ int main (int argc, char* argv[]) /* Initialize the default segment names */ InitSegNames (); + /* Initialize the segment address sizes table */ + InitSegAddrSizes (); + /* Initialize the include search paths */ InitIncludePaths (); @@ -1060,13 +1068,16 @@ int main (int argc, char* argv[]) IS_Set (&Standard, STD_DEFAULT); } + /* Track string buffer allocation */ + InitDiagnosticStrBufs (); + /* Go! */ Compile (InputFile); /* Create the output file if we didn't had any errors */ if (PreprocessOnly == 0 && (ErrorCount == 0 || Debug)) { - /* Emit literals, externals, do cleanup and optimizations */ + /* Emit literals, do cleanup and optimizations */ FinishCompile (); /* Open the file */ @@ -1083,6 +1094,12 @@ int main (int argc, char* argv[]) CreateDependencies (); } + /* Done with tracked string buffer allocation */ + DoneDiagnosticStrBufs (); + + /* Free up the segment address sizes table */ + DoneSegAddrSizes (); + /* Return an apropriate exit code */ return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cc65/opcodes.c b/src/cc65/opcodes.c index 792b92f7c..aeea0297b 100644 --- a/src/cc65/opcodes.c +++ b/src/cc65/opcodes.c @@ -61,394 +61,394 @@ const OPCDesc OPCTable[OP65_COUNT] = { { OP65_ADC, /* opcode */ "adc", /* mnemonic */ 0, /* size */ - REG_A, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ, /* flags */ + REG_A | PSTATE_C, /* use */ + REG_A | PSTATE_CZVN /* chg */ }, { OP65_AND, /* opcode */ "and", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_ASL, /* opcode */ "asl", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + PSTATE_CZN /* chg */ }, { OP65_BCC, /* opcode */ "bcc", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_BCS, /* opcode */ "bcs", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_BEQ, /* opcode */ "beq", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_BIT, /* opcode */ "bit", /* mnemonic */ 0, /* size */ + OF_READ, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_SETF /* flags */ + PSTATE_ZVN /* chg */ }, { OP65_BMI, /* opcode */ "bmi", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_FBRA /* flags */ + OF_CBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_BNE, /* opcode */ "bne", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_BPL, /* opcode */ "bpl", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_FBRA /* flags */ + OF_CBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_BRA, /* opcode */ "bra", /* mnemonic */ 2, /* size */ + OF_UBRA, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_UBRA /* flags */ + REG_NONE /* chg */ }, { OP65_BRK, /* opcode */ "brk", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_B /* chg */ }, { OP65_BVC, /* opcode */ "bvc", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_BVS, /* opcode */ "bvs", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_CLC, /* opcode */ "clc", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_C /* chg */ }, { OP65_CLD, /* opcode */ "cld", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_D /* chg */ }, { OP65_CLI, /* opcode */ "cli", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_I /* chg */ }, { OP65_CLV, /* opcode */ "clv", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_V /* chg */ }, { OP65_CMP, /* opcode */ "cmp", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + PSTATE_CZN /* chg */ }, { OP65_CPX, /* opcode */ "cpx", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + PSTATE_CZN /* chg */ }, { OP65_CPY, /* opcode */ "cpy", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + PSTATE_CZN /* chg */ }, { OP65_DEA, /* opcode */ "dea", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_DEC, /* opcode */ "dec", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + PSTATE_ZN /* chg */ }, { OP65_DEX, /* opcode */ "dex", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_X, /* use */ - REG_X, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_DEY, /* opcode */ "dey", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_Y, /* use */ - REG_Y, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_EOR, /* opcode */ "eor", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_INA, /* opcode */ "ina", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_INC, /* opcode */ "inc", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + PSTATE_ZN /* chg */ }, { OP65_INX, /* opcode */ "inx", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_X, /* use */ - REG_X, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_INY, /* opcode */ "iny", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_Y, /* use */ - REG_Y, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_JCC, /* opcode */ "jcc", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_JCS, /* opcode */ "jcs", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_JEQ, /* opcode */ "jeq", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_JMI, /* opcode */ "jmi", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_JMP, /* opcode */ "jmp", /* mnemonic */ 3, /* size */ + OF_UBRA | OF_LBRA | OF_READ, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_UBRA | OF_LBRA /* flags */ + REG_NONE /* chg */ }, { OP65_JNE, /* opcode */ "jne", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_JPL, /* opcode */ "jpl", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_JSR, /* opcode */ "jsr", /* mnemonic */ 3, /* size */ + OF_CALL | OF_READ, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CALL /* flags */ + REG_NONE /* chg */ }, { OP65_JVC, /* opcode */ "jvc", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_JVS, /* opcode */ "jvs", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_LDA, /* opcode */ "lda", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_A, /* chg */ - OF_LOAD | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_LDX, /* opcode */ "ldx", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_LOAD | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_LDY, /* opcode */ "ldy", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_Y, /* chg */ - OF_LOAD | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_LSR, /* opcode */ "lsr", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + PSTATE_CZN /* chg */ }, { OP65_NOP, /* opcode */ "nop", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_ORA, /* opcode */ "ora", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_PHA, /* opcode */ "pha", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PHP, /* opcode */ "php", /* mnemonic */ 1, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + OF_NONE, /* flags */ + PSTATE_ALL, /* use */ + REG_NONE /* chg */ }, { OP65_PHX, /* opcode */ "phx", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PHY, /* opcode */ "phy", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PLA, /* opcode */ "pla", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_PLP, /* opcode */ "plp", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_ALL /* chg */ }, { OP65_PLX, /* opcode */ "plx", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_PLY, /* opcode */ "ply", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_Y, /* chg */ - OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_ROL, /* opcode */ "rol", /* mnemonic */ 0, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ + PSTATE_C, /* use */ + PSTATE_CZN /* chg */ }, { OP65_ROR, /* opcode */ "ror", /* mnemonic */ 0, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ + PSTATE_C, /* use */ + PSTATE_CZN /* chg */ }, /* Mark RTI as "uses all registers but doesn't change them", so the ** optimizer won't remove preceeding loads. @@ -456,135 +456,135 @@ const OPCDesc OPCTable[OP65_COUNT] = { { OP65_RTI, /* opcode */ "rti", /* mnemonic */ 1, /* size */ + OF_RET, /* flags */ REG_AXY, /* use */ - REG_NONE, /* chg */ - OF_RET /* flags */ + PSTATE_ALL /* chg */ }, { OP65_RTS, /* opcode */ "rts", /* mnemonic */ 1, /* size */ + OF_RET, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_RET /* flags */ + REG_NONE /* chg */ }, { OP65_SBC, /* opcode */ "sbc", /* mnemonic */ 0, /* size */ - REG_A, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ, /* flags */ + REG_A | PSTATE_C, /* use */ + REG_A | PSTATE_CZVN /* chg */ }, { OP65_SEC, /* opcode */ "sec", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_C /* chg */ }, { OP65_SED, /* opcode */ "sed", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_D /* chg */ }, { OP65_SEI, /* opcode */ "sei", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_I /* chg */ }, { OP65_STA, /* opcode */ "sta", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_STORE /* flags */ + REG_NONE /* chg */ }, { OP65_STP, /* opcode */ "stp", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_STX, /* opcode */ "stx", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_STORE /* flags */ + REG_NONE /* chg */ }, { OP65_STY, /* opcode */ "sty", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_STORE /* flags */ + REG_NONE /* chg */ }, { OP65_STZ, /* opcode */ "stz", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_STORE /* flags */ + REG_NONE /* chg */ }, { OP65_TAX, /* opcode */ "tax", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_A, /* use */ - REG_X, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_TAY, /* opcode */ "tay", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_A, /* use */ - REG_Y, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_TRB, /* opcode */ "trb", /* mnemonic */ 0, /* size */ + OF_RMW, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_SETF /* flags */ + PSTATE_Z /* chg */ }, { OP65_TSB, /* opcode */ "tsb", /* mnemonic */ 0, /* size */ + OF_RMW, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_SETF /* flags */ + PSTATE_Z /* chg */ }, { OP65_TSX, /* opcode */ "tsx", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_TXA, /* opcode */ "txa", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_X, /* use */ - REG_A, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_TXS, /* opcode */ "txs", /* mnemonic */ 1, /* size */ + OF_XFR, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_XFR /* flags */ + REG_NONE /* chg */ }, { OP65_TYA, /* opcode */ "tya", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_Y, /* use */ - REG_A, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, }; @@ -649,6 +649,7 @@ unsigned GetInsnSize (opc_t OPC, am_t AM) case AM65_IMM: return 2; case AM65_ZP: return 2; case AM65_ZPX: return 2; + case AM65_ZPY: return 2; case AM65_ABS: return 3; case AM65_ABSX: return 3; case AM65_ABSY: return 3; @@ -733,7 +734,7 @@ opc_t MakeShortBranch (opc_t OPC) case OP65_BVS: case OP65_JVS: return OP65_BVS; case OP65_BRA: - case OP65_JMP: return (CPUIsets[CPU] & CPU_ISET_65SC02)? OP65_BRA : OP65_JMP; + case OP65_JMP: return (CPUIsets[CPU] & (CPU_ISET_65SC02 | CPU_ISET_6502DTV)) ? OP65_BRA : OP65_JMP; default: Internal ("MakeShortBranch: Invalid opcode: %d", OPC); return 0; diff --git a/src/cc65/opcodes.h b/src/cc65/opcodes.h index bdb0c28a2..980cc649a 100644 --- a/src/cc65/opcodes.h +++ b/src/cc65/opcodes.h @@ -128,7 +128,10 @@ typedef enum { OP65_TYA, /* Number of opcodes available */ - OP65_COUNT + OP65_COUNT, + + /* Invalid opcode */ + OP65_INVALID = OP65_COUNT, } opc_t; /* 65XX addressing modes */ @@ -173,22 +176,25 @@ typedef enum { #define OF_XFR 0x0100U /* Transfer instruction */ #define OF_CALL 0x0200U /* A subroutine call */ #define OF_REG_INCDEC 0x0400U /* A register increment or decrement */ -#define OF_SETF 0x0800U /* Insn will set all load flags (not carry) */ +#define OF_SETF 0x0800U /* Insn will set both Z and N flags according to the result */ #define OF_CMP 0x1000U /* A compare A/X/Y instruction */ #define OF_NOIMP 0x2000U /* Implicit addressing mode is actually A */ +#define OF_READ 0x4000U /* Read from the memory address */ +#define OF_WRITE 0x8000U /* Write to the memory address */ /* Combined infos */ #define OF_BRA (OF_UBRA | OF_CBRA) /* Operation is a jump/branch */ #define OF_DEAD (OF_UBRA | OF_RET) /* Dead end - no exec behind this point */ +#define OF_RMW (OF_READ | OF_WRITE) /* Read, Modify and Write */ /* Opcode description */ typedef struct { opc_t OPC; /* Opcode */ char Mnemo[9]; /* Mnemonic */ unsigned char Size; /* Size, 0 = check addressing mode */ - unsigned short Use; /* Registers used by this insn */ - unsigned short Chg; /* Registers changed by this insn */ unsigned short Info; /* Additional information */ + unsigned int Use; /* Registers used by this insn */ + unsigned int Chg; /* Registers changed by this insn */ } OPCDesc; /* Opcode description table */ diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index b05ef6122..b0478ce2a 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -37,6 +37,7 @@ #include <string.h> /* common */ +#include "addrsize.h" #include "chartype.h" #include "segnames.h" #include "tgttrans.h" @@ -45,6 +46,7 @@ #include "codegen.h" #include "error.h" #include "expr.h" +#include "funcdesc.h" #include "global.h" #include "litpool.h" #include "scanner.h" @@ -389,7 +391,9 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) /* Handle a pragma that expects a segment name parameter */ { const char* Name; + unsigned char AddrSize = ADDR_SIZE_INVALID; StrBuf S = AUTO_STRBUF_INITIALIZER; + StrBuf A = AUTO_STRBUF_INITIALIZER; int Push = 0; /* Check for the "push" or "pop" keywords */ @@ -430,13 +434,35 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) goto ExitPoint; } - /* Get the string */ + /* Get the name string of the segment */ Name = SB_GetConstBuf (&S); /* Check if the name is valid */ if (ValidSegName (Name)) { - /* Set the new name */ + /* Skip the following comma */ + SB_SkipWhite (B); + if (SB_Peek (B) == ',') { + SB_Skip (B); + SB_SkipWhite (B); + + /* A string argument must follow */ + if (!GetString (B, &A)) { + goto ExitPoint; + } + + /* Get the address size for the segment */ + AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); + + /* Set the address size for the segment if valid */ + if (AddrSize != ADDR_SIZE_INVALID) { + SetSegAddrSize (Name, AddrSize); + } else { + Warning ("Invalid address size for segment!"); + } + } + + /* Set the new name and optionally address size */ if (Push) { PushSegName (Seg, Name); } else { @@ -460,6 +486,7 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) ExitPoint: /* Call the string buf destructor */ SB_Done (&S); + SB_Done (&A); } @@ -504,16 +531,19 @@ static void WrappedCallPragma (StrBuf* B) /* Skip the following comma */ if (!GetComma (B)) { /* Error already flagged by GetComma */ + Error ("Value or the word 'bank' required for wrapped-call identifier"); + goto ExitPoint; + } + + /* Next must be either a numeric value, or "bank" */ + if (HasStr (B, "bank")) { + Val = WRAPPED_CALL_USE_BANK; + } else if (!GetNumber (B, &Val)) { Error ("Value required for wrapped-call identifier"); goto ExitPoint; } - if (!GetNumber (B, &Val)) { - Error ("Value required for wrapped-call identifier"); - goto ExitPoint; - } - - if (Val < 0 || Val > 255) { + if (!(Val == WRAPPED_CALL_USE_BANK) && (Val < 0 || Val > 255)) { Error ("Identifier must be between 0-255"); goto ExitPoint; } @@ -523,11 +553,11 @@ static void WrappedCallPragma (StrBuf* B) Entry = FindSym(Name); /* Check if the name is valid */ - if (Entry && Entry->Flags & SC_FUNC) { + if (Entry && (Entry->Flags & SC_FUNC) == SC_FUNC) { - PushWrappedCall(Entry, (unsigned char) Val); + PushWrappedCall(Entry, (unsigned int) Val); Entry->Flags |= SC_REF; - Entry->V.F.Func->Flags |= FD_CALL_WRAPPER; + GetFuncDesc (Entry->Type)->Flags |= FD_CALL_WRAPPER; } else { @@ -651,7 +681,7 @@ static void WarnPragma (StrBuf* B) static void FlagPragma (StrBuf* B, IntStack* Stack) -/* Handle a pragma that expects a boolean paramater */ +/* Handle a pragma that expects a boolean parameter */ { StrBuf Ident = AUTO_STRBUF_INITIALIZER; long Val; @@ -701,7 +731,7 @@ ExitPoint: static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) -/* Handle a pragma that expects an int paramater */ +/* Handle a pragma that expects an int parameter */ { long Val; int Push; @@ -754,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); + fprintf (stderr, "%s:%u: Note: %s\n", GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Message); } diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 1c4837d94..a607e3217 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -71,7 +71,7 @@ unsigned char Preprocessing = 0; /* Management data for #if */ -#define MAX_IFS 64 +#define MAX_IFS 256 #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U @@ -1042,8 +1042,6 @@ static void DoError (void) static int DoIf (int Skip) /* Process #if directive */ { - ExprDesc Expr; - /* 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 @@ -1078,7 +1076,7 @@ static int DoIf (int Skip) NextToken (); /* Call the expression parser */ - ConstExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstExpr (hie1); /* End preprocessing mode */ Preprocessing = 0; @@ -1398,7 +1396,7 @@ void Preprocess (void) Done: if (Verbosity > 1 && SB_NotEmpty (Line)) { - printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (), + printf ("%s:%u: %.*s\n", GetCurrentFile (), GetCurrentLine (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } } diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 8d7b3c374..78a91a590 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -44,7 +44,7 @@ -/* Set when the preprocessor calls ConstExpr() recursively */ +/* Set when the preprocessor calls NoCodeConstExpr() recursively */ extern unsigned char Preprocessing; diff --git a/src/cc65/reginfo.c b/src/cc65/reginfo.c index 383c6ec24..601f0f688 100644 --- a/src/cc65/reginfo.c +++ b/src/cc65/reginfo.c @@ -74,6 +74,15 @@ void RC_InvalidateZP (RegContents* C) +void RC_InvalidatePS (RegContents* C) +/* Invalidate processor status */ +{ + C->PFlags = UNKNOWN_PFVAL_ALL; + C->ZNRegs = ZNREG_NONE; +} + + + static void RC_Dump1 (FILE* F, const char* Desc, short Val) /* Dump one register value */ { @@ -102,6 +111,44 @@ void RC_Dump (FILE* F, const RegContents* RC) +#if !defined(HAVE_INLINE) +int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return ((PFlags << (PSTATE_BITS_SHIFT - 8)) & WhatStates & PSTATE_BITS_MASK) == 0; +} +#endif + + + +#if !defined(HAVE_INLINE) +int PStatesAreSet (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be set. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == WhatStates >> PSTATE_BITS_SHIFT; +} +#endif + + + +#if !defined(HAVE_INLINE) +int PStatesAreClear (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be cleared. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == 0; +} +#endif + + + RegInfo* NewRegInfo (const RegContents* RC) /* Allocate a new register info, initialize and return it. If RC is not ** a NULL pointer, it is used to initialize both, the input and output @@ -120,6 +167,9 @@ RegInfo* NewRegInfo (const RegContents* RC) RC_Invalidate (&RI->In); RC_Invalidate (&RI->Out); RC_Invalidate (&RI->Out2); + RC_InvalidatePS (&RI->In); + RC_InvalidatePS (&RI->Out); + RC_InvalidatePS (&RI->Out2); } /* Return the new struct */ diff --git a/src/cc65/reginfo.h b/src/cc65/reginfo.h index 7354c301f..2066ab505 100644 --- a/src/cc65/reginfo.h +++ b/src/cc65/reginfo.h @@ -54,6 +54,69 @@ /* Encoding for an unknown register value */ #define UNKNOWN_REGVAL -1 +/* Encoding for an unknown processor status: +** For Bit N in the flags, ((flags >> N) & 0x0101) == 0x0101 means 'unknown' +*/ +#define UNKNOWN_PFVAL_C 0x0101U /* Carray */ +#define UNKNOWN_PFVAL_Z 0x0202U /* Zero */ +#define UNKNOWN_PFVAL_I 0x0404U /* Interrupt */ +#define UNKNOWN_PFVAL_D 0x0808U /* Decimal */ +#define UNKNOWN_PFVAL_U 0x1010U /* Unused */ +#define UNKNOWN_PFVAL_B 0x2020U /* Break */ +#define UNKNOWN_PFVAL_V 0x4040U /* Overflow */ +#define UNKNOWN_PFVAL_N 0x8080U /* Negative */ +#define UNKNOWN_PFVAL_CZ (UNKNOWN_PFVAL_C | UNKNOWN_PFVAL_Z) +#define UNKNOWN_PFVAL_CZN (UNKNOWN_PFVAL_N | UNKNOWN_PFVAL_CZ) +#define UNKNOWN_PFVAL_CZVN (UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_CZN) +#define UNKNOWN_PFVAL_ZN (UNKNOWN_PFVAL_Z | UNKNOWN_PFVAL_N) +#define UNKNOWN_PFVAL_ZVN (UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_ZN) +#define UNKNOWN_PFVAL_6502 0xE7E7U +#define UNKNOWN_PFVAL_ALL 0xFFFFU + +/* Encoding for a known processor status */ +#define PFVAL_C 0x0001U /* Carray set */ +#define PFVAL_Z 0x0002U /* Zero set */ +#define PFVAL_I 0x0004U /* Interrupt set */ +#define PFVAL_D 0x0008U /* Decimal set */ +#define PFVAL_U 0x0010U /* Unused set */ +#define PFVAL_B 0x0020U /* Break set */ +#define PFVAL_V 0x0040U /* Overflow set */ +#define PFVAL_N 0x0080U /* Negative set */ +#define PFVAL_CZ (PFVAL_C | PFVAL_Z) +#define PFVAL_CZN (PFVAL_N | PFVAL_CZ) +#define PFVAL_CZVN (PFVAL_V | PFVAL_CZN) +#define PFVAL_ZN (PFVAL_Z | PFVAL_N) +#define PFVAL_ZVN (PFVAL_V | PFVAL_ZN) +#define PFVAL_6502 0x00E7U +#define PFVAL_ALL 0x00FFU + +/* Used for functions to convert the processor states to processor flags */ +#define PSTATE_BITS_SHIFT 24 +#define PSTATE_BITS_MASK (0xFFU << PSTATE_BITS_SHIFT) + +/* Encoding for unknown Z/N status origin */ +#define UNKNOWN_ZNREG 0x0000U + +/* Encoding for known register Z/N status origins */ +#define ZNREG_NONE 0x0000U /* None */ +#define ZNREG_A REG_A +#define ZNREG_X REG_X +#define ZNREG_Y REG_Y +#define ZNREG_TMP1 REG_TMP1 +#define ZNREG_PTR1_LO REG_PTR1_LO +#define ZNREG_PTR1_HI REG_PTR1_HI +#define ZNREG_PTR2_LO REG_PTR2_LO +#define ZNREG_PTR2_HI REG_PTR2_HI +#define ZNREG_SREG_LO REG_SREG_LO +#define ZNREG_SREG_HI REG_SREG_HI +#define ZNREG_SAVE_LO REG_SAVE_LO +#define ZNREG_SAVE_HI REG_SAVE_HI +#define ZNREG_AX (REG_A | REG_X) +#define ZNREG_AY (REG_A | REG_Y) +#define ZNREG_AXY (REG_A | REG_X | REG_Y) + + + /* Register contents */ typedef struct RegContents RegContents; struct RegContents { @@ -65,6 +128,8 @@ struct RegContents { short Ptr1Lo; short Ptr1Hi; short Tmp1; + unsigned short PFlags; /* Processor flags */ + unsigned short ZNRegs; /* Which register(s) the Z/N flags reflect */ }; /* Register change info */ @@ -89,6 +154,9 @@ void RC_Invalidate (RegContents* C); void RC_InvalidateZP (RegContents* C); /* Invalidate all ZP registers */ +void RC_InvalidatePS (RegContents* C); +/* Invalidate processor status */ + void RC_Dump (FILE* F, const RegContents* RC); /* Dump the contents of the given RegContents struct */ @@ -112,6 +180,56 @@ INLINE int RegValIsUnknown (short Val) # define RegValIsUnknown(S) ((S) < 0) #endif +#if defined(HAVE_INLINE) +INLINE int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return ((PFlags << (PSTATE_BITS_SHIFT - 8)) & WhatStates & PSTATE_BITS_MASK) == 0; +} +#else +int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates); +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreUnknown (unsigned short PFlags, unsigned WhatStates) +/* Return true if any queried processor states are unknown. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return !PStatesAreKnown (PFlags, WhatStates); +} +#else +# define PStatesAreUnknown(V, B) (!PStatesAreKnown (V, B)) +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreSet (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be set. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == WhatStates >> PSTATE_BITS_SHIFT; +} +#else +int PStatesAreSet (unsigned short PFlags, unsigned WhatStates); +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreClear (unsigned short PFlags, unsigned WhatStates) +/* Return true if the queried processor states are known to be cleared. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == 0; +} +#else +int PStatesAreClear (unsigned short PFlags, unsigned WhatStates); +#endif + RegInfo* NewRegInfo (const RegContents* RC); /* Allocate a new register info, initialize and return it. If RC is not ** a NULL pointer, it is used to initialize both, the input and output diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 3e829aabb..70203d027 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -86,6 +86,7 @@ static const struct Keyword { unsigned char Std; /* Token supported in which standards? */ } Keywords [] = { { "_Pragma", TOK_PRAGMA, TT_C89 | TT_C99 | TT_CC65 }, /* !! */ + { "_Static_assert", TOK_STATIC_ASSERT, TT_CC65 }, /* C11 */ { "__AX__", TOK_AX, TT_C89 | TT_C99 | TT_CC65 }, { "__A__", TOK_A, TT_C89 | TT_C99 | TT_CC65 }, { "__EAX__", TOK_EAX, TT_C89 | TT_C99 | TT_CC65 }, @@ -530,10 +531,12 @@ static void NumericConst (void) if (!IsFloat) { unsigned Types; - int HaveSuffix; + unsigned WarnTypes = 0; - /* Check for a suffix and determine the possible types */ - HaveSuffix = 1; + /* Check for a suffix and determine the possible types. It is always + ** 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') { /* Unsigned type */ NextChar (); @@ -548,17 +551,18 @@ static void NumericConst (void) NextChar (); if (toupper (CurC) != 'U') { Types = IT_LONG | IT_ULONG; + WarnTypes = IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else { - HaveSuffix = 0; if (Prefix == 10) { /* Decimal constants are of any type but uint */ Types = IT_INT | IT_LONG | IT_ULONG; + WarnTypes = IT_LONG | IT_ULONG; } else { - /* Octal or hex constants are of any type */ + /* Binary, octal and hex constants can be of any type */ Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; } } @@ -568,11 +572,14 @@ static void NumericConst (void) /* Out of range for int */ Types &= ~IT_INT; /* If the value is in the range 0x8000..0xFFFF, unsigned int is not - ** allowed, and we don't have a type specifying suffix, emit a - ** warning, because the constant is of type long. + ** allowed, and we don't have a long type specifying suffix, emit a + ** warning, because the constant is of type long while the user + ** might expect an unsigned int. */ - if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) { - Warning ("Constant is long"); + if (IVal <= 0xFFFF && + (Types & IT_UINT) == 0 && + (WarnTypes & IT_LONG) != 0) { + Warning ("Integer constant is long"); } } if (IVal > 0xFFFF) { @@ -582,6 +589,15 @@ static void NumericConst (void) if (IVal > 0x7FFFFFFF) { /* Out of range for long int */ Types &= ~IT_LONG; + /* If the value is in the range 0x80000000..0xFFFFFFFF, decimal, + ** and we have no unsigned type specifying suffix, emit a warning, + ** because the constant is of type unsigned long while the user + ** might expect a signed integer constant, especially if there is + ** a preceding unary op or when it is used in constant calculation. + */ + if (WarnTypes & IT_ULONG) { + Warning ("Integer constant is unsigned long"); + } } /* Now set the type string to the smallest type in types */ diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 1242d78fd..e6a362bf3 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -173,6 +173,7 @@ typedef enum token_t { TOK_WCSCONST, TOK_ATTRIBUTE, + TOK_STATIC_ASSERT, TOK_FAR, TOK_NEAR, TOK_A, @@ -204,7 +205,7 @@ struct Token { struct Literal* SVal; /* String literal is any */ ident Ident; /* Identifier if IDENT */ LineInfo* LI; /* Source line where the token comes from */ - Type* Type; /* Type if integer or float constant */ + const Type* Type; /* Type if integer or float constant */ }; extern Token CurTok; /* The current token */ diff --git a/src/cc65/scanstrbuf.c b/src/cc65/scanstrbuf.c index bbee569d2..2e3be8854 100644 --- a/src/cc65/scanstrbuf.c +++ b/src/cc65/scanstrbuf.c @@ -185,7 +185,7 @@ int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars) SB_AppendChar (Ident, C); SB_Skip (B); C = SB_Peek (B); - } while (IsIdent (C) || IsDigit (C) || + } while (IsIdent (C) || IsDigit (C) || (C != '\0' && strchr (SpecialChars, C) != 0)); SB_Terminate (Ident); return 1; diff --git a/src/cc65/segments.c b/src/cc65/segments.c index 6efbf68b7..8283b9da5 100644 --- a/src/cc65/segments.c +++ b/src/cc65/segments.c @@ -37,6 +37,7 @@ #include <string.h> /* common */ +#include "addrsize.h" #include "chartype.h" #include "check.h" #include "coll.h" @@ -61,6 +62,13 @@ +/* Table struct for address sizes of segments */ +typedef struct { + StrBuf Name; + unsigned char AddrSize; +} SegAddrSize_t; + + /* Pointer to the current segment list. Output goes here. */ Segments* CS = 0; @@ -70,6 +78,9 @@ Segments* GS = 0; /* Actual names for the segments */ static StrStack SegmentNames[SEG_COUNT]; +/* Address size for the segments */ +static Collection SegmentAddrSizes; + /* We're using a collection for the stack instead of a linked list. Since ** functions may not be nested (at least in the current implementation), the ** maximum stack depth is 2, so there is not really a need for a better @@ -85,6 +96,85 @@ static Collection SegmentStack = STATIC_COLLECTION_INITIALIZER; +void InitSegAddrSizes (void) +/* Initialize the segment address sizes */ +{ + InitCollection (&SegmentAddrSizes); +} + + + +void DoneSegAddrSizes (void) +/* Free the segment address sizes */ +{ + SegAddrSize_t* A; + int I; + for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) { + A = CollAtUnchecked (&SegmentAddrSizes, I); + SB_Done (&A->Name); + xfree (A); + } + DoneCollection (&SegmentAddrSizes); +} + + + +static SegAddrSize_t* FindSegAddrSize (const char* Name) +/* Find already specified address size for a segment by name. +** Return the found struct or 0 if not found. +*/ +{ + SegAddrSize_t* A; + int I; + for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) { + A = CollAtUnchecked (&SegmentAddrSizes, I); + if (A && strcmp (SB_GetConstBuf (&A->Name), Name) == 0) { + return A; + } + } + return 0; +} + + + +void SetSegAddrSize (const char* Name, unsigned char AddrSize) +/* Set the address size for a segment */ +{ + SegAddrSize_t* A = FindSegAddrSize (Name); + if (!A) { + /* New one */ + A = xmalloc (sizeof (SegAddrSize_t)); + SB_Init (&A->Name); + SB_CopyStr (&A->Name, Name); + SB_Terminate (&A->Name); + CollAppend (&SegmentAddrSizes, A); + } else { + /* Check for mismatching address sizes */ + if (A->AddrSize != AddrSize) { + Warning ("Segment address size changed from last time!"); + } + } + + /* Set the address size anyway */ + A->AddrSize = AddrSize; +} + + + +unsigned char GetSegAddrSize (const char* Name) +/* Get the address size of the given segment. +** Return ADDR_SIZE_INVALID if not found. +*/ +{ + SegAddrSize_t* A = FindSegAddrSize (Name); + if (A) { + return A->AddrSize; + } + return ADDR_SIZE_INVALID; +} + + + void InitSegNames (void) /* Initialize the segment names */ { @@ -143,12 +233,14 @@ static Segments* NewSegments (SymEntry* Func) Segments* S = xmalloc (sizeof (Segments)); /* Initialize the fields */ - S->Text = NewTextSeg (Func); - S->Code = NewCodeSeg (GetSegName (SEG_CODE), Func); - S->Data = NewDataSeg (GetSegName (SEG_DATA), Func); - S->ROData = NewDataSeg (GetSegName (SEG_RODATA), Func); - S->BSS = NewDataSeg (GetSegName (SEG_BSS), Func); - S->CurDSeg = SEG_DATA; + S->Text = NewTextSeg (Func); + S->Code = NewCodeSeg (GetSegName (SEG_CODE), Func); + S->Data = NewDataSeg (GetSegName (SEG_DATA), Func); + S->ROData = NewDataSeg (GetSegName (SEG_RODATA), Func); + S->BSS = NewDataSeg (GetSegName (SEG_BSS), Func); + S->CurDSeg = SEG_DATA; + S->NextLabel = 0; + S->NextDataLabel = 0; /* Return the new struct */ return S; diff --git a/src/cc65/segments.h b/src/cc65/segments.h index f55be688b..91d702df6 100644 --- a/src/cc65/segments.h +++ b/src/cc65/segments.h @@ -86,6 +86,8 @@ struct Segments { struct DataSeg* ROData; /* Readonly data segment */ struct DataSeg* BSS; /* Segment for uninitialized data */ segment_t CurDSeg; /* Current data segment */ + unsigned NextLabel; /* Number to generate unique code labels */ + unsigned NextDataLabel; /* Number to generate unique data labels */ }; /* Pointer to the current segment list. Output goes here. */ @@ -101,6 +103,19 @@ extern Segments* GS; /*****************************************************************************/ +void InitSegAddrSizes (void); +/* Initialize the segment address sizes */ + +void DoneSegAddrSizes (void); +/* Free the segment address sizes */ + +void SetSegAddrSize (const char* Name, unsigned char AddrSize); +/* Set the address size for a segment */ + +unsigned char GetSegAddrSize (const char* Name); +/* Get the address size of the given segment. +** Return ADDR_SIZE_INVALID if not found. +*/ void InitSegNames (void); /* Initialize the segment names */ diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index c61514f43..168574a1b 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -61,12 +61,11 @@ void ShiftExpr (struct ExprDesc* Expr) /* Parse the << and >> operators. */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; token_t Tok; /* The operator token */ - Type* EffType; /* Effective lhs type */ - Type* ResultType; /* Type of the result */ + const Type* EffType; /* Effective lhs type */ + const Type* ResultType; /* Type of the result */ unsigned ExprBits; /* Bits of the lhs operand */ unsigned GenFlags; /* Generator flags */ unsigned ltype; @@ -78,6 +77,10 @@ void ShiftExpr (struct ExprDesc* Expr) while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* All operators that call this function expect an int on the lhs */ if (!IsClassInt (Expr->Type)) { Error ("Integer expression expected"); @@ -139,9 +142,14 @@ void ShiftExpr (struct ExprDesc* Expr) ** the operand, the behaviour is undefined according to the ** standard. */ - if (Expr2.IVal < 0 || Expr2.IVal >= (long) ExprBits) { + if (Expr2.IVal < 0) { - Warning ("Shift count too large for operand type"); + 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; } @@ -173,8 +181,8 @@ void ShiftExpr (struct ExprDesc* Expr) goto Next; } - /* If we're shifting an integer or unsigned to the right, the - ** lhs has a const address, and the shift count is larger than 8, + /* 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 ** signedness, and reduce the shift count by 8. If the remaining ** shift count is zero, we're done. @@ -182,10 +190,10 @@ void ShiftExpr (struct ExprDesc* Expr) if (Tok == TOK_SHR && IsTypeInt (Expr->Type) && ED_IsLVal (Expr) && - (ED_IsLocConst (Expr) || ED_IsLocStack (Expr)) && + ED_IsLocQuasiConst (Expr) && Expr2.IVal >= 8) { - Type* OldType; + const Type* OldType; /* Increase the address by one and decrease the shift count */ ++Expr->IVal; @@ -227,8 +235,8 @@ void ShiftExpr (struct ExprDesc* Expr) } MakeRVal: - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); Next: /* Set the type of the result */ diff --git a/src/cc65/shiftexpr.h b/src/cc65/shiftexpr.h index 2a000fd58..1f1a8c283 100644 --- a/src/cc65/shiftexpr.h +++ b/src/cc65/shiftexpr.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/stackptr.c b/src/cc65/stackptr.c index 1f10e8500..1381d68b4 100644 --- a/src/cc65/stackptr.c +++ b/src/cc65/stackptr.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004-2006 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/stackptr.h b/src/cc65/stackptr.h index 4f90f8dcc..4e5ea732e 100644 --- a/src/cc65/stackptr.h +++ b/src/cc65/stackptr.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004-2006 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c new file mode 100644 index 000000000..1bf8dd4c5 --- /dev/null +++ b/src/cc65/staticassert.c @@ -0,0 +1,105 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 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 "expr.h" +#include "litpool.h" +#include "scanner.h" +#include "staticassert.h" + + + +/*****************************************************************************/ +/* _Static_assert handling functions */ +/*****************************************************************************/ + + + +void ParseStaticAssert () +{ + /* + ** static_assert-declaration ::= + ** _Static_assert ( constant-expression ) ; + ** _Static_assert ( constant-expression , string-literal ) ; + */ + ExprDesc Expr; + int failed; + + /* Skip the _Static_assert token itself */ + CHECK (CurTok.Tok == TOK_STATIC_ASSERT); + NextToken (); + + /* We expect an opening paren */ + if (!ConsumeLParen ()) { + return; + } + + /* Parse assertion condition */ + Expr = NoCodeConstAbsIntExpr (hie1); + failed = !Expr.IVal; + + /* If there is a comma, we also have an error message. The message is optional because we + ** support the C2X syntax with only an expression. + */ + if (CurTok.Tok == TOK_COMMA) { + /* Skip the comma. */ + NextToken (); + + /* String literal */ + if (CurTok.Tok != TOK_SCONST) { + Error ("String literal expected for static_assert message"); + return; + } + + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } + + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + if (!Consume (TOK_SCONST, "String literal expected")) { + return; + } + } else { + /* No message. */ + if (failed) { + Error ("static_assert failed"); + } + } + + /* Closing paren and semi-colon needed */ + ConsumeRParen (); + ConsumeSemi (); +} diff --git a/src/cc65/staticassert.h b/src/cc65/staticassert.h new file mode 100644 index 000000000..cf6314424 --- /dev/null +++ b/src/cc65/staticassert.h @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 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 STATICASSERT_H +#define STATICASSERT_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ParseStaticAssert (void); +/* Handle _Static_assert. These are a C11 feature. */ + + + +/* End of staticassert.h */ + +#endif diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 2d4317ef8..37566a455 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -141,7 +141,7 @@ static long ArrayElementCount (const ArgDesc* Arg) -static void ParseArg (ArgDesc* Arg, Type* Type) +static void ParseArg (ArgDesc* Arg, const Type* Type, ExprDesc* Expr) /* Parse one argument but do not push it onto the stack. Make all fields in ** Arg valid. */ @@ -152,6 +152,10 @@ static void ParseArg (ArgDesc* Arg, Type* Type) /* Remember the required argument type */ Arg->ArgType = Type; + /* Init expression */ + ED_Init (&Arg->Expr); + Arg->Expr.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Read the expression we're going to pass to the function */ MarkedExprWithCheck (hie1, &Arg->Expr); @@ -208,9 +212,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Handle the memcpy function */ { /* Argument types: (void*, const void*, size_t) */ - static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) }; - static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) }; - static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) }; + static const Type* Arg1Type = type_void_p; + static const Type* Arg2Type = type_c_void_p; + static const Type* Arg3Type = type_size_t; ArgDesc Arg1, Arg2, Arg3; unsigned ParamSize = 0; @@ -218,14 +222,14 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) int Offs; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); ConsumeComma (); /* Argument #2 */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); g_push (Arg2.Flags, Arg2.Expr.IVal); GetCodePos (&Arg2.End); ParamSize += SizeOf (Arg2Type); @@ -236,11 +240,14 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg3, Arg3Type); + ParseArg (&Arg3, Arg3Type, Expr); if (Arg3.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg3.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg3.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_memcpy, ParamSize); @@ -343,7 +350,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && - !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); + !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -421,7 +428,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && - !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); + !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ Offs = ED_GetStackOffs (&Arg2.Expr, 0); @@ -520,7 +527,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("lda ptr1"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* Bail out, no need for further processing */ @@ -529,7 +536,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -549,9 +556,9 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Handle the memset function */ { /* Argument types: (void*, int, size_t) */ - static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) }; - static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) }; - static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) }; + static const Type* Arg1Type = type_void_p; + static const Type* Arg2Type = type_int; + static const Type* Arg3Type = type_size_t; ArgDesc Arg1, Arg2, Arg3; int MemSet = 1; /* Use real memset if true */ @@ -559,7 +566,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) unsigned Label; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); @@ -568,7 +575,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Argument #2. This argument is special in that we will call another ** function if it is a constant zero. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) { /* Don't call memset, call bzero instead */ MemSet = 0; @@ -585,11 +592,14 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg3, Arg3Type); + ParseArg (&Arg3, Arg3Type, Expr); if (Arg3.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg3.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + 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); @@ -743,7 +753,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("lda ptr1"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* Bail out, no need for further processing */ @@ -752,7 +762,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -772,8 +782,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Handle the strcmp function */ { /* Argument types: (const char*, const char*) */ - static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; - static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; + static const Type* Arg1Type = type_c_char_p; + static const Type* Arg2Type = type_c_char_p; ArgDesc Arg1, Arg2; unsigned ParamSize = 0; @@ -782,18 +792,14 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) int IsArray; int Offs; - /* Setup the argument type string */ - Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST; - Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; - /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); ParamSize += SizeOf (Arg1Type); ConsumeComma (); /* Argument #2. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); /* Since strcmp is a fastcall function, we must load the ** arg into the primary if it is not already there. This parameter is @@ -804,6 +810,9 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) LoadExpr (CF_NONE, &Arg2.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg2.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_strcmp, ParamSize); @@ -823,8 +832,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.LVal) == 1 && - GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { + GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') { /* Drop the generated code so we have the first argument in the ** primary @@ -832,7 +841,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) RemoveCode (&Arg1.Push); /* We don't need the literal any longer */ - ReleaseLiteral (Arg2.Expr.LVal); + ReleaseLiteral (Arg2.Expr.V.LVal); /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. @@ -955,7 +964,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* We expect the closing brace */ @@ -974,20 +983,16 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Handle the strcpy function */ { /* Argument types: (char*, const char*) */ - static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) }; - static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; + static const Type* Arg1Type = type_char_p; + static const Type* Arg2Type = type_c_char_p; ArgDesc Arg1, Arg2; unsigned ParamSize = 0; long ECount; unsigned L1; - /* Setup the argument type string */ - Arg1Type[1].C = GetDefaultChar (); - Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; - /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); @@ -998,11 +1003,14 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); if (Arg2.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg2.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg2.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_strcpy, ParamSize); @@ -1069,7 +1077,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && - !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); + !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg2.Expr, 0); @@ -1116,7 +1124,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && - !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); + !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -1153,7 +1161,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -1172,7 +1180,7 @@ ExitPoint: static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Handle the strlen function */ { - static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; + static const Type* ArgType = type_c_char_p; ExprDesc Arg; int IsArray; int IsPtr; @@ -1180,12 +1188,15 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) long ECount; unsigned L; - /* Setup the argument type string */ - ArgType[1].C = GetDefaultChar () | T_QUAL_CONST; + ED_Init (&Arg); + Arg.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Evaluate the parameter */ hie1 (&Arg); + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg); + /* Check if the argument is an array. If so, remember the element count. ** Otherwise set the element count to undefined. */ @@ -1221,10 +1232,10 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); /* We don't need the literal any longer */ - ReleaseLiteral (Arg.LVal); + ReleaseLiteral (Arg.V.LVal); /* Bail out, no need for further improvements */ goto ExitPoint; @@ -1250,7 +1261,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1279,7 +1290,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("ldx #$00"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1304,7 +1315,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1333,7 +1344,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1348,7 +1359,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("jsr _%s", Func_strlen); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; ExitPoint: diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 657bc9963..022a8475c 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -163,7 +163,7 @@ static int IfStatement (void) TestResult = TestInParens (Label1, 0); /* Parse the if body */ - GotBreak = Statement (0); + GotBreak = AnyStatement (0); /* Else clause present? */ if (CurTok.Tok != TOK_ELSE) { @@ -187,7 +187,7 @@ static int IfStatement (void) /* If the if expression was always true, the code in the else branch ** is never executed. Output a warning if this is the case. */ - if (TestResult == TESTEXPR_TRUE) { + if (TestResult == TESTEXPR_TRUE && IS_Get (&WarnUnreachableCode)) { Warning ("Unreachable code"); } @@ -195,7 +195,7 @@ static int IfStatement (void) g_defcodelabel (Label1); /* Total break only if both branches had a break. */ - GotBreak &= Statement (0); + GotBreak &= AnyStatement (0); /* Generate the label for the else clause */ g_defcodelabel (Label2); @@ -225,7 +225,7 @@ static void DoStatement (void) g_defcodelabel (LoopLabel); /* Parse the loop body */ - Statement (0); + AnyStatement (0); /* Output the label for a continue */ g_defcodelabel (ContinueLabel); @@ -283,7 +283,7 @@ static void WhileStatement (void) g_defcodelabel (LoopLabel); /* Loop body */ - Statement (&PendingToken); + AnyStatement (&PendingToken); /* Emit the while condition label */ g_defcodelabel (CondLabel); @@ -309,26 +309,52 @@ static void WhileStatement (void) static void ReturnStatement (void) /* Handle the 'return' statement */ { - ExprDesc Expr; + ExprDesc Expr; + const Type* ReturnType; + ED_Init (&Expr); NextToken (); if (CurTok.Tok != TOK_SEMI) { /* Evaluate the return expression */ hie0 (&Expr); - /* If we return something in a void function, print an error and - ** ignore the value. Otherwise convert the value to the type of the - ** return. + /* If we return something in a function with void or incomplete return + ** type, print an error and ignore the value. Otherwise convert the + ** value to the type of the return. */ if (F_HasVoidReturn (CurrentFunc)) { - Error ("Returning a value in function with return type void"); + Error ("Returning a value in function with return type 'void'"); } else { - /* Convert the return value to the type of the function result */ - TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); - /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); + /* Check the return type first */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* Avoid excess errors */ + if (ErrorCount == 0) { + Error ("Returning a value in function with incomplete return type"); + } + } else { + /* Convert the return value to the type of the function result */ + TypeConversion (&Expr, ReturnType); + + /* Load the value into the primary */ + if (IsClassStruct (Expr.Type)) { + /* Handle struct/union specially */ + ReturnType = GetStructReplacementType (Expr.Type); + if (ReturnType == Expr.Type) { + Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); + } + LoadExpr (TypeOf (ReturnType), &Expr); + + } else { + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + } + } + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EAX, &Expr); } } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) { @@ -412,8 +438,6 @@ static void ContinueStatement (void) static void ForStatement (void) /* Handle a 'for' statement */ { - ExprDesc lval1; - ExprDesc lval3; int HaveIncExpr; CodeMark IncExprStart; CodeMark IncExprEnd; @@ -438,6 +462,10 @@ static void ForStatement (void) /* Parse the initializer expression */ if (CurTok.Tok != TOK_SEMI) { + /* The value of the expression is unused */ + ExprDesc lval1; + ED_Init (&lval1); + lval1.Flags = E_NEED_NONE; Expression0 (&lval1); } ConsumeSemi (); @@ -463,6 +491,10 @@ static void ForStatement (void) /* Parse the increment expression */ HaveIncExpr = (CurTok.Tok != TOK_RPAREN); if (HaveIncExpr) { + /* The value of the expression is unused */ + ExprDesc lval3; + ED_Init (&lval3); + lval3.Flags = E_NEED_NONE; Expression0 (&lval3); } @@ -477,7 +509,7 @@ static void ForStatement (void) /* Loop body */ g_defcodelabel (BodyLabel); - Statement (&PendingToken); + AnyStatement (&PendingToken); /* If we had an increment expression, move the code to the bottom of ** the loop. In this case we don't need to jump there at the end of @@ -504,17 +536,20 @@ static void ForStatement (void) -static int CompoundStatement (void) +static int CompoundStatement (int* PendingToken) /* Compound statement. Allow any number of statements inside braces. The ** function returns true if the last statement was a break or return. */ { - int GotBreak; + int GotBreak = 0; /* Remember the stack at block entry */ int OldStack = StackPtr; unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); + /* Skip '{' */ + NextToken (); + /* Enter a new lexical level */ EnterBlockLevel (); @@ -522,16 +557,15 @@ static int CompoundStatement (void) DeclareLocals (); /* Now process statements in this block */ - GotBreak = 0; while (CurTok.Tok != TOK_RCURLY) { if (CurTok.Tok != TOK_CEOF) { - GotBreak = Statement (0); + GotBreak = AnyStatement (0); } else { break; } } - /* Clean up the stack. */ + /* Clean up the stack if the codeflow may reach the end */ if (!GotBreak) { g_space (StackPtr - OldStack); } @@ -551,12 +585,80 @@ static int CompoundStatement (void) /* Leave the lexical level */ LeaveBlockLevel (); + /* Skip '}' */ + CheckTok (TOK_RCURLY, "'}' expected", PendingToken); + return GotBreak; } -int Statement (int* PendingToken) +static void Statement (int* PendingToken) +/* Single-line statement */ +{ + ExprDesc Expr; + unsigned PrevErrorCount; + CodeMark Start, End; + + /* Remember the current error count and code position */ + PrevErrorCount = ErrorCount; + GetCodePos (&Start); + + /* Actual statement */ + ED_Init (&Expr); + Expr.Flags |= E_NEED_NONE; + Expression0 (&Expr); + + /* If the statement didn't generate code, and is not of type + ** void, emit a warning. + */ + GetCodePos (&End); + if (!ED_YetToLoad (&Expr) && + !ED_MayHaveNoEffect (&Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + CheckSemi (PendingToken); +} + + + +static int ParseAnyLabels (void) +/* Return -1 if there are any labels with a statement */ +{ + unsigned PrevErrorCount = ErrorCount; + int HasLabels = 0; + for (;;) { + if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { + /* C 'goto' label */ + DoLabel (); + } else if (CurTok.Tok == TOK_CASE) { + /* C 'case' label */ + CaseLabel (); + } else if (CurTok.Tok == TOK_DEFAULT) { + /* C 'default' label */ + DefaultLabel (); + } else { + /* No labels */ + break; + } + HasLabels = 1; + } + + if (HasLabels) { + if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) { + return -1; + } + } + + return 0; +} + + + +int AnyStatement (int* PendingToken) /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or @@ -566,37 +668,27 @@ int Statement (int* PendingToken) ** NULL, the function will skip the token. */ { - ExprDesc Expr; - int GotBreak; - CodeMark Start, End; - /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; } - /* Check for a label. A label is always part of a statement, it does not + /* Handle any labels. A label is always part of a statement, it does not ** replace one. */ - while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { - /* Handle the label */ - DoLabel (); - if (CheckLabelWithoutStatement ()) { - return 0; - } + if (ParseAnyLabels ()) { + return 0; } switch (CurTok.Tok) { - case TOK_LCURLY: - NextToken (); - GotBreak = CompoundStatement (); - CheckTok (TOK_RCURLY, "'{' expected", PendingToken); - return GotBreak; - case TOK_IF: return IfStatement (); + case TOK_SWITCH: + SwitchStatement (); + break; + case TOK_WHILE: WhileStatement (); break; @@ -605,10 +697,15 @@ int Statement (int* PendingToken) DoStatement (); break; - case TOK_SWITCH: - SwitchStatement (); + case TOK_FOR: + ForStatement (); break; + case TOK_GOTO: + GotoStatement (); + CheckSemi (PendingToken); + return 1; + case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); @@ -624,55 +721,22 @@ int Statement (int* PendingToken) CheckSemi (PendingToken); return 1; - case TOK_FOR: - ForStatement (); - break; - - case TOK_GOTO: - GotoStatement (); - CheckSemi (PendingToken); - return 1; - - case TOK_SEMI: - /* Ignore it */ - CheckSemi (PendingToken); - break; - case TOK_PRAGMA: DoPragma (); break; - case TOK_CASE: - CaseLabel (); - CheckLabelWithoutStatement (); + case TOK_SEMI: + /* Empty statement. Ignore it */ + CheckSemi (PendingToken); break; - case TOK_DEFAULT: - DefaultLabel (); - CheckLabelWithoutStatement (); - break; + case TOK_LCURLY: + return CompoundStatement (PendingToken); default: - /* Remember the current code position */ - GetCodePos (&Start); - /* Actual statement */ - ExprWithCheck (hie0, &Expr); - /* Load the result only if it is an lvalue and the type is - ** marked as volatile. Otherwise the load is useless. - */ - if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) { - LoadExpr (CF_NONE, &Expr); - } - /* If the statement didn't generate code, and is not of type - ** void, emit a warning. - */ - GetCodePos (&End); - if (CodeRangeIsEmpty (&Start, &End) && - !IsTypeVoid (Expr.Type) && - IS_Get (&WarnNoEffect)) { - Warning ("Statement has no effect"); - } - CheckSemi (PendingToken); + /* Simple statement */ + Statement (PendingToken); + break; } return 0; } diff --git a/src/cc65/stmt.h b/src/cc65/stmt.h index 04ef728a3..8cff99d92 100644 --- a/src/cc65/stmt.h +++ b/src/cc65/stmt.h @@ -44,7 +44,7 @@ -int Statement (int* PendingToken); +int AnyStatement (int* PendingToken); /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 4a3730283..ee0bd1a85 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -97,7 +97,6 @@ void SwitchStatement (void) SwitchCtrl* OldSwitch; /* Pointer to old switch control data */ SwitchCtrl SwitchData; /* New switch data */ - /* Eat the "switch" token */ NextToken (); @@ -105,6 +104,8 @@ void SwitchStatement (void) ** integer type. */ ConsumeLParen (); + + ED_Init (&SwitchExpr); Expression0 (&SwitchExpr); if (!IsClassInt (SwitchExpr.Type)) { Error ("Switch quantity is not an integer"); @@ -132,7 +133,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = UnqualifiedType (SwitchExpr.Type[0].C); + SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; @@ -147,7 +148,7 @@ void SwitchStatement (void) /* Parse the following statement, which may actually be a compound ** statement if there is a curly brace at the current input position */ - HaveBreak = Statement (&RCurlyBrace); + HaveBreak = AnyStatement (&RCurlyBrace); /* Check if we had any labels */ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { @@ -211,12 +212,11 @@ void CaseLabel (void) long Val; /* Case label value */ unsigned CodeLabel; /* Code label for this case */ - /* Skip the "case" token */ NextToken (); /* Read the selector expression */ - ConstAbsIntExpr (hie1, &CaseExpr); + CaseExpr = NoCodeConstAbsIntExpr (hie1); Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 18cc026ea..cc790c931 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -104,29 +104,38 @@ void FreeSymEntry (SymEntry* E) void DumpSymEntry (FILE* F, const SymEntry* E) /* Dump the given symbol table entry to the file in readable form */ { - static const struct { + typedef const struct { const char* Name; unsigned Val; - } Flags [] = { - /* Beware: Order is important! */ + } SCFlagTable; + + static SCFlagTable ESUTypes[] = { { "SC_TYPEDEF", SC_TYPEDEF }, - { "SC_BITFIELD", SC_BITFIELD }, - { "SC_STRUCTFIELD", SC_STRUCTFIELD }, { "SC_UNION", SC_UNION }, { "SC_STRUCT", SC_STRUCT }, - { "SC_AUTO", SC_AUTO }, - { "SC_REGISTER", SC_REGISTER }, - { "SC_STATIC", SC_STATIC }, - { "SC_EXTERN", SC_EXTERN }, { "SC_ENUM", SC_ENUM }, + }; + + static SCFlagTable Types[] = { + { "SC_BITFIELD", SC_BITFIELD }, + { "SC_STRUCTFIELD", SC_STRUCTFIELD }, + { "SC_ENUMERATOR", SC_ENUMERATOR }, { "SC_CONST", SC_CONST }, { "SC_LABEL", SC_LABEL }, { "SC_PARAM", SC_PARAM }, { "SC_FUNC", SC_FUNC }, + }; + + static SCFlagTable Storages[] = { + { "SC_AUTO", SC_AUTO }, + { "SC_REGISTER", SC_REGISTER }, + { "SC_STATIC", SC_STATIC }, + { "SC_EXTERN", SC_EXTERN }, { "SC_STORAGE", SC_STORAGE }, + { "SC_ZEROPAGE", SC_ZEROPAGE }, + { "SC_DECL", SC_DECL }, { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, - { "SC_ZEROPAGE", SC_ZEROPAGE }, }; unsigned I; @@ -143,10 +152,28 @@ void DumpSymEntry (FILE* F, const SymEntry* E) /* Print the flags */ SymFlags = E->Flags; fprintf (F, " Flags:"); - for (I = 0; I < sizeof (Flags) / sizeof (Flags[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Flags[I].Val) == Flags[I].Val) { - SymFlags &= ~Flags[I].Val; - fprintf (F, " %s", Flags[I].Name); + /* Enum, struct, union and typedefs */ + if ((SymFlags & SC_ESUTYPEMASK) != 0) { + for (I = 0; I < sizeof (ESUTypes) / sizeof (ESUTypes[0]); ++I) { + if ((SymFlags & SC_ESUTYPEMASK) == ESUTypes[I].Val) { + SymFlags &= ~SC_ESUTYPEMASK; + fprintf (F, " %s", ESUTypes[I].Name); + break; + } + } + } + /* Other type flags */ + for (I = 0; I < sizeof (Types) / sizeof (Types[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Types[I].Val) == Types[I].Val) { + SymFlags &= ~Types[I].Val; + fprintf (F, " %s", Types[I].Name); + } + } + /* Storage flags */ + for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Storages[I].Val) == Storages[I].Val) { + SymFlags &= ~Storages[I].Val; + fprintf (F, " %s", Storages[I].Name); } } if (SymFlags != 0) { @@ -251,7 +278,40 @@ void CvtRegVarToAuto (SymEntry* Sym) -void ChangeSymType (SymEntry* Entry, Type* T) +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) /* Change the type of the given symbol */ { TypeFree (Entry->Type); diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 62bf0b0e7..bb87c7472 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -69,40 +69,45 @@ struct CodeEntry; /* Storage classes and flags */ -#define SC_AUTO 0x0001U /* Auto variable */ -#define SC_REGISTER 0x0002U /* Register variable */ -#define SC_STATIC 0x0004U /* Static */ -#define SC_EXTERN 0x0008U /* Extern linkage */ - -#define SC_ENUM 0x0030U /* An enum */ -#define SC_CONST 0x0020U /* A numeric constant with a type */ -#define SC_LABEL 0x0040U /* A goto label */ -#define SC_PARAM 0x0080U /* A function parameter */ -#define SC_FUNC 0x0100U /* A function */ +#define SC_NONE 0x0000U /* Nothing */ +#define SC_STRUCT 0x0001U /* Struct */ +#define SC_UNION 0x0002U /* Union */ +#define SC_ENUM 0x0003U /* Enum */ +#define SC_TYPEDEF 0x0004U /* Typedef */ +#define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ +#define SC_ENUMERATOR 0x0008U /* An enumerator */ +#define SC_BITFIELD 0x0010U /* A bit-field inside a struct or union */ +#define SC_TYPEMASK 0x001FU /* Mask for above types */ +#define SC_FUNC 0x0020U /* A function */ +#define SC_LABEL 0x0040U /* A goto code label */ +#define SC_CONST 0x0080U /* A numeric constant with a type */ +#define SC_PARAM 0x0100U /* A function parameter */ #define SC_DEFTYPE 0x0200U /* Parameter has default type (=int, old style) */ -#define SC_STORAGE 0x0400U /* Symbol with associated storage */ -#define SC_DEFAULT 0x0800U /* Flag: default storage class was used */ +#define SC_STRUCTFIELD 0x0400U /* Struct or union field */ + +#define SC_ZEROPAGE 0x0800U /* Symbol marked as zeropage */ #define SC_DEF 0x1000U /* Symbol is defined */ #define SC_REF 0x2000U /* Symbol is referenced */ +#define SC_DECL 0x4000U /* Symbol is declared in global scope */ +#define SC_STORAGE 0x8000U /* Symbol with associated storage */ -#define SC_TYPE 0x4000U /* This is a type, struct, typedef, etc. */ -#define SC_STRUCT 0x4001U /* Struct */ -#define SC_UNION 0x4002U /* Union */ -#define SC_STRUCTFIELD 0x4003U /* Struct or union field */ -#define SC_BITFIELD 0x4004U /* A bit-field inside a struct or union */ -#define SC_TYPEDEF 0x4005U /* A typedef */ -#define SC_TYPEMASK 0x400FU /* Mask for above types */ +#define SC_AUTO 0x010000U /* Auto variable */ +#define SC_REGISTER 0x020000U /* Register variable */ +#define SC_STATIC 0x040000U /* Static - not to be confused with other *_STATIC */ +#define SC_EXTERN 0x080000U /* Extern linkage */ +#define SC_STORAGEMASK 0x0F0000U /* Storage type mask */ -#define SC_ZEROPAGE 0x8000U /* Symbol marked as zeropage */ +#define SC_HAVEATTR 0x100000U /* Symbol has attributes */ -#define SC_HAVEATTR 0x10000U /* Symbol has attributes */ - -#define SC_GOTO 0x20000U -#define SC_SPADJUSTMENT 0x40000U -#define SC_GOTO_IND 0x80000U /* Indirect goto */ +#define SC_GOTO 0x200000U +#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_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ @@ -136,6 +141,14 @@ struct SymEntry { /* Offset for locals or struct members */ 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; @@ -161,18 +174,17 @@ struct SymEntry { struct { struct SymTable* SymTab; /* Member symbol table */ unsigned Size; /* Size of the union/struct */ + unsigned ACount; /* Count of anonymous fields */ } S; - /* Data for bit fields */ + /* Data for enums */ struct { - unsigned Offs; /* Byte offset into struct */ - unsigned BitOffs; /* Bit offset into storage unit */ - unsigned BitWidth; /* Width in bits */ - } B; + struct SymTable* SymTab; /* Member symbol table */ + const Type* Type; /* Underlying type */ + } E; /* Data for functions */ struct { - struct FuncDesc* Func; /* Function descriptor */ struct Segments* Seg; /* Segments for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ } F; @@ -214,10 +226,10 @@ INLINE int SymIsBitField (const SymEntry* Sym) INLINE int SymIsTypeDef (const SymEntry* Sym) /* Return true if the given entry is a typedef entry */ { - return ((Sym->Flags & SC_TYPEDEF) == SC_TYPEDEF); + return ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF); } #else -# define SymIsTypeDef(Sym) (((Sym)->Flags & SC_TYPEDEF) == SC_TYPEDEF) +# define SymIsTypeDef(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_TYPEDEF) #endif #if defined(HAVE_INLINE) @@ -245,15 +257,25 @@ INLINE int SymIsRegVar (const SymEntry* Sym) /* Return true if the given entry is a register variable */ /* ### HACK! Fix the ugly type flags! */ { - return ((Sym->Flags & (SC_REGISTER|SC_TYPE)) == SC_REGISTER); + return ((Sym->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_TYPE)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER) #endif int SymIsOutputFunc (const SymEntry* Sym); /* Return true if this is a function that must be output */ +#if defined(HAVE_INLINE) +INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) +/* Return true if the given entry has a flexible array member */ +{ + return ((Sym->Flags & SC_HAVEFAM) == SC_HAVEFAM); +} +#else +# define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM) +#endif + #if defined(HAVE_INLINE) INLINE const char* SymGetAsmName (const SymEntry* Sym) /* Return the assembler label name for the symbol (beware: may be NULL!) */ @@ -286,7 +308,17 @@ void SymSetAsmName (SymEntry* Sym); void CvtRegVarToAuto (SymEntry* Sym); /* Convert a register variable to an auto variable */ -void ChangeSymType (SymEntry* Entry, Type* T); +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); /* Change the type of the given symbol */ void ChangeAsmName (SymEntry* Entry, const char* NewAsmName); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index f19a251bd..5d7bd1436 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -45,6 +45,7 @@ #include "xmalloc.h" /* cc65 */ +#include "anonname.h" #include "asmcode.h" #include "asmlabel.h" #include "codegen.h" @@ -52,13 +53,14 @@ #include "declare.h" #include "error.h" #include "funcdesc.h" +#include "function.h" #include "global.h" +#include "input.h" #include "stackptr.h" #include "symentry.h" #include "typecmp.h" +#include "typeconv.h" #include "symtab.h" -#include "function.h" -#include "input.h" @@ -91,8 +93,10 @@ static SymTable* SymTab0 = 0; static SymTable* SymTab = 0; static SymTable* TagTab0 = 0; static SymTable* TagTab = 0; +static SymTable* FieldTab = 0; static SymTable* LabelTab = 0; static SymTable* SPAdjustTab = 0; +static SymTable* FailSafeTab = 0; /* For errors */ /*****************************************************************************/ @@ -141,7 +145,6 @@ static void FreeSymTable (SymTable* S) } - /*****************************************************************************/ /* Check symbols in a table */ /*****************************************************************************/ @@ -170,9 +173,13 @@ static void CheckSymTable (SymTable* Tab) if (IS_Get (&WarnUnusedParam)) { Warning ("Parameter '%s' is never used", Entry->Name); } + } else if (Flags & SC_FUNC) { + if (IS_Get (&WarnUnusedFunc)) { + Warning ("Function '%s' is defined but never used", Entry->Name); + } } else { if (IS_Get (&WarnUnusedVar)) { - Warning ("'%s' is defined but never used", Entry->Name); + Warning ("Variable '%s' is defined but never used", Entry->Name); } } } @@ -186,7 +193,7 @@ static void CheckSymTable (SymTable* Tab) } else if (!SymIsRef (Entry)) { /* Defined but not used */ if (IS_Get (&WarnUnusedLabel)) { - Warning ("'%s' is defined but never used", Entry->Name); + Warning ("Label '%s' is defined but never used", Entry->Name); } } } @@ -228,6 +235,9 @@ void EnterGlobalLevel (void) /* Create and assign the table of SP adjustment symbols */ SPAdjustTab = NewSymTable (SYMTAB_SIZE_GLOBAL); + + /* Create and assign the table of fictitious symbols used with errors */ + FailSafeTab = NewSymTable (SYMTAB_SIZE_GLOBAL); } @@ -386,9 +396,9 @@ void EnterStructLevel (void) ** nested in struct scope are NOT local to the struct but visible in the ** outside scope. So we will NOT create a new struct or enum table. */ - S = NewSymTable (SYMTAB_SIZE_BLOCK); - S->PrevTab = SymTab; - SymTab = S; + S = NewSymTable (SYMTAB_SIZE_STRUCT); + S->PrevTab = FieldTab; + FieldTab = S; } @@ -397,7 +407,7 @@ void LeaveStructLevel (void) /* Leave a nested block for a struct definition */ { /* Don't delete the table */ - SymTab = SymTab->PrevTab; + FieldTab = FieldTab->PrevTab; } @@ -487,31 +497,52 @@ SymEntry* FindTagSym (const char* Name) -SymEntry* FindStructField (const Type* T, const char* Name) -/* Find a struct field in the fields list */ +SymEntry FindStructField (const Type* T, const char* Name) +/* Find a struct/union field in the fields list. +** Return the info about the found field symbol filled in an entry struct by +** value, or an empty entry struct if the field is not found. +*/ { - SymEntry* Field = 0; + SymEntry* Entry = 0; + SymEntry Field; + int Offs = 0; - /* The given type may actually be a pointer to struct */ + /* The given type may actually be a pointer to struct/union */ if (IsTypePtr (T)) { ++T; } - /* Non-structs do not have any struct fields... */ + /* Only structs/unions have struct/union fields... */ if (IsClassStruct (T)) { /* Get a pointer to the struct/union type */ - const SymEntry* Struct = GetSymEntry (T); + const SymEntry* Struct = GetESUSymEntry (T); CHECK (Struct != 0); - /* Now search in the struct symbol table. Beware: The table may not - ** exist. + /* Now search in the struct/union symbol table. Beware: The table may + ** not exist. */ if (Struct->V.S.SymTab) { - Field = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + Entry = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + + if (Entry != 0) { + Offs = Entry->V.Offs; + } + + while (Entry != 0 && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the real field */ + Entry = Entry->V.A.Field; + } } } + if (Entry != 0) { + Field = *Entry; + Field.V.Offs = Offs; + } else { + memset (&Field, 0, sizeof(SymEntry)); + } + return Field; } @@ -523,6 +554,129 @@ SymEntry* FindStructField (const Type* T, const char* Name) +static int IsDistinctRedef (const Type* lhst, const Type* rhst, typecmpcode_t Code, typecmpflag_t Flags) +/* Return if type compatibility result is "worse" than Code or if any bit of +** qualifier Flags is set. +*/ +{ + typecmp_t Result = TypeCmp (lhst, rhst); + if (Result.C < Code || (Result.F & TCF_MASK_QUAL & Flags) != 0) { + return 1; + } + return 0; +} + + +static int HandleSymRedefinition (SymEntry* Entry, 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; + unsigned SCType = Flags & SC_TYPEMASK; + + /* Some symbols may be redeclared if certain requirements are met */ + if (E_SCType == SC_TYPEDEF) { + + /* 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; + } + } else { + Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } + + } else if ((Entry->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 + ** an actual function definition follows. Be sure not to use the + ** new descriptor if it contains a function declaration with an + ** empty parameter list. + */ + if (IsTypeFunc (T)) { + + /* Check for duplicate function definitions */ + if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { + Error ("Body for function '%s' has already been defined", + Entry->Name); + Entry = 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); + Entry = 0; + } else { + /* Refine the existing composite prototype with this new + ** one. + */ + RefineFuncDesc (Entry->Type, T); + } + } + + } else { + Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } + + } else { + + /* Redeclarations of ESU types are checked elsewhere */ + if (IsTypeArray (T) && IsTypeArray (E_Type)) { + + /* Get the array sizes */ + long Size = GetElementCount (T); + long ESize = GetElementCount (E_Type); + + /* If we are handling arrays, the old entry or the new entry may be + ** an incomplete declaration. Accept this, and if the exsting entry + ** is incomplete, complete it. + */ + 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; + } else { + /* Check if we have a size in the existing definition */ + if (ESize == UNSPECIFIED) { + /* Existing, size not given, use size from new def */ + SetElementCount (E_Type, Size); + } + } + + } else { + + /* New type must be equivalent */ + if (SCType != E_SCType) { + Error ("Redefinition of '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } else if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) { + Error ("Conflicting types for '%s'", Entry->Name); + Entry = 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; + } + } + } + + /* Return if there are any errors */ + return Entry == 0; +} + + + static void AddSymEntry (SymTable* T, SymEntry* S) /* Add a symbol to a symbol table */ { @@ -551,44 +705,76 @@ static void AddSymEntry (SymTable* T, SymEntry* S) -SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab) -/* Add a struct/union entry and return it */ +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags) +/* Add an enum entry and return it */ { + SymTable* CurTagTab = TagTab; SymEntry* Entry; - /* Type must be struct or union */ - PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); + if ((Flags & SC_FICTITIOUS) == 0) { + /* Do we have an entry with this name already? */ + Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + } else { + /* Add a fictitious symbol in the fail-safe table */ + Entry = 0; + CurTagTab = FailSafeTab; + } - /* Do we have an entry with this name already? */ - Entry = FindSymInTable (TagTab, Name, HashStr (Name)); if (Entry) { /* We do have an entry. This may be a forward, so check it. */ - if ((Entry->Flags & SC_TYPEMASK) != Type) { - /* Existing symbol is not a struct */ + if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) { + /* Existing symbol is not an enum */ Error ("Symbol '%s' is already different kind", Name); - } else if (Size > 0 && Entry->V.S.Size > 0) { - /* Both structs are definitions. */ - Error ("Multiple definition for '%s'", Name); - } else { - /* Define the struct size if it is given */ - if (Size > 0) { - Entry->V.S.SymTab = Tab; - Entry->V.S.Size = Size; + Entry = 0; + } else if (Type != 0) { + /* Define the struct size if the underlying type is given. */ + if (Entry->V.E.Type != 0) { + /* Both are definitions. */ + Error ("Multiple definition for 'enum %s'", Name); + Entry = 0; + } else { + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + Entry->Flags &= ~SC_DECL; + Entry->Flags |= SC_DEF; + + /* Remember this is the first definition of this type */ + if (DSFlags != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } } } - } else { + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + CurTagTab = FailSafeTab; + } + } + + if (Entry == 0) { /* Create a new entry */ - Entry = NewSymEntry (Name, Type); + Entry = NewSymEntry (Name, SC_ENUM); - /* Set the struct data */ - Entry->V.S.SymTab = Tab; - Entry->V.S.Size = Size; + /* Set the enum type data */ + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + + if (Type != 0) { + Entry->Flags |= SC_DEF; + } + + /* Remember this is the first definition of this type */ + if (CurTagTab != FailSafeTab && DSFlags != 0) { + if ((Entry->Flags & SC_DEF) != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } + *DSFlags |= DS_NEW_TYPE_DECL; + } /* Add it to the current table */ - AddSymEntry (TagTab, Entry); + AddSymEntry (CurTagTab, Entry); } /* Return the entry */ @@ -597,29 +783,125 @@ SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable -SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsigned BitWidth) +SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags) +/* Add a struct/union entry and return it */ +{ + SymTable* CurTagTab = TagTab; + SymEntry* Entry; + unsigned Type = (Flags & SC_TYPEMASK); + + /* Type must be struct or union */ + PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); + + if ((Flags & SC_FICTITIOUS) == 0) { + /* Do we have an entry with this name already? */ + Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + } else { + /* Add a fictitious symbol in the fail-safe table */ + Entry = 0; + CurTagTab = FailSafeTab; + } + + if (Entry) { + + /* We do have an entry. This may be a forward, so check it. */ + if ((Entry->Flags & SC_TYPEMASK) != Type) { + /* 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) { + /* Both structs are definitions. */ + if (Type == SC_STRUCT) { + Error ("Multiple definition for 'struct %s'", Name); + } else { + Error ("Multiple definition for 'union %s'", Name); + } + Entry = 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; + + /* Remember this is the first definition of this type */ + if (DSFlags != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } + } + } + + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + CurTagTab = FailSafeTab; + } + } + + if (Entry == 0) { + + /* Create a new entry */ + Entry = NewSymEntry (Name, Flags); + + /* Set the struct data */ + Entry->V.S.SymTab = Tab; + Entry->V.S.Size = Size; + + /* Remember this is the first definition of this type */ + if (CurTagTab != FailSafeTab && DSFlags != 0) { + if ((Entry->Flags & SC_DEF) != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } + *DSFlags |= DS_NEW_TYPE_DECL; + } + + /* Add it to the current tag table */ + AddSymEntry (CurTagTab, Entry); + } + + /* Return the entry */ + return Entry; +} + + + +SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, + unsigned BitOffs, unsigned BitWidth, int SignednessSpecified) /* Add a bit field to the local symbol table and return the symbol entry */ { /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (FieldTab, Name, HashStr (Name)); if (Entry) { /* We have a symbol with this name already */ - Error ("Multiple definition for '%s'", Name); + Error ("Multiple definition for bit-field '%s'", Name); } else { /* Create a new entry */ Entry = NewSymEntry (Name, SC_BITFIELD); - /* Set the symbol attributes. Bit-fields are always of type unsigned */ - Entry->Type = type_uint; - Entry->V.B.Offs = Offs; - Entry->V.B.BitOffs = BitOffs; - Entry->V.B.BitWidth = BitWidth; + /* Set the symbol attributes. Bit-fields are always integral types. */ + Entry->Type = NewBitFieldType (T, BitOffs, BitWidth); + Entry->V.Offs = Offs; + + if (!SignednessSpecified) { + /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, + ** since this is allowed for bit-fields and avoids sign-extension, so is much faster. + ** enums set SignednessSpecified to 1 to avoid this adjustment. Character types + ** actually distinguish 3 types of char; char may either be signed or unsigned, which + ** is controlled by `--signed-chars`. In bit-fields, however, we perform the same + ** `char -> unsigned char` adjustment that is performed with other integral types. + */ + CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || + IsTypeChar (Entry->Type)); + Entry->Type[0].C &= ~T_MASK_SIGN; + Entry->Type[0].C |= T_SIGN_UNSIGNED; + Entry->Type[1].C &= ~T_MASK_SIGN; + Entry->Type[1].C |= T_SIGN_UNSIGNED; + } /* Add the entry to the symbol table */ - AddSymEntry (SymTab, Entry); + AddSymEntry (FieldTab, Entry); } @@ -632,16 +914,13 @@ SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsign SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val) /* Add an constant symbol to the symbol table and return it */ { - /* Enums must be inserted in the global symbol table */ - SymTable* Tab = ((Flags & SC_ENUM) == SC_ENUM)? SymTab0 : SymTab; - /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); if (Entry) { if ((Entry->Flags & SC_CONST) != SC_CONST) { Error ("Symbol '%s' is already different kind", Name); } else { - Error ("Multiple definition for '%s'", Name); + Error ("Multiple definition for constant '%s'", Name); } return Entry; } @@ -649,20 +928,21 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val /* Create a new entry */ Entry = NewSymEntry (Name, Flags); - /* Enum values are ints */ + /* We only have integer constants for now */ Entry->Type = TypeDup (T); - /* Set the enum data */ + /* Set the constant data */ Entry->V.ConstVal = Val; /* Add the entry to the symbol table */ - AddSymEntry (Tab, Entry); + AddSymEntry (SymTab, Entry); /* Return the entry */ return Entry; } + DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) /* Add definition or reference to the SymEntry and preserve its attributes */ { @@ -675,11 +955,13 @@ DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) DOR->Flags = Flags; DOR->StackPtr = StackPtr; DOR->Depth = CollCount (&CurrentFunc->LocalsBlockStack); - DOR->LateSP_Label = GetLocalLabel (); + DOR->LateSP_Label = GetLocalDataLabel (); return DOR; } + + unsigned short FindSPAdjustment (const char* Name) /* Search for an entry in the table of SP adjustments */ { @@ -692,8 +974,10 @@ unsigned short FindSPAdjustment (const char* Name) return Entry->V.SPAdjustment; } + + SymEntry* AddLabelSym (const char* Name, unsigned Flags) -/* Add a goto label to the label table */ +/* Add a C goto label to the label table */ { unsigned i; DefOrRef *DOR, *NewDOR; @@ -717,7 +1001,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) for (i = 0; i < CollCount (Entry->V.L.DefsOrRefs); i++) { DOR = CollAt (Entry->V.L.DefsOrRefs, i); - if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & (SC_GOTO|SC_GOTO_IND))) { + if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & (SC_GOTO | SC_GOTO_IND))) { /* We're processing a goto and here is its destination label. ** This means the difference between SP values is already known, ** so we simply emit the SP adjustment code. @@ -739,7 +1023,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) } - if ((DOR->Flags & SC_REF) && (DOR->Flags & (SC_GOTO|SC_GOTO_IND)) && (Flags & SC_DEF)) { + if ((DOR->Flags & SC_REF) && (DOR->Flags & (SC_GOTO | SC_GOTO_IND)) && (Flags & SC_DEF)) { /* We're processing a label, let's update all gotos encountered ** so far */ @@ -752,7 +1036,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Optimizer will need the information about the value of SP adjustment ** later, so let's preserve it. */ - E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); + E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); E->V.SPAdjustment = StackPtr - DOR->StackPtr; AddSymEntry (SPAdjustTab, E); } @@ -805,43 +1089,84 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs) -/* Add a local symbol and return the symbol entry */ +/* Add a local or struct/union field symbol and return the symbol entry */ { + SymTable* Tab = (Flags & SC_STRUCTFIELD) == 0 ? SymTab : FieldTab; + ident Ident; + /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { /* We have a symbol with this name already */ - Error ("Multiple definition for '%s'", Name); + if (HandleSymRedefinition (Entry, T, Flags)) { + Entry = 0; + } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + /* Redefinitions are not allowed */ + if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { + Error ("Multiple definition of '%s'", Entry->Name); + Entry = 0; + } else if ((Flags & (SC_AUTO | SC_REGISTER)) != 0 && + (Entry->Flags & SC_EXTERN) != 0) { + /* Check for local storage class conflict */ + Error ("Declaration of '%s' with no linkage follows extern declaration", + Name); + Entry = 0; + } else { + /* If a static declaration follows a non-static declaration, + ** then it is an error. + */ + if ((Flags & SC_DEF) && + (Flags & SC_EXTERN) == 0 && + (Entry->Flags & SC_EXTERN) != 0) { + Error ("Static declaration of '%s' follows extern declaration", Name); + Entry = 0; + } + } + } - } else { + if (Entry == 0) { + if ((Flags & SC_PARAM) != 0) { + /* Use anonymous names */ + Name = AnonName (Ident, "param"); + } else { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; + } + } + } + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); /* Set the symbol attributes */ Entry->Type = TypeDup (T); - if ((Flags & SC_AUTO) == SC_AUTO) { + + if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || + (Flags & SC_ESUTYPEMASK) == SC_TYPEDEF) { + if ((Flags & SC_ALIAS) != SC_ALIAS) { + Entry->V.Offs = Offs; + } + } else if ((Flags & SC_AUTO) == SC_AUTO) { Entry->V.Offs = Offs; } else if ((Flags & SC_REGISTER) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; Entry->V.R.SaveOffs = StackPtr; - } else if ((Flags & SC_EXTERN) == SC_EXTERN) { + } else if ((Flags & SC_EXTERN) == SC_EXTERN || + (Flags & SC_FUNC) == SC_FUNC) { Entry->V.L.Label = Offs; SymSetAsmName (Entry); } else if ((Flags & SC_STATIC) == SC_STATIC) { - /* Generate the assembler name from the label number */ + /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; - Entry->AsmName = xstrdup (LocalLabelName (Entry->V.L.Label)); - } else if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD) { - Entry->V.Offs = Offs; + Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); } else { Internal ("Invalid flags in AddLocalSym: %04X", Flags); } /* Add the entry to the symbol table */ - AddSymEntry (SymTab, Entry); - + AddSymEntry (Tab, Entry); } /* Return the entry */ @@ -853,105 +1178,69 @@ 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 */ { - /* There is some special handling for functions, so check if it is one */ - int IsFunc = IsTypeFunc (T); - - /* Functions must be inserted in the global symbol table */ - SymTable* Tab = IsFunc? SymTab0 : SymTab; + /* Start from the local symbol table */ + SymTable* Tab = SymTab; /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTree (Tab, Name); if (Entry) { - Type* EType; - - /* If the existing symbol is an enumerated constant, - ** then avoid a compiler crash. See GitHub issue #728. - */ - if (Entry->Flags & SC_ENUM) { - Fatal ("Can't redeclare enum constant '%s' as global variable", Name); - } /* We have a symbol with this name already */ - if (Entry->Flags & SC_TYPE) { - Error ("Multiple definition for '%s'", Name); - return Entry; - } - - /* Get the type string of the existing symbol */ - EType = Entry->Type; - - /* If we are handling arrays, the old entry or the new entry may be an - ** incomplete declaration. Accept this, and if the exsting entry is - ** incomplete, complete it. - */ - if (IsTypeArray (T) && IsTypeArray (EType)) { - - /* Get the array sizes */ - long Size = GetElementCount (T); - long ESize = GetElementCount (EType); - - if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || - TypeCmp (T + 1, EType + 1) < TC_EQUAL) { - /* Types not identical: Conflicting types */ - Error ("Conflicting types for '%s'", Name); - return Entry; - } else { - /* Check if we have a size in the existing definition */ - if (ESize == UNSPECIFIED) { - /* Existing, size not given, use size from new def */ - SetElementCount (EType, Size); - } - } - - } else { - /* New type must be identical */ - if (TypeCmp (EType, T) < TC_EQUAL) { - Error ("Conflicting types for '%s'", Name); - return Entry; - } - - /* In case of a function, use the new type descriptor, since it - ** contains pointers to the new symbol tables that are needed if - ** an actual function definition follows. Be sure not to use the - ** new descriptor if it contains a function declaration with an - ** empty parameter list. + if (HandleSymRedefinition (Entry, T, Flags)) { + Entry = 0; + } else if ((Entry->Flags & (SC_AUTO | SC_REGISTER)) != 0) { + /* Check for local storage class conflict */ + Error ("Extern declaration of '%s' follows declaration with no linkage", + Name); + Entry = 0; + } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + /* If a static declaration follows a non-static declaration, then + ** diagnose the conflict. It will warn and compile an extern + ** declaration if both declarations are global, otherwise give an + ** error. */ - if (IsFunc) { - /* Get the function descriptor from the new type */ - FuncDesc* F = GetFuncDesc (T); - /* Use this new function descriptor if it doesn't contain - ** an empty parameter list. + if (Tab == SymTab0 && + (Flags & SC_EXTERN) == 0 && + (Entry->Flags & SC_EXTERN) != 0) { + Warning ("Static declaration of '%s' follows non-static declaration", Name); + } else if ((Flags & SC_EXTERN) != 0 && + (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && + (Entry->Flags & SC_EXTERN) == 0) { + /* It is OK if a global extern declaration follows a global + ** non-static declaration, but an error if either of them is + ** local, as the two would be referring to different objects. + ** It is an error as well if a global non-static declaration + ** follows a global static declaration. */ - if ((F->Flags & FD_EMPTY) == 0) { - Entry->V.F.Func = F; - SetFuncDesc (EType, F); + if (Entry->Owner == SymTab0) { + if ((Flags & SC_STORAGE) == 0) { + /* Linkage must be unchanged */ + Flags &= ~SC_EXTERN; + } else { + Error ("Non-static declaration of '%s' follows static declaration", Name); + } + } else { + Error ("Extern declaration of '%s' follows static declaration", Name); + Entry = 0; } } + + if (Entry) { + /* Add the new flags */ + Entry->Flags |= Flags; + } } - /* If a static declaration follows a non-static declaration, then - ** warn about the conflict. (It will compile a public declaration.) - */ - if ((Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { - Warning ("static declaration follows non-static declaration of '%s'.", Name); + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; } - /* An extern declaration must not change the current linkage. */ - if (IsFunc || (Flags & (SC_EXTERN | SC_STORAGE)) == SC_EXTERN) { - Flags &= ~SC_EXTERN; - } - - /* If a public declaration follows a static declaration, then - ** warn about the conflict. (It will compile a public declaration.) - */ - if ((Flags & SC_EXTERN) != 0 && (Entry->Flags & SC_EXTERN) == 0) { - Warning ("public declaration follows static declaration of '%s'.", Name); - } - - /* Add the new flags */ - Entry->Flags |= Flags; - - } else { + } 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) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -959,12 +1248,9 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Set the symbol attributes */ Entry->Type = TypeDup (T); - /* If this is a function, set the function descriptor and clear - ** additional fields. - */ - if (IsFunc) { - Entry->V.F.Func = GetFuncDesc (Entry->Type); - Entry->V.F.Seg = 0; + /* If this is a function, clear additional fields */ + if (IsTypeFunc (T)) { + Entry->V.F.Seg = 0; } /* Add the assembler name of the symbol */ @@ -1000,6 +1286,16 @@ SymTable* GetGlobalSymTab (void) return SymTab0; } + + +SymTable* GetFieldSymTab (void) +/* Return the current field symbol table */ +{ + return FieldTab; +} + + + SymTable* GetLabelSymTab (void) /* Return the global symbol table */ { @@ -1109,7 +1405,7 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST|SC_TYPE)) == 0) { + if ((Sym->Flags & (SC_CONST | SC_TYPEMASK)) == 0) { if (Sym->Flags & SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 62e731042..469a4ba77 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -133,8 +133,11 @@ SymEntry* FindLocalSym (const char* Name); SymEntry* FindTagSym (const char* Name); /* Find the symbol with the given name in the tag table */ -SymEntry* FindStructField (const Type* TypeArray, const char* Name); -/* Find a struct field in the fields list */ +SymEntry FindStructField (const Type* TypeArray, const char* Name); +/* Find a struct/union field in the fields list. +** Return the info about the found field symbol filled in an entry struct by +** value, or an empty entry struct if the field is not found. +*/ unsigned short FindSPAdjustment (const char* Name); /* Search for an entry in the table of SP adjustments */ @@ -146,10 +149,14 @@ unsigned short FindSPAdjustment (const char* Name); -SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab); +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags); +/* Add an enum 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 */ -SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsigned BitWidth); +SymEntry* AddBitField (const char* Name, const Type* Type, unsigned Offs, + unsigned BitOffs, unsigned BitWidth, int SignednessSpecified); /* Add a bit field to the local symbol table and return the symbol entry */ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val); @@ -159,7 +166,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags); /* Add a goto label to the symbol table */ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs); -/* Add a local symbol and return the symbol entry */ +/* Add a local or struct/union field symbol and return the symbol entry */ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags); /* Add an external or global symbol to the symbol table and return the entry */ @@ -178,6 +185,9 @@ SymTable* GetSymTab (void); SymTable* GetGlobalSymTab (void); /* Return the global symbol table */ +SymTable* GetFieldSymTab (void); +/* Return the current field symbol table */ + SymTable* GetLabelSymTab (void); /* Return the label symbol table */ diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index 5bb7bf7e4..bad8b95f1 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -32,7 +32,7 @@ /*****************************************************************************/ - + /* cc65 */ #include "codegen.h" #include "error.h" @@ -58,14 +58,19 @@ unsigned Test (unsigned Label, int Invert) ExprDesc Expr; unsigned Result; + ED_Init (&Expr); + /* Read a boolean expression */ BoolExpr (hie0, &Expr); - /* Check for a constant expression */ + /* Check for a constant numeric expression */ if (ED_IsConstAbs (&Expr)) { + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); + /* Result is constant, so we know the outcome */ - Result = (Expr.IVal != 0); + Result = (Expr.IVal != 0) ? TESTEXPR_TRUE : TESTEXPR_FALSE; /* Constant rvalue */ if (!Invert && Expr.IVal == 0) { @@ -75,19 +80,33 @@ unsigned Test (unsigned Label, int Invert) g_jump (Label); } + } else if (ED_IsAddrExpr (&Expr)) { + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); + + /* Object addresses are non-NULL */ + Result = TESTEXPR_TRUE; + + /* Condition is always true */ + if (Invert) { + g_jump (Label); + } + } else { /* Result is unknown */ Result = TESTEXPR_UNKNOWN; - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (&Expr)) { - ED_MarkForTest (&Expr); - } + /* Set the test flag */ + ED_RequireTest (&Expr); /* Load the value into the primary register */ LoadExpr (CF_FORCECHAR, &Expr); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr); + /* Generate the jump */ if (Invert) { g_truejump (CF_NONE, Label); diff --git a/src/cc65/testexpr.h b/src/cc65/testexpr.h index 478280a28..84b957af2 100644 --- a/src/cc65/testexpr.h +++ b/src/cc65/testexpr.h @@ -44,9 +44,9 @@ -#define TESTEXPR_UNKNOWN 0 /* Result of expression unknown */ +#define TESTEXPR_UNKNOWN -1 /* Result of expression unknown */ #define TESTEXPR_TRUE 1 /* Expression yields true */ -#define TESTEXPR_FALSE 2 /* Expression yields false */ +#define TESTEXPR_FALSE 0 /* Expression yields false */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 673dfa163..6052f4a84 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -36,6 +36,7 @@ #include <string.h> /* cc65 */ +#include "error.h" #include "funcdesc.h" #include "global.h" #include "symtab.h" @@ -49,51 +50,6 @@ -static void SetResult (typecmp_t* Result, typecmp_t Val) -/* Set a new result value if it is less than the existing one */ -{ - if (Val < *Result) { - /* printf ("SetResult = %d\n", Val); */ - *Result = Val; - } -} - - - -static int ParamsHaveDefaultPromotions (const FuncDesc* F) -/* Check if any of the parameters of function F has a default promotion. In -** this case, the function is not compatible with an empty parameter name list -** declaration. -*/ -{ - /* Get the symbol table */ - const SymTable* Tab = F->SymTab; - - /* Get the first parameter in the list */ - const SymEntry* Sym = Tab->SymHead; - - /* Walk over all parameters */ - while (Sym && (Sym->Flags & SC_PARAM)) { - - /* If this is an integer type, check if the promoted type is equal - ** to the original type. If not, we have a default promotion. - */ - if (IsClassInt (Sym->Type)) { - if (IntPromotion (Sym->Type) != Sym->Type) { - return 1; - } - } - - /* Get the pointer to the next param */ - Sym = Sym->NextSym; - } - - /* No default promotions in the parameter list */ - return 0; -} - - - static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) /* Compare two function symbol tables regarding function parameters. Return 1 ** if they are equal and 0 otherwise. @@ -111,8 +67,8 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) { /* Get the symbol types */ - Type* Type1 = Sym1->Type; - Type* Type2 = Sym2->Type; + const Type* Type1 = Sym1->Type; + const Type* Type2 = Sym2->Type; /* If either of both functions is old style, apply the default ** promotions to the parameter type. @@ -129,7 +85,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) } /* Compare this field */ - if (TypeCmp (Type1, Type2) < TC_EQUAL) { + if (TypeCmp (Type1, Type2).C < TC_EQUAL) { /* Field types not equal */ return 0; } @@ -148,37 +104,135 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) -static int EqualSymTables (SymTable* Tab1, SymTable* Tab2) -/* Compare two symbol tables. Return 1 if they are equal and 0 otherwise */ +static void SetResult (typecmp_t* Result, typecmpcode_t Val) +/* Set a new result value if it is less than the existing one */ { - /* Compare the parameter lists */ - SymEntry* Sym1 = Tab1->SymHead; - SymEntry* Sym2 = Tab2->SymHead; - - /* Compare the fields */ - while (Sym1 && Sym2) { - - /* Compare the names of this field */ - if (!HasAnonName (Sym1) || !HasAnonName (Sym2)) { - if (strcmp (Sym1->Name, Sym2->Name) != 0) { - /* Names are not identical */ - return 0; + if (Val < Result->C) { + if (Result->Indirections > 0) { + if (Val >= TC_STRICT_COMPATIBLE) { + /* Arrays etc. */ + Result->C = Val; + } else if (Result->Indirections == 1) { + /* C Standard allows implicit conversion as long as one side is + ** a pointer to void type, but doesn't care which side is. + */ + if ((Result->F & TCF_MASK_VOID_PTR) != 0) { + Result->C = TC_VOID_PTR; + } else if (Val == TC_SIGN_DIFF) { + /* Special treatment with pointee signedness difference */ + Result->C = TC_PTR_SIGN_DIFF; + } else { + /* Incompatible */ + Result->C = TC_PTR_INCOMPATIBLE; + } + } else { + /* Pointer-to-pointer types must have compatible pointte types, + ** or they are just incompatible. + */ + Result->C = TC_PTR_INCOMPATIBLE; } + } else { + Result->C = Val; } + /* printf ("SetResult = %d\n", Val); */ + } +} - /* Compare the types of this field */ - if (TypeCmp (Sym1->Type, Sym2->Type) < TC_EQUAL) { - /* Field types not equal */ - return 0; + + +static typecmp_t* CmpQuals (const Type* lhst, const Type* rhst, typecmp_t* Result) +/* Copare the types regarding thier qualifiers. Return the Result */ +{ + TypeCode LeftQual, RightQual; + + /* Get the left and right qualifiers */ + LeftQual = GetQualifier (lhst); + RightQual = GetQualifier (rhst); + + /* If type is function without a calling convention set explicitly, + ** then assume the default one. + */ + if (IsTypeFunc (lhst)) { + if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) { + LeftQual |= (AutoCDecl || IsVariadicFunc (lhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; + } + } + if (IsTypeFunc (rhst)) { + if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) { + RightQual |= (AutoCDecl || IsVariadicFunc (rhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; } - - /* Get the pointers to the next fields */ - Sym1 = Sym1->NextSym; - Sym2 = Sym2->NextSym; } - /* Check both pointers against NULL to compare the field count */ - return (Sym1 == 0 && Sym2 == 0); + /* Default address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) { + LeftQual |= (IsTypeFunc (lhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ()); + } + if ((RightQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) { + RightQual |= (IsTypeFunc (rhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ()); + } + + /* Just return if nothing to do */ + if (LeftQual == RightQual) { + return Result; + } + + /* On the first indirection level, different qualifiers mean that the types + ** are still compatible. On the second level, that is a (maybe minor) error. + ** We create a special return-code if a qualifier is dropped from a pointer. + ** But, different calling conventions are incompatible. Starting from the + ** next level, the types are incompatible if the qualifiers differ. + */ + /* (Debugging statement) */ + /* printf ("Ind = %d %06X != %06X\n", Result->Indirections, LeftQual, RightQual); */ + switch (Result->Indirections) { + case 0: + /* Compare C qualifiers */ + if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_QUAL_IMPLICIT; + } else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_QUAL_DIFF; + } + + /* Compare address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) { + Result->F |= TCF_ADDRSIZE_QUAL_DIFF; + } + + /* Compare function calling conventions */ + if ((LeftQual & T_QUAL_CCONV) != (RightQual & T_QUAL_CCONV)) { + SetResult (Result, TC_INCOMPATIBLE); + } + break; + + case 1: + /* A non-const value on the right is compatible to a + ** const one to the left, same for volatile. + */ + if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_PTR_QUAL_IMPLICIT; + } else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) { + Result->F |= TCF_PTR_QUAL_DIFF; + } + + /* Compare address size qualifiers */ + if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) { + Result->F |= TCF_ADDRSIZE_QUAL_DIFF; + } + + /* Compare function calling conventions */ + if ((!IsTypeFunc (lhst) && !IsTypeFunc (rhst)) || + (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) { + break; + } + /* else fall through */ + + default: + /* Pointer types mismatch */ + SetResult (Result, TC_INCOMPATIBLE); + break; + } + + return Result; } @@ -186,120 +240,126 @@ static int EqualSymTables (SymTable* Tab1, SymTable* Tab2) static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Recursively compare two types. */ { - unsigned Indirections; - unsigned ElementCount; SymEntry* Sym1; SymEntry* Sym2; - SymTable* Tab1; - SymTable* Tab2; FuncDesc* F1; FuncDesc* F2; + TypeCode LeftType, RightType; + long LeftCount, RightCount; - /* Initialize stuff */ - Indirections = 0; - ElementCount = 0; - /* Compare two types. Determine, where they differ */ while (lhs->C != T_END) { - TypeCode LeftType, RightType; - TypeCode LeftSign, RightSign; - TypeCode LeftQual, RightQual; - long LeftCount, RightCount; - /* Check if the end of the type string is reached */ if (rhs->C == T_END) { /* End of comparison reached */ + break; + } + + /* Compare qualifiers */ + if (CmpQuals (lhs, rhs, Result)->C == TC_INCOMPATIBLE) { return; } - /* Get the raw left and right types, signs and qualifiers */ - LeftType = GetType (lhs); - RightType = GetType (rhs); - LeftSign = GetSignedness (lhs); - RightSign = GetSignedness (rhs); - LeftQual = GetQualifier (lhs); - RightQual = GetQualifier (rhs); + /* Get the left and right types */ + LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE); + RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE); - /* If the left type is a pointer and the right is an array, both - ** are compatible. + /* 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 the raw types are not identical, the types are incompatible */ + /* Bit-fields are considered compatible if they have the same + ** signedness, bit-offset and bit-width. + */ + if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { + if (!IsTypeBitField (lhs) || + !IsTypeBitField (rhs) || + lhs->A.B.Offs != rhs->A.B.Offs || + lhs->A.B.Width != rhs->A.B.Width) { + SetResult (Result, TC_INCOMPATIBLE); + } + if (LeftType != RightType) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } + + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); return; } - /* On indirection level zero, a qualifier or sign difference is - ** accepted. The types are no longer equal, but compatible. - */ - if (LeftSign != RightSign) { - if (ElementCount == 0) { - SetResult (Result, TC_SIGN_DIFF); - } else { - SetResult (Result, TC_INCOMPATIBLE); - return; - } - } + /* Enums must be handled specially */ + if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { - if (LeftType == T_TYPE_FUNC) { - /* If a calling convention wasn't set explicitly, - ** then assume the default one. - */ - if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) { - LeftQual |= (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) { - RightQual |= (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL; - } - } + /* Compare the tag types */ + Sym1 = IsTypeEnum (lhs) ? GetESUSymEntry (lhs) : 0; + Sym2 = IsTypeEnum (rhs) ? GetESUSymEntry (rhs) : 0; - if (LeftQual != RightQual) { - /* On the first indirection level, different qualifiers mean - ** that the types still are compatible. On the second level, - ** that is a (maybe minor) error. We create a special return-code - ** if a qualifier is dropped from a pointer. But, different calling - ** conventions are incompatible. Starting from the next level, - ** the types are incompatible if the qualifiers differ. - */ - /* (Debugging statement) */ - /* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */ - switch (Indirections) { - case 0: + if (Sym1 != Sym2) { + if (Sym1 == 0 || Sym2 == 0) { + + /* Only one is an enum. So they can't be identical */ SetResult (Result, TC_STRICT_COMPATIBLE); - break; - case 1: - /* A non-const value on the right is compatible to a - ** const one to the left, same for volatile. + } else { + /* For the two to be identical, they must be in the same + ** scope and have the same name. */ - if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) || - (LeftQual & T_QUAL_VOLATILE) < (RightQual & T_QUAL_VOLATILE)) { - SetResult (Result, TC_QUAL_DIFF); - } else { - SetResult (Result, TC_STRICT_COMPATIBLE); - } + if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { - if (LeftType != T_TYPE_FUNC || (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) { - break; + /* If any one of the two is incomplete, we can't guess + ** their underlying types and have to assume that they + ** be incompatible. + */ + if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { + SetResult (Result, TC_INCOMPATIBLE); + return; + } } - /* else fall through */ - - default: - SetResult (Result, TC_INCOMPATIBLE); - return; + } } + + } + + /* 'char' is neither 'signed char' nor 'unsigned char' */ + if ((IsISOChar (lhs) && !IsISOChar (rhs)) || + (!IsISOChar (lhs) && IsISOChar (rhs))) { + SetResult (Result, TC_SIGN_DIFF); + } + + /* On indirection level zero, a sign difference is accepted. + ** The types are no longer equal, but compatible. + */ + if (GetSignedness (lhs) != GetSignedness (rhs)) { + SetResult (Result, TC_SIGN_DIFF); } /* Check for special type elements */ switch (LeftType) { case T_TYPE_PTR: - ++Indirections; + ++Result->Indirections; + if (Result->Indirections == 1) { + if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + Result->F |= TCF_VOID_PTR_ON_LEFT; + } + if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) { + Result->F |= TCF_VOID_PTR_ON_RIGHT; + } + } else { + Result->F &= ~TCF_MASK_VOID_PTR; + } break; case T_TYPE_FUNC: @@ -307,29 +367,16 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) F1 = GetFuncDesc (lhs); F2 = GetFuncDesc (rhs); - /* If one of both functions has an empty parameter list (which - ** does also mean, it is not a function definition, because the - ** flag is reset in this case), it is considered equal to any - ** other definition, provided that the other has no default - ** promotions in the parameter list. If none of both parameter - ** lists is empty, we have to check the parameter lists and - ** other attributes. + /* If one of both function declarations has an empty parameter + ** list (which does also mean, it is not a function definition, + ** because the flag is reset in this case), it is ignored for + ** parameter comparison and considered equal to the other one, + ** provided both have the same return type and other attributes. + ** If neither of both parameter lists is empty, we have to check + ** the parameter lists. */ - if (F1->Flags & FD_EMPTY) { - if ((F2->Flags & FD_EMPTY) == 0) { - if (ParamsHaveDefaultPromotions (F2)) { - /* Flags differ */ - SetResult (Result, TC_INCOMPATIBLE); - return; - } - } - } else if (F2->Flags & FD_EMPTY) { - if (ParamsHaveDefaultPromotions (F1)) { - /* Flags differ */ - SetResult (Result, TC_INCOMPATIBLE); - return; - } - } else { + if ((F1->Flags & FD_EMPTY) == 0 && + (F2->Flags & FD_EMPTY) == 0) { /* Check the remaining flags */ if ((F1->Flags & ~FD_IGNORE) != (F2->Flags & ~FD_IGNORE)) { @@ -353,64 +400,57 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check member count */ LeftCount = GetElementCount (lhs); RightCount = GetElementCount (rhs); - if (LeftCount != UNSPECIFIED && - RightCount != UNSPECIFIED && - LeftCount != RightCount) { - /* Member count given but different */ - SetResult (Result, TC_INCOMPATIBLE); - return; + if (LeftCount != RightCount) { + if (LeftCount != UNSPECIFIED && + RightCount != UNSPECIFIED) { + /* Member count given but different */ + SetResult (Result, TC_INCOMPATIBLE); + return; + } + + /* We take into account which side is more specified */ + if (LeftCount == UNSPECIFIED) { + SetResult (Result, TC_UNSPECIFY); + } else { + SetResult (Result, TC_EQUAL); + } } break; case T_TYPE_STRUCT: case T_TYPE_UNION: - /* Compare the fields recursively. To do that, we fetch the - ** pointer to the struct definition from the type, and compare - ** the fields. - */ - Sym1 = GetSymEntry (lhs); - Sym2 = GetSymEntry (rhs); + /* Compare the tag types */ + Sym1 = GetESUSymEntry (lhs); + Sym2 = GetESUSymEntry (rhs); - /* If one symbol has a name, the names must be identical */ - if (!HasAnonName (Sym1) || !HasAnonName (Sym2)) { - if (strcmp (Sym1->Name, Sym2->Name) != 0) { - /* Names are not identical */ + CHECK (Sym1 != 0 || Sym2 != 0); + + if (Sym1 != Sym2) { + /* Both must be in the same scope and have the same name to + ** be identical. + */ + if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { + /* This shouldn't happen in the current code base, but + ** we still handle this case to be future-proof. + */ SetResult (Result, TC_INCOMPATIBLE); return; } } - /* Get the field tables from the struct entry */ - Tab1 = Sym1->V.S.SymTab; - Tab2 = Sym2->V.S.SymTab; - - /* One or both structs may be forward definitions. In this case, - ** the symbol tables are both non existant. Assume that the - ** structs are equal in this case. - */ - if (Tab1 != 0 && Tab2 != 0) { - - if (EqualSymTables (Tab1, Tab2) == 0) { - /* Field lists are not equal */ - SetResult (Result, TC_INCOMPATIBLE); - return; - } - - } - - /* Structs are equal */ + /* Both are identical */ break; } /* Next type string element */ ++lhs; ++rhs; - ++ElementCount; } - /* Check if end of rhs reached */ - if (rhs->C == T_END) { - SetResult (Result, TC_EQUAL); + /* Check if lhs and rhs both reached ends */ + if (lhs->C == T_END && rhs->C == T_END) { + SetResult (Result, TC_IDENTICAL); } else { SetResult (Result, TC_INCOMPATIBLE); } @@ -422,7 +462,7 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) /* Compare two types and return the result */ { /* Assume the types are identical */ - typecmp_t Result = TC_IDENTICAL; + typecmp_t Result = TYPECMP_INITIALIZER; #if 0 printf ("Left : "); PrintRawType (stdout, lhs); @@ -437,3 +477,21 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) /* Return the result */ return Result; } + + + +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) +/* Print error or warning message about type compatibility with proper type names */ +{ + StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; + StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; + GetFullTypeNameBuf (&NewTypeName, NewType); + GetFullTypeNameBuf (&OldTypeName, OldType); + if (IsError) { + Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); + } else { + Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); + } + SB_Done (&OldTypeName); + SB_Done (&NewTypeName); +} diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index 5f95e42a1..367df5245 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -48,18 +48,44 @@ -/* Degree of type compatibility. Must be in ascending order */ +/* Degree of type compatibility affected. Must be in ascending order */ typedef enum { - TC_INCOMPATIBLE, /* Distinct types */ - TC_SIGN_DIFF, /* Signedness differs */ - TC_COMPATIBLE = TC_SIGN_DIFF, /* Compatible types */ - TC_QUAL_DIFF, /* Types differ in qualifier of pointer */ - TC_STRICT_COMPATIBLE, /* Strict compatibility */ - TC_EQUAL, /* Types are equal */ - TC_IDENTICAL /* Types are identical */ + TC_INCOMPATIBLE, /* Distinct types */ + TC_SIGN_DIFF, /* Signedness differs */ + TC_PTR_SIGN_DIFF, /* Pointee signedness differs */ + TC_PTR_INCOMPATIBLE, /* Distinct pointer types */ + TC_VOID_PTR, /* Non-void and void pointers */ + TC_STRICT_COMPATIBLE, /* Strict compatibility according to the C Standard */ + TC_PTR_DECAY, /* rhs is an array and lhs is a pointer */ + TC_EQUAL, /* Array types with unspecified lengths */ + TC_UNSPECIFY, /* lhs has unspecified length while rhs has specified length */ + TC_IDENTICAL /* Types are identical */ +} typecmpcode_t; + +/* Degree of type compatibility affected by qualifiers as well as some extra info */ +typedef enum { + TCF_NONE = 0x00, /* None of the below */ + TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */ + TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */ + TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT, + TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */ + TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */ + TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */ + TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */ + TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */ + TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */ + TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */ + TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF, + TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL, +} typecmpflag_t; + +typedef struct { + typecmpcode_t C; + typecmpflag_t F; + int Indirections; } typecmp_t; - +#define TYPECMP_INITIALIZER { TC_IDENTICAL, TCF_NONE, 0 } /*****************************************************************************/ /* Code */ @@ -70,6 +96,9 @@ typedef enum { typecmp_t TypeCmp (const Type* lhs, const Type* rhs); /* Compare two types and return the result */ +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); +/* Print error or warning message about type compatibility with proper type names */ + /* End of typecmp.h */ diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 21ad33f12..a7528a2f8 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -42,8 +42,8 @@ #include "declare.h" #include "error.h" #include "expr.h" +#include "funcdesc.h" #include "loadexpr.h" -#include "scanner.h" #include "typecmp.h" #include "typeconv.h" @@ -58,9 +58,9 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Emit code to convert the given expression to a new type. */ { - Type* OldType; - unsigned OldSize; - unsigned NewSize; + const Type* OldType; + unsigned OldBits; + unsigned NewBits; /* Remember the old type */ @@ -70,7 +70,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** conversion void -> void. */ if (IsTypeVoid (NewType)) { - ED_MakeRVal (Expr); /* Never an lvalue */ + ED_MarkExprAsRVal (Expr); /* Never an lvalue */ goto ExitPoint; } @@ -83,8 +83,17 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Get the sizes of the types. Since we've excluded void types, checking ** for known sizes makes sense here. */ - OldSize = CheckedSizeOf (OldType); - NewSize = CheckedSizeOf (NewType); + if (IsTypeBitField (OldType)) { + OldBits = OldType->A.B.Width; + } else { + OldBits = CheckedSizeOf (OldType) * CHAR_BITS; + } + + /* If the new type is a bit-field, we use its underlying type instead */ + if (IsTypeBitField (NewType)) { + NewType = GetUnderlyingType (NewType); + } + NewBits = CheckedSizeOf (NewType) * CHAR_BITS; /* lvalue? */ if (ED_IsLVal (Expr)) { @@ -97,27 +106,23 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** If both sizes are equal, do also leave the value alone. ** If the new size is larger, we must convert the value. */ - if (NewSize > OldSize) { + if (NewBits > OldBits) { /* Load the value into the primary */ LoadExpr (CF_NONE, Expr); /* Emit typecast code */ - g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); + g_typecast (TypeOf (NewType), TypeOf (OldType)); /* Value is now in primary and an rvalue */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } - } else if (ED_IsLocAbs (Expr)) { + } else if (ED_IsConstAbs (Expr)) { /* A cast of a constant numeric value to another type. Be sure ** to handle sign extension correctly. */ - /* Get the current and new size of the value */ - unsigned OldBits = OldSize * 8; - unsigned NewBits = NewSize * 8; - /* Check if the new datatype will have a smaller range. If it ** has a larger range, things are OK, since the value is ** internally already represented by a long. @@ -136,22 +141,31 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) } } + /* Do the integer constant <-> absolute address conversion if necessary */ + if (IsClassPtr (NewType)) { + Expr->Flags &= ~E_LOC_NONE; + Expr->Flags |= E_LOC_ABS | E_ADDRESS_OF; + } else if (IsClassInt (NewType)) { + Expr->Flags &= ~(E_LOC_ABS | E_ADDRESS_OF); + Expr->Flags |= E_LOC_NONE; + } + } else { /* The value is not a constant. If the sizes of the types are ** not equal, add conversion code. Be sure to convert chars ** correctly. */ - if (OldSize != NewSize) { + if (OldBits != NewBits) { /* Load the value into the primary */ LoadExpr (CF_NONE, Expr); /* Emit typecast code. */ - g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); + g_typecast (TypeOf (NewType), TypeOf (OldType)); - /* Value is now a rvalue in the primary */ - ED_MakeRValExpr (Expr); + /* Value is now an rvalue in the primary */ + ED_FinalizeRValLoad (Expr); } } @@ -162,7 +176,7 @@ ExitPoint: -void TypeConversion (ExprDesc* Expr, Type* NewType) +void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or ** impossible. @@ -177,24 +191,25 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) printf ("\n"); PrintRawType (stdout, NewType); #endif - /* First, do some type checking */ - if (IsTypeVoid (NewType) || IsTypeVoid (Expr->Type)) { - /* If one of the sides are of type void, output a more apropriate - ** error message. - */ - Error ("Illegal type"); + typecmp_t Result = TYPECMP_INITIALIZER; + int HasError = 0; + const char* Msg = 0; + const Type* OldType = Expr->Type; + + + /* If one of the sides is of type void, it is an error */ + if (IsTypeVoid (NewType) || IsTypeVoid (OldType)) { + HasError = 1; } - /* If Expr is a function, convert it to pointer to function */ - if (IsTypeFunc(Expr->Type)) { - Expr->Type = PointerTo (Expr->Type); - } - - /* If both types are equal, no conversion is needed */ - if (TypeCmp (Expr->Type, NewType) >= TC_EQUAL) { - /* We're already done */ - return; + /* If both types are the same, no conversion is needed */ + Result = TypeCmp (NewType, OldType); + if (Result.C < TC_IDENTICAL && (IsTypeArray (OldType) || IsTypeFunc (OldType))) { + /* If Expr is an array or a function, convert it to a pointer */ + Expr->Type = PtrConversion (Expr->Type); + /* Recompare */ + Result = TypeCmp (NewType, Expr->Type); } /* Check for conversion problems */ @@ -202,72 +217,85 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) /* Handle conversions to int type */ if (IsClassPtr (Expr->Type)) { - /* Pointer -> int conversion. Convert array to pointer */ - if (IsTypeArray (Expr->Type)) { - Expr->Type = ArrayToPtr (Expr->Type); - } Warning ("Converting pointer to integer without a cast"); } else if (!IsClassInt (Expr->Type) && !IsClassFloat (Expr->Type)) { - Error ("Incompatible types"); + HasError = 1; } - } else if (IsClassFloat (NewType)) { - if (!IsClassFloat (Expr->Type) && !IsClassInt (Expr->Type)) { - Error ("Incompatible types"); + HasError = 1; } - } else if (IsClassPtr (NewType)) { /* Handle conversions to pointer type */ if (IsClassPtr (Expr->Type)) { - /* Convert array to pointer */ - if (IsTypeArray (Expr->Type)) { - Expr->Type = ArrayToPtr (Expr->Type); - } - - /* Pointer to pointer assignment is valid, if: + /* Implicit pointer-to-pointer conversion is valid, if: ** - both point to the same types, or ** - the rhs pointer is a void pointer, or ** - the lhs pointer is a void pointer. + ** Note: We additionally allow converting function pointers to and from + ** void pointers, just with warnings. */ - if (!IsTypeVoid (Indirect (NewType)) && !IsTypeVoid (Indirect (Expr->Type))) { - /* Compare the types */ - switch (TypeCmp (NewType, Expr->Type)) { - - case TC_INCOMPATIBLE: - Error ("Incompatible pointer types at '%s'", (Expr->Sym? Expr->Sym->Name : "Unknown")); - break; - - case TC_QUAL_DIFF: - Error ("Pointer types differ in type qualifiers"); - break; - - default: - /* Ok */ - break; + if (Result.C == TC_PTR_SIGN_DIFF) { + /* Specific warning for pointee signedness difference */ + if (IS_Get (&WarnPointerSign)) { + TypeCompatibilityDiagnostic (NewType, Expr->Type, + 0, "Pointer conversion to '%s' from '%s' changes pointee signedness"); + } + } else if ((Result.C <= TC_PTR_INCOMPATIBLE || + (Result.F & TCF_INCOMPATIBLE_QUAL) != 0)) { + /* Incompatible pointee types or qualifiers */ + if (IS_Get (&WarnPointerTypes)) { + TypeCompatibilityDiagnostic (NewType, Expr->Type, + 0, "Incompatible pointer conversion to '%s' from '%s'"); } } + if ((Result.F & TCF_PTR_QUAL_DIFF) != 0) { + /* Discarding qualifiers is a bad thing and we always warn */ + TypeCompatibilityDiagnostic (NewType, Expr->Type, + 0, "Pointer conversion to '%s' from '%s' discards qualifiers"); + } + } else if (IsClassInt (Expr->Type)) { - /* Int to pointer assignment is valid only for constant zero */ + /* Int to pointer conversion is valid only for constant zero */ if (!ED_IsConstAbsInt (Expr) || Expr->IVal != 0) { Warning ("Converting integer to pointer without a cast"); } } else { - Error ("Incompatible types"); + HasError = 1; } - } else { - - /* Invalid automatic conversion */ - Error ("Incompatible types"); - + } else if (Result.C < TC_IDENTICAL) { + /* Invalid automatic conversion */ + HasError = 1; } - /* Do the actual conversion */ - DoConversion (Expr, NewType); + /* Set default diagnostic message */ + if (Msg == 0) { + Msg = "Converting to '%s' from '%s'"; + } + + if (HasError) { + TypeCompatibilityDiagnostic (NewType, OldType, 1, Msg); + } else { + /* Both types must be complete */ + if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { + /* Do the actual conversion */ + DoConversion (Expr, NewType); + } else { + /* We should have already generated error elsewhere so that we + ** could just silently fail here to avoid excess errors, but to + ** be safe, we must ensure that we do have errors. + */ + if (IsIncompleteESUType (NewType)) { + Error ("Conversion to incomplete type '%s'", GetFullTypeName (NewType)); + } else { + Error ("Conversion from incomplete type '%s'", GetFullTypeName (Expr->Type)); + } + } + } } @@ -289,9 +317,193 @@ void TypeCast (ExprDesc* Expr) /* Read the expression we have to cast */ hie10 (Expr); - /* Convert functions and arrays to "pointer to" object */ - Expr->Type = PtrConversion (Expr->Type); + /* Only allow casts to arithmetic, pointer or void types */ + if (IsCastType (NewType)) { + if (!IsIncompleteESUType (NewType)) { + /* Convert functions and arrays to "pointer to" object */ + Expr->Type = PtrConversion (Expr->Type); - /* Convert the value. */ - DoConversion (Expr, NewType); + if (TypeCmp (NewType, Expr->Type).C >= TC_PTR_INCOMPATIBLE) { + /* If the new type has the same underlying presentation, just + ** use it to replace the old one. + */ + ReplaceType (Expr, NewType); + } else if (IsCastType (Expr->Type)) { + /* Convert the value. The result has always the new type */ + DoConversion (Expr, NewType); + } else { + TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, + "Cast to incompatible type '%s' from '%s'"); + } + } else { + Error ("Cast to incomplete type '%s'", + GetFullTypeName (NewType)); + } + } else { + Error ("Arithmetic or pointer type expected but %s is used", + GetBasicTypeName (NewType)); + } + + /* If the new type is void, the cast expression can have no effects */ + if (IsTypeVoid (NewType)) { + Expr->Flags |= E_EVAL_MAYBE_UNUSED; + } + + /* The result is always an rvalue */ + ED_MarkExprAsRVal (Expr); +} + + + +static void ComposeFuncParamList (const FuncDesc* F1, const FuncDesc* F2) +/* Compose two function symbol tables regarding function parameters into F1 */ +{ + /* Get the symbol tables */ + const SymTable* Tab1 = F1->SymTab; + const SymTable* Tab2 = F2->SymTab; + + /* Compose the parameter lists */ + SymEntry* Sym1 = Tab1->SymHead; + SymEntry* Sym2 = Tab2->SymHead; + + /* Sanity check */ + CHECK ((F1->Flags & FD_EMPTY) == 0 && (F2->Flags & FD_EMPTY) == 0); + + /* Compose the fields */ + while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) { + + /* Get the symbol types */ + const Type* Type1 = Sym1->Type; + const Type* Type2 = Sym2->Type; + + /* If either of both functions is old style, apply the default + ** promotions to the parameter type. + */ + if (F1->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type1)) { + Type1 = IntPromotion (Type1); + } + } + if (F2->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type2)) { + Type2 = IntPromotion (Type2); + } + } + + /* When we compose two function parameter lists with any FD_OLDSTYLE + ** flags set, we are either refining the declaration of the function + ** with its definition seen, or determining the result type of a + ** ternary operation. In either case, we can just replace the types + ** with the promoted ones since the original types of the parameters + ** only matters inside the function definition. + */ + if (Type1 != Sym1->Type) { + Sym1->Type = TypeDup (Type1); + } + + /* Compose this field */ + TypeComposition (Sym1->Type, Type2); + + /* Get the pointers to the next fields */ + Sym1 = Sym1->NextSym; + Sym2 = Sym2->NextSym; + } +} + + + +void TypeComposition (Type* lhs, const Type* rhs) +/* Recursively compose two types into lhs. The two types must have compatible +** type or this fails with a critical check. +*/ +{ + FuncDesc* F1; + FuncDesc* F2; + long LeftCount, RightCount; + + /* Compose two types */ + while (lhs->C != T_END) { + + /* Check if the end of the type string is reached */ + if (rhs->C == T_END) { + break; + } + + /* Check for sanity */ + CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); + + /* Check for special type elements */ + if (IsTypeFunc (lhs)) { + /* Compose the function descriptors */ + F1 = GetFuncDesc (lhs); + F2 = GetFuncDesc (rhs); + + /* If F1 has an empty parameter list (which does also mean, it is + ** not a function definition, because the flag is reset in this + ** case), its declaration is replaced by the other declaration. If + ** neither of the parameter lists is empty, we have to compose them + ** as well as other attributes. + */ + if ((F1->Flags & FD_EMPTY) == FD_EMPTY) { + if ((F2->Flags & FD_EMPTY) == 0) { + /* Copy the parameters and flags */ + TypeCopy (lhs, rhs); + F1->Flags = F2->Flags; + } + } else if ((F2->Flags & FD_EMPTY) == 0) { + /* Compose the parameter lists */ + ComposeFuncParamList (F1, F2); + /* Prefer non-old-style */ + if ((F2->Flags & FD_OLDSTYLE) == 0) { + F1->Flags &= ~FD_OLDSTYLE; + } + } + } else if (IsTypeArray (lhs)) { + /* Check member count */ + LeftCount = GetElementCount (lhs); + RightCount = GetElementCount (rhs); + + /* Set composite type if it is requested */ + if (LeftCount != UNSPECIFIED) { + SetElementCount (lhs, LeftCount); + } else if (RightCount != UNSPECIFIED) { + SetElementCount (lhs, RightCount); + } + } else { + /* Combine the qualifiers */ + if (IsClassPtr (lhs)) { + ++lhs; + ++rhs; + lhs->C |= GetQualifier (rhs); + } + } + + /* Next type string element */ + ++lhs; + ++rhs; + } + + return; +} + + + +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) +/* Refine the existing function descriptor with a new one */ +{ + FuncDesc* Old = GetFuncDesc (OldType); + FuncDesc* New = GetFuncDesc (NewType); + + CHECK (Old != 0 && New != 0); + + if ((New->Flags & FD_EMPTY) == 0) { + if ((Old->Flags & FD_EMPTY) == 0) { + TypeComposition (OldType, NewType); + } else { + TypeCopy (OldType, NewType); + Old->Flags &= ~FD_EMPTY; + } + } + + return Old; } diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 8321aded4..839c5e43e 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -49,7 +49,7 @@ -void TypeConversion (ExprDesc* Expr, Type* NewType); +void TypeConversion (ExprDesc* Expr, const Type* NewType); /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or ** impossible. @@ -58,6 +58,14 @@ void TypeConversion (ExprDesc* Expr, Type* NewType); void TypeCast (ExprDesc* Expr); /* Handle an explicit cast. */ +void TypeComposition (Type* lhs, const Type* rhs); +/* Recursively compose two types into lhs. The two types must have compatible +** type or this fails with a critical check. +*/ + +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); +/* Refine the existing function descriptor with a new one */ + /* End of typeconv.h */ diff --git a/src/cc65/wrappedcall.c b/src/cc65/wrappedcall.c index 18cb507ac..ecf2c3a53 100644 --- a/src/cc65/wrappedcall.c +++ b/src/cc65/wrappedcall.c @@ -64,7 +64,7 @@ static IntPtrStack WrappedCalls; -void PushWrappedCall (void *Ptr, unsigned char Val) +void PushWrappedCall (void *Ptr, unsigned int Val) /* Push the current WrappedCall */ { if (IPS_IsFull (&WrappedCalls)) { @@ -88,7 +88,7 @@ void PopWrappedCall (void) -void GetWrappedCall (void **Ptr, unsigned char *Val) +void GetWrappedCall (void **Ptr, unsigned int *Val) /* Get the current WrappedCall */ { if (IPS_GetCount (&WrappedCalls) < 1) { @@ -97,6 +97,6 @@ void GetWrappedCall (void **Ptr, unsigned char *Val) } else { long Temp; IPS_Get (&WrappedCalls, &Temp, Ptr); - *Val = (unsigned char) Temp; + *Val = (unsigned int) Temp; } } diff --git a/src/cc65/wrappedcall.h b/src/cc65/wrappedcall.h index 3517c2465..9a1bb51bf 100644 --- a/src/cc65/wrappedcall.h +++ b/src/cc65/wrappedcall.h @@ -50,13 +50,13 @@ -void PushWrappedCall (void *Ptr, unsigned char Val); +void PushWrappedCall (void *Ptr, unsigned int Val); /* Push the current WrappedCall */ void PopWrappedCall (void); /* Pop the current WrappedCall */ -void GetWrappedCall (void **Ptr, unsigned char *Val); +void GetWrappedCall (void **Ptr, unsigned int *Val); /* Get the current WrappedCall, if any */ diff --git a/src/chrcvt65.vcxproj b/src/chrcvt65.vcxproj index 351f6077e..1e5c753b5 100644 --- a/src/chrcvt65.vcxproj +++ b/src/chrcvt65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{1C7A3FEF-DD0B-4B10-BC33-C3BE29BF67CC}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>chrcvt65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/cl65.vcxproj b/src/cl65.vcxproj index dab77e196..67b7eb087 100644 --- a/src/cl65.vcxproj +++ b/src/cl65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{F657912F-050A-488B-B203-50ED5715CDD7}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>cl65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/cl65/error.h b/src/cl65/error.h index b1ff30660..fdf55c758 100644 --- a/src/cl65/error.h +++ b/src/cl65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/cl65/main.c b/src/cl65/main.c index 9d536db82..dba4915f2 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -112,6 +112,9 @@ static CmdDesc CO65 = { 0, 0, 0, 0, 0, 0, 0 }; static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 }; static CmdDesc GRC = { 0, 0, 0, 0, 0, 0, 0 }; +/* Pseudo-command to track files we want to delete */ +static CmdDesc RM = { 0, 0, 0, 0, 0, 0, 0 }; + /* Variables controlling the steps we're doing */ static int DoLink = 1; static int DoAssemble = 1; @@ -228,12 +231,8 @@ static char* CmdAllocArg (const char* Arg, unsigned Len) static void CmdExpand (CmdDesc* Cmd) /* Expand the argument vector */ { - unsigned NewMax = Cmd->ArgMax + 10; - char** NewArgs = xmalloc (NewMax * sizeof (char*)); - memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*)); - xfree (Cmd->Args); - Cmd->Args = NewArgs; - Cmd->ArgMax = NewMax; + Cmd->ArgMax += 10; + Cmd->Args = xrealloc (Cmd->Args, Cmd->ArgMax * sizeof (char*)); } @@ -321,12 +320,8 @@ static void CmdAddFile (CmdDesc* Cmd, const char* File) { /* Expand the file vector if needed */ if (Cmd->FileCount == Cmd->FileMax) { - unsigned NewMax = Cmd->FileMax + 10; - char** NewFiles = xmalloc (NewMax * sizeof (char*)); - memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*)); - xfree (Cmd->Files); - Cmd->Files = NewFiles; - Cmd->FileMax = NewMax; + Cmd->FileMax += 10; + Cmd->Files = xrealloc (Cmd->Files, Cmd->FileMax * sizeof (char*)); } /* If the file name is not NULL (which is legal and is used to terminate @@ -456,6 +451,20 @@ static void ExecProgram (CmdDesc* Cmd) +static void RemoveTempFiles (void) +{ + unsigned I; + + for (I = 0; I < RM.FileCount; ++I) { + if (remove (RM.Files[I]) < 0) { + Warning ("Cannot remove temporary file '%s': %s", + RM.Files[I], strerror (errno)); + } + } +} + + + static void Link (void) /* Link the resulting executable */ { @@ -534,6 +543,8 @@ static void AssembleFile (const char* File, unsigned ArgCount) */ char* ObjName = MakeFilename (File, ".o"); CmdAddFile (&LD65, ObjName); + /* This is just a temporary file, schedule it for removal */ + CmdAddFile (&RM, ObjName); xfree (ObjName); } else { /* This is the final step. If an output name is given, set it */ @@ -1641,6 +1652,8 @@ int main (int argc, char* argv []) Link (); } + RemoveTempFiles (); + /* Return an apropriate exit code */ return EXIT_SUCCESS; } diff --git a/src/co65.vcxproj b/src/co65.vcxproj index 9b4f18786..9f5959d89 100644 --- a/src/co65.vcxproj +++ b/src/co65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{F5DB5D1A-05BC-48FE-B346-4E96DD522AA2}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>co65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/co65/convert.h b/src/co65/convert.h index 22ef25424..8c7782ff3 100644 --- a/src/co65/convert.h +++ b/src/co65/convert.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/error.c b/src/co65/error.c index 1c1625207..1fa099c94 100644 --- a/src/co65/error.c +++ b/src/co65/error.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/error.h b/src/co65/error.h index 4fb9d370d..23901e52e 100644 --- a/src/co65/error.h +++ b/src/co65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/fileio.c b/src/co65/fileio.c index 9241797c6..18813d5e4 100644 --- a/src/co65/fileio.c +++ b/src/co65/fileio.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/fileio.h b/src/co65/fileio.h index c5e9a003c..150c4fca7 100644 --- a/src/co65/fileio.h +++ b/src/co65/fileio.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/global.c b/src/co65/global.c index bc9b0099b..405cda7d5 100644 --- a/src/co65/global.c +++ b/src/co65/global.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/co65/global.h b/src/co65/global.h index 29c17ca29..8f1e0c4f8 100644 --- a/src/co65/global.h +++ b/src/co65/global.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common.vcxproj b/src/common.vcxproj index eb0dffd66..f7929df2b 100644 --- a/src/common.vcxproj +++ b/src/common.vcxproj @@ -10,6 +10,40 @@ <Platform>Win32</Platform> </ProjectConfiguration> </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="common\abend.h" /> <ClInclude Include="common\addrsize.h" /> @@ -106,80 +140,6 @@ <ClCompile Include="common\xmalloc.c" /> <ClCompile Include="common\xsprintf.c" /> </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{71DC1F68-BFC4-478C-8655-C8E9C9654D2B}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>common</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <ConfigurationType>StaticLibrary</ConfigurationType> - <PlatformToolset>v141</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</OutDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> - <OutDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</OutDir> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_LIB;_DEBUG</PreprocessorDefinitions> - <TreatWarningAsError>true</TreatWarningAsError> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - <Lib /> - <Lib> - <OutputFile>$(IntDir)$(TargetName)$(TargetExt)</OutputFile> - </Lib> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_LIB;NDEBUG</PreprocessorDefinitions> - <TreatWarningAsError>true</TreatWarningAsError> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - <Lib /> - <Lib> - <OutputFile>$(IntDir)$(TargetName)$(TargetExt)</OutputFile> - </Lib> - </ItemDefinitionGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> diff --git a/src/common/cpu.c b/src/common/cpu.c index b4c5c8dfe..b55a5ab00 100644 --- a/src/common/cpu.c +++ b/src/common/cpu.c @@ -55,6 +55,7 @@ const char* CPUNames[CPU_COUNT] = { "none", "6502", "6502X", + "6502DTV", "65SC02", "65C02", "65816", @@ -69,6 +70,7 @@ const unsigned CPUIsets[CPU_COUNT] = { CPU_ISET_NONE, CPU_ISET_6502, CPU_ISET_6502 | CPU_ISET_6502X, + CPU_ISET_6502 | CPU_ISET_6502DTV, CPU_ISET_6502 | CPU_ISET_65SC02, CPU_ISET_6502 | CPU_ISET_65SC02 | CPU_ISET_65C02, CPU_ISET_6502 | CPU_ISET_65SC02 | CPU_ISET_65C02 | CPU_ISET_65816, diff --git a/src/common/cpu.h b/src/common/cpu.h index dcf1815db..2e75feaaf 100644 --- a/src/common/cpu.h +++ b/src/common/cpu.h @@ -50,6 +50,7 @@ typedef enum { CPU_NONE, /* No CPU - for assembler */ CPU_6502, CPU_6502X, /* "Extended", that is: with illegal opcodes */ + CPU_6502DTV, /* CPU_6502 + DTV extra and illegal opcodes */ CPU_65SC02, CPU_65C02, CPU_65816, @@ -65,6 +66,7 @@ enum { CPU_ISET_NONE = 1 << CPU_NONE, CPU_ISET_6502 = 1 << CPU_6502, CPU_ISET_6502X = 1 << CPU_6502X, + CPU_ISET_6502DTV = 1 << CPU_6502DTV, CPU_ISET_65SC02 = 1 << CPU_65SC02, CPU_ISET_65C02 = 1 << CPU_65C02, CPU_ISET_65816 = 1 << CPU_65816, diff --git a/src/common/fragdefs.h b/src/common/fragdefs.h index c3e589cb2..80dcad491 100644 --- a/src/common/fragdefs.h +++ b/src/common/fragdefs.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/inline.h b/src/common/inline.h index b90b3d1f4..2453547ac 100644 --- a/src/common/inline.h +++ b/src/common/inline.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2001-2005 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/inttypes.h b/src/common/inttypes.h index 29ac778ef..28ffb2cc5 100644 --- a/src/common/inttypes.h +++ b/src/common/inttypes.h @@ -38,7 +38,8 @@ -/* If we have <stdint.h>, include it; otherwise, adapt types from <stddef.h>. +/* If we have <stdint.h>, include it; otherwise, adapt types from <stddef.h> +** and define integer boundary constants. ** gcc and msvc don't define __STDC_VERSION__ without special flags, so check ** for them explicitly. Undefined symbols are replaced by zero; so, checks for ** defined(__GNUC__) and defined(_MSC_VER) aren't necessary. @@ -50,6 +51,7 @@ /* Assume that ptrdiff_t and size_t are wide enough to hold pointers. ** Assume that they are the widest type. */ +#include <limits.h> #include <stddef.h> typedef ptrdiff_t intptr_t; @@ -57,6 +59,62 @@ typedef size_t uintptr_t; typedef ptrdiff_t intmax_t; typedef size_t uintmax_t; +#define INT8_MAX (0x7F) +#define INT16_MAX (0x7FFF) +#define INT32_MAX (0x7FFFFFFF) + +#define INT8_MIN (-INT8_MAX - 1) +#define INT16_MIN (-INT16_MAX - 1) +#define INT32_MIN (-INT32_MAX - 1) + +#define UINT8_MAX (0xFF) +#define UINT16_MAX (0xFFFF) +#define UINT32_MAX (0xFFFFFFFF) + +#if UCHAR_MAX == UINT8_MAX +typedef unsigned char uint8_t; +#else +#error "No suitable type for uint8_t found." +#endif + +#if SCHAR_MIN == INT8_MIN && SCHAR_MAX == INT8_MAX +typedef signed char int8_t; +#else +#error "No suitable type for int8_t found." +#endif + +#if UINT_MAX == UINT16_MAX +typedef unsigned int uint16_t; +#elif USHRT_MAX == UINT16_MAX +typedef unsigned short uint16_t; +#else +#error "No suitable type for uint16_t found." +#endif + +#if INT_MIN == INT16_MIN && INT_MAX == INT16_MAX +typedef int int16_t; +#elif SHRT_MIN == INT16_MIN && SHRT_MAX == INT16_MAX +typedef short int16_t; +#else +#error "No suitable type for int16_t found." +#endif + +#if UINT_MAX == UINT32_MAX +typedef unsigned int uint32_t; +#elif ULONG_MAX == UINT32_MAX +typedef unsigned long uint32_t; +#else +#error "No suitable type for uint32_t found." +#endif + +#if INT_MIN == INT32_MIN && INT_MAX == INT32_MAX +typedef int int32_t; +#elif LONG_MIN == INT32_MIN && LONG_MAX == INT32_MAX +typedef long int32_t; +#else +#error "No suitable type for int32_t found." +#endif + #endif diff --git a/src/common/optdefs.h b/src/common/optdefs.h index fae517667..95af8ebba 100644 --- a/src/common/optdefs.h +++ b/src/common/optdefs.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/segnames.c b/src/common/segnames.c index bb9aac351..ea8a0125a 100644 --- a/src/common/segnames.c +++ b/src/common/segnames.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/segnames.h b/src/common/segnames.h index 0d57d6ac3..c4401a302 100644 --- a/src/common/segnames.h +++ b/src/common/segnames.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/strstack.c b/src/common/strstack.c index 508af178e..29dd10426 100644 --- a/src/common/strstack.c +++ b/src/common/strstack.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/strstack.h b/src/common/strstack.h index d29a47993..b0ff22bfb 100644 --- a/src/common/strstack.h +++ b/src/common/strstack.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/target.c b/src/common/target.c index a21ef2125..a35bf67a8 100644 --- a/src/common/target.c +++ b/src/common/target.c @@ -174,6 +174,7 @@ static const TargetEntry TargetMap[] = { { "sim6502", TGT_SIM6502 }, { "sim65c02", TGT_SIM65C02 }, { "supervision", TGT_SUPERVISION }, + { "sym1", TGT_SYM1 }, { "telestrat", TGT_TELESTRAT }, { "vic20", TGT_VIC20 }, }; @@ -215,6 +216,7 @@ static const TargetProperties PropertyTable[TGT_COUNT] = { { "gamate", CPU_6502, BINFMT_BINARY, CTNone }, { "c65", CPU_4510, BINFMT_BINARY, CTPET }, { "cx16", CPU_65C02, BINFMT_BINARY, CTPET }, + { "sym1", CPU_6502, BINFMT_BINARY, CTNone }, }; /* Target system */ diff --git a/src/common/target.h b/src/common/target.h index 50c400e2e..7f85713cf 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -85,6 +85,7 @@ typedef enum { TGT_GAMATE, TGT_C65, TGT_CX16, + TGT_SYM1, TGT_COUNT /* Number of target systems */ } target_t; diff --git a/src/common/tgttrans.c b/src/common/tgttrans.c index bd2056505..3310eab81 100644 --- a/src/common/tgttrans.c +++ b/src/common/tgttrans.c @@ -39,6 +39,8 @@ #include "check.h" #include "target.h" #include "tgttrans.h" +#include "coll.h" +#include "xmalloc.h" @@ -68,6 +70,9 @@ static unsigned char Tab[256] = { 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, }; +#define MAX_CHARMAP_STACK 16 +static Collection CharmapStack = STATIC_COLLECTION_INITIALIZER; + /*****************************************************************************/ @@ -127,3 +132,52 @@ void TgtTranslateSet (unsigned Index, unsigned char C) CHECK (Index < sizeof (Tab)); Tab[Index] = C; } + + + +int TgtTranslatePush (void) +/* Pushes the current translation table to the internal stack +** Returns 1 on success, 0 on stack full +*/ +{ + unsigned char* TempTab; + + if (CollCount (&CharmapStack) >= MAX_CHARMAP_STACK) { + return 0; + } + + TempTab = xmalloc (sizeof (Tab)); + memcpy (TempTab, Tab, sizeof (Tab)); + + CollAppend (&CharmapStack, TempTab); + return 1; +} + + + +int TgtTranslatePop (void) +/* Pops a translation table from the internal stack into the current table +** Returns 1 on success, 0 on stack empty +*/ +{ + unsigned char* TempTab; + + if (CollCount (&CharmapStack) == 0) { + return 0; + } + + TempTab = CollPop (&CharmapStack); + + memcpy (Tab, TempTab, sizeof (Tab)); + + xfree (TempTab); + return 1; +} + + + +int TgtTranslateStackIsEmpty (void) +/* Returns 1 if the internal stack is empty */ +{ + return CollCount (&CharmapStack) == 0; +} diff --git a/src/common/tgttrans.h b/src/common/tgttrans.h index 46981ec0f..a86d126db 100644 --- a/src/common/tgttrans.h +++ b/src/common/tgttrans.h @@ -70,6 +70,19 @@ void TgtTranslateStrBuf (StrBuf* Buf); void TgtTranslateSet (unsigned Index, unsigned char C); /* Set the translation code for the given character */ +int TgtTranslatePush (void); +/* Pushes the current translation table to the internal stack +** Returns 1 on success, 0 on stack full +*/ + +int TgtTranslatePop (void); +/* Pops a translation table from the internal stack into the current table +** Returns 1 on success, 0 on stack empty +*/ + +int TgtTranslateStackIsEmpty (void); +/* Returns 1 if the internal stack is empty */ + /* End of tgttrans.h */ diff --git a/src/common/va_copy.h b/src/common/va_copy.h index 2f56efa1a..4aa2428db 100644 --- a/src/common/va_copy.h +++ b/src/common/va_copy.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2004 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/version.c b/src/common/version.c index 202c61cc3..992be45ee 100644 --- a/src/common/version.c +++ b/src/common/version.c @@ -47,7 +47,7 @@ #define VER_MAJOR 2U -#define VER_MINOR 18U +#define VER_MINOR 19U diff --git a/src/common/xmalloc.c b/src/common/xmalloc.c index 327d378fe..192e8fadd 100644 --- a/src/common/xmalloc.c +++ b/src/common/xmalloc.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2006 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/common/xmalloc.h b/src/common/xmalloc.h index eb196b6a1..fc919a16f 100644 --- a/src/common/xmalloc.h +++ b/src/common/xmalloc.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2006 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65.vcxproj b/src/da65.vcxproj index 7a8b0de68..a40daf1d6 100644 --- a/src/da65.vcxproj +++ b/src/da65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{0BCFB793-2B25-40E2-B265-75848824AC4C}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>da65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> @@ -90,6 +63,7 @@ <ClCompile Include="da65\opc4510.c" /> <ClCompile Include="da65\opc6502.c" /> <ClCompile Include="da65\opc6502x.c" /> + <ClCompile Include="da65\opc6502dtv.c" /> <ClCompile Include="da65\opc65816.c" /> <ClCompile Include="da65\opc65c02.c" /> <ClCompile Include="da65\opc65sc02.c" /> @@ -114,6 +88,7 @@ <ClInclude Include="da65\opc4510.h" /> <ClInclude Include="da65\opc6502.h" /> <ClInclude Include="da65\opc6502x.h" /> + <ClInclude Include="da65\opc6502dtv.h" /> <ClInclude Include="da65\opc65816.h" /> <ClInclude Include="da65\opc65c02.h" /> <ClInclude Include="da65\opc65sc02.h" /> diff --git a/src/da65/asminc.c b/src/da65/asminc.c index 59ba0aab4..4d9da2594 100644 --- a/src/da65/asminc.c +++ b/src/da65/asminc.c @@ -133,7 +133,7 @@ void AsmInc (const char* Filename, char CommentStart, int IgnoreUnknown) SB_Terminate (&Ident); } else { if (!IgnoreUnknown) { - Error ("%s(%u): Syntax error", Filename, Line); + Error ("%s:%u: Syntax error", Filename, Line); } continue; } @@ -148,7 +148,7 @@ void AsmInc (const char* Filename, char CommentStart, int IgnoreUnknown) ++L; } else { if (!IgnoreUnknown) { - Error ("%s(%u): Missing '='", Filename, Line); + Error ("%s:%u: Missing '='", Filename, Line); } continue; } @@ -192,7 +192,7 @@ void AsmInc (const char* Filename, char CommentStart, int IgnoreUnknown) /* Must have at least one digit */ if (Digits == 0) { if (!IgnoreUnknown) { - Error ("%s(%u): Error in number format", Filename, Line); + Error ("%s:%u: Error in number format", Filename, Line); } continue; } @@ -213,7 +213,7 @@ void AsmInc (const char* Filename, char CommentStart, int IgnoreUnknown) /* Check for a comment character or end of line */ if (*L != CommentStart && *L != '\0') { if (!IgnoreUnknown) { - Error ("%s(%u): Trailing garbage", Filename, Line); + Error ("%s:%u: Trailing garbage", Filename, Line); } continue; } diff --git a/src/da65/asminc.h b/src/da65/asminc.h index bf48710e9..6d58cd155 100644 --- a/src/da65/asminc.h +++ b/src/da65/asminc.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2005 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/code.h b/src/da65/code.h index 0d21e61e1..50e68ebdf 100644 --- a/src/da65/code.h +++ b/src/da65/code.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/comments.c b/src/da65/comments.c index 64b64ca28..cf0b9d4e9 100644 --- a/src/da65/comments.c +++ b/src/da65/comments.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2006 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/comments.h b/src/da65/comments.h index 1b8bc1771..1d95111a9 100644 --- a/src/da65/comments.h +++ b/src/da65/comments.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2006 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/error.h b/src/da65/error.h index d0221bcc4..8027b3c1f 100644 --- a/src/da65/error.h +++ b/src/da65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/global.c b/src/da65/global.c index 7df1bd977..e258aecdd 100644 --- a/src/da65/global.c +++ b/src/da65/global.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2006 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/handler.c b/src/da65/handler.c index f8a778b22..255b8da86 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -346,7 +346,12 @@ void OH_Relative (const OpcDesc* D) GenerateLabel (D->Flags, Addr); /* Output the line */ - OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); + if (HaveLabel(Addr)) { + OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); + } else { + /* No label -- make a relative address expression */ + OneLine (D, "* + (%d)", (int) Offs + 2); + } } diff --git a/src/da65/infofile.h b/src/da65/infofile.h index b8b3f53a8..49ddfcd5d 100644 --- a/src/da65/infofile.h +++ b/src/da65/infofile.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opc4510.h b/src/da65/opc4510.h index 10735952c..a87254cd1 100644 --- a/src/da65/opc4510.h +++ b/src/da65/opc4510.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opc6502.h b/src/da65/opc6502.h index c890e241b..f9d03c085 100644 --- a/src/da65/opc6502.h +++ b/src/da65/opc6502.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opc6502dtv.c b/src/da65/opc6502dtv.c new file mode 100644 index 000000000..fe83ad598 --- /dev/null +++ b/src/da65/opc6502dtv.c @@ -0,0 +1,308 @@ +/*****************************************************************************/ +/* */ +/* opc6502dtv.c */ +/* */ +/* 6502 opcode description table with NMOS illegals and DTV opcodes */ +/* */ +/* */ +/* */ +/* (C) 2003-2011, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* da65 */ +#include "handler.h" +#include "opc6502dtv.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Descriptions for all opcodes. Base table from opc6502x.c with DTV opcodes, +** where illegal opcodes are filtered based on their support on DTV. +*/ +const OpcDesc OpcTable_6502DTV[256] = { + { "brk", 1, flNone, OH_Implicit }, /* $00 */ + { "ora", 2, flUseLabel, OH_DirectXIndirect }, /* $01 */ + { "", 1, flIllegal, OH_Illegal, }, /* $02 */ + { "", 1, flIllegal, OH_Illegal, }, /* $03 */ + { "nop", 2, flUseLabel, OH_Direct }, /* $04 */ + { "ora", 2, flUseLabel, OH_Direct }, /* $05 */ + { "asl", 2, flUseLabel, OH_Direct }, /* $06 */ + { "", 1, flIllegal, OH_Illegal, }, /* $07 */ + { "php", 1, flNone, OH_Implicit }, /* $08 */ + { "ora", 2, flNone, OH_Immediate }, /* $09 */ + { "asl", 1, flNone, OH_Accumulator }, /* $0a */ + { "anc", 2, flNone, OH_Immediate }, /* $0b */ + { "nop", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0c */ + { "ora", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0d */ + { "asl", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $0e */ + { "", 1, flIllegal, OH_Illegal, }, /* $0f */ + { "bpl", 2, flLabel, OH_Relative }, /* $10 */ + { "ora", 2, flUseLabel, OH_DirectIndirectY }, /* $11 */ + { "bra", 2, flLabel, OH_Relative }, /* $12 */ + { "", 1, flIllegal, OH_Illegal, }, /* $13 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $14 */ + { "ora", 2, flUseLabel, OH_DirectX }, /* $15 */ + { "asl", 2, flUseLabel, OH_DirectX }, /* $16 */ + { "", 1, flIllegal, OH_Illegal, }, /* $17 */ + { "clc", 1, flNone, OH_Implicit }, /* $18 */ + { "ora", 3, flUseLabel, OH_AbsoluteY }, /* $19 */ + { "nop", 1, flNone, OH_Implicit }, /* $1a */ + { "", 1, flIllegal, OH_Illegal, }, /* $1b */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1c */ + { "ora", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1d */ + { "asl", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $1e */ + { "", 1, flIllegal, OH_Illegal, }, /* $1f */ + { "jsr", 3, flLabel, OH_Absolute }, /* $20 */ + { "and", 2, flUseLabel, OH_DirectXIndirect }, /* $21 */ + { "", 1, flIllegal, OH_Illegal, }, /* $22 */ + { "rla", 2, flUseLabel, OH_DirectXIndirect }, /* $23 */ + { "bit", 2, flUseLabel, OH_Direct }, /* $24 */ + { "and", 2, flUseLabel, OH_Direct }, /* $25 */ + { "rol", 2, flUseLabel, OH_Direct }, /* $26 */ + { "rla", 2, flUseLabel, OH_Direct }, /* $27 */ + { "plp", 1, flNone, OH_Implicit }, /* $28 */ + { "and", 2, flNone, OH_Immediate }, /* $29 */ + { "rol", 1, flNone, OH_Accumulator }, /* $2a */ + { "anc", 2, flNone, OH_Immediate }, /* $2b */ + { "bit", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2c */ + { "and", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2d */ + { "rol", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2e */ + { "rla", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $2f */ + { "bmi", 2, flLabel, OH_Relative }, /* $30 */ + { "and", 2, flUseLabel, OH_DirectIndirectY }, /* $31 */ + { "sac", 2, flNone, OH_Immediate }, /* $32 */ + { "rla", 2, flUseLabel, OH_DirectIndirectY }, /* $33 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $34 */ + { "and", 2, flUseLabel, OH_DirectX }, /* $35 */ + { "rol", 2, flUseLabel, OH_DirectX }, /* $36 */ + { "rla", 2, flUseLabel, OH_DirectX }, /* $37 */ + { "sec", 1, flNone, OH_Implicit }, /* $38 */ + { "and", 3, flUseLabel, OH_AbsoluteY }, /* $39 */ + { "nop", 1, flNone, OH_Implicit }, /* $3a */ + { "rla", 3, flUseLabel, OH_AbsoluteY }, /* $3b */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3c */ + { "and", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3d */ + { "rol", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3e */ + { "rla", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3f */ + { "rti", 1, flNone, OH_Rts }, /* $40 */ + { "eor", 2, flUseLabel, OH_DirectXIndirect }, /* $41 */ + { "sir", 2, flNone, OH_Immediate }, /* $42 */ + { "", 1, flIllegal, OH_Illegal, }, /* $43 */ + { "nop", 2, flUseLabel, OH_Direct }, /* $44 */ + { "eor", 2, flUseLabel, OH_Direct }, /* $45 */ + { "lsr", 2, flUseLabel, OH_Direct }, /* $46 */ + { "", 1, flIllegal, OH_Illegal, }, /* $47 */ + { "pha", 1, flNone, OH_Implicit }, /* $48 */ + { "eor", 2, flNone, OH_Immediate }, /* $49 */ + { "lsr", 1, flNone, OH_Accumulator }, /* $4a */ + { "alr", 2, flNone, OH_Immediate }, /* $4b */ + { "jmp", 3, flLabel, OH_JmpAbsolute }, /* $4c */ + { "eor", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4d */ + { "lsr", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $4e */ + { "", 1, flIllegal, OH_Illegal, }, /* $4f */ + { "bvc", 2, flLabel, OH_Relative }, /* $50 */ + { "eor", 2, flUseLabel, OH_DirectIndirectY }, /* $51 */ + { "", 1, flIllegal, OH_Illegal, }, /* $52 */ + { "", 1, flIllegal, OH_Illegal, }, /* $53 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $54 */ + { "eor", 2, flUseLabel, OH_DirectX }, /* $55 */ + { "lsr", 2, flUseLabel, OH_DirectX }, /* $56 */ + { "", 1, flIllegal, OH_Illegal, }, /* $57 */ + { "cli", 1, flNone, OH_Implicit }, /* $58 */ + { "eor", 3, flUseLabel, OH_AbsoluteY }, /* $59 */ + { "nop", 1, flNone, OH_Implicit }, /* $5a */ + { "", 1, flIllegal, OH_Illegal, }, /* $5b */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5c */ + { "eor", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5d */ + { "lsr", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $5e */ + { "", 1, flIllegal, OH_Illegal, }, /* $5f */ + { "rts", 1, flNone, OH_Rts }, /* $60 */ + { "adc", 2, flUseLabel, OH_DirectXIndirect }, /* $61 */ + { "", 1, flIllegal, OH_Illegal, }, /* $62 */ + { "rra", 2, flUseLabel, OH_DirectXIndirect }, /* $63 */ + { "nop", 2, flUseLabel, OH_Direct }, /* $64 */ + { "adc", 2, flUseLabel, OH_Direct }, /* $65 */ + { "ror", 2, flUseLabel, OH_Direct }, /* $66 */ + { "rra", 2, flUseLabel, OH_Direct }, /* $67 */ + { "pla", 1, flNone, OH_Implicit }, /* $68 */ + { "adc", 2, flNone, OH_Immediate }, /* $69 */ + { "ror", 1, flNone, OH_Accumulator }, /* $6a */ + { "arr", 2, flNone, OH_Immediate }, /* $6b */ + { "jmp", 3, flLabel, OH_JmpAbsoluteIndirect }, /* $6c */ + { "adc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6d */ + { "ror", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6e */ + { "rra", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $6f */ + { "bvs", 2, flLabel, OH_Relative }, /* $70 */ + { "adc", 2, flUseLabel, OH_DirectIndirectY }, /* $71 */ + { "", 1, flIllegal, OH_Illegal, }, /* $72 */ + { "rra", 2, flUseLabel, OH_DirectIndirectY }, /* $73 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $74 */ + { "adc", 2, flUseLabel, OH_DirectX }, /* $75 */ + { "ror", 2, flUseLabel, OH_DirectX }, /* $76 */ + { "rra", 2, flUseLabel, OH_DirectX }, /* $77 */ + { "sei", 1, flNone, OH_Implicit }, /* $78 */ + { "adc", 3, flUseLabel, OH_AbsoluteY }, /* $79 */ + { "nop", 1, flNone, OH_Implicit }, /* $7a */ + { "rra", 3, flUseLabel, OH_AbsoluteY }, /* $7b */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7c */ + { "adc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7d */ + { "ror", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7e */ + { "rra", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $7f */ + { "nop", 2, flNone, OH_Immediate }, /* $80 */ + { "sta", 2, flUseLabel, OH_DirectXIndirect }, /* $81 */ + { "nop", 2, flNone, OH_Immediate }, /* $82 */ + { "", 1, flIllegal, OH_Illegal, }, /* $83 */ + { "sty", 2, flUseLabel, OH_Direct }, /* $84 */ + { "sta", 2, flUseLabel, OH_Direct }, /* $85 */ + { "stx", 2, flUseLabel, OH_Direct }, /* $86 */ + { "", 1, flIllegal, OH_Illegal, }, /* $87 */ + { "dey", 1, flNone, OH_Implicit }, /* $88 */ + { "nop", 2, flNone, OH_Immediate }, /* $89 */ + { "txa", 1, flNone, OH_Implicit }, /* $8a */ + { "", 1, flIllegal, OH_Illegal, }, /* $8b */ + { "sty", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8c */ + { "sta", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8d */ + { "stx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $8e */ + { "", 1, flIllegal, OH_Illegal, }, /* $8f */ + { "bcc", 2, flLabel, OH_Relative }, /* $90 */ + { "sta", 2, flUseLabel, OH_DirectIndirectY }, /* $91 */ + { "", 1, flIllegal, OH_Illegal, }, /* $92 */ + { "", 1, flIllegal, OH_Illegal, }, /* $93 */ + { "sty", 2, flUseLabel, OH_DirectX }, /* $94 */ + { "sta", 2, flUseLabel, OH_DirectX }, /* $95 */ + { "stx", 2, flUseLabel, OH_DirectY }, /* $96 */ + { "", 1, flIllegal, OH_Illegal, }, /* $97 */ + { "tya", 1, flNone, OH_Implicit }, /* $98 */ + { "sta", 3, flUseLabel, OH_AbsoluteY }, /* $99 */ + { "txs", 1, flNone, OH_Implicit }, /* $9a */ + { "", 1, flIllegal, OH_Illegal, }, /* $9b */ + { "shy", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9c */ + { "sta", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $9d */ + { "shx", 3, flUseLabel, OH_AbsoluteY }, /* $9e */ + { "", 1, flIllegal, OH_Illegal, }, /* $9f */ + { "ldy", 2, flNone, OH_Immediate }, /* $a0 */ + { "lda", 2, flUseLabel, OH_DirectXIndirect }, /* $a1 */ + { "ldx", 2, flNone, OH_Immediate }, /* $a2 */ + { "lax", 2, flUseLabel, OH_DirectXIndirect }, /* $a3 */ + { "ldy", 2, flUseLabel, OH_Direct }, /* $a4 */ + { "lda", 2, flUseLabel, OH_Direct }, /* $a5 */ + { "ldx", 2, flUseLabel, OH_Direct }, /* $a6 */ + { "lax", 2, flUseLabel, OH_Direct }, /* $a7 */ + { "tay", 1, flNone, OH_Implicit }, /* $a8 */ + { "lda", 2, flNone, OH_Immediate }, /* $a9 */ + { "tax", 1, flNone, OH_Implicit }, /* $aa */ + { "lax", 2, flNone, OH_Immediate }, /* $ab */ + { "ldy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ac */ + { "lda", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ad */ + { "ldx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ae */ + { "lax", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $af */ + { "bcs", 2, flLabel, OH_Relative }, /* $b0 */ + { "lda", 2, flUseLabel, OH_DirectIndirectY }, /* $b1 */ + { "", 1, flIllegal, OH_Illegal, }, /* $b2 */ + { "lax", 2, flUseLabel, OH_DirectIndirectY }, /* $b3 */ + { "ldy", 2, flUseLabel, OH_DirectX }, /* $b4 */ + { "lda", 2, flUseLabel, OH_DirectX }, /* $b5 */ + { "ldx", 2, flUseLabel, OH_DirectY }, /* $b6 */ + { "lax", 2, flUseLabel, OH_DirectY }, /* $b7 */ + { "clv", 1, flNone, OH_Implicit }, /* $b8 */ + { "lda", 3, flUseLabel, OH_AbsoluteY }, /* $b9 */ + { "tsx", 1, flNone, OH_Implicit }, /* $ba */ + { "las", 3, flUseLabel, OH_AbsoluteY }, /* $bb */ + { "ldy", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bc */ + { "lda", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $bd */ + { "ldx", 3, flUseLabel|flAbsOverride, OH_AbsoluteY }, /* $be */ + { "lax", 3, flUseLabel|flAbsOverride, OH_AbsoluteY }, /* $bf */ + { "cpy", 2, flNone, OH_Immediate }, /* $c0 */ + { "cmp", 2, flUseLabel, OH_DirectXIndirect }, /* $c1 */ + { "nop", 2, flNone, OH_Immediate }, /* $c2 */ + { "", 1, flIllegal, OH_Illegal, }, /* $c3 */ + { "cpy", 2, flUseLabel, OH_Direct }, /* $c4 */ + { "cmp", 2, flUseLabel, OH_Direct }, /* $c5 */ + { "dec", 2, flUseLabel, OH_Direct }, /* $c6 */ + { "", 1, flIllegal, OH_Illegal, }, /* $c7 */ + { "iny", 1, flNone, OH_Implicit }, /* $c8 */ + { "cmp", 2, flNone, OH_Immediate }, /* $c9 */ + { "dex", 1, flNone, OH_Implicit }, /* $ca */ + { "axs", 2, flNone, OH_Immediate }, /* $cb */ + { "cpy", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cc */ + { "cmp", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $cd */ + { "dec", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ce */ + { "", 1, flIllegal, OH_Illegal, }, /* $cf */ + { "bne", 2, flLabel, OH_Relative }, /* $d0 */ + { "cmp", 2, flUseLabel, OH_DirectIndirectY }, /* $d1 */ + { "", 1, flIllegal, OH_Illegal, }, /* $d2 */ + { "", 1, flIllegal, OH_Illegal, }, /* $d3 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $d4 */ + { "cmp", 2, flUseLabel, OH_DirectX }, /* $d5 */ + { "dec", 2, flUseLabel, OH_DirectX }, /* $d6 */ + { "", 1, flIllegal, OH_Illegal, }, /* $d7 */ + { "cld", 1, flNone, OH_Implicit }, /* $d8 */ + { "cmp", 3, flUseLabel, OH_AbsoluteY }, /* $d9 */ + { "nop", 1, flNone, OH_Implicit }, /* $da */ + { "", 1, flIllegal, OH_Illegal, }, /* $db */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $dc */ + { "cmp", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $dd */ + { "dec", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $de */ + { "", 1, flIllegal, OH_Illegal, }, /* $df */ + { "cpx", 2, flNone, OH_Immediate }, /* $e0 */ + { "sbc", 2, flUseLabel, OH_DirectXIndirect }, /* $e1 */ + { "nop", 2, flNone, OH_Immediate }, /* $e2 */ + { "", 1, flIllegal, OH_Illegal, }, /* $e3 */ + { "cpx", 2, flUseLabel, OH_Direct }, /* $e4 */ + { "sbc", 2, flUseLabel, OH_Direct }, /* $e5 */ + { "inc", 2, flUseLabel, OH_Direct }, /* $e6 */ + { "", 1, flIllegal, OH_Illegal, }, /* $e7 */ + { "inx", 1, flNone, OH_Implicit }, /* $e8 */ + { "sbc", 2, flNone, OH_Immediate }, /* $e9 */ + { "nop", 1, flNone, OH_Implicit }, /* $ea */ + { "sbc", 2, flNone, OH_Immediate }, /* $eb */ + { "cpx", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ec */ + { "sbc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ed */ + { "inc", 3, flUseLabel|flAbsOverride, OH_Absolute }, /* $ee */ + { "", 1, flIllegal, OH_Illegal, }, /* $ef */ + { "beq", 2, flLabel, OH_Relative }, /* $f0 */ + { "sbc", 2, flUseLabel, OH_DirectIndirectY }, /* $f1 */ + { "", 1, flIllegal, OH_Illegal, }, /* $f2 */ + { "", 1, flIllegal, OH_Illegal, }, /* $f3 */ + { "nop", 2, flUseLabel, OH_DirectX }, /* $f4 */ + { "sbc", 2, flUseLabel, OH_DirectX }, /* $f5 */ + { "inc", 2, flUseLabel, OH_DirectX }, /* $f6 */ + { "", 1, flIllegal, OH_Illegal, }, /* $f7 */ + { "sed", 1, flNone, OH_Implicit }, /* $f8 */ + { "sbc", 3, flUseLabel, OH_AbsoluteY }, /* $f9 */ + { "nop", 1, flNone, OH_Implicit }, /* $fa */ + { "", 1, flIllegal, OH_Illegal, }, /* $fb */ + { "nop", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fc */ + { "sbc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fd */ + { "inc", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $fe */ + { "", 1, flIllegal, OH_Illegal, }, /* $ff */ +}; diff --git a/src/da65/opc6502dtv.h b/src/da65/opc6502dtv.h new file mode 100644 index 000000000..e63e4e44c --- /dev/null +++ b/src/da65/opc6502dtv.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* opc6502dtv.h */ +/* */ +/* 6502 opcode description table with NMOS illegals and DTV opcodes */ +/* */ +/* */ +/* */ +/* (C) 2003-2011, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef OPC6502DTV_H +#define OPC6502DTV_H + + + +#include "opcdesc.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Descriptions for all opcodes */ +extern const OpcDesc OpcTable_6502DTV[256]; + + + +/* End of opc6502dtv.h */ +#endif + + + + diff --git a/src/da65/opc65816.h b/src/da65/opc65816.h index 12ffc4a37..341fbf085 100644 --- a/src/da65/opc65816.h +++ b/src/da65/opc65816.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opc65c02.h b/src/da65/opc65c02.h index 38138aa51..aa2fa9756 100644 --- a/src/da65/opc65c02.h +++ b/src/da65/opc65c02.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opc65sc02.h b/src/da65/opc65sc02.h index 391f425ea..c00a8e91f 100644 --- a/src/da65/opc65sc02.h +++ b/src/da65/opc65sc02.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opcdesc.h b/src/da65/opcdesc.h index 7913131cd..399a0962d 100644 --- a/src/da65/opcdesc.h +++ b/src/da65/opcdesc.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/opctable.c b/src/da65/opctable.c index 031b1239b..255a3557f 100644 --- a/src/da65/opctable.c +++ b/src/da65/opctable.c @@ -38,6 +38,7 @@ #include "opc4510.h" #include "opc6502.h" #include "opc6502x.h" +#include "opc6502dtv.h" #include "opc65816.h" #include "opc65c02.h" #include "opc65sc02.h" @@ -70,6 +71,7 @@ void SetOpcTable (cpu_t CPU) switch (CPU) { case CPU_6502: OpcTable = OpcTable_6502; break; case CPU_6502X: OpcTable = OpcTable_6502X; break; + case CPU_6502DTV: OpcTable = OpcTable_6502DTV; break; case CPU_65SC02: OpcTable = OpcTable_65SC02; break; case CPU_65C02: OpcTable = OpcTable_65C02; break; case CPU_HUC6280: OpcTable = OpcTable_HuC6280; break; diff --git a/src/da65/opctable.h b/src/da65/opctable.h index d5c81b216..69a64db9c 100644 --- a/src/da65/opctable.h +++ b/src/da65/opctable.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2000-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/da65/scanner.c b/src/da65/scanner.c index 85853d6c4..33fb3a826 100644 --- a/src/da65/scanner.c +++ b/src/da65/scanner.c @@ -94,7 +94,7 @@ void InfoWarning (const char* Format, ...) xvsprintf (Buf, sizeof (Buf), Format, ap); va_end (ap); - fprintf (stderr, "%s(%u): Warning: %s\n", + fprintf (stderr, "%s:%u: Warning: %s\n", InputSrcName, InfoErrorLine, Buf); } @@ -110,7 +110,7 @@ void InfoError (const char* Format, ...) xvsprintf (Buf, sizeof (Buf), Format, ap); va_end (ap); - fprintf (stderr, "%s(%u): Error: %s\n", + fprintf (stderr, "%s:%u: Error: %s\n", InputSrcName, InfoErrorLine, Buf); exit (EXIT_FAILURE); } diff --git a/src/grc65.vcxproj b/src/grc65.vcxproj index 841ec635a..fbd44fa3e 100644 --- a/src/grc65.vcxproj +++ b/src/grc65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{E0FD0AB3-3BEE-496F-8108-A8E0F8933F39}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>grc65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/ld65.vcxproj b/src/ld65.vcxproj index a78f3128b..9e4b08621 100644 --- a/src/ld65.vcxproj +++ b/src/ld65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{26C749A0-814C-47A2-9D36-AE92AE932FE4}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>ld65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/ld65/asserts.c b/src/ld65/asserts.c index 626ed94a6..e9bae83e5 100644 --- a/src/ld65/asserts.c +++ b/src/ld65/asserts.c @@ -140,12 +140,12 @@ void CheckAssertions (void) case ASSERT_ACT_WARN: case ASSERT_ACT_LDWARN: - Warning ("%s(%u): %s", Module, Line, Message); + Warning ("%s:%u: %s", Module, Line, Message); break; case ASSERT_ACT_ERROR: case ASSERT_ACT_LDERROR: - Error ("%s(%u): %s", Module, Line, Message); + Error ("%s:%u: %s", Module, Line, Message); break; default: diff --git a/src/ld65/dbgfile.h b/src/ld65/dbgfile.h index b8e1a534f..cabb60f8a 100644 --- a/src/ld65/dbgfile.h +++ b/src/ld65/dbgfile.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ld65/error.h b/src/ld65/error.h index b49d8919c..75b8e0bc1 100644 --- a/src/ld65/error.h +++ b/src/ld65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ld65/exports.c b/src/ld65/exports.c index 5df7a37c9..35de5c8f2 100644 --- a/src/ld65/exports.c +++ b/src/ld65/exports.c @@ -166,7 +166,7 @@ Import* ReadImport (FILE* F, ObjData* Obj) */ if (ObjHasFiles (I->Obj)) { const LineInfo* LI = GetImportPos (I); - Error ("Invalid import size in for '%s', imported from %s(%u): 0x%02X", + Error ("Invalid import size in for '%s', imported from %s:%u: 0x%02X", GetString (I->Name), GetSourceName (LI), GetSourceLine (LI), @@ -690,12 +690,12 @@ static void CheckSymType (const Export* E) */ if (E->Obj) { /* The export comes from an object file */ - SB_Printf (&ExportLoc, "%s, %s(%u)", + SB_Printf (&ExportLoc, "%s, %s:%u", GetString (E->Obj->Name), GetSourceName (ExportLI), GetSourceLine (ExportLI)); } else if (ExportLI) { - SB_Printf (&ExportLoc, "%s(%u)", + SB_Printf (&ExportLoc, "%s:%u", GetSourceName (ExportLI), GetSourceLine (ExportLI)); } else { @@ -706,7 +706,7 @@ static void CheckSymType (const Export* E) } if (I->Obj) { /* The import comes from an object file */ - SB_Printf (&ImportLoc, "%s, %s(%u)", + SB_Printf (&ImportLoc, "%s, %s:%u", GetString (I->Obj->Name), GetSourceName (ImportLI), GetSourceLine (ImportLI)); @@ -714,7 +714,7 @@ static void CheckSymType (const Export* E) /* The import is linker generated and we have line ** information */ - SB_Printf (&ImportLoc, "%s(%u)", + SB_Printf (&ImportLoc, "%s:%u", GetSourceName (ImportLI), GetSourceLine (ImportLI)); } else { @@ -774,17 +774,16 @@ static void PrintUnresolved (ExpCheckFunc F, void* Data) if (E->Expr == 0 && E->ImpCount > 0 && F (E->Name, Data) == 0) { /* Unresolved external */ Import* Imp = E->ImpList; - fprintf (stderr, - "Unresolved external '%s' referenced in:\n", - GetString (E->Name)); + const char* name = GetString (E->Name); while (Imp) { unsigned J; for (J = 0; J < CollCount (&Imp->RefLines); ++J) { const LineInfo* LI = CollConstAt (&Imp->RefLines, J); fprintf (stderr, - " %s(%u)\n", + "%s:%u: Error: Unresolved external '%s'\n", GetSourceName (LI), - GetSourceLine (LI)); + GetSourceLine (LI), + name); } Imp = Imp->Next; } @@ -996,7 +995,7 @@ void PrintImportMap (FILE* F) const LineInfo* LI = GetImportPos (Imp); if (LI) { fprintf (F, - " %-25s %s(%u)\n", + " %-25s %s:%u\n", GetObjFileName (Imp->Obj), GetSourceName (LI), GetSourceLine (LI)); @@ -1058,7 +1057,7 @@ void CircularRefError (const Export* E) /* Print an error about a circular reference using to define the given export */ { const LineInfo* LI = GetExportPos (E); - Error ("Circular reference for symbol '%s', %s(%u)", + Error ("Circular reference for symbol '%s', %s:%u", GetString (E->Name), GetSourceName (LI), GetSourceLine (LI)); diff --git a/src/ld65/expr.c b/src/ld65/expr.c index bc3d7941c..ff210e315 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -321,20 +321,18 @@ long GetExprVal (ExprNode* Expr) return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); case EXPR_DIV: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Division by zero"); } - return Left / Right; + return GetExprVal (Expr->Left) / Right; case EXPR_MOD: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Modulo operation with zero"); } - return Left % Right; + return GetExprVal (Expr->Left) % Right; case EXPR_OR: return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); @@ -403,17 +401,20 @@ long GetExprVal (ExprNode* Expr) case EXPR_BANK: GetSegExprVal (Expr->Left, &D); - if (D.TooComplex || D.Seg == 0) { - Error ("Argument for .BANK is not segment relative or too complex"); + if (D.TooComplex) { + Error ("Argument of .BANK() is too complex"); + } + if (D.Seg == 0) { + Error ("Argument of .BANK() isn't a label attached to a segment"); } if (D.Seg->MemArea == 0) { - Error ("Segment '%s' is referenced by .BANK but " - "not assigned to a memory area", + Error ("Segment '%s' is referenced by .BANK()," + " but not assigned to a memory area", GetString (D.Seg->Name)); } if (D.Seg->MemArea->BankExpr == 0) { - Error ("Memory area '%s' is referenced by .BANK but " - "has no BANK attribute", + Error ("Memory area '%s' is referenced by .BANK()," + " but has no BANK attribute", GetString (D.Seg->MemArea->Name)); } return GetExprVal (D.Seg->MemArea->BankExpr); @@ -457,13 +458,15 @@ long GetExprVal (ExprNode* Expr) static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) /* Check if the given expression consists of a segment reference and only -** constant values, additions and subtractions. If anything else is found, +** constant values, additions, and subtractions. If anything else is found, ** set D->TooComplex to true. ** Internal, recursive routine. */ { Export* E; + CHECK (Expr != 0); + switch (Expr->Op) { case EXPR_LITERAL: @@ -479,7 +482,7 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) */ if (ExportHasMark (E)) { CircularRefError (E); - } else { + } else if (E->Expr != 0) { MarkExport (E); GetSegExprValInternal (E->Expr, D, Sign); UnmarkExport (E); diff --git a/src/ld65/o65.h b/src/ld65/o65.h index 2112537ff..68ed94ba7 100644 --- a/src/ld65/o65.h +++ b/src/ld65/o65.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1999-2005 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/ld65/scanner.c b/src/ld65/scanner.c index d6278abbd..256d47f07 100644 --- a/src/ld65/scanner.c +++ b/src/ld65/scanner.c @@ -92,7 +92,7 @@ void CfgWarning (const FilePos* Pos, const char* Format, ...) SB_VPrintf (&Buf, Format, ap); va_end (ap); - Warning ("%s(%u): %s", + Warning ("%s:%u: %s", GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf)); SB_Done (&Buf); } @@ -109,7 +109,7 @@ void CfgError (const FilePos* Pos, const char* Format, ...) SB_VPrintf (&Buf, Format, ap); va_end (ap); - Error ("%s(%u): %s", + Error ("%s:%u: %s", GetString (Pos->Name), Pos->Line, SB_GetConstBuf (&Buf)); SB_Done (&Buf); } diff --git a/src/ld65/xex.c b/src/ld65/xex.c index c57fa0a8c..94f3920b5 100644 --- a/src/ld65/xex.c +++ b/src/ld65/xex.c @@ -241,9 +241,6 @@ static unsigned long XexWriteMem (XexDesc* D, MemoryArea* M) /* Store initial position to get total file size */ unsigned long StartPos = ftell (D->F); - /* Always write a segment header for each memory area */ - D->HeadPos = 0; - /* Get the start address and size of this memory area */ unsigned long Addr = M->Start; @@ -400,6 +397,7 @@ void XexWriteTarget (XexDesc* D, struct File* F) if (D->F == 0) { Error ("Cannot open `%s': %s", D->Filename, strerror (errno)); } + D->HeadPos = 0; /* Keep the user happy */ Print (stdout, 1, "Opened `%s'...\n", D->Filename); @@ -415,6 +413,8 @@ void XexWriteTarget (XexDesc* D, struct File* F) Write16 (D->F, 0x2E2); Write16 (D->F, 0x2E3); Write16 (D->F, GetExportVal (I->InitAd->Exp)); + /* Always write a new segment header after an INITAD segment */ + D->HeadPos = 0; } } diff --git a/src/msbuild.cmd b/src/msbuild.cmd new file mode 100644 index 000000000..2e1821f0a --- /dev/null +++ b/src/msbuild.cmd @@ -0,0 +1,18 @@ +@echo off + +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" goto vs2017 +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" goto vs2019 + +echo Error: VsDevCmd.bat not found! +goto:eof + +:vs2017 +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" +goto run + +:vs2019 +call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" +goto run + +:run +msbuild.exe %* diff --git a/src/od65.vcxproj b/src/od65.vcxproj index 03ea41fce..1a1527067 100644 --- a/src/od65.vcxproj +++ b/src/od65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{FF8576C2-1253-44FE-A51B-D9AE35F3CEAD}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>od65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/od65/error.h b/src/od65/error.h index 8e1469a34..8a3ac0652 100644 --- a/src/od65/error.h +++ b/src/od65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/od65/fileio.h b/src/od65/fileio.h index 068c4d9a3..af5559f6b 100644 --- a/src/od65/fileio.h +++ b/src/od65/fileio.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 1998-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/sim65.vcxproj b/src/sim65.vcxproj index 9dc61e53f..97fc3855a 100644 --- a/src/sim65.vcxproj +++ b/src/sim65.vcxproj @@ -13,67 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{002A366E-2863-46A8-BDDE-DDF534AAEC73}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>sim65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/sim65/error.c b/src/sim65/error.c index 30d90c700..441b07d2a 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2002-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/sim65/error.h b/src/sim65/error.h index cbb785875..ea54fa048 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2002-2003 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 603a07e9a..e73bd3400 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2013-2013 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index 99c28fa02..bfa38e047 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -7,7 +7,7 @@ /* */ /* */ /* (C) 2013-2013 Ullrich von Bassewitz */ -/* Römerstrasse 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ diff --git a/src/sp65.vcxproj b/src/sp65.vcxproj index 3b770ec57..1b7a18427 100644 --- a/src/sp65.vcxproj +++ b/src/sp65.vcxproj @@ -13,66 +13,39 @@ <PropertyGroup Label="Globals"> <ProjectGuid>{4388D1AF-C7EA-4AD4-8E80-CA1FB7BF76BF}</ProjectGuid> <Keyword>Win32Proj</Keyword> - <RootNamespace>sp65</RootNamespace> - <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="cc65.props" /> + </ImportGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <PlatformToolset>v141</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <OutDir>$(SolutionDir)..\bin\</OutDir> - <IntDir>$(SolutionDir)..\wrk\$(ProjectName)\$(Configuration)\</IntDir> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <PreprocessorDefinitions>_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NDEBUG</PreprocessorDefinitions> - <AdditionalIncludeDirectories>common</AdditionalIncludeDirectories> - <TreatWarningAsError>true</TreatWarningAsError> + <PreprocessorDefinitions>_CONSOLE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> - <GenerateDebugInformation>false</GenerateDebugInformation> - <AdditionalDependencies>$(IntDir)..\..\common\$(Configuration)\common.lib</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/targettest/Makefile b/targettest/Makefile new file mode 100644 index 000000000..f3694335c --- /dev/null +++ b/targettest/Makefile @@ -0,0 +1,401 @@ +# +# Makefile for cc65 testcode +# +# This Makefile requires GNU make +# + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to define a variable +# containing a single space character. +SPACE := +SPACE += + +# 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 + +ifneq ($(filter disk testcode.%,$(MAKECMDGOALS)),) + ifdef CC65_HOME + TARGET_PATH = $(CC65_HOME)/target + else + TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path)) + endif + + # If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make + # has very limited support for paths containing spaces. $(wildcard) is the only function + # that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped + # spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a + # return value consisting of 12 plain words :-(( + # + # Fortunately we can work around that behaviour here because we know that the files we + # are looking for have known extensions. So we can $(filter) the in our example above 12 + # words for file extensions so we come up with 4 path fragments. Then we remove those + # path fragments with $(notdir) from the file names. + # + # So far so good. But here we want to process files from different paths in a single + # recipe further down below and therefore want to prepend the paths to the files with + # $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is). + # Therefore, we need to replace the spaces with some other character temporarily in order + # to have $(foreach) generate one invocation per file. We use the character '?' for that + # purpose here, just because it is known to not be part of file names. + # + # Inside the recipe generated per file we then replace the '?' again with a space. As we + # want to be compatible with cmd.exe for execution we're not using an escaped space but + # rather double-quote the whole path. + # + # Note: The "strange" $(wildcard) further down below just serves the purpose to unescape + # spaces for cmd.exe. This could have as well been done with another $(subst). + + SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH)) + + EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*) + MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*) + TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*) + + EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD)))) + MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU)))) + TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI)))) + + # This one comes with the VICE emulator. + # See http://vice-emu.sourceforge.net/ + C1541 ?= c1541 + + # For this one, see https://applecommander.github.io/ + AC ?= ac.jar + + # For this one, see https://www.horus.com/~hias/atari/ + DIR2ATR ?= dir2atr +endif + +DISK_c64 = testcode.d64 +DISK_apple2 = testcode.dsk +DISK_apple2enh = testcode.dsk +DISK_atari = testcode.atr +DISK_atarixl = testcode.atr + +# -------------------------------------------------------------------------- +# System-dependent settings +# For convenience, these groups and lines are sorted alphabetically, first +# by target-machine group, then by mission, then by program and sub-target. + +# -------------------------------------------------------------------------- +# Generic rules + +.PHONY: all mostlyclean clean zip testcode disk + +%: %.c +%: %.s + +.c.o: + $(CC) $(CFLAGS) -Ors --codesize 500 -T -g -t $(SYS) $< + $(AS) $(<:.c=.s) + +.s.o: + $(AS) $(ASFLAGS) -t $(SYS) $< + +.PRECIOUS: %.o + +.o: +ifeq ($(SYS),vic20) + $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib +else + $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -t $(SYS) -m $@.map $^ $(SYS).lib +endif + +# -------------------------------------------------------------------------- +# Lists of executables + +# omitted: seek +EXELIST_c64 = \ + arg-test \ + clock \ + clock-test \ + conio \ + cpeek-test \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +# omitted: seek clock-test mouse-test ser-test +EXELIST_vic20 = \ + arg-test \ + clock \ + conio \ + cpeek-test \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +# omitted: cpeek-test, clock +EXELIST_apple2 = \ + arg-test \ + clock-test \ + conio \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + seek \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +EXELIST_apple2enh = $(EXELIST_apple2) + +# omitted: cpeek-test +EXELIST_atari = \ + arg-test \ + clock-test \ + clock \ + conio \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + seek \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +EXELIST_atarixl = $(EXELIST_atari) + +# none of the testcode can work on the 2600 +# EXELIST_atari2600 = + +# none of the testcode can work on supervision +# EXELIST_supervision = + +# 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. +ifndef EXELIST_$(SYS) +EXELIST_$(SYS) := ${patsubst %.c,%,$(wildcard *.c)} +endif + +# -------------------------------------------------------------------------- +# Rules to make the binaries and the disk + +testcode: $(EXELIST_$(SYS)) + +disk: $(DISK_$(SYS)) + +all: testcode + make -C accelerator + make -C apple2 + make -C atari + make -C atari5200 + make -C cbm SYS=$(SYS) + make -C gamate + make -C pce + +# -------------------------------------------------------------------------- +# some programs link against getsp.o + +mouse-test: mouse-test.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib + +ifneq ($(SYS),vic20) +ft: ft.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib + +tinyshell: tinyshell.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib +endif + +# some programs need more memory on the vic20 + +ifeq ($(SYS),vic20) +ft: ft.o getsp.o + $(LD) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib + +tinyshell: tinyshell.o getsp.o + $(LD) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib +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))" $(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 + +testcode.d64: testcode + @$(C1541) -format testcode,AA d64 $@ >$(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 +# named 'prodos.dsk'. + +define DSK_WRITE_BIN_recipe + +$(if $(findstring BF00,$(LDFLAGS_$(notdir $(file))_$(SYS))), \ + java -jar $(AC) -p $@ $(notdir $(file)).system sys <"$(wildcard $(TARGET_PATH)/$(SYS)/util/loader.system)") +java -jar $(AC) -as $@ $(notdir $(file)) <"$(file)" + +endef # DSK_WRITE_BIN_recipe + +define DSK_WRITE_REL_recipe + +java -jar $(AC) -p $@ $(notdir $(file)) rel 0 <"$(subst ?,$(SPACE),$(file))" + +endef # DSK_WRITE_REL_recipe + +testcode.dsk: testcode + cp prodos.dsk $@ + $(foreach file,$(EXELIST_$(SYS)),$(DSK_WRITE_BIN_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(DSK_WRITE_REL_recipe)) + +# -------------------------------------------------------------------------- +# Rule to make an Atari disk with all testcode. Needs the dir2atr program +# available at http://www.horus.com/~hias/atari/ and the MyDos4534 variant +# of dos.sys and dup.sys. + +define ATR_WRITE_recipe + +cp "$(subst ?,$(SPACE),$(file))" atr/$(notdir $(file)) + +endef # ATR_WRITE_recipe + +testcode.atr: testcode + @mkdir atr + cp "dos.sys" atr/dos.sys + cp "dup.sys" atr/dup.sys + @$(foreach file,$(EXELIST_$(SYS)),$(ATR_WRITE_recipe)) +# @$(foreach file,$(EMD) $(MOU) $(TGI),$(ATR_WRITE_recipe)) + $(DIR2ATR) -d -b MyDos4534 3200 $@ atr + @$(RMDIR) atr + +# -------------------------------------------------------------------------- +# Clean-up rules + +mostlyclean: + @$(DEL) *.lbl *.map *.o 2>$(NULLDEV) +# we cant use .s since we have asm files in the directory that we want to keep + @$(DEL) ${patsubst %.c,%.s,$(wildcard *.c)} 2>$(NULLDEV) + +clean: mostlyclean + @$(DEL) $(EXELIST_$(SYS)) $(DISK_$(SYS)) 2>$(NULLDEV) + make -C accelerator clean + make -C apple2 clean + make -C atari clean + make -C atari5200 clean + make -C cbm SYS=$(SYS) clean + make -C gamate clean + make -C pce clean diff --git a/testcode/lib/accelerator/Makefile b/targettest/accelerator/Makefile similarity index 54% rename from testcode/lib/accelerator/Makefile rename to targettest/accelerator/Makefile index 6b90a9556..527b13f33 100644 --- a/testcode/lib/accelerator/Makefile +++ b/targettest/accelerator/Makefile @@ -1,4 +1,30 @@ -CL ?= cl65 +# 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 all: c64-scpu-test.prg c128-scpu-test.prg c64dtv-test.prg \ c64-c128-test.prg c128-test.prg chameleon-test.prg \ @@ -27,3 +53,6 @@ c65-test.prg: c65-test.c turbomaster-test.prg: turbomaster-test.c $(CL) -t c64 turbomaster-test.c -o turbomaster-test.prg + +clean: + @$(DEL) *.prg 2>$(NULLDEV) diff --git a/testcode/lib/accelerator/c64-c128-scpu-test.c b/targettest/accelerator/c64-c128-scpu-test.c similarity index 100% rename from testcode/lib/accelerator/c64-c128-scpu-test.c rename to targettest/accelerator/c64-c128-scpu-test.c diff --git a/testcode/lib/accelerator/c64-c128-test.c b/targettest/accelerator/c64-c128-test.c similarity index 100% rename from testcode/lib/accelerator/c64-c128-test.c rename to targettest/accelerator/c64-c128-test.c diff --git a/testcode/lib/accelerator/c64dtv-test.c b/targettest/accelerator/c64dtv-test.c similarity index 100% rename from testcode/lib/accelerator/c64dtv-test.c rename to targettest/accelerator/c64dtv-test.c diff --git a/testcode/lib/accelerator/c65-test.c b/targettest/accelerator/c65-test.c similarity index 100% rename from testcode/lib/accelerator/c65-test.c rename to targettest/accelerator/c65-test.c diff --git a/testcode/lib/accelerator/chameleon-test.c b/targettest/accelerator/chameleon-test.c old mode 100755 new mode 100644 similarity index 100% rename from testcode/lib/accelerator/chameleon-test.c rename to targettest/accelerator/chameleon-test.c diff --git a/testcode/lib/accelerator/turbo-test.c b/targettest/accelerator/turbo-test.c similarity index 100% rename from testcode/lib/accelerator/turbo-test.c rename to targettest/accelerator/turbo-test.c diff --git a/testcode/lib/accelerator/turbomaster-test.c b/targettest/accelerator/turbomaster-test.c old mode 100755 new mode 100644 similarity index 100% rename from testcode/lib/accelerator/turbomaster-test.c rename to targettest/accelerator/turbomaster-test.c diff --git a/testcode/lib/apple2/Makefile b/targettest/apple2/Makefile similarity index 59% rename from testcode/lib/apple2/Makefile rename to targettest/apple2/Makefile index 9d551aa62..f8167b47c 100644 --- a/testcode/lib/apple2/Makefile +++ b/targettest/apple2/Makefile @@ -1,9 +1,37 @@ # For this one see https://applecommander.github.io/ AC ?= ac.jar -CL ?= cl65 +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif -all: hgr.dsk dhgr.dsk +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 + +all: hgrshow hgrtest dhgrshow + +disk: hgr.dsk dhgr.dsk hgr.dsk: hgrshow hgrtest cp prodos.dsk $@ @@ -35,3 +63,9 @@ dhgr.dsk: dhgrshow dhgrshow: dhgrshow.c $(CL) -Oirs -t apple2enh --start-addr 0x4000 -m dhgrshow.map $^ + +clean: + @$(DEL) hgr.dsk dhgr.dsk 2>$(NULLDEV) + @$(DEL) hgrshow hgrshow.map 2>$(NULLDEV) + @$(DEL) hgrtest hgrtest.map 2>$(NULLDEV) + @$(DEL) dhgrshow dhgrshow.map 2>$(NULLDEV) diff --git a/testcode/lib/apple2/astronaut.hgr b/targettest/apple2/astronaut.hgr similarity index 100% rename from testcode/lib/apple2/astronaut.hgr rename to targettest/apple2/astronaut.hgr diff --git a/testcode/lib/apple2/catface.dhgr b/targettest/apple2/catface.dhgr similarity index 100% rename from testcode/lib/apple2/catface.dhgr rename to targettest/apple2/catface.dhgr diff --git a/testcode/lib/apple2/chips.hgr b/targettest/apple2/chips.hgr similarity index 100% rename from testcode/lib/apple2/chips.hgr rename to targettest/apple2/chips.hgr diff --git a/testcode/lib/apple2/dhgrshow.c b/targettest/apple2/dhgrshow.c similarity index 100% rename from testcode/lib/apple2/dhgrshow.c rename to targettest/apple2/dhgrshow.c diff --git a/testcode/lib/apple2/gatsby.dhgr b/targettest/apple2/gatsby.dhgr similarity index 100% rename from testcode/lib/apple2/gatsby.dhgr rename to targettest/apple2/gatsby.dhgr diff --git a/testcode/lib/apple2/girl.dhgr b/targettest/apple2/girl.dhgr similarity index 100% rename from testcode/lib/apple2/girl.dhgr rename to targettest/apple2/girl.dhgr diff --git a/testcode/lib/apple2/hgrshow.c b/targettest/apple2/hgrshow.c similarity index 100% rename from testcode/lib/apple2/hgrshow.c rename to targettest/apple2/hgrshow.c diff --git a/testcode/lib/apple2/hgrtest.c b/targettest/apple2/hgrtest.c similarity index 100% rename from testcode/lib/apple2/hgrtest.c rename to targettest/apple2/hgrtest.c diff --git a/testcode/lib/apple2/macrometer.hgr b/targettest/apple2/macrometer.hgr similarity index 100% rename from testcode/lib/apple2/macrometer.hgr rename to targettest/apple2/macrometer.hgr diff --git a/testcode/lib/apple2/mariner.hgr b/targettest/apple2/mariner.hgr similarity index 100% rename from testcode/lib/apple2/mariner.hgr rename to targettest/apple2/mariner.hgr diff --git a/testcode/lib/apple2/monarch.dhgr b/targettest/apple2/monarch.dhgr similarity index 100% rename from testcode/lib/apple2/monarch.dhgr rename to targettest/apple2/monarch.dhgr diff --git a/testcode/lib/apple2/rose.hgr b/targettest/apple2/rose.hgr similarity index 100% rename from testcode/lib/apple2/rose.hgr rename to targettest/apple2/rose.hgr diff --git a/testcode/lib/apple2/superman.dhgr b/targettest/apple2/superman.dhgr similarity index 100% rename from testcode/lib/apple2/superman.dhgr rename to targettest/apple2/superman.dhgr diff --git a/testcode/lib/apple2/venice.dhgr b/targettest/apple2/venice.dhgr similarity index 100% rename from testcode/lib/apple2/venice.dhgr rename to targettest/apple2/venice.dhgr diff --git a/testcode/lib/apple2/werner.hgr b/targettest/apple2/werner.hgr similarity index 100% rename from testcode/lib/apple2/werner.hgr rename to targettest/apple2/werner.hgr diff --git a/testcode/lib/apple2/werner.s b/targettest/apple2/werner.s similarity index 100% rename from testcode/lib/apple2/werner.s rename to targettest/apple2/werner.s diff --git a/testcode/lib/apple2/winston.hgr b/targettest/apple2/winston.hgr similarity index 100% rename from testcode/lib/apple2/winston.hgr rename to targettest/apple2/winston.hgr diff --git a/testcode/lib/arg-test.c b/targettest/arg-test.c similarity index 100% rename from testcode/lib/arg-test.c rename to targettest/arg-test.c diff --git a/targettest/atari/Makefile b/targettest/atari/Makefile new file mode 100644 index 000000000..18ddf55ce --- /dev/null +++ b/targettest/atari/Makefile @@ -0,0 +1,61 @@ + +# 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 + +all: charmapping.xex defdev.xex displaylist.xex mem.xex multi.xex ostype.xex \ + scrcode.com sys.xex + +charmapping.xex: charmapping.c + $(CL) -t atari -o charmapping.xex charmapping.c +defdev.xex: defdev.c + $(CL) -t atari -o defdev.xex defdev.c +displaylist.xex: displaylist.c + $(CL) -t atari -o displaylist.xex displaylist.c +mem.xex: mem.c ../getsp.s + $(CL) -t atari -o mem.xex mem.c ../getsp.s +multi.xex: multi-xex.s multi-xex.cfg + $(CL) -t atari -C multi-xex.cfg multi-xex.s -o multi.xex +ostype.xex: ostype.c + $(CL) -t atari -o ostype.xex ostype.c +scrcode.com: scrcode.s +# ca65 -t atari -o scrcode.o scrcode.s +# ld65 -C atari-asm.cfg -o scrcode.com scrcode.o + $(CL) -t atari -C atari-asm.cfg -o scrcode.com scrcode.s +sys.xex: sys.c + $(CL) -t atari -o sys.xex sys.c + +clean: + @$(DEL) charmapping.xex 2>$(NULLDEV) + @$(DEL) defdev.xex 2>$(NULLDEV) + @$(DEL) displaylist.xex 2>$(NULLDEV) + @$(DEL) mem.xex 2>$(NULLDEV) + @$(DEL) multi.xex 2>$(NULLDEV) + @$(DEL) ostype.xex 2>$(NULLDEV) + @$(DEL) scrcode.o 2>$(NULLDEV) + @$(DEL) scrcode.com 2>$(NULLDEV) + @$(DEL) sys.xex 2>$(NULLDEV) diff --git a/testcode/lib/atari/asm-xex.s b/targettest/atari/asm-xex.s similarity index 100% rename from testcode/lib/atari/asm-xex.s rename to targettest/atari/asm-xex.s diff --git a/testcode/lib/atari/charmapping.c b/targettest/atari/charmapping.c similarity index 100% rename from testcode/lib/atari/charmapping.c rename to targettest/atari/charmapping.c diff --git a/testcode/lib/atari/defdev.c b/targettest/atari/defdev.c similarity index 100% rename from testcode/lib/atari/defdev.c rename to targettest/atari/defdev.c diff --git a/testcode/lib/atari/displaylist.c b/targettest/atari/displaylist.c similarity index 100% rename from testcode/lib/atari/displaylist.c rename to targettest/atari/displaylist.c diff --git a/testcode/lib/atari/mem.c b/targettest/atari/mem.c similarity index 100% rename from testcode/lib/atari/mem.c rename to targettest/atari/mem.c diff --git a/testcode/lib/atari/multi-xex.cfg b/targettest/atari/multi-xex.cfg similarity index 82% rename from testcode/lib/atari/multi-xex.cfg rename to targettest/atari/multi-xex.cfg index 7558aa895..f13a9eabe 100644 --- a/testcode/lib/atari/multi-xex.cfg +++ b/targettest/atari/multi-xex.cfg @@ -5,13 +5,13 @@ MEMORY { ZP: file = "", define = yes, start = $0082, size = $007E; # First memory segment in file, show message LOADER: file = %O, start = $680, size = 128; - # First memory segment in file, load over COLOR registers: + # Second memory segment in file, load over COLOR registers: COLOR: file = %O, start = $2C4, size = 5; - # Second memory segment, load at page 6: + # Third memory segment, load at page 6: PAGE6: file = %O, start = $600, size = 128; - # Third memory segment in file, load over SDLST register: + # Fourth memory segment in file, load over SDLST register: SDLST: file = %O, start = $230, size = 2; - # Main segment, load at "STARTADDRESS" + # Fifth/Main segment, load at "STARTADDRESS" MAIN: file = %O, start = %S, size = $BC20 - %S; } FILES { diff --git a/testcode/lib/atari/multi-xex.s b/targettest/atari/multi-xex.s similarity index 100% rename from testcode/lib/atari/multi-xex.s rename to targettest/atari/multi-xex.s diff --git a/testcode/lib/atari/ostype.c b/targettest/atari/ostype.c similarity index 97% rename from testcode/lib/atari/ostype.c rename to targettest/atari/ostype.c index 552735ac8..5561f64fd 100644 --- a/testcode/lib/atari/ostype.c +++ b/targettest/atari/ostype.c @@ -9,9 +9,9 @@ int main(void) { + char *rev; unsigned int t, v; unsigned char palntsc; - unsigned char *rev; unsigned char minor; unsigned char c; diff --git a/testcode/lib/atari/scrcode.s b/targettest/atari/scrcode.s similarity index 90% rename from testcode/lib/atari/scrcode.s rename to targettest/atari/scrcode.s index cd4290781..ba778579b 100644 --- a/testcode/lib/atari/scrcode.s +++ b/targettest/atari/scrcode.s @@ -43,15 +43,20 @@ key: lda CH dispdata: scrcode "fooBa", 'r', $66, 3+4 disp_len = * - dispdata +.export __AUTOSTART__: absolute = 1 .segment "AUTOSTRT" .word $02E0 .word $02E1 .word __CODE_LOAD__+1 +.export __EXEHDR__: absolute = 1 .segment "EXEHDR" .word $FFFF + +.segment "MAINHDR" + .word __CODE_LOAD__ .word __BSS_LOAD__ - 1 diff --git a/testcode/lib/atari/sys.c b/targettest/atari/sys.c similarity index 96% rename from testcode/lib/atari/sys.c rename to targettest/atari/sys.c index 9ec7aa631..59debd758 100644 --- a/testcode/lib/atari/sys.c +++ b/targettest/atari/sys.c @@ -10,6 +10,8 @@ #include <6502.h> #include <conio.h> +#define IOCB (OS.iocb[0]) + static struct regs regs; static struct __iocb *iocb = &IOCB; /* use IOCB #0 */ diff --git a/targettest/atari5200/Makefile b/targettest/atari5200/Makefile new file mode 100644 index 000000000..3a8114975 --- /dev/null +++ b/targettest/atari5200/Makefile @@ -0,0 +1,36 @@ + +# 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 + +all: hello + +hello: hello.c + $(CL) -t atari5200 -o hello hello.c + +clean: + @$(DEL) hello 2>$(NULLDEV) diff --git a/testcode/lib/atari5200/hello.c b/targettest/atari5200/hello.c similarity index 100% rename from testcode/lib/atari5200/hello.c rename to targettest/atari5200/hello.c diff --git a/targettest/cbm/Makefile b/targettest/cbm/Makefile new file mode 100644 index 000000000..298f80d62 --- /dev/null +++ b/targettest/cbm/Makefile @@ -0,0 +1,42 @@ +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# 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 + +all: petscii.prg cbmdir-test.prg + +petscii.prg: petscii.c + $(CL) -t $(SYS) -O -o petscii.prg petscii.c + +cbmdir-test.prg: cbmdir-test.c + $(CL) -t $(SYS) -Oris -o $@ $< + +clean: + @$(DEL) petscii.prg cbmdir-test.prg 2>$(NULLDEV) diff --git a/targettest/cbm/cbmdir-test.c b/targettest/cbm/cbmdir-test.c new file mode 100644 index 000000000..0db9856b7 --- /dev/null +++ b/targettest/cbm/cbmdir-test.c @@ -0,0 +1,119 @@ +/* +** Tests the CBM-specific directory functions. +** +** 2021-08-12, Greg King +*/ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <conio.h> +#include <cbm.h> + + +/* device number */ +#define UNIT 8 + +/* directory patterm */ +static const char name[] = "$"; + + +static const char* const type[] = { + "DEL", + "CBM", + "DIR", + "LNK", + "???", + "Directory header", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SEQ", + "PRG", + "USR", + "REL", + "VRP" +}; + +static const char* const access[] = { + "unknown", + "read-only", + "write-only", + "read/write" +}; + +static const char* const error[] = { + "", + "couldn't read it", + "", + "couldn't find start of file-name", + "couldn't find end of file-name", + "couldn't read file-type", + "premature end of file" +}; + + +int main(void) +{ + unsigned char go = 0; + unsigned char rc; + struct cbm_dirent E; + + /* Explain use, and wait for a key. */ + printf ("use the following keys:\n" + " g -> go ahead without stopping\n" + " q -> quit directory lister\n" + "tap any key to start ...\n\n"); + cgetc (); + + /* Open the directory. */ + if (cbm_opendir (1, UNIT, name) != 0) { + printf("error opening %s:\n %s\n", name, _stroserror (_oserror)); + return 1; + } + + /* Output the directory. */ + printf("contents of \"%s\":\n", name); + while ((rc = cbm_readdir (1, &E)) != 2) { + if (rc != 0) { + goto oops; + } + + printf (" name[]: \"%s\"\n", E.name); + printf (" size :%6u\n", E.size); + printf (" type : %s\n", type[E.type]); + printf (" access: %s\n", access[E.access > 3 ? 0 : E.access]); + printf ("----\n"); + + if (!go) { + switch (cgetc ()) { + case 'q': + goto done; + + case 'g': + go = 1; + } + } + } + + printf (" size :%6u free.\n", E.size); + +done: + /* Close the directory. */ + cbm_closedir (1); + return 0; + +oops: + if (rc <= 6) { + printf ("\ndirectory error:\n %s.\n", error[rc]); + } + cbm_closedir (1); + return 1; +} diff --git a/targettest/cbm/petscii.c b/targettest/cbm/petscii.c new file mode 100644 index 000000000..7d5ba9528 --- /dev/null +++ b/targettest/cbm/petscii.c @@ -0,0 +1,162 @@ + +/* this program prints all available "petscii" characters to screen, once + using putchar (which wraps to kernal i/o) and once using conio (which + will do direct videoram access). after that the produced screencodes + are compared (they should match) (related to issue #988 */ + +#include <conio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#if defined(__C64__) +#define VRAMPEEK(x) (*(char*)(0x0400 + (x))) +#define VRAMPOKE(x, y) *(char*)(0x0400 + (x)) = (y) +#define CRAMPEEK(x) ((*(char*)(0xd800 + (x))) & 15) +#define CRAMPOKE(x, y) *(char*)(0xd800 + (x)) = (y) +#else +#error "this target is not supported yet" +#endif + +unsigned char x, y, c; +unsigned char c1, c2; +unsigned char *p1, *p2; + +int err = 0; + +int main(void) +{ + clrscr(); + bgcolor(COLOR_BLACK); + bordercolor(COLOR_BLACK); + + /* output all characters using putchar() */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + /* skip the codes that are unprintable control codes */ + if (!((c < 32) || ((c > 127) && (c < 160)))) { + gotoxy(x, y); putchar(c); + } + c++; + } + } + + /* output all characters using conio */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + /* skip the codes that are unprintable control codes */ + if (!((c < 32) || ((c > 127) && (c < 160)))) { + gotoxy(x + 20, y); cputc(c); + } + c++; + } + } + + /* compare the two outputs */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + c1 = VRAMPEEK((y * 40) + x); + c2 = VRAMPEEK((y * 40) + x + 0x14); + if (c1 == c2) { + c = COLOR_GREEN; + } else { + c = COLOR_RED; + err = 1; + } + CRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x + 0x14, c); + } + } + + /* show the result */ + textcolor(COLOR_WHITE); + gotoxy(0, 17); + if (err) { + bordercolor(COLOR_RED); + cputs("errors detected"); + } else { + bordercolor(COLOR_GREEN); + cputs("all fine"); + } + cputs(" - press a key "); + cursor(1); + cgetc(); + cursor(0); + + clrscr(); + bordercolor(COLOR_BLACK); + + /* output all characters directly to the scree */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + VRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x, c & 15); + c++; + } + } + + /* read the characters with conio peek functions and output with conio */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + gotoxy(x, y); + c1 = cpeekc(); + c2 = cpeekrevers(); + c = cpeekcolor(); + + gotoxy(x + 0x14, y); + revers(c2); + textcolor(c); + cputc(c1); + } + } + + revers(0); + textcolor(COLOR_WHITE); + gotoxy(0, 17); + cputs("press a key to compare "); + cursor(1); + cgetc(); + cursor(0); + + /* compare the two outputs */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + c = COLOR_GREEN; + c1 = VRAMPEEK((y * 40) + x); + c2 = VRAMPEEK((y * 40) + x + 0x14); + if (c1 != c2) { + c = COLOR_RED; + err = 1; + } + c1 = CRAMPEEK((y * 40) + x); + c2 = CRAMPEEK((y * 40) + x + 0x14); + if (c1 != c2) { + c = COLOR_RED; + err = 1; + } + CRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x + 0x14, c); + } + } + + /* show the result */ + revers(0); + textcolor(COLOR_WHITE); + gotoxy(0, 17); + if (err) { + bordercolor(COLOR_RED); + cputs("errors detected"); + } else { + bordercolor(COLOR_GREEN); + cputs("all fine"); + } + cputs(" - press a key "); + cursor(1); + cgetc(); + cursor(0); + + return 0; +} diff --git a/testcode/lib/clock-test.c b/targettest/clock-test.c similarity index 100% rename from testcode/lib/clock-test.c rename to targettest/clock-test.c diff --git a/testcode/lib/clock.c b/targettest/clock.c similarity index 100% rename from testcode/lib/clock.c rename to targettest/clock.c diff --git a/testcode/lib/conio.c b/targettest/conio.c similarity index 100% rename from testcode/lib/conio.c rename to targettest/conio.c diff --git a/testcode/lib/cpeek-test.c b/targettest/cpeek-test.c similarity index 100% rename from testcode/lib/cpeek-test.c rename to targettest/cpeek-test.c diff --git a/testcode/lib/cprintf.c b/targettest/cprintf.c similarity index 100% rename from testcode/lib/cprintf.c rename to targettest/cprintf.c diff --git a/testcode/lib/cursor.c b/targettest/cursor.c similarity index 100% rename from testcode/lib/cursor.c rename to targettest/cursor.c diff --git a/testcode/lib/deb.c b/targettest/deb.c similarity index 100% rename from testcode/lib/deb.c rename to targettest/deb.c diff --git a/testcode/lib/dir-test.c b/targettest/dir-test.c similarity index 90% rename from testcode/lib/dir-test.c rename to targettest/dir-test.c index 9102fc74d..61e42247e 100644 --- a/testcode/lib/dir-test.c +++ b/targettest/dir-test.c @@ -19,9 +19,13 @@ #include <string.h> #include <errno.h> #include <dirent.h> -#include <cbm.h> #include <conio.h> +#if defined(__CBM__) +#include <cbm.h> +#elif defined(__APPLE2__) +#include <apple2.h> +#endif int main(void) { @@ -51,9 +55,13 @@ int main(void) printf("contents of \"%s\":\n", name); while ((E = readdir (D)) != 0) { printf ("dirent.d_name[] : \"%s\"\n", E->d_name); +#if !defined(__ATARI__) printf ("dirent.d_blocks : %10u\n", E->d_blocks); +#endif printf ("dirent.d_type : %10d\n", E->d_type); +#if !defined(__APPLE2__) && !defined(__ATARI__) printf ("telldir() : %10lu\n", telldir (D)); +#endif printf ("---\n"); if (!go) { switch (cgetc ()) { @@ -63,14 +71,16 @@ int main(void) case 'q': goto done; - +#if !defined(__APPLE2__) && !defined(__ATARI__) case 'r': seekdir (D, E->d_off); break; - +#endif +#if !defined(__ATARI__) case 's': rewinddir (D); break; +#endif } } diff --git a/testcode/lib/div-test.c b/targettest/div-test.c similarity index 100% rename from testcode/lib/div-test.c rename to targettest/div-test.c diff --git a/testcode/lib/em-test.c b/targettest/em-test.c similarity index 100% rename from testcode/lib/em-test.c rename to targettest/em-test.c diff --git a/testcode/lib/exec-test1.c b/targettest/exec-test1.c similarity index 100% rename from testcode/lib/exec-test1.c rename to targettest/exec-test1.c diff --git a/testcode/lib/exec-test2.c b/targettest/exec-test2.c similarity index 100% rename from testcode/lib/exec-test2.c rename to targettest/exec-test2.c diff --git a/testcode/lib/fileio-test.c b/targettest/fileio-test.c similarity index 100% rename from testcode/lib/fileio-test.c rename to targettest/fileio-test.c diff --git a/testcode/lib/ft.c b/targettest/ft.c similarity index 100% rename from testcode/lib/ft.c rename to targettest/ft.c diff --git a/targettest/gamate/Makefile b/targettest/gamate/Makefile new file mode 100644 index 000000000..a14b8a854 --- /dev/null +++ b/targettest/gamate/Makefile @@ -0,0 +1,45 @@ + +# 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 + +all: audiotest.bin lcdtest.bin ctest.bin + +audiotest.bin: audiotest.s + $(CL) -l audiotest.lst -t gamate -o audiotest.bin audiotest.s +lcdtest.bin: lcdtest.s + $(CL) -l lcdtest.lst -t gamate -o lcdtest.bin lcdtest.s +ctest.bin: ctest.c + $(CL) -l ctest.lst -t gamate -o ctest.bin ctest.c +nachtm.bin: nachtm.c + $(CL) -Os -l nachtm.lst -t gamate -o nachtm.bin nachtm.c + gamate-fixcart nachtm.bin + +clean: + @$(DEL) lcdtest.o audiotest.o ctest.o 2>$(NULLDEV) + @$(DEL) lcdtest.bin audiotest.bin ctest.bin nachtm.bin 2>$(NULLDEV) + @$(DEL) audiotest.lst lcdtest.lst ctest.lst 2>$(NULLDEV) diff --git a/testcode/lib/gamate/audiotest.s b/targettest/gamate/audiotest.s similarity index 100% rename from testcode/lib/gamate/audiotest.s rename to targettest/gamate/audiotest.s diff --git a/testcode/lib/gamate/cga2.chr b/targettest/gamate/cga2.chr similarity index 100% rename from testcode/lib/gamate/cga2.chr rename to targettest/gamate/cga2.chr diff --git a/testcode/lib/gamate/ctest.c b/targettest/gamate/ctest.c similarity index 83% rename from testcode/lib/gamate/ctest.c rename to targettest/gamate/ctest.c index 793770cee..bff3f5986 100644 --- a/testcode/lib/gamate/ctest.c +++ b/targettest/gamate/ctest.c @@ -25,19 +25,19 @@ int main(int argc, char *argv[]) gotoxy(0,2);cprintf("%04x %02x %02x %02x", n, x, y, *((unsigned char*)JOY_DATA)); switch((*((unsigned char*)JOY_DATA))) { - case 0xff ^ JOY_DATA_UP: + case 0xff ^ JOY_UP_MASK: ++y; if (y == 0xc8) y = 0; break; - case 0xff ^ JOY_DATA_DOWN: + case 0xff ^ JOY_DOWN_MASK: --y; if (y == 0xff) y = 0xc7; break; - case 0xff ^ JOY_DATA_LEFT: + case 0xff ^ JOY_LEFT_MASK: ++x; break; - case 0xff ^ JOY_DATA_RIGHT: + case 0xff ^ JOY_RIGHT_MASK: --x; break; - case 0xff ^ JOY_DATA_FIRE_A: + case 0xff ^ JOY_BTN_A_MASK: break; } diff --git a/testcode/lib/gamate/lcdtest.s b/targettest/gamate/lcdtest.s similarity index 100% rename from testcode/lib/gamate/lcdtest.s rename to targettest/gamate/lcdtest.s diff --git a/testcode/lib/gamate/nachtm.c b/targettest/gamate/nachtm.c similarity index 100% rename from testcode/lib/gamate/nachtm.c rename to targettest/gamate/nachtm.c diff --git a/testcode/lib/getopt-test.c b/targettest/getopt-test.c similarity index 100% rename from testcode/lib/getopt-test.c rename to targettest/getopt-test.c diff --git a/testcode/lib/getsp.s b/targettest/getsp.s similarity index 100% rename from testcode/lib/getsp.s rename to targettest/getsp.s diff --git a/testcode/lib/heaptest.c b/targettest/heaptest.c similarity index 98% rename from testcode/lib/heaptest.c rename to targettest/heaptest.c index d776e2f0c..560694bee 100644 --- a/testcode/lib/heaptest.c +++ b/targettest/heaptest.c @@ -214,10 +214,10 @@ int main (void) /* Show info at start */ ShowInfo (); - +#if !defined(__APPLE2__) /* Remember the time */ T = clock (); - +#endif /* Do the tests */ Test1 (); Test2 (); @@ -226,10 +226,11 @@ int main (void) Test5 (); Test6 (); +#if !defined(__APPLE2__) /* Calculate the time and print it */ T = clock () - T; printf ("Time needed: %lu ticks\n", T); - +#endif /* Done */ return EXIT_SUCCESS; } diff --git a/testcode/lib/joy-test.c b/targettest/joy-test.c similarity index 100% rename from testcode/lib/joy-test.c rename to targettest/joy-test.c diff --git a/testcode/lib/moddiv-test.c b/targettest/moddiv-test.c similarity index 100% rename from testcode/lib/moddiv-test.c rename to targettest/moddiv-test.c diff --git a/testcode/lib/mouse-test.c b/targettest/mouse-test.c similarity index 99% rename from testcode/lib/mouse-test.c rename to targettest/mouse-test.c index ea8311d19..64482b937 100644 --- a/testcode/lib/mouse-test.c +++ b/targettest/mouse-test.c @@ -190,7 +190,7 @@ int main (void) #endif /* Set dark-on-light colors. Clear the screen. */ -#ifdef __CBM__ +#if defined(__CBM__) && !defined(__VIC20__) (void) bordercolor (COLOR_GRAY2); (void) bgcolor (COLOR_WHITE); (void) textcolor (COLOR_GRAY1); diff --git a/testcode/lib/mul-test.c b/targettest/mul-test.c similarity index 100% rename from testcode/lib/mul-test.c rename to targettest/mul-test.c diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile new file mode 100644 index 000000000..f91f0eed8 --- /dev/null +++ b/targettest/pce/Makefile @@ -0,0 +1,56 @@ + +# 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 + +.PHONY: all clean test + +# Size of cartridge to generate. +# Possible values: +# 8K = 0x2000 +# 16K = 0x4000 +# 32K = 0x8000 +CARTSIZE := 0x2000 + +ifeq (${CARTSIZE},0x8000) +COUNT := 3 +else +COUNT := 1 +endif + +all: conio.bin + +%.bin: %.c + $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ + @echo "use 'make conio.pce' to produce a .pce file using dd" + +%.pce: %.bin + dd if=$< bs=8K skip=${COUNT} > $@ + dd if=$< bs=8K count=${COUNT} >> $@ + +clean: + @$(DEL) conio.o conio.??? 2>$(NULLDEV) diff --git a/testcode/lib/pce/conio.c b/targettest/pce/conio.c similarity index 81% rename from testcode/lib/pce/conio.c rename to targettest/pce/conio.c index 858f01918..55f828f26 100644 --- a/testcode/lib/pce/conio.c +++ b/targettest/pce/conio.c @@ -7,6 +7,10 @@ static int datavar = 10; +static char hex[16] = { "0123456789abcdef" }; +static char charbuf[0x20]; +static char colbuf[0x20]; + void main(void) { int stackvar = 42; @@ -21,11 +25,27 @@ void main(void) screensize(&xsize, &ysize); cputs("hello world"); + gotoxy(0,0); + cpeeks(charbuf, 11); + gotoxy(12,0); + cputs(charbuf); + cputsxy(0, 2, "colors:" ); for (i = 0; i < 16; ++i) { textcolor(i); - cputc('X'); + cputc(hex[i]); } + for (i = 0; i < 16; ++i) { + gotoxy(7 + i, 2); + charbuf[i] = cpeekc(); + colbuf[i] = cpeekcolor(); + } + gotoxy(25, 2); + for (i = 0; i < 16; ++i) { + textcolor(colbuf[i]); + cputc(charbuf[i]); + } + textcolor(1); gotoxy(0,4); @@ -115,6 +135,17 @@ void main(void) cputs(" revers"); revers(0); + for (i = 0; i < 9; ++i) { + gotoxy(xsize - 10 + i, 3); + charbuf[i] = cpeekc(); + colbuf[i] = cpeekrevers(); + } + gotoxy(xsize - 10, 4); + for (i = 0; i < 9; ++i) { + revers(colbuf[i]); + cputc(charbuf[i]); + } + if ((n & 0x1f) == 0x00) { nn = p[15]; ((char*)memmove(p + 1, p, 15))[-1] = nn; diff --git a/testcode/lib/posixio-test.c b/targettest/posixio-test.c similarity index 100% rename from testcode/lib/posixio-test.c rename to targettest/posixio-test.c diff --git a/testcode/lib/rename-test.c b/targettest/rename-test.c similarity index 100% rename from testcode/lib/rename-test.c rename to targettest/rename-test.c diff --git a/testcode/lib/scanf-test.c b/targettest/scanf-test.c similarity index 97% rename from testcode/lib/scanf-test.c rename to targettest/scanf-test.c index 9fce01b77..f17b62294 100644 --- a/testcode/lib/scanf-test.c +++ b/targettest/scanf-test.c @@ -14,9 +14,7 @@ /* Define USE_STDIO, when you want to use the stdio functions. ** Do not define it, when you want to use the conio functions. */ -/* #define USE_STDIO -*/ #include <stdio.h> #include <string.h> @@ -35,22 +33,23 @@ #define ARRAYSIZE(a) (sizeof (a) / sizeof (a)[0]) +typedef enum { + INT, + CHAR +} TYPE; + +typedef union { + int nvalue; + const char *svalue; +} VALUE; + static const struct { const char *input, *format; int rvalue; - enum TYPE { - INT, - CHAR - } type1; - union { - int nvalue; - const char *svalue; - } v1; - enum TYPE type2; - union { - int nvalue; - const char *svalue; - } v2; + TYPE type1; + VALUE v1; + TYPE type2; + VALUE v2; } test_data[] = { /* Input sequences for character specifiers must be less than 80 characters ** long. These format strings are allowwed a maximum of two assignment diff --git a/testcode/lib/seek.c b/targettest/seek.c similarity index 100% rename from testcode/lib/seek.c rename to targettest/seek.c diff --git a/testcode/lib/ser-test.c b/targettest/ser-test.c similarity index 100% rename from testcode/lib/ser-test.c rename to targettest/ser-test.c diff --git a/testcode/lib/strdup-test.c b/targettest/strdup-test.c similarity index 78% rename from testcode/lib/strdup-test.c rename to targettest/strdup-test.c index 5514471f9..2fcc9816f 100644 --- a/testcode/lib/strdup-test.c +++ b/targettest/strdup-test.c @@ -3,34 +3,25 @@ #include <string.h> #include <time.h> - -/* From _heap.h */ -extern unsigned _horg; /* Bottom of heap */ -extern unsigned _hptr; /* Current top */ -extern unsigned _hend; /* Upper limit */ -extern unsigned _hfirst; /* First free block in list */ -extern unsigned _hlast; /* Last free block in list */ - +#include <_heap.h> static unsigned char* V[256]; - - static void ShowInfo (void) /* Show heap info */ { /* Count free blocks */ unsigned Count = 0; - unsigned** P = (unsigned**) _hfirst; + unsigned** P = (unsigned**) _heapfirst; while (P) { ++Count; P = P[1]; } printf ("%04X %04X %04X %04X %04X %u\n", - _horg, _hptr, _hend, _hfirst, _hlast, Count); + _heaporg, _heapptr, _heapend, _heapfirst, _heaplast, Count); if (Count) { - P = (unsigned**) _hfirst; + P = (unsigned**) _heapfirst; while (P) { printf ("%04X %04X %04X %04X(%u)\n", (unsigned) P, P[2], P[1], P[0], P[0]); @@ -93,19 +84,21 @@ int main (void) /* Show info at start */ ShowInfo (); - +#if !defined(__APPLE2__) /* Remember the time */ T = clock (); - +#endif /* Do the tests */ FillArray (); ShowInfo (); FreeArray (); ShowInfo (); +#if !defined(__APPLE2__) /* Calculate the time and print it */ T = clock () - T; printf ("Time needed: %lu ticks\n", T); +#endif /* Done */ return EXIT_SUCCESS; diff --git a/testcode/lib/stroserror-test.c b/targettest/stroserror-test.c similarity index 100% rename from testcode/lib/stroserror-test.c rename to targettest/stroserror-test.c diff --git a/testcode/lib/strqtok-test.c b/targettest/strqtok-test.c similarity index 100% rename from testcode/lib/strqtok-test.c rename to targettest/strqtok-test.c diff --git a/targettest/sym1/Makefile b/targettest/sym1/Makefile new file mode 100644 index 000000000..23742f6d1 --- /dev/null +++ b/targettest/sym1/Makefile @@ -0,0 +1,51 @@ +# 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 + +all: symHello.bin symTiny.bin symDisplay.bin symIO.bin symNotepad.bin + +symHello.bin: symHello.c + $(CL) -t sym1 -O -o symHello.bin symHello.c + +symTiny.bin: symTiny.c + $(CL) -t sym1 -O -o symTiny.bin symTiny.c + +symDisplay.bin: symDisplay.c + $(CL) -t sym1 -O -o symDisplay.bin symDisplay.c + +symIO.bin: symIO.c + $(CL) -t sym1 -C sym1-32k.cfg -O -o symIO.bin symIO.c + +symNotepad.bin: symNotepad.c + $(CL) -t sym1 -C sym1-32k.cfg -O -o symNotepad.bin symNotepad.c + +clean: + @$(DEL) symHello.bin 2>$(NULLDEV) + @$(DEL) symTiny.bin 2>$(NULLDEV) + @$(DEL) symDisplay.bin 2>$(NULLDEV) + @$(DEL) symIO.bin 2>$(NULLDEV) + @$(DEL) symNotepad.bin 2>$(NULLDEV) diff --git a/targettest/sym1/symDisplay.c b/targettest/sym1/symDisplay.c new file mode 100644 index 000000000..dce39f6b9 --- /dev/null +++ b/targettest/sym1/symDisplay.c @@ -0,0 +1,358 @@ +// -------------------------------------------------------------------------- +// Sym-1 front panel display example +// +// Wayne Parham +// +// wayne@parhamdata.com +// -------------------------------------------------------------------------- + +#include <stdio.h> +#include <sym1.h> + +int main (void) { + int delay = 10; + int flashes = 255; + int displayable = 1; + int e = 0; + int r = 0; + int d = 0; + int i = 0; + int l = 0; + int t = 0; + int z = 0; + char c = 0x00; + char buffer[41] = { 0x00 }; + + puts ("\nType a message (40 chars max) and press ENTER, please:\n"); + + while ( (c != '\n') && (i < 40) ) { + c = getchar(); + buffer[i] = c; + i++; + if ( i == 40 ) { + puts ("\n\n--- Reached 40 character limit. ---"); + } + } + + i--; // index is one past end + + while ( z == 0 ) { + puts ("\n\nHow many times (0 for forever) to repeat?"); + c = getchar(); + if ( (c >= '0') && (c <= '9') ) {// between 1 and 9 loops allowed + z = 1; // a number was pressed + t = c - '0'; // convert char to int + puts ("\n\nLook at the front panel.\n"); + } + else { + puts ("\nWhat?"); + z = 0; // keep asking for a number + } + } + + z = 0; + while ( (z < t) || (t == 0) ) { + + z++; + + putchar ( '\r' ); // Send CR to console + + DISPLAY.d0 = DISP_SPACE; // Clear the display + DISPLAY.d1 = DISP_SPACE; + DISPLAY.d2 = DISP_SPACE; + DISPLAY.d3 = DISP_SPACE; + DISPLAY.d4 = DISP_SPACE; + DISPLAY.d5 = DISP_SPACE; + DISPLAY.d6 = DISP_SPACE; + + for ( d = 0; d < flashes ; d++ ) { + fdisp(); // Display + } + + for ( l = 0; l <= i; l++ ) { + + displayable = 1; // Assume character is mapped + + switch ( buffer[l] ) { // Put the typed charaters + case '1': // into the display buffer + DISPLAY.d6 = DISP_1; // one at a time + break; + case '2': + DISPLAY.d6 = DISP_2; + break; + case '3': + DISPLAY.d6 = DISP_3; + break; + case '4': + DISPLAY.d6 = DISP_4; + break; + case '5': + DISPLAY.d6 = DISP_5; + break; + case '6': + DISPLAY.d6 = DISP_6; + break; + case '7': + DISPLAY.d6 = DISP_7; + break; + case '8': + DISPLAY.d6 = DISP_8; + break; + case '9': + DISPLAY.d6 = DISP_9; + break; + case '0': + DISPLAY.d6 = DISP_0; + break; + case 'A': + DISPLAY.d6 = DISP_A; + break; + case 'a': + DISPLAY.d6 = DISP_A; + break; + case 'B': + DISPLAY.d6 = DISP_b; + break; + case 'b': + DISPLAY.d6 = DISP_b; + break; + case 'C': + DISPLAY.d6 = DISP_C; + break; + case 'c': + DISPLAY.d6 = DISP_c; + break; + case 'D': + DISPLAY.d6 = DISP_d; + break; + case 'd': + DISPLAY.d6 = DISP_d; + break; + case 'E': + DISPLAY.d6 = DISP_E; + break; + case 'e': + DISPLAY.d6 = DISP_e; + break; + case 'F': + DISPLAY.d6 = DISP_F; + break; + case 'f': + DISPLAY.d6 = DISP_F; + break; + case 'G': + DISPLAY.d6 = DISP_G; + break; + case 'g': + DISPLAY.d6 = DISP_g; + break; + case 'H': + DISPLAY.d6 = DISP_H; + break; + case 'h': + DISPLAY.d6 = DISP_h; + break; + case 'I': + DISPLAY.d6 = DISP_I; + break; + case 'i': + DISPLAY.d6 = DISP_i; + break; + case 'J': + DISPLAY.d6 = DISP_J; + break; + case 'j': + DISPLAY.d6 = DISP_J; + break; + case 'K': + DISPLAY.d6 = DISP_K; + break; + case 'k': + DISPLAY.d6 = DISP_K; + break; + case 'L': + DISPLAY.d6 = DISP_L; + break; + case 'l': + DISPLAY.d6 = DISP_L; + break; + case 'M': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_M_1; + DISPLAY.d6 = DISP_M_2; + break; + case 'm': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_M_1; + DISPLAY.d6 = DISP_M_2; + break; + case 'N': + DISPLAY.d6 = DISP_n; + break; + case 'n': + DISPLAY.d6 = DISP_n; + break; + case 'O': + DISPLAY.d6 = DISP_O; + break; + case 'o': + DISPLAY.d6 = DISP_o; + break; + case 'P': + DISPLAY.d6 = DISP_P; + break; + case 'p': + DISPLAY.d6 = DISP_P; + break; + case 'Q': + DISPLAY.d6 = DISP_q; + break; + case 'q': + DISPLAY.d6 = DISP_q; + break; + case 'R': + DISPLAY.d6 = DISP_r; + break; + case 'r': + DISPLAY.d6 = DISP_r; + break; + case 'S': + DISPLAY.d6 = DISP_S; + break; + case 's': + DISPLAY.d6 = DISP_S; + break; + case 'T': + DISPLAY.d6 = DISP_t; + break; + case 't': + DISPLAY.d6 = DISP_t; + break; + case 'U': + DISPLAY.d6 = DISP_U; + break; + case 'u': + DISPLAY.d6 = DISP_u; + break; + case 'V': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_V_1; + DISPLAY.d6 = DISP_V_2; + break; + case 'v': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_V_1; + DISPLAY.d6 = DISP_V_2; + break; + case 'W': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_W_1; + DISPLAY.d6 = DISP_W_2; + break; + case 'w': + DISPLAY.d0 = DISPLAY.d1; + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_W_1; + DISPLAY.d6 = DISP_W_2; + break; + case 'Y': + DISPLAY.d6 = DISP_Y; + break; + case 'y': + DISPLAY.d6 = DISP_Y; + break; + case 'Z': + DISPLAY.d6 = DISP_Z; + break; + case 'z': + DISPLAY.d6 = DISP_Z; + break; + case ' ': + DISPLAY.d6 = DISP_SPACE; + break; + case '.': + DISPLAY.d6 = DISP_PERIOD; + break; + case '-': + DISPLAY.d6 = DISP_HYPHEN; + break; + case '\'': + DISPLAY.d6 = DISP_APOSTR; + break; + case '"': + DISPLAY.d6 = DISP_APOSTR; + break; + case '=': + DISPLAY.d6 = DISP_EQUAL; + break; + case '_': + DISPLAY.d6 = DISP_BOTTOM; + break; + case '/': + DISPLAY.d6 = DISP_SLASH; + break; + case '\\': + DISPLAY.d6 = DISP_BACKSLASH; + break; + default: + displayable = 0; // Character not mapped + } + + if ( displayable ) { + + putchar ( buffer[l] ); // Send it to the console + + DISPLAY.d0 = DISPLAY.d1; // Scroll to the left + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISPLAY.d6; + + for ( d = 0; d < flashes ; d++ ) { + fdisp(); // Display + } + } + } + + for ( e = 0; e < 6; e++ ) { // Gradually fill the + DISPLAY.d0 = DISPLAY.d1; // display with spaces + DISPLAY.d1 = DISPLAY.d2; + DISPLAY.d2 = DISPLAY.d3; + DISPLAY.d3 = DISPLAY.d4; + DISPLAY.d4 = DISPLAY.d5; + DISPLAY.d5 = DISP_SPACE; + DISPLAY.d6 = DISP_SPACE; + for ( d = 0; d < flashes ; d++ ) { + fdisp(); // Display + } + } + } + + puts ("\n\nEnjoy your day!\n\n"); + + return 0; +} diff --git a/targettest/sym1/symHello.c b/targettest/sym1/symHello.c new file mode 100644 index 000000000..afc1d94c5 --- /dev/null +++ b/targettest/sym1/symHello.c @@ -0,0 +1,39 @@ +// -------------------------------------------------------------------------- +// Hello World for Sym-1 +// +// Wayne Parham +// +// wayne@parhamdata.com +// -------------------------------------------------------------------------- + +#include <stdio.h> +#include <sym1.h> + +int main (void) { + char c = 0x00; + int d = 0x00; + int l = 0x00; + + printf ("\nHello World!\n\n"); + + for ( l = 0; l < 2; l++ ) { + beep(); + for ( d = 0; d < 10 ; d++ ) { + } + } + printf ("Type a line and press ENTER, please.\n\n"); + + while ( c != '\n' ) { + c = getchar(); + } + + printf ("\n\nThanks!\n\n"); + + for ( l = 0; l < 5; l++ ) { + beep(); + for ( d = 0; d < 10 ; d++ ) { + } + } + + return 0; +} diff --git a/targettest/sym1/symIO.c b/targettest/sym1/symIO.c new file mode 100644 index 000000000..50fefc303 --- /dev/null +++ b/targettest/sym1/symIO.c @@ -0,0 +1,172 @@ +// -------------------------------------------------------------------------- +// Sym-1 digital I/O interface example +// +// Wayne Parham +// +// wayne@parhamdata.com +// -------------------------------------------------------------------------- + +#include <sym1.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main (void) { + unsigned char ddr1a = 0x00; + unsigned char ior1a = 0x00; + unsigned char ddr1b = 0x00; + unsigned char ior1b = 0x00; + unsigned char ddr2a = 0x00; + unsigned char ior2a = 0x00; + unsigned char ddr2b = 0x00; + unsigned char ior2b = 0x00; + unsigned char ddr3a = 0x00; + unsigned char ior3a = 0x00; + unsigned char ddr3b = 0x00; + unsigned char ior3b = 0x00; + unsigned char val = 0x00; + int going = 0x01; + int instr = 0x01; + int l = 0x00; + char* vp = 0x00; + char cmd[20] = { 0x00 }; + + while ( going ) { + + putchar ( '\r' ); + for ( l = 0; l < 25; l++ ) { + putchar ( '\n' ); + } + + ddr1a = VIA1.ddra; + ior1a = VIA1.pra; + ddr1b = VIA1.ddrb; + ior1b = VIA1.prb; + ddr2a = VIA2.ddra; + ior2a = VIA2.pra; + ddr2b = VIA2.ddrb; + ior2b = VIA2.prb; + ddr3a = VIA3.ddra; + ior3a = VIA3.pra; + ddr3b = VIA3.ddrb; + ior3b = VIA3.prb; + + puts ("================== Digital I/O Status =================="); + puts (" Port1A Port1B Port2A Port2B Port3A Port3B" ); + printf ("DDR %02X %02X %02X %02X %02X %02X\n\r",ddr1a,ddr1b,ddr2a,ddr2b,ddr3a,ddr3b); + printf ("IOR %02X %02X %02X %02X %02X %02X\n\r",ior1a,ior1b,ior2a,ior2b,ior3a,ior3b); + puts ("========================================================\n"); + + if ( instr ) { + puts ("You can set any register by typing 'register value' so"); + puts ("as an example, to set register IOR2A with the top five"); + puts ("bits off and the bottom three on, type 'IOR2A 07'."); + puts ("Press ENTER without any command to see register values"); + puts ("without changing any of them. Type 'help' to see these"); + puts ("instructions again and type 'quit' to end the program.\n"); + puts ("Available registers: DDR1A, IOR1A, DDR1B, IOR1B, DDR2A"); + puts ("IOR2A, DDR2B, IOR2B, DDR3A, IOR3A, DDR3B and IOR3B."); + instr = 0; + } + + printf ("\n Command: "); + + fgets ( cmd, sizeof(cmd)-1, stdin ); + cmd[strlen(cmd)-1] = '\0'; + + if ( strncasecmp(cmd, "quit", 4) == 0 ) { + going = 0; + } + else if ( strncasecmp(cmd, "help", 4) == 0 ) { + instr = 1; + } + else if ( strncasecmp(cmd, "ddr1a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA1.ddra = val; + } + } + else if ( strncasecmp(cmd, "ior1a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA1.pra = val; + } + } + else if ( strncasecmp(cmd, "ddr1b", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA1.ddrb = val; + } + } + else if ( strncasecmp(cmd, "ior1b", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA1.prb = val; + } + } + else if ( strncasecmp(cmd, "ddr2a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA2.ddra = val; + } + } + else if ( strncasecmp(cmd, "ior2a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA2.pra = val; + } + } + else if ( strncasecmp(cmd, "ddr2b", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA2.ddrb = val; + } + } + else if ( strncasecmp(cmd, "ior2b", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA2.prb = val; + } + } + else if ( strncasecmp(cmd, "ddr3a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA3.ddra = val; + } + } + else if ( strncasecmp(cmd, "ior3a", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA3.pra = val; + } + } + else if ( strncasecmp(cmd, "ddr3b", 5) == 0 ) { + vp = strchr ( cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA3.ddrb = val; + } + } + else if ( strncasecmp(cmd, "ior3b", 5) == 0 ) { + vp = strchr (cmd, ' ' ); + if ( vp ) { + val = (unsigned char) strtol( vp, NULL, 0 ); + VIA3.prb = val; + } + } + } + + puts ("\n\nEnjoy your day!\n\n"); + + return 0; +} diff --git a/targettest/sym1/symNotepad.c b/targettest/sym1/symNotepad.c new file mode 100644 index 000000000..1d0541ab6 --- /dev/null +++ b/targettest/sym1/symNotepad.c @@ -0,0 +1,192 @@ +// -------------------------------------------------------------------------- +// Sym-1 Notepad +// +// Wayne Parham +// +// wayne@parhamdata.com +// -------------------------------------------------------------------------- +// +// Note: This program requires RAM memory in locations 0xE000 - 0xEFFF +// Alternatively, the tape I/O buffer location and size can be +// changed by altering the defined TAPIO values below. +// +// -------------------------------------------------------------------------- + +#include <sym1.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define TAPIO_ADDRESS 0xE000 +#define TAPIO_MAX_SIZE 0x0FFF + +int main (void) { + char c = 0x00; + int l = 0x00; + int p = 0x00; + int error = 0x00; + int running = 0x01; + int writing = 0x01; + int instruction_needed = 0x01; + int heap_size = 0x00; + char* tapio = (char*) TAPIO_ADDRESS; + char* buffer; + + heap_size = _heapmaxavail(); + + if ( heap_size > TAPIO_MAX_SIZE ) { // No need to malloc more than + heap_size = TAPIO_MAX_SIZE; // the interface allows + } + + buffer = malloc ( heap_size ); + memset ( buffer, 0x00, heap_size ); + + if ( buffer == 0x00 ) { + puts ("Memory full."); + running = 0; + } + + tapio[0] = 0x00; // Check tape interface memory + if ( tapio[0] != 0x00 ) + error = 1; + + tapio[0] = 0xFF; + if ( tapio[0] != 0xFF ) + error = 1; + + tapio[TAPIO_MAX_SIZE] = 0x00; + if ( tapio[TAPIO_MAX_SIZE] != 0x00 ) + error = 1; + + tapio[TAPIO_MAX_SIZE] = 0xFF; + if ( tapio[TAPIO_MAX_SIZE] != 0xFF ) + error = 1; + + if ( error ) { + printf ("\nNo memory at location %p, aborting.\n", tapio); + running = 0; + } + else { + memset ( tapio, 0, TAPIO_MAX_SIZE ); + } + + + while ( running ) { + + putchar ( '\r' ); + for ( l = 0; l < 25; l++ ) { + putchar ( '\n' ); + } + + puts ("===================== Sym-1 Notepad ===================="); + + if ( instruction_needed ) { + puts ("Enter text and you can save it to tape for reloading"); + puts ("later. There are four special 'command' characters:\n"); + puts (" Control-S Save to tape"); + puts (" Control-L Load from tape"); + puts (" Control-C Clear memory"); + puts (" Control-X Exit"); + puts ("========================================================\n"); + } + + while ( writing ) { + + c = getchar(); + + if ( c == 0x08 ) { // Backspace + if ( p > 0 ) { + buffer[p] = 0x00; + p--; + } + } + else if ( c == 0x13 ) { // Save + puts ("\n========================= Save ========================="); + puts ("\nPress any key to save."); + c = getchar(); + for ( l = 0; l <= p; l++ ) { + tapio[l] = buffer[l]; + } + l++; + tapio[l] = 0x00; + puts ("Saving to tape."); + error = dumpt ( 'N', tapio, tapio+p ); + if ( error ) { + puts ("\nTape error."); + } + else + { + putchar ( '\r' ); + for ( l = 0; l < 25; l++ ) { + putchar ( '\n' ); + } + } + puts ("===================== Sym-1 Notepad ====================\n"); + for ( l = 0; l <= p; l++ ) { + putchar ( buffer[l] ); + } + } + else if ( c == 0x0C ) { // Load + p = 0; + puts ("\nLoading from tape."); + memset ( buffer, 0, heap_size ); + memset ( tapio, 0, TAPIO_MAX_SIZE ); + error = loadt ( 'N' ); + if ( error ) { + puts ("\nTape error."); + puts ("===================== Sym-1 Notepad ====================\n"); + } + else + { + for ( l = 0; l <= heap_size; l++ ) { + buffer[l] = tapio[l]; + } + + p = strlen ( buffer ); + + putchar ( '\r' ); + for ( l = 0; l < 25; l++ ) { + putchar ( '\n' ); + } + puts ("===================== Sym-1 Notepad ====================\n"); + + for ( l = 0; l <= p; l++ ) { + putchar ( buffer[l] ); + } + } + } + else if ( c == 0x03 ) { // Clear + p = 0; + memset ( buffer, 0, heap_size ); + putchar ( '\r' ); + for ( l = 0; l < 25; l++ ) { + putchar ( '\n' ); + } + puts ("===================== Sym-1 Notepad ====================\n"); + } + else if ( c == 0x18 ) { // Exit + writing = 0; + running = 0; + } + else { + if ( p >= heap_size - 1 ) { + puts ("\n========================= End ========================="); + puts ("Buffer full."); + } + else { + if ( c == '\n' ) { + putchar ( '\n' ); + } + buffer[p] = c; + } + p++; + } + } + } + + free ( buffer ); + + puts ("\nEnjoy your day!\n"); + + return 0; +} diff --git a/targettest/sym1/symTiny.c b/targettest/sym1/symTiny.c new file mode 100644 index 000000000..574ac36bc --- /dev/null +++ b/targettest/sym1/symTiny.c @@ -0,0 +1,42 @@ +// -------------------------------------------------------------------------- +// Hello World for Sym-1 +// +// Uses only getchar, putchar and puts, generating smaller code than printf +// +// Wayne Parham +// +// wayne@parhamdata.com +// -------------------------------------------------------------------------- + +#include <stdio.h> +#include <sym1.h> + +int main (void) { + char c = 0x00; + int d = 0x00; + int l = 0x00; + + puts ("Hello World!\n"); + + puts ("Type a line and press ENTER, please:\n"); + + for ( l = 0; l < 2; l++ ) { + beep(); + for ( d = 0; d < 10 ; d++ ) { + } + } + + while ( c != '\n' ) { + c = getchar(); + } + + puts ("\n\nThanks!\n"); + + for ( l = 0; l < 5; l++ ) { + beep(); + for ( d = 0; d < 10 ; d++ ) { + } + } + + return 0; +} diff --git a/testcode/lib/tinyshell.c b/targettest/tinyshell.c similarity index 100% rename from testcode/lib/tinyshell.c rename to targettest/tinyshell.c diff --git a/testcode/lib/uname-test.c b/targettest/uname-test.c similarity index 100% rename from testcode/lib/uname-test.c rename to targettest/uname-test.c diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index 5761abcfd..000000000 --- a/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/test/Makefile b/test/Makefile index c85883517..abc70d58f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -12,13 +12,9 @@ endif WORKDIR = ../testwrk -.PHONY: all dotests continue mostlyclean clean +.PHONY: test continue mostlyclean clean -all: dotests - -.NOTPARALLEL: - -dotests: mostlyclean continue +test: mostlyclean continue continue: @$(MAKE) -C asm all @@ -27,6 +23,7 @@ continue: @$(MAKE) -C ref all @$(MAKE) -C err all @$(MAKE) -C misc all + @$(MAKE) -C todo all mostlyclean: @$(MAKE) -C asm clean @@ -35,6 +32,7 @@ mostlyclean: @$(MAKE) -C ref clean @$(MAKE) -C err clean @$(MAKE) -C misc clean + @$(MAKE) -C todo clean clean: mostlyclean @$(call RMDIR,$(WORKDIR)) diff --git a/test/asm/6502dtv-cpudetect.ref b/test/asm/6502dtv-cpudetect.ref new file mode 100644 index 000000000..77a865eb4 Binary files /dev/null and b/test/asm/6502dtv-cpudetect.ref differ diff --git a/test/asm/6502dtv-opcodes.ref b/test/asm/6502dtv-opcodes.ref new file mode 100644 index 000000000..408d89be2 Binary files /dev/null and b/test/asm/6502dtv-opcodes.ref differ diff --git a/test/asm/6502dtv-opcodes.s b/test/asm/6502dtv-opcodes.s new file mode 100644 index 000000000..f2446cbe0 --- /dev/null +++ b/test/asm/6502dtv-opcodes.s @@ -0,0 +1,258 @@ +.setcpu "6502DTV" + + brk + ora ($12,x) + .byte $02 + .byte $03 + nop $12 + ora $12 + asl $12 + .byte $07 + php + ora #$12 + asl a + anc #$12 + nop $3456 + ora $3456 + asl $3456 + .byte $0f + bpl *+122 + ora ($12),y + bra *+122 + .byte $13 + nop $12,x + ora $12,x + asl $12,x + .byte $17 + clc + ora $3456,y + .byte $1a ; nop + .byte $1b + nop $3456,x + ora $3456,x + asl $3456,x + .byte $1f + jsr $3456 + and ($12,x) + .byte $22 + rla ($12,x) + bit $12 + and $12 + rol $12 + rla $12 + plp + and #$12 + rol a + .byte $2b,$12 ; anc #$12 + bit $3456 + and $3456 + rol $3456 + rla $3456 + bmi *+122 + and ($12),y + sac #$12 + rla ($12),y + .byte $34,$12 ; nop $12,x + and $12,x + rol $12,x + rla $12,x + sec + and $3456,y + .byte $3a ; nop + rla $3456,y + .byte $3c,$56,$34 ; nop $3456,x + and $3456,x + rol $3456,x + rla $3456,x + rti + eor ($12,x) + sir #$12 + .byte $43 + .byte $44,$12 ; nop $12 + eor $12 + lsr $12 + .byte $47 + pha + eor #$12 + lsr a + alr #$12 + jmp $3456 + eor $3456 + lsr $3456 + .byte $4f + bvc *+122 + eor ($12),y + .byte $52 + .byte $53 + .byte $54,$12 ; nop $12,x + eor $12,x + lsr $12,x + .byte $57 + cli + eor $3456,y + .byte $5a ; nop + .byte $5b + .byte $5c,$56,$34 ; nop $3456,x + eor $3456,x + lsr $3456,x + .byte $5f + rts + adc ($12,x) + .byte $62 + rra ($12,x) + .byte $64,$12 ; nop $12 + adc $12 + ror $12 + rra $12 + pla + adc #$12 + ror a + arr #$12 + jmp ($3456) + adc $3456 + ror $3456 + rra $3456 + bvs *+122 + adc ($12),y + .byte $72 + rra ($12),y + .byte $74,$12 ; nop $12,x + adc $12,x + ror $12,x + rra $12,x + sei + adc $3456,y + .byte $7a ; nop + rra $3456,y + .byte $7c,$56,$34 ; nop $3456,x + adc $3456,x + ror $3456,x + rra $3456,x + nop #$12 + sta ($12,x) + .byte $82,$12 ; nop #$12 + .byte $83 + sty $12 + sta $12 + stx $12 + .byte $87 + dey + .byte $89,$12 ; nop #$12 + txa + .byte $8b + sty $3456 + sta $3456 + stx $3456 + .byte $8f + bcc *+122 + sta ($12),y + .byte $92 + .byte $93 + sty $12,x + sta $12,x + stx $12,y + .byte $97 + tya + sta $3456,y + txs + .byte $9b + shy $3456,x + sta $3456,x + shx $3456,y + .byte $9f + ldy #$12 + lda ($12,x) + ldx #$12 + lax ($12,x) + ldy $12 + lda $12 + ldx $12 + lax $12 + tay + lda #$12 + tax + lax #$12 + ldy $3456 + lda $3456 + ldx $3456 + lax $3456 + bcs *+122 + lda ($12),y + .byte $b2 + lax ($12),y + ldy $12,x + lda $12,x + ldx $12,y + lax $12,y + clv + lda $3456,y + tsx + las $3456,y + ldy $3456,x + lda $3456,x + ldx $3456,y + lax $3456,y + cpy #$12 + cmp ($12,x) + .byte $c2,$12 ; nop #$12 + .byte $c3 + cpy $12 + cmp $12 + dec $12 + .byte $c7 + iny + cmp #$12 + dex + axs #$12 + cpy $3456 + cmp $3456 + dec $3456 + .byte $cf + bne *+122 + cmp ($12),y + .byte $d2 + .byte $d3 + .byte $d4,$12 ; nop $12,x + cmp $12,x + dec $12,x + .byte $d7 + cld + cmp $3456,y + .byte $da ; nop + .byte $db + .byte $dc,$56,$34 ; nop $3456,x + cmp $3456,x + dec $3456,x + .byte $df + cpx #$12 + sbc ($12,x) + .byte $e2,$12 ; nop #$12 + .byte $e3 + cpx $12 + sbc $12 + inc $12 + .byte $e7 + inx + sbc #$12 + nop + .byte $eb,$12 ; sbc #$12 + cpx $3456 + sbc $3456 + inc $3456 + .byte $ef + beq *+122 + sbc ($12),y + .byte $f2 + .byte $f3 + .byte $f4,$12 ; nop $12,x + sbc $12,x + inc $12,x + .byte $f7 + sed + sbc $3456,y + .byte $fa ; nop + .byte $fb + .byte $fc,$56,$34 ; nop $3456,x + sbc $3456,x + inc $3456,x + .byte $ff diff --git a/test/asm/Makefile b/test/asm/Makefile index 93210aaee..e951c2015 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -7,24 +7,23 @@ endif ifdef CMD_EXE EXE = .exe MKDIR = mkdir $(subst /,\,$1) - RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) + RMDIR = -rmdir /q /s $(subst /,\,$1) else EXE = MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET .SILENT: 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) WORKDIR = ../../testwrk/asm -DIFF = $(WORKDIR)/bdiff$(EXE) +ISEQUAL = ../../testwrk/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -32,27 +31,28 @@ CFLAGS = -O2 .PHONY: all clean OPCODE_REFS := $(wildcard *-opcodes.ref) -OPCODE_CPUS = $(foreach ref,$(OPCODE_REFS),$(ref:%-opcodes.ref=%)) -OPCODE_BINS = $(foreach cpu,$(OPCODE_CPUS),$(WORKDIR)/$(cpu)-opcodes.bin) +OPCODE_BINS = $(OPCODE_REFS:%.ref=$(WORKDIR)/%.bin) +OPCODE_CPUS = $(OPCODE_REFS:%-opcodes.ref=%) CPUDETECT_REFS := $(wildcard *-cpudetect.ref) -CPUDETECT_CPUS = $(foreach ref,$(CPUDETECT_REFS),$(ref:%-cpudetect.ref=%)) -CPUDETECT_BINS = $(foreach cpu,$(CPUDETECT_CPUS),$(WORKDIR)/$(cpu)-cpudetect.bin) +CPUDETECT_BINS = $(CPUDETECT_REFS:%.ref=$(WORKDIR)/%.bin) +CPUDETECT_CPUS = $(CPUDETECT_REFS:%-cpudetect.ref=%) -all: $(OPCODE_BINS) $(CPUDETECT_BINS) +all: $(OPCODE_BINS) $(CPUDETECT_BINS) $(WORKDIR)/paramcount.o $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define OPCODE_template -$(WORKDIR)/$1-opcodes.bin: $1-opcodes.s $(DIFF) +$(WORKDIR)/$1-opcodes.bin: $1-opcodes.s $(ISEQUAL) $(if $(QUIET),echo asm/$1-opcodes.bin) - $(CL65) --cpu $1 -t none -l $(WORKDIR)/$1-opcodes.lst -o $$@ $$< - $(DIFF) $$@ $1-opcodes.ref + $(CA65) -t none --cpu $1 -l $$(@:.bin=.lst) -o $$(@:.bin=.o) $$< + $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib + $(ISEQUAL) $1-opcodes.ref $$@ endef # OPCODE_template @@ -60,15 +60,18 @@ $(foreach cpu,$(OPCODE_CPUS),$(eval $(call OPCODE_template,$(cpu)))) define CPUDETECT_template -$(WORKDIR)/$1-cpudetect.bin: cpudetect.s $(DIFF) +$(WORKDIR)/$1-cpudetect.bin: cpudetect.s $1-cpudetect.ref $(ISEQUAL) $(if $(QUIET),echo asm/$1-cpudetect.bin) - $(CL65) --cpu $1 -t none -l $(WORKDIR)/$1-cpudetect.lst -o $$@ $$< - $(DIFF) $$@ $1-cpudetect.ref + $(CA65) -t none --cpu $1 -l $$(@:.bin=.lst) -o $$(@:.bin=.o) $$< + $(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib + $(ISEQUAL) $1-cpudetect.ref $$@ endef # CPUDETECT_template $(foreach cpu,$(CPUDETECT_CPUS),$(eval $(call CPUDETECT_template,$(cpu)))) +$(WORKDIR)/%.o: %.s | $(WORKDIR) + $(CA65) -l $(@:.o=.lst) -o $@ $< + clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(OPCODE_REFS:.ref=.o) cpudetect.o) diff --git a/test/asm/cpudetect.s b/test/asm/cpudetect.s index adad7c1dc..7b2363b7f 100644 --- a/test/asm/cpudetect.s +++ b/test/asm/cpudetect.s @@ -24,6 +24,10 @@ taz .endif +.ifpdtv + sac #$00 +.endif + ; step 2: check for bitwise compatibility of instructions sets ; (made verbose for better reading with hexdump/hd(1)) @@ -64,3 +68,7 @@ .byte 0,"CPU_ISET_4510" .endif +.if (.cpu .bitand CPU_ISET_6502DTV) + .byte 0,"CPU_ISET_6502DTV" +.endif + diff --git a/testcode/assembler/paramcount.s b/test/asm/paramcount.s similarity index 100% rename from testcode/assembler/paramcount.s rename to test/asm/paramcount.s diff --git a/test/asm/README b/test/asm/readme.txt similarity index 68% rename from test/asm/README rename to test/asm/readme.txt index a2b1e9a41..1d135c895 100644 --- a/test/asm/README +++ b/test/asm/readme.txt @@ -1,4 +1,3 @@ - Assembler Testcases =================== @@ -7,7 +6,7 @@ Opcode Tests: These testcases are inspired by the ones now removed from test/assembler. The main purpose is to have each possible opcode generated at least once, -either by an assembly instruction or a ".byte"-placeholder. Typically +either by an Assembly instruction or a ".byte"-placeholder. Typically generated by disassembling a binary dump that contains data in the form of the pattern that each opcode is stated once in order followed by easy to recognise: @@ -24,25 +23,25 @@ leftover dummy opcode parameters with something more recognizable. The testcases for 6502, 6502x, 65sc02, 65c02, 4510, and huc6280 have been put together by Sven Oliver ("SvOlli") Moll, as well as a template for the -m740 instructions set. +m740 instructions set. Later 6502dtv support was also added. Still to do is to find a way to implement an opcode testcase for the 65816 processor, since it's capable of executing instructions with an 8-bit and -a 16-bit operator alike, only distinguished by one processor flag. +a 16-bit operator alike, distinguished by only one processor flag. -CPU detect Tests +CPU Detect Tests ---------------- These tests all assemble the same file "cpudetect.s" which contains several conditionals for several CPUs, only using every option known to the "--cpu" -commandline switch of ca65/cl65. +command-line switch of ca65/cl65. Reference (".ref") Files ------------------------ -A hint on creating these files: when running the test, it will fail due to -the missing ".ref" file. Review the output of the ".lst" very pedantic, then -copy the ".bin" to the ".ref" file. - +Some hints about creating new files: +Make an empty file with the CPU's name prepended to "-cpudetect.ref". Run the +tests; one of them will fail due to a mismatch. Review the output of the +".lst" file pedantically, then copy the ".bin" over the empty ".ref" file. diff --git a/test/bdiff.c b/test/bdiff.c deleted file mode 100644 index 797ba4302..000000000 --- a/test/bdiff.c +++ /dev/null @@ -1,28 +0,0 @@ - -// minimal tool to compare two binaries - -#include <stdlib.h> -#include <stdio.h> - -int main(int argc, char *argv[]) -{ - FILE *f1, *f2; - if (argc < 3) { - return EXIT_FAILURE; - } - f1 = fopen(argv[1], "rb"); - f2 = fopen(argv[2], "rb"); - if ((f1 == NULL) || (f2 == NULL)) { - return EXIT_FAILURE; - } - for(;;) { - if (feof(f1) && feof(f2)) { - return EXIT_SUCCESS; - } else if (feof(f1) || feof(f2)) { - return EXIT_FAILURE; - } - if (fgetc(f1) != fgetc(f2)) { - return EXIT_FAILURE; - } - } -} diff --git a/test/dasm/Makefile b/test/dasm/Makefile index d70711491..542ce7d5e 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -7,13 +7,11 @@ endif ifdef CMD_EXE EXE = .exe MKDIR = mkdir $(subst /,\,$1) - RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) + RMDIR = -rmdir /q /s $(subst /,\,$1) else EXE = MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -25,7 +23,7 @@ DA65 := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) WORKDIR = ../../testwrk/dasm -DIFF = $(WORKDIR)/bdiff$(EXE) +ISEQUAL = ../../testwrk/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -35,16 +33,15 @@ START = --start-addr 0x8000 .PHONY: all clean SOURCES := $(wildcard *.s) -CPUS = $(foreach src,$(SOURCES),$(src:%-disass.s=%)) -BINS = $(foreach cpu,$(CPUS),$(WORKDIR)/$(cpu)-reass.bin) +BINS = $(SOURCES:%disass.s=$(WORKDIR)/%reass.bin) +CPUS = $(SOURCES:%-disass.s=%) -# default target defined later all: $(BINS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define DISASS_template @@ -55,10 +52,10 @@ $(WORKDIR)/$1-disass.bin: $1-disass.s | $(WORKDIR) $(WORKDIR)/$1-reass.s: $(WORKDIR)/$1-disass.bin $(DA65) --cpu $1 $(START) -o $$@ $$< -$(WORKDIR)/$1-reass.bin: $(WORKDIR)/$1-reass.s $(DIFF) +$(WORKDIR)/$1-reass.bin: $(WORKDIR)/$1-reass.s $(ISEQUAL) $(if $(QUIET),echo dasm/$1-reass.bin) $(CL65) --cpu $1 -t none $(START) -o $$@ $$< - $(DIFF) $$@ $(WORKDIR)/$1-disass.bin + $(ISEQUAL) $(WORKDIR)/$1-disass.bin $$@ endef # DISASS_template @@ -66,4 +63,3 @@ $(foreach cpu,$(CPUS),$(eval $(call DISASS_template,$(cpu)))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.s=.o)) diff --git a/test/err/Makefile b/test/err/Makefile index 4b05ca5db..1273bbb2c 100644 --- a/test/err/Makefile +++ b/test/err/Makefile @@ -34,7 +34,10 @@ TESTS = $(patsubst %.c,$(WORKDIR)/%.s,$(SOURCES)) all: $(TESTS) -$(WORKDIR)/%.s: %.c +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +$(WORKDIR)/%.s: %.c | $(WORKDIR) $(if $(QUIET),echo err/$*.s) $(NOT) $(CC65) -o $@ $< $(NULLERR) diff --git a/test/err/bitfield-named-zero-width.c b/test/err/bitfield-named-zero-width.c new file mode 100644 index 000000000..108b195d0 --- /dev/null +++ b/test/err/bitfield-named-zero-width.c @@ -0,0 +1,29 @@ +/* + Copyright 2020 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 bit-field with zero-width named field +*/ + +struct s { + unsigned int x : 4; + unsigned int y : 0; + unsigned int z : 4; +}; diff --git a/test/err/bitfield-negative-width.c b/test/err/bitfield-negative-width.c new file mode 100644 index 000000000..a90199f38 --- /dev/null +++ b/test/err/bitfield-negative-width.c @@ -0,0 +1,27 @@ +/* + Copyright 2020 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 bit-field with negative width. +*/ + +struct s { + unsigned int x : -1; +}; diff --git a/test/err/bitfield-too-wide.c b/test/err/bitfield-too-wide.c new file mode 100644 index 000000000..424cf9c05 --- /dev/null +++ b/test/err/bitfield-too-wide.c @@ -0,0 +1,27 @@ +/* + Copyright 2020 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 bit-field with too large width. +*/ + +struct s { + unsigned int x : 17; +}; diff --git a/test/err/bug1048.c b/test/err/bug1048.c new file mode 100644 index 000000000..d022474d6 --- /dev/null +++ b/test/err/bug1048.c @@ -0,0 +1,15 @@ +/* bug #1048: The following code has two errors: a redeclared enum type and an + undeclared enum type: */ + +#include <stdlib.h> + +// this should NOT compile - but with cc65 it does +enum e { x }; +enum e { y }; + +int f() { return sizeof(enum undeclared); } + +int main(void) +{ + return EXIT_SUCCESS; +} diff --git a/test/err/bug1098.c b/test/err/bug1098.c new file mode 100644 index 000000000..c49296245 --- /dev/null +++ b/test/err/bug1098.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +enum { +}; + +int main(void) +{ + return 0; +} diff --git a/test/err/bug1098a.c b/test/err/bug1098a.c new file mode 100644 index 000000000..63c1c8da0 --- /dev/null +++ b/test/err/bug1098a.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +struct { +}; + +int main(void) +{ + return 0; +} diff --git a/test/err/bug1098b.c b/test/err/bug1098b.c new file mode 100644 index 000000000..ebd3e94c8 --- /dev/null +++ b/test/err/bug1098b.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +union { +}; + +int main(void) +{ + return 0; +} diff --git a/test/err/bug1113.c b/test/err/bug1113.c new file mode 100644 index 000000000..d1aecaa0b --- /dev/null +++ b/test/err/bug1113.c @@ -0,0 +1,12 @@ + +/* bug #1113 - Compiler crashes when calling functions "redefined" as other types */ + +void f() {} + +int f; + +int main(void) +{ + f(); + return 0; +} diff --git a/test/err/bug1143err.c b/test/err/bug1143err.c new file mode 100644 index 000000000..03a6e6d35 --- /dev/null +++ b/test/err/bug1143err.c @@ -0,0 +1,11 @@ + +/* bug #1143 - Multiple storage class specifiers in one declaration? */ + +static static void* y[1]; /* warning */ +extern static int a; /* error */ +extern typedef int A; /* error */ + +int main(void) +{ + return 0; +} diff --git a/test/err/bug1145.c b/test/err/bug1145.c new file mode 100644 index 000000000..23401f91b --- /dev/null +++ b/test/err/bug1145.c @@ -0,0 +1,13 @@ + +/* bug #1145 - Internal error with function type object */ + +void f() +{ + f = 0; /* internal error */ +} + +int main(void) +{ + f(); + return 0; +} diff --git a/test/err/bug264.c b/test/err/bug264.c new file mode 100644 index 000000000..6898f3790 --- /dev/null +++ b/test/err/bug264.c @@ -0,0 +1,62 @@ +/* bug #264 - cc65 fails to warn about a function returning struct */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +typedef uint32_t u32; +typedef uint16_t u16; + +/* this struct is too large, we can only handle max 4 bytes right now */ +typedef struct { + u32 quot; + u32 rem; +} udiv_t; + +udiv_t div3(u32 in) { + + udiv_t u; + u32 q = 0; + + while (in >= 300) { + in -= 300; + q += 100; + } + + while (in >= 30) { + in -= 30; + q += 10; + } + + while (in >= 3) { + in -= 3; + ++q; + } + + u.quot = q; + u.rem = in; + + return u; /* error */ +} + +int res = 0; + +int main(void) { + + u32 i; + div_t d; + udiv_t u; + + for (i = 1024; i; i--) { + d = div((u16)i, 3); + u = div3(i); + + if (d.quot != u.quot || d.rem != u.rem) { + printf("Mismatch at %u/3, div %u %u, div3 %u %u\n", i, + d.quot, d.rem, u.quot, u.rem); + res++; + } + } + + return res; +} diff --git a/test/err/pr1110.c b/test/err/pr1110.c new file mode 100644 index 000000000..86955c720 --- /dev/null +++ b/test/err/pr1110.c @@ -0,0 +1,15 @@ + +/* pr #1110 - not only should the current test case for #975 compile and work, + * but also the code piece below fail to compile and generate errors like commented: */ + +static const unsigned char array[3]; /* OK */ +static const unsigned char array[] = { 0, 1, 2 }; /* OK - complete definition*/ +static const unsigned char array[3]; /* OK */ +static const unsigned char array[]; /* OK */ +static const unsigned char array[] = { 1, 2, 3 }; /* Error - redefinition */ +static const unsigned char array[4]; /* Error - conflicting size */ + +int main(void) +{ + return 0; +} diff --git a/test/err/pr1135.c b/test/err/pr1135.c new file mode 100644 index 000000000..01eff7d93 --- /dev/null +++ b/test/err/pr1135.c @@ -0,0 +1,8 @@ + +void f(void) {} +void f(int); /* Should fail */ + +int main(void) +{ + return 0; +} diff --git a/test/err/pr1189.c b/test/err/pr1189.c new file mode 100644 index 000000000..af1d237f1 --- /dev/null +++ b/test/err/pr1189.c @@ -0,0 +1,12 @@ +/* pr #1189 - Fixed compiler CHECK failure when calling functions defined with duplicate param names */ + +void f(int a, int a) +{ + +} + +int main(void) +{ + f(0, 1); /* Check failed: (Param->Flags & SC_PARAM) != 0 */ + return 0; +} diff --git a/test/err/staticassert-nomsg.c b/test/err/staticassert-nomsg.c new file mode 100644 index 000000000..8cdcb09a4 --- /dev/null +++ b/test/err/staticassert-nomsg.c @@ -0,0 +1,26 @@ +/* + Copyright 2020 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 failing _Static_assert. +**/ + + +_Static_assert(0 == 1); diff --git a/test/err/staticassert.c b/test/err/staticassert.c new file mode 100644 index 000000000..60cb37529 --- /dev/null +++ b/test/err/staticassert.c @@ -0,0 +1,26 @@ +/* + Copyright 2020 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 failing _Static_assert. +**/ + + +_Static_assert(0, "0 should be false."); diff --git a/test/isequal.c b/test/isequal.c new file mode 100644 index 000000000..b3806c7e4 --- /dev/null +++ b/test/isequal.c @@ -0,0 +1,54 @@ + +// minimal tool to compare two text files + +#include <stdlib.h> +#include <stdio.h> + +/* get the next character from FILE and convert commonly used line-endings all + into the same value (0x0a, as used on *nix systems) + + recognized values/pairs: + + 0x0a (LF) Linux, macOS + 0x0d, 0x0a (CR, LF) Windows, MSDOS, OS/2 + 0x0d (CR) classic MacOS +*/ + +int getnext(FILE *f) +{ + int c = fgetc(f); + if (c == 0x0d) { + if (!feof(f)) { + int n = fgetc(f); + if (n != 0x0a) { + ungetc(n, f); + } + clearerr(f); /* clears EOF when we did not push back */ + } + return 0x0a; + } + return c; +} + +int main(int argc, char *argv[]) +{ + FILE *f1, *f2; + if (argc < 3) { + return EXIT_FAILURE; + } + f1 = fopen(argv[1], "rb"); + f2 = fopen(argv[2], "rb"); + if ((f1 == NULL) || (f2 == NULL)) { + return EXIT_FAILURE; + } + for(;;) { + if (feof(f1) && feof(f2)) { + return EXIT_SUCCESS; + } else if (feof(f1) || feof(f2)) { + return EXIT_FAILURE; + } + if (getnext(f1) != getnext(f2)) { + return EXIT_FAILURE; + } + } +} diff --git a/test/misc/Makefile b/test/misc/Makefile index 15999ba3c..e6c58c5a4 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -11,7 +11,6 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NOT = ! @@ -19,7 +18,6 @@ else NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -30,14 +28,16 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +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 = ..$S..$Stestwrk$Smisc OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sbdiff$(EXE) +ISEQUAL = ..$S..$Stestwrk$Sisequal$(EXE) CC = gcc CFLAGS = -O2 @@ -53,39 +53,107 @@ all: $(TESTS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define PRG_template +# should compile, but gives an error +$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug760.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1437.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev.$1.$2.prg) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev-2.$1.$2.prg: bug1209-ind-goto-rev-2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev-2.$1.$2.prg) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev-3.$1.$2.prg: bug1209-ind-goto-rev-3.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev-3.$1.$2.prg) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/pptest2.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1263.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1357.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but compiler exits with internal error +$(WORKDIR)/bug1211-ice-move-refs-2.$1.$2.prg: bug1211-ice-move-refs-2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1211-ice-move-refs-2.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# this one requires --std=c89, it fails with --std=c99 +$(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1265.$1.$2.prg) + $(CC65) --standard c89 -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) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -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) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) # these need reference data that can't be generated by a host-compiled program, # in a useful way -$(WORKDIR)/limits.$1.$2.prg: limits.c $(DIFF) +$(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/limits.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/limits.$1.out - $(DIFF) $(WORKDIR)/limits.$1.out limits.ref + $(CC65) -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) $$@ > $(WORKDIR)/limits.$1.$2.out + $(ISEQUAL) $(WORKDIR)/limits.$1.$2.out limits.ref -$(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) +$(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/goto.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.out - $(DIFF) $(WORKDIR)/goto.$1.out goto.ref + $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out + $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref # the rest are tests that fail currently for one reason or another -$(WORKDIR)/fields.$1.$2.prg: fields.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently will fail." - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently will fail." - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# -$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) + @echo "FIXME: " $$@ "currently does not compile." + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -94,4 +162,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/misc/bug1209-ind-goto-rev-2.c b/test/misc/bug1209-ind-goto-rev-2.c new file mode 100644 index 000000000..8918e5878 --- /dev/null +++ b/test/misc/bug1209-ind-goto-rev-2.c @@ -0,0 +1,54 @@ +/* + Copyright 2020 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 indirect goto without dynamic labels and order label def, label ref, goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include <stdio.h> +#include <stdlib.h> + +/* When operating correctly, this returns 0. */ +static unsigned char y = 0; +int f (void) { +L: if (y) return 0; + { + static const void *const x[1] = {&&L}; + y = 1; + goto *x[0]; + } +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/misc/bug1209-ind-goto-rev-3.c b/test/misc/bug1209-ind-goto-rev-3.c new file mode 100644 index 000000000..4c3268c9a --- /dev/null +++ b/test/misc/bug1209-ind-goto-rev-3.c @@ -0,0 +1,52 @@ +/* + Copyright 2020 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 indirect goto without dynamic labels and order label ref, label def, goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include <stdio.h> +#include <stdlib.h> + +/* When operating correctly, this returns 0. */ +static unsigned char y = 0; +int f (void) { + static const void *const x[1] = {&&L}; +L: if (y) return 0; + y = 1; + goto *x[0]; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/misc/bug1211-ice-move-refs-2.c b/test/misc/bug1211-ice-move-refs-2.c new file mode 100644 index 000000000..504433f45 --- /dev/null +++ b/test/misc/bug1211-ice-move-refs-2.c @@ -0,0 +1,51 @@ +/* + Copyright 2020 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 indirect goto with label merge ICE. + https://github.com/cc65/cc65/issues/1211 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include <stdio.h> + +/* When operating correctly, this returns 0. */ +int f (void) +{ + static const void *const x[2] = {&&L0, &&L1}; + goto *x[0]; +L0: +L1: return 0; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/misc/bug1265.c b/test/misc/bug1265.c new file mode 100644 index 000000000..36d1459a7 --- /dev/null +++ b/test/misc/bug1265.c @@ -0,0 +1,47 @@ +/* bug #1265 - misadjusted stack from unprototyped function call */ + +#include <stdio.h> +#include <string.h> + +int failures = 0; + +char str1[10]; +char str2[10]; + +int f2 (int x) { return x == 2345 ? 23 : -1; } + +int main (void) { + int x, n; + + sprintf (str1, "%p\n", &x); + x = 1234; + n = f1 (x); + sprintf (str2, "%p\n", &x); + + if (strcmp(str1, str2)) { + puts("not equal"); + failures++; + } + if (n != 42) { + puts("f1 returns wrong value"); + failures++; + } + + sprintf (str1, "%p\n", &x); + x = 2345; + n = f2 (x); + sprintf (str2, "%p\n", &x); + + if (strcmp(str1, str2)) { + puts("not equal"); + failures++; + } + if (n != 23) { + puts("f2 returns wrong value"); + failures++; + } + + return failures; +} + +int f1 (int x) { return x == 1234 ? 42 : -1; } diff --git a/test/misc/bug1357.c b/test/misc/bug1357.c new file mode 100644 index 000000000..40415f868 --- /dev/null +++ b/test/misc/bug1357.c @@ -0,0 +1,30 @@ + +/* issue #1357 - X Macros don't work with C preprocessor */ + +#define OPCODES(X) \ + X(PUSHNIL) \ + X(PUSHTRUE) \ + X(PUSHFALSE) + +enum { +#define X(op) op, +OPCODES(X) +#undef X + N_OPS +}; + +/* cc65 -E bug1357.c -o bug1357.c.pre + should produce something like this: + +enum { +PUSHNIL, +PUSHTRUE, +PUSHFALSE, + N_OPS +}; +*/ + +int main(void) +{ + return 0; +} diff --git a/test/misc/bug760.c b/test/misc/bug760.c new file mode 100644 index 000000000..dc8573b6d --- /dev/null +++ b/test/misc/bug760.c @@ -0,0 +1,12 @@ +/* bug#760 - Error when using macros as pragma arguments */ + +#include <stdlib.h> + +#define BANK "PRG0" + +#pragma rodata-name(push, BANK) + +int main(void) +{ + return EXIT_SUCCESS; +} diff --git a/test/misc/common.h b/test/misc/common.h deleted file mode 100644 index dada61a14..000000000 --- a/test/misc/common.h +++ /dev/null @@ -1,22 +0,0 @@ - -#include <stdio.h> -#include <stdlib.h> - -#define NO_OLD_FUNC_DECL -#define NO_TYPELESS_INT -#define NO_TYPELESS_INT_PTR -#define MAIN_RETURNS_INT -#define NO_IMPLICIT_FUNC_PROTOTYPES -#define NO_FLOATS -#define NO_WCHAR -#define NO_EMPTY_FUNC_ARGS -#define NO_SLOPPY_STRUCT_INIT -#define NO_FUNCS_TAKE_STRUCTS -#define NO_FUNCS_RETURN_STRUCTS -#define CAST_STRUCT_PTR -#define NO_TYPELESS_STRUCT_PTR -#define NO_IMPLICIT_FUNCPTR_CONV -#define SIZEOF_INT_16BIT -#define SIZEOF_LONG_32BIT -#define UNSIGNED_CHARS -#define UNSIGNED_BITFIELDS diff --git a/test/misc/goto.ref b/test/misc/goto.ref index 85dc20a61..2e0819887 100644 --- a/test/misc/goto.ref +++ b/test/misc/goto.ref @@ -1,150 +1,150 @@ -goto.c(8): Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration -goto.c(97): Warning: 'a' is defined but never used -goto.c(117): Warning: 'a' is defined but never used -goto.c(137): Warning: 'a' is defined but never used -goto.c(159): Warning: Goto at line 23 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 44 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 65 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 86 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 106 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 126 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(159): Warning: Goto at line 146 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 24 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 45 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 66 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 87 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 107 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 127 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 147 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(180): Warning: Goto at line 168 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 25 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 46 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 67 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 88 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 108 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 128 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 148 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 169 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(201): Warning: Goto at line 190 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 26 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 47 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 68 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 89 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 109 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 129 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 149 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(221): Warning: Goto at line 170 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(231): Warning: Goto at line 231 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 27 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 48 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 69 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 90 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 110 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 130 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(241): Warning: Goto at line 150 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(250): Warning: Goto at line 250 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(251): Warning: Goto at line 251 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(252): Warning: Goto at line 252 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 28 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 49 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 70 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 91 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 111 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 131 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 151 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 172 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 193 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 214 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 234 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(263): Warning: Goto at line 254 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(271): Warning: Goto at line 271 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(272): Warning: Goto at line 272 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(273): Warning: Goto at line 273 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(274): Warning: Goto at line 274 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(275): Warning: Goto at line 275 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 29 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 50 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 71 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 92 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 112 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 132 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 152 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 173 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 194 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 215 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 235 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 255 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(284): Warning: Goto at line 277 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(292): Warning: Goto at line 292 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(293): Warning: Goto at line 293 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(294): Warning: Goto at line 294 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(295): Warning: Goto at line 295 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(296): Warning: Goto at line 296 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 30 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 51 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 72 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 93 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 113 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 133 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 153 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 174 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 195 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 216 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 236 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 256 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 278 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(305): Warning: Goto at line 299 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(313): Warning: Goto at line 313 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(314): Warning: Goto at line 314 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(315): Warning: Goto at line 315 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(316): Warning: Goto at line 316 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(317): Warning: Goto at line 317 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 31 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 52 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 73 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 94 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 114 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 134 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 154 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 175 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 196 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 217 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 237 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 257 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(325): Warning: Goto at line 279 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(333): Warning: Goto at line 333 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(334): Warning: Goto at line 334 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(335): Warning: Goto at line 335 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(336): Warning: Goto at line 336 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(337): Warning: Goto at line 337 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(340): Warning: Goto at line 340 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 32 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 53 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 74 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 95 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 115 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 135 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 155 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 176 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 197 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 218 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 238 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(345): Warning: Goto at line 258 to label lh jumps into a block with initialization of an object that has automatic storage duration -goto.c(353): Warning: Goto at line 353 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(354): Warning: Goto at line 354 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(355): Warning: Goto at line 355 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(356): Warning: Goto at line 356 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(357): Warning: Goto at line 357 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(359): Warning: Goto at line 359 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(360): Warning: Goto at line 360 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(361): Warning: Goto at line 361 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(373): Warning: Goto at line 373 to label l8 jumps into a block with initialization of an object that has automatic storage duration -goto.c(374): Warning: Goto at line 374 to label l9 jumps into a block with initialization of an object that has automatic storage duration -goto.c(375): Warning: Goto at line 375 to label la jumps into a block with initialization of an object that has automatic storage duration -goto.c(376): Warning: Goto at line 376 to label lb jumps into a block with initialization of an object that has automatic storage duration -goto.c(377): Warning: Goto at line 377 to label lc jumps into a block with initialization of an object that has automatic storage duration -goto.c(378): Warning: Goto at line 378 to label ld jumps into a block with initialization of an object that has automatic storage duration -goto.c(379): Warning: Goto at line 379 to label le jumps into a block with initialization of an object that has automatic storage duration -goto.c(380): Warning: Goto at line 380 to label lf jumps into a block with initialization of an object that has automatic storage duration -goto.c(381): Warning: Goto at line 381 to label lg jumps into a block with initialization of an object that has automatic storage duration -goto.c(382): Warning: Goto at line 382 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:8: Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration +goto.c:97: Warning: Variable 'a' is defined but never used +goto.c:117: Warning: Variable 'a' is defined but never used +goto.c:137: Warning: Variable 'a' is defined but never used +goto.c:159: Warning: Goto at line 23 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 44 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 65 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 86 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 106 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 126 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:159: Warning: Goto at line 146 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 24 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 45 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 66 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 87 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 107 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 127 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 147 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:180: Warning: Goto at line 168 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 25 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 46 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 67 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 88 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 108 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 128 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 148 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 169 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:201: Warning: Goto at line 190 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 26 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 47 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 68 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 89 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 109 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 129 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 149 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:221: Warning: Goto at line 170 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:231: Warning: Goto at line 231 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 27 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 48 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 69 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 90 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 110 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 130 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:241: Warning: Goto at line 150 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:250: Warning: Goto at line 250 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:251: Warning: Goto at line 251 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:252: Warning: Goto at line 252 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 28 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 49 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 70 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 91 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 111 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 131 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 151 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 172 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 193 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 214 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 234 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:263: Warning: Goto at line 254 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:271: Warning: Goto at line 271 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:272: Warning: Goto at line 272 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:273: Warning: Goto at line 273 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:274: Warning: Goto at line 274 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:275: Warning: Goto at line 275 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 29 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 50 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 71 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 92 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 112 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 132 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 152 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 173 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 194 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 215 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 235 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 255 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:284: Warning: Goto at line 277 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:292: Warning: Goto at line 292 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:293: Warning: Goto at line 293 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:294: Warning: Goto at line 294 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:295: Warning: Goto at line 295 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:296: Warning: Goto at line 296 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 30 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 51 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 72 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 93 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 113 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 133 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 153 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 174 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 195 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 216 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 236 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 256 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 278 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:305: Warning: Goto at line 299 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:313: Warning: Goto at line 313 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:314: Warning: Goto at line 314 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:315: Warning: Goto at line 315 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:316: Warning: Goto at line 316 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:317: Warning: Goto at line 317 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 31 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 52 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 73 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 94 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 114 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 134 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 154 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 175 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 196 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 217 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 237 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 257 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:325: Warning: Goto at line 279 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:333: Warning: Goto at line 333 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:334: Warning: Goto at line 334 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:335: Warning: Goto at line 335 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:336: Warning: Goto at line 336 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:337: Warning: Goto at line 337 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:340: Warning: Goto at line 340 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 32 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 53 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 74 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 95 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 115 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 135 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 155 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 176 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 197 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 218 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 238 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:345: Warning: Goto at line 258 to label lh jumps into a block with initialization of an object that has automatic storage duration +goto.c:353: Warning: Goto at line 353 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:354: Warning: Goto at line 354 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:355: Warning: Goto at line 355 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:356: Warning: Goto at line 356 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:357: Warning: Goto at line 357 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:359: Warning: Goto at line 359 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:360: Warning: Goto at line 360 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:361: Warning: Goto at line 361 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:373: Warning: Goto at line 373 to label l8 jumps into a block with initialization of an object that has automatic storage duration +goto.c:374: Warning: Goto at line 374 to label l9 jumps into a block with initialization of an object that has automatic storage duration +goto.c:375: Warning: Goto at line 375 to label la jumps into a block with initialization of an object that has automatic storage duration +goto.c:376: Warning: Goto at line 376 to label lb jumps into a block with initialization of an object that has automatic storage duration +goto.c:377: Warning: Goto at line 377 to label lc jumps into a block with initialization of an object that has automatic storage duration +goto.c:378: Warning: Goto at line 378 to label ld jumps into a block with initialization of an object that has automatic storage duration +goto.c:379: Warning: Goto at line 379 to label le jumps into a block with initialization of an object that has automatic storage duration +goto.c:380: Warning: Goto at line 380 to label lf jumps into a block with initialization of an object that has automatic storage duration +goto.c:381: Warning: Goto at line 381 to label lg jumps into a block with initialization of an object that has automatic storage duration +goto.c:382: Warning: Goto at line 382 to label lh jumps into a block with initialization of an object that has automatic storage duration diff --git a/test/misc/pptest2.c b/test/misc/pptest2.c new file mode 100644 index 000000000..7857c67b2 --- /dev/null +++ b/test/misc/pptest2.c @@ -0,0 +1,73 @@ + +/* preprocessor test #2 */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +int y = 7; +int z[] = {1, 2, 3}; +int res; + +int f(int i) { + printf("f: %d\n", i); + return i + 1; +} +int t(int i) { + printf("t: %d\n", i); + return i + 1; +} +int m(int i, int j) { + printf("m: %d %d\n", i, j); + return i + j + 1; +} + +#define x 3 +#define f(a) f(x * (a)) +#undef x +#define x 2 +#define g f +#define z z[0] +#define h g(~ +#define m(a) a(w) +#define w 0,1 +#define t(a) a +#define p() int +#define q(x) x +#define r(x,y) x ## y +#define str(x) # x + +int main(void) +{ + res = f(y+1) + f(f(z)) % t(t(g) (0) + t)(1); + printf("res: %d expected: 19\n", res); + if (res != 19) { + return EXIT_FAILURE; + } + + res = g(x+(3,4)-w) | h 5) & m(f)^m(m); + printf("res: %d expected: 3\n", res); + if (res != 3) { + return EXIT_FAILURE; + } + + p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; + printf("i[]: %d %d %d %d\n", i[0], i[1], i[2], i[3]); + printf("expected: %d %d %d %d\n", 1, 23, 4, 5); + if ((i[0] != 1) || (i[1] != 23) || (i[2] != 4) || (i[3] != 5)) { + return EXIT_FAILURE; + } + + char c[2][6] = { str(hello), str() }; + printf ("c[0]: %s expected: %s\n", c[0], "hello"); + printf ("c[1]: %s expected: %s\n", c[1], ""); + if (strcmp(c[0], "hello") != 0) { + return EXIT_FAILURE; + } + if (strcmp(c[1], "") != 0) { + return EXIT_FAILURE; + } + + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/test/misc/sitest.c b/test/misc/sitest.c index 3332c064e..d13acf92c 100644 --- a/test/misc/sitest.c +++ b/test/misc/sitest.c @@ -19,8 +19,9 @@ NOTE: This is not a thorough validation test of the facilities. */ +#define STANDARD_CC65 +#define NO_WCHAR #define NO_INTERNAL_WCHAR -/*#define STANDALONE*/ #include <errno.h> #include <limits.h> /* for CHAR_BIT */ @@ -37,7 +38,7 @@ #ifdef NO_WCHAR -#warn "this test checks C99 features, but NO_WCHAR is defined so the test will most definetly fails." +#warn "this test checks C99 features, but NO_WCHAR is defined so the test will most definitely fail." #endif @@ -51,16 +52,6 @@ #include <inttypes.h> /* test idempotency */ -#ifdef STANDALONE - -FILE *outfile=NULL; -#define opentest(x) outfile=stdout; -#define closetest(x) - -#else - -#endif - #if __STDC_VERSION__ >= 199901 #ifndef __Q8_QT #define __Q8_QT long long @@ -1588,4 +1579,4 @@ main() { return status; } -#endif \ No newline at end of file +#endif diff --git a/test/readme.txt b/test/readme.txt index 2d4413f45..49ae363cc 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -1,27 +1,48 @@ This directory contains test code for automatic regression testing of the CC65 -compiler. - - -/val - the bulk of tests are contained here, individual tests should exit with - an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error - -/ref - these tests produce output that must be compared with reference output - -/err - contains tests that MUST NOT compile - -/todo - these tests fail due to open compiler issues +compiler and tools. /asm - contains the assembler regression tests /dasm - contains the disassembler regression tests + +/val, /ref and /err generally contain the tests that are used to verify that the +compiler is working as expected (when the tests behave as described): + +/val - The bulk of tests are contained here, individual tests should exit with + an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error. + +/ref - These tests produce output that must be compared with reference output. + +/err - contains tests that MUST NOT compile + + +/todo and /misc generally contain the tests that fail because of known bugs: + +/todo - These tests fail due to open compiler issues. + + The makefile in this directory _expects_ the tests to fail, because of + that when an issue was fixed it will break the CI. The test should get + moved to /val in the PR fixing the issue, which will make CI pass again. + No changes to makefiles are required! + + /misc - a few tests that need special care of some sort + Tests that (incorrectly) fail to compile and other tests that fail and + do NOT return an exit code are collected here. The makefile _expects_ + those tests to fail, so when an issue is fixed it will break the CI. + When this happens, the PR fixing the issue should also "invert" the + failing condition in the makefile by adding a $(NOT) before the + offending line (or removing it when it is already there), which will + make the CI pass again. The test should then be moved elsewhere later, + which will require additional changes to the makefile(s). -to run the tests use "make" in this (top) directory, the makefile should exit + +To run the tests use "make" in this (top) directory, the makefile should exit with no error. -when a test failed you can use "make continue" to run further tests +When a test failed you can use "make continue" to run further tests. -------------------------------------------------------------------------------- diff --git a/test/ref/Makefile b/test/ref/Makefile index a94f65dd8..d9c9817ee 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -11,14 +11,12 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / EXE = NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -28,14 +26,16 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +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 = ..$S..$Stestwrk$Sref OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sbdiff$(EXE) +ISEQUAL = ..$S..$Stestwrk$Sisequal$(EXE) CC = gcc CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow @@ -57,7 +57,7 @@ $(WORKDIR)/%.ref: %.c | $(WORKDIR) $(CC) $(CFLAGS) -o $(WORKDIR)/$*.host $< $(NULLERR) $(WORKDIR)$S$*.host > $@ -$(DIFF): ../bdiff.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< # "yaccdbg.c" includes "yacc.c". @@ -68,11 +68,13 @@ $(WORKDIR)/yaccdbg.%.prg: yacc.c define PRG_template -$(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(DIFF) +$(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(ISEQUAL) $(if $(QUIET),echo ref/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(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) $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out - $(DIFF) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref + $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref endef # PRG_template @@ -81,4 +83,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/ref/cc65110210.c b/test/ref/cc65110210.c index ca5b39203..319b2c529 100644 --- a/test/ref/cc65110210.c +++ b/test/ref/cc65110210.c @@ -20,27 +20,10 @@ cl65: Subprocess 'ld65' aborted by signal 11 */ -/* #define STANDALONE */ - #include <stdio.h> #include <limits.h> -#ifdef STANDALONE - -#define NO_IMPLICIT_FUNCPTR_CONV - -#define OPENTEST() -#define CLOSETEST() - -#else - -#endif - -#ifdef NO_IMPLICIT_FUNCPTR_CONV -void (*p1func)(void); -#else void (*p1func)(); -#endif void func(void) { diff --git a/test/ref/cf.in b/test/ref/cf.in old mode 100755 new mode 100644 diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c new file mode 100644 index 000000000..0d5c1d800 --- /dev/null +++ b/test/ref/pr1220.c @@ -0,0 +1,360 @@ +/* PR #1220 - test constant ternary, AND and OR */ + +#include <stdio.h> + +/* test AND/OR, results as integers */ +#define CONTEXT_A(x) do {\ + s = 0, flags = 0, t = (x),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +/* test AND/OR in ternary context */ +#define CONTEXT_B(x) do {\ + s = 0, flags = 0, t = (x ? 42 : -42),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +int s, t; +unsigned flags; + +int f(int x) +/* The call to this function should be and only be skipped strictly according to +** the short-circuit evaluation rule. +*/ +{ + flags |= (x != 0) << s; + ++s; + return x; +} + +#define _A f(a) +#define _B f(b) +#define _C f(c) +#define _D f(d) +#define _T (f(0), 256) +#define _F (f(256), 0) + +void f0() +/* constant short-circuit */ +{ + printf("f0()\n"); + + CONTEXT_A(_T && _T && _T); + CONTEXT_A(_F && _F && _F); + + CONTEXT_A(_T || _T || _T); + CONTEXT_A(_F || _F || _F); + + CONTEXT_A(_T && _T || _T && _T); + CONTEXT_A(_F && _F || _F && _F); + CONTEXT_A(_T && _F || _T && _F); + CONTEXT_A(_F && _T || _F && _T); + + CONTEXT_A((_T && _T) || (_T && _T)); + CONTEXT_A((_F && _F) || (_F && _F)); + CONTEXT_A((_T && _F) || (_T && _F)); + CONTEXT_A((_F && _T) || (_F && _T)); + + CONTEXT_A((_T || _T) && (_T || _T)); + CONTEXT_A((_F || _F) && (_F || _F)); + CONTEXT_A((_T || _F) && (_T || _F)); + CONTEXT_A((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1(int a, int b, int c) +/* AND */ +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A && _B && _C); + + CONTEXT_A(_T && _B && _C); + CONTEXT_A(_A && _T && _C); + CONTEXT_A(_A && _B && _T); + + CONTEXT_A(_F && _B && _C); + CONTEXT_A(_A && _F && _C); + CONTEXT_A(_A && _B && _F); + + CONTEXT_A(_T && _T && _C); + CONTEXT_A(_A && _T && _T); + CONTEXT_A(_T && _B && _T); + + printf("\n"); +} + +void f2(int a, int b, int c) +/* OR */ +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A || _B || _C); + + CONTEXT_A(_T || _B || _C); + CONTEXT_A(_A || _T || _C); + CONTEXT_A(_A || _B || _T); + + CONTEXT_A(_F || _B || _C); + CONTEXT_A(_A || _F || _C); + CONTEXT_A(_A || _B || _F); + + CONTEXT_A(_F || _F || _C); + CONTEXT_A(_A || _F || _F); + CONTEXT_A(_F || _B || _F); + + printf("\n"); +} + +void f3(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A(_A && _B || _C && _D); + CONTEXT_A(_T && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _T); + + CONTEXT_A(_T && _B || _C && _D); + CONTEXT_A(_A && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _D); + CONTEXT_A(_A && _B || _C && _T); + + CONTEXT_A(_F && _B || _C && _D); + CONTEXT_A(_A && _F || _C && _D); + CONTEXT_A(_A && _B || _F && _D); + CONTEXT_A(_A && _B || _C && _F); + + printf("\n"); +} + +void f4(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A && _B) || (_C && _D)); + CONTEXT_A((_T && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _T)); + + CONTEXT_A((_T && _B) || (_C && _D)); + CONTEXT_A((_A && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _D)); + CONTEXT_A((_A && _B) || (_C && _T)); + + CONTEXT_A((_F && _B) || (_C && _D)); + CONTEXT_A((_A && _F) || (_C && _D)); + CONTEXT_A((_A && _B) || (_F && _D)); + CONTEXT_A((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A || _B) && (_C || _D)); + CONTEXT_A((_F || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _F)); + + CONTEXT_A((_T || _B) && (_C || _D)); + CONTEXT_A((_A || _T) && (_C || _D)); + CONTEXT_A((_A || _B) && (_T || _D)); + CONTEXT_A((_A || _B) && (_C || _T)); + + CONTEXT_A((_F || _B) && (_C || _D)); + CONTEXT_A((_A || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _D)); + CONTEXT_A((_A || _B) && (_C || _F)); + + printf("\n"); +} + +void f0_B() +/* constant short-circuit */ +{ + printf("f0_B()\n"); + + CONTEXT_B(_T && _T && _T); + CONTEXT_B(_F && _F && _F); + + CONTEXT_B(_T || _T || _T); + CONTEXT_B(_F || _F || _F); + + CONTEXT_B(_T && _T || _T && _T); + CONTEXT_B(_F && _F || _F && _F); + CONTEXT_B(_T && _F || _T && _F); + CONTEXT_B(_F && _T || _F && _T); + + CONTEXT_B((_T && _T) || (_T && _T)); + CONTEXT_B((_F && _F) || (_F && _F)); + CONTEXT_B((_T && _F) || (_T && _F)); + CONTEXT_B((_F && _T) || (_F && _T)); + + CONTEXT_B((_T || _T) && (_T || _T)); + CONTEXT_B((_F || _F) && (_F || _F)); + CONTEXT_B((_T || _F) && (_T || _F)); + CONTEXT_B((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1_B(int a, int b, int c) +/* AND */ +{ + printf("f1_B(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A && _B && _C); + + CONTEXT_B(_T && _B && _C); + CONTEXT_B(_A && _T && _C); + CONTEXT_B(_A && _B && _T); + + CONTEXT_B(_F && _B && _C); + CONTEXT_B(_A && _F && _C); + CONTEXT_B(_A && _B && _F); + + CONTEXT_B(_T && _T && _C); + CONTEXT_B(_A && _T && _T); + CONTEXT_B(_T && _B && _T); + + printf("\n"); +} + +void f2_B(int a, int b, int c) +/* OR */ +{ + printf("f2_B(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A || _B || _C); + + CONTEXT_B(_T || _B || _C); + CONTEXT_B(_A || _T || _C); + CONTEXT_B(_A || _B || _T); + + CONTEXT_B(_F || _B || _C); + CONTEXT_B(_A || _F || _C); + CONTEXT_B(_A || _B || _F); + + CONTEXT_B(_F || _F || _C); + CONTEXT_B(_A || _F || _F); + CONTEXT_B(_F || _B || _F); + + printf("\n"); +} + +void f3_B(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B(_A && _B || _C && _D); + CONTEXT_B(_T && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _T); + + CONTEXT_B(_T && _B || _C && _D); + CONTEXT_B(_A && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _D); + CONTEXT_B(_A && _B || _C && _T); + + CONTEXT_B(_F && _B || _C && _D); + CONTEXT_B(_A && _F || _C && _D); + CONTEXT_B(_A && _B || _F && _D); + CONTEXT_B(_A && _B || _C && _F); + + printf("\n"); +} + +void f4_B(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A && _B) || (_C && _D)); + CONTEXT_B((_T && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _T)); + + CONTEXT_B((_T && _B) || (_C && _D)); + CONTEXT_B((_A && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _D)); + CONTEXT_B((_A && _B) || (_C && _T)); + + CONTEXT_B((_F && _B) || (_C && _D)); + CONTEXT_B((_A && _F) || (_C && _D)); + CONTEXT_B((_A && _B) || (_F && _D)); + CONTEXT_B((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5_B(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A || _B) && (_C || _D)); + CONTEXT_B((_F || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _F)); + + CONTEXT_B((_T || _B) && (_C || _D)); + CONTEXT_B((_A || _T) && (_C || _D)); + CONTEXT_B((_A || _B) && (_T || _D)); + CONTEXT_B((_A || _B) && (_C || _T)); + + CONTEXT_B((_F || _B) && (_C || _D)); + CONTEXT_B((_A || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _D)); + CONTEXT_B((_A || _B) && (_C || _F)); + + printf("\n"); +} + +int main() +{ + f0(); + + f1(0, 0, 0); + f2(0, 0, 0); + f3(0, 0, 0, 0); + f4(0, 0, 0, 0); + f5(0, 0, 0, 0); + + f1(1, 1, 1); + f2(1, 1, 1); + f3(1, 1, 1, 1); + f4(1, 1, 1, 1); + f5(1, 1, 1, 1); + + f3(1, 0, 1, 0); + f4(1, 0, 1, 0); + f5(1, 0, 1, 0); + f3(0, 1, 0, 1); + f4(0, 1, 0, 1); + f5(0, 1, 0, 1); + + f0_B(); + + f1_B(0, 0, 0); + f2_B(0, 0, 0); + f3_B(0, 0, 0, 0); + f4_B(0, 0, 0, 0); + f5_B(0, 0, 0, 0); + + f1_B(1, 1, 1); + f2_B(1, 1, 1); + f3_B(1, 1, 1, 1); + f4_B(1, 1, 1, 1); + f5_B(1, 1, 1, 1); + + f3_B(1, 0, 1, 0); + f4_B(1, 0, 1, 0); + f5_B(1, 0, 1, 0); + f3_B(0, 1, 0, 1); + f4_B(0, 1, 0, 1); + f5_B(0, 1, 0, 1); + + return 0; +} diff --git a/test/ref/wf1.c b/test/ref/wf1.c index e1ed7a417..574e7205f 100644 --- a/test/ref/wf1.c +++ b/test/ref/wf1.c @@ -25,35 +25,21 @@ int next; /* index of next free entry in words */ /*struct node *lookup();*/ -#if defined(NO_NEW_PROTOTYPES_FOR_OLD_FUNC_DECL) && !defined(NO_OLD_FUNC_DECL) - -#else - int err(char *s); int getword(char *buf); void tprint(struct node *tree); struct node *lookup(char *word, struct node **p); -#endif - int isletter(char c); /* err - print error message s and die */ -#ifndef NO_OLD_FUNC_DECL err(s) char *s; { -#else -int err(char *s) { -#endif printf("? %s\n", s); exit(EXIT_FAILURE); } /* getword - get next input word into buf, return 0 on EOF */ -#ifndef NO_OLD_FUNC_DECL int getword(buf) char *buf; -#else -int getword(char *buf) -#endif { char *s; int c; @@ -77,12 +63,8 @@ int isletter(char c) } /* lookup - lookup word in tree; install if necessary */ -#ifndef NO_OLD_FUNC_DECL struct node *lookup(word, p) char *word; struct node **p; -#else -struct node *lookup(char *word, struct node **p) -#endif { int cond; /* char *malloc(); */ @@ -108,11 +90,7 @@ struct node *lookup(char *word, struct node **p) } /* tprint - print tree */ -#ifndef NO_OLD_FUNC_DECL void tprint(tree) struct node *tree; { -#else -void tprint(struct node *tree) { -#endif if (tree) { tprint(tree->left); printf("%d:%s\n", tree->count, tree->word); diff --git a/test/ref/yacc.in b/test/ref/yacc.in old mode 100755 new mode 100644 diff --git a/test/todo/Makefile b/test/todo/Makefile index ab5eb598c..17561f8f4 100644 --- a/test/todo/Makefile +++ b/test/todo/Makefile @@ -10,14 +10,12 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NOT = ! NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -28,7 +26,9 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +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 @@ -50,7 +50,9 @@ define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) $(if $(QUIET),echo val/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(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) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -60,4 +62,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/todo/bug927.c b/test/todo/bug927.c new file mode 100644 index 000000000..e0f916e66 --- /dev/null +++ b/test/todo/bug927.c @@ -0,0 +1,47 @@ + +/* bug #927: format specifiers related to leading zeros do not work as expected */ + +/* expected output: +0023 +0023 +-0023 +-023 +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char b1[10]; +char b2[10]; +char b3[10]; +char b4[10]; + +int main(void) { + printf("%.4d\n", 23); + printf("%04d\n", 23); + printf("%.4d\n", -23); + printf("%04d\n\n", -23); + + sprintf(b1, "%.4d", 23); + sprintf(b2, "%04d", 23); + sprintf(b3, "%.4d", -23); + sprintf(b4, "%04d", -23); + + printf("%s\n", b1); + printf("%s\n", b2); + printf("%s\n", b3); + printf("%s\n\n", b4); + + printf("%d\n", strcmp(b1, "0023")); + printf("%d\n", strcmp(b2, "0023")); + printf("%d\n", strcmp(b3, "-0023")); + printf("%d\n", strcmp(b4, "-023")); + + if(strcmp(b1, "0023") != 0) return EXIT_FAILURE; + if(strcmp(b2, "0023") != 0) return EXIT_FAILURE; + if(strcmp(b3, "-0023") != 0) return EXIT_FAILURE; + if(strcmp(b4, "-023") != 0) return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/testcode/lib/sprintf-test.c b/test/todo/sprintf-test.c similarity index 99% rename from testcode/lib/sprintf-test.c rename to test/todo/sprintf-test.c index 55354be20..bd5de44b4 100644 --- a/testcode/lib/sprintf-test.c +++ b/test/todo/sprintf-test.c @@ -1,13 +1,13 @@ #include <stdio.h> #include <string.h> #include <stdarg.h> -#if defined(__CC65__) +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) #include <conio.h> #endif /* Flag to #ifdef the tests out that crash the old implementation */ -/*#define NOCRASH 1 */ +#define NOCRASH 1 @@ -532,9 +532,9 @@ int main (void) /* Alternative form with zero value */ #ifndef NOCRASH OneTest (__LINE__, "0", 1, "%#o", 0U); +#endif OneTest (__LINE__, "0", 1, "%#x", 0U); OneTest (__LINE__, "0", 1, "%#X", 0U); -#endif /* Alternative form with zero value and precision 1 */ OneTest (__LINE__, "0", 1, "%#.1o", 0U); @@ -570,8 +570,8 @@ int main (void) } /* Wait for a key so we can read the result */ -#if defined(__CC65__) +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc (); #endif - return 0; + return Failures; } diff --git a/test/val/Makefile b/test/val/Makefile index df1d314e4..1a9fa9a45 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -9,13 +9,11 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -24,9 +22,11 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 200000000 +SIM65FLAGS = -x 5000000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +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 @@ -48,7 +48,9 @@ define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) $(if $(QUIET),echo val/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(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) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -58,4 +60,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/val/add2.c b/test/val/add2.c index 90f0f4175..6fc23e5da 100644 --- a/test/val/add2.c +++ b/test/val/add2.c @@ -6,33 +6,18 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; unsigned char dummy=0; -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -unsigned short aint2 = 0; -unsigned short aint3 = 0; - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -unsigned int aint2 = 0; -unsigned int aint3 = 0; - -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -unsigned int aint2 = 0; -unsigned int aint3 = 0; - -#endif +uint16_t aint0 = 0; +uint16_t aint1 = 0; +uint16_t aint2 = 0; +uint16_t aint3 = 0; unsigned char achar0 = 0; unsigned char achar1 = 0; diff --git a/test/val/add3a.c b/test/val/add3a.c old mode 100755 new mode 100644 diff --git a/testcode/lib/atoi-test.c b/test/val/atoi-test.c similarity index 100% rename from testcode/lib/atoi-test.c rename to test/val/atoi-test.c diff --git a/test/val/binlit.c b/test/val/binlit.c index 47e7a196e..f89ca60fc 100644 --- a/test/val/binlit.c +++ b/test/val/binlit.c @@ -3,531 +3,531 @@ static unsigned big[256]; int main() { - unsigned i; + unsigned i; - small[0] = 0b0; - small[1] = 0b1; - small[2] = 0b10; - small[3] = 0b11; - small[4] = 0b100; - small[5] = 0b101; - small[6] = 0b110; - small[7] = 0b000111; - small[8] = 0b1000; - small[9] = 0b1001; - small[10] = 0b0001010; - small[11] = 0b0001011; - small[12] = 0b1100; - small[13] = 0b1101; - small[14] = 0b1110; - small[15] = 0b1111; - small[16] = 0b10000; - small[17] = 0b10001; - small[18] = 0b00010010; - small[19] = 0b00010011; - small[20] = 0b00010100; - small[21] = 0b00010101; - small[22] = 0b10110; - small[23] = 0b10111; - small[24] = 0b11000; - small[25] = 0b11001; - small[26] = 0b11010; - small[27] = 0b11011; - small[28] = 0b11100; - small[29] = 0b11101; - small[30] = 0b11110; - small[31] = 0b11111; - small[32] = 0b00000000100000; - small[33] = 0b00000000100001; - small[34] = 0b100010; - small[35] = 0b100011; - small[36] = 0b100100; - small[37] = 0b100101; - small[38] = 0b100110; - small[39] = 0b100111; - small[40] = 0b101000; - small[41] = 0b101001; - small[42] = 0b101010; - small[43] = 0b101011; - small[44] = 0b101100; - small[45] = 0b101101; - small[46] = 0b101110; - small[47] = 0b101111; - small[48] = 0b110000; - small[49] = 0b110001; - small[50] = 0b110010; - small[51] = 0b110011; - small[52] = 0b110100; - small[53] = 0b110101; - small[54] = 0b110110; - small[55] = 0b110111; - small[56] = 0b111000; - small[57] = 0b111001; - small[58] = 0b111010; - small[59] = 0b111011; - small[60] = 0b111100; - small[61] = 0b111101; - small[62] = 0b111110; - small[63] = 0b111111; - small[64] = 0b1000000; - small[65] = 0b1000001; - small[66] = 0b1000010; - small[67] = 0b1000011; - small[68] = 0b1000100; - small[69] = 0b1000101; - small[70] = 0b1000110; - small[71] = 0b1000111; - small[72] = 0b1001000; - small[73] = 0b1001001; - small[74] = 0b1001010; - small[75] = 0b1001011; - small[76] = 0b1001100; - small[77] = 0b1001101; - small[78] = 0b1001110; - small[79] = 0b1001111; - small[80] = 0b1010000; - small[81] = 0b1010001; - small[82] = 0b1010010; - small[83] = 0b1010011; - small[84] = 0b1010100; - small[85] = 0b1010101; - small[86] = 0b1010110; - small[87] = 0b1010111; - small[88] = 0b1011000; - small[89] = 0b1011001; - small[90] = 0b1011010; - small[91] = 0b1011011; - small[92] = 0b1011100; - small[93] = 0b1011101; - small[94] = 0b1011110; - small[95] = 0b1011111; - small[96] = 0b1100000; - small[97] = 0b1100001; - small[98] = 0b1100010; - small[99] = 0b1100011; - small[100] = 0b1100100; - small[101] = 0b1100101; - small[102] = 0b1100110; - small[103] = 0b1100111; - small[104] = 0b1101000; - small[105] = 0b1101001; - small[106] = 0b1101010; - small[107] = 0b1101011; - small[108] = 0b1101100; - small[109] = 0b1101101; - small[110] = 0b1101110; - small[111] = 0b1101111; - small[112] = 0b1110000; - small[113] = 0b1110001; - small[114] = 0b1110010; - small[115] = 0b1110011; - small[116] = 0b1110100; - small[117] = 0b1110101; - small[118] = 0b1110110; - small[119] = 0b1110111; - small[120] = 0b1111000; - small[121] = 0b1111001; - small[122] = 0b1111010; - small[123] = 0b1111011; - small[124] = 0b1111100; - small[125] = 0b1111101; - small[126] = 0b1111110; - small[127] = 0b1111111; - small[128] = 0b10000000; - small[129] = 0b10000001; - small[130] = 0b10000010; - small[131] = 0b10000011; - small[132] = 0b10000100; - small[133] = 0b10000101; - small[134] = 0b10000110; - small[135] = 0b10000111; - small[136] = 0b10001000; - small[137] = 0b10001001; - small[138] = 0b10001010; - small[139] = 0b10001011; - small[140] = 0b10001100; - small[141] = 0b10001101; - small[142] = 0b10001110; - small[143] = 0b10001111; - small[144] = 0b10010000; - small[145] = 0b10010001; - small[146] = 0b10010010; - small[147] = 0b10010011; - small[148] = 0b10010100; - small[149] = 0b10010101; - small[150] = 0b10010110; - small[151] = 0b10010111; - small[152] = 0b10011000; - small[153] = 0b10011001; - small[154] = 0b10011010; - small[155] = 0b10011011; - small[156] = 0b10011100; - small[157] = 0b10011101; - small[158] = 0b10011110; - small[159] = 0b10011111; - small[160] = 0b10100000; - small[161] = 0b10100001; - small[162] = 0b10100010; - small[163] = 0b10100011; - small[164] = 0b10100100; - small[165] = 0b10100101; - small[166] = 0b10100110; - small[167] = 0b10100111; - small[168] = 0b10101000; - small[169] = 0b10101001; - small[170] = 0b10101010; - small[171] = 0b10101011; - small[172] = 0b10101100; - small[173] = 0b10101101; - small[174] = 0b10101110; - small[175] = 0b10101111; - small[176] = 0b10110000; - small[177] = 0b10110001; - small[178] = 0b10110010; - small[179] = 0b10110011; - small[180] = 0b10110100; - small[181] = 0b10110101; - small[182] = 0b10110110; - small[183] = 0b10110111; - small[184] = 0b10111000; - small[185] = 0b10111001; - small[186] = 0b10111010; - small[187] = 0b10111011; - small[188] = 0b10111100; - small[189] = 0b10111101; - small[190] = 0b10111110; - small[191] = 0b10111111; - small[192] = 0b11000000; - small[193] = 0b11000001; - small[194] = 0b11000010; - small[195] = 0b11000011; - small[196] = 0b11000100; - small[197] = 0b11000101; - small[198] = 0b11000110; - small[199] = 0b11000111; - small[200] = 0b11001000; - small[201] = 0b11001001; - small[202] = 0b11001010; - small[203] = 0b11001011; - small[204] = 0b11001100; - small[205] = 0b11001101; - small[206] = 0b11001110; - small[207] = 0b11001111; - small[208] = 0b11010000; - small[209] = 0b11010001; - small[210] = 0b11010010; - small[211] = 0b11010011; - small[212] = 0b11010100; - small[213] = 0b11010101; - small[214] = 0b11010110; - small[215] = 0b11010111; - small[216] = 0b11011000; - small[217] = 0b11011001; - small[218] = 0b11011010; - small[219] = 0b11011011; - small[220] = 0b11011100; - small[221] = 0b11011101; - small[222] = 0b11011110; - small[223] = 0b11011111; - small[224] = 0b11100000; - small[225] = 0b11100001; - small[226] = 0b11100010; - small[227] = 0b11100011; - small[228] = 0b11100100; - small[229] = 0b11100101; - small[230] = 0b11100110; - small[231] = 0b11100111; - small[232] = 0b11101000; - small[233] = 0b11101001; - small[234] = 0b11101010; - small[235] = 0b11101011; - small[236] = 0b11101100; - small[237] = 0b11101101; - small[238] = 0b11101110; - small[239] = 0b11101111; - small[240] = 0b11110000; - small[241] = 0b11110001; - small[242] = 0b11110010; - small[243] = 0b11110011; - small[244] = 0b11110100; - small[245] = 0b11110101; - small[246] = 0b11110110; - small[247] = 0b11110111; - small[248] = 0b11111000; - small[249] = 0b11111001; - small[250] = 0b11111010; - small[251] = 0b11111011; - small[252] = 0b11111100; - small[253] = 0b11111101; - small[254] = 0b11111110; - small[255] = 0b11111111; + small[0] = 0b0; + small[1] = 0b1; + small[2] = 0b10; + small[3] = 0b11; + small[4] = 0b100; + small[5] = 0b101; + small[6] = 0b110; + small[7] = 0b000111; + small[8] = 0b1000; + small[9] = 0b1001; + small[10] = 0b0001010; + small[11] = 0b0001011; + small[12] = 0b1100; + small[13] = 0b1101; + small[14] = 0b1110; + small[15] = 0b1111; + small[16] = 0b10000; + small[17] = 0b10001; + small[18] = 0b00010010; + small[19] = 0b00010011; + small[20] = 0b00010100; + small[21] = 0b00010101; + small[22] = 0b10110; + small[23] = 0b10111; + small[24] = 0b11000; + small[25] = 0b11001; + small[26] = 0b11010; + small[27] = 0b11011; + small[28] = 0b11100; + small[29] = 0b11101; + small[30] = 0b11110; + small[31] = 0b11111; + small[32] = 0b00000000100000; + small[33] = 0b00000000100001; + small[34] = 0b100010; + small[35] = 0b100011; + small[36] = 0b100100; + small[37] = 0b100101; + small[38] = 0b100110; + small[39] = 0b100111; + small[40] = 0b101000; + small[41] = 0b101001; + small[42] = 0b101010; + small[43] = 0b101011; + small[44] = 0b101100; + small[45] = 0b101101; + small[46] = 0b101110; + small[47] = 0b101111; + small[48] = 0b110000; + small[49] = 0b110001; + small[50] = 0b110010; + small[51] = 0b110011; + small[52] = 0b110100; + small[53] = 0b110101; + small[54] = 0b110110; + small[55] = 0b110111; + small[56] = 0b111000; + small[57] = 0b111001; + small[58] = 0b111010; + small[59] = 0b111011; + small[60] = 0b111100; + small[61] = 0b111101; + small[62] = 0b111110; + small[63] = 0b111111; + small[64] = 0b1000000; + small[65] = 0b1000001; + small[66] = 0b1000010; + small[67] = 0b1000011; + small[68] = 0b1000100; + small[69] = 0b1000101; + small[70] = 0b1000110; + small[71] = 0b1000111; + small[72] = 0b1001000; + small[73] = 0b1001001; + small[74] = 0b1001010; + small[75] = 0b1001011; + small[76] = 0b1001100; + small[77] = 0b1001101; + small[78] = 0b1001110; + small[79] = 0b1001111; + small[80] = 0b1010000; + small[81] = 0b1010001; + small[82] = 0b1010010; + small[83] = 0b1010011; + small[84] = 0b1010100; + small[85] = 0b1010101; + small[86] = 0b1010110; + small[87] = 0b1010111; + small[88] = 0b1011000; + small[89] = 0b1011001; + small[90] = 0b1011010; + small[91] = 0b1011011; + small[92] = 0b1011100; + small[93] = 0b1011101; + small[94] = 0b1011110; + small[95] = 0b1011111; + small[96] = 0b1100000; + small[97] = 0b1100001; + small[98] = 0b1100010; + small[99] = 0b1100011; + small[100] = 0b1100100; + small[101] = 0b1100101; + small[102] = 0b1100110; + small[103] = 0b1100111; + small[104] = 0b1101000; + small[105] = 0b1101001; + small[106] = 0b1101010; + small[107] = 0b1101011; + small[108] = 0b1101100; + small[109] = 0b1101101; + small[110] = 0b1101110; + small[111] = 0b1101111; + small[112] = 0b1110000; + small[113] = 0b1110001; + small[114] = 0b1110010; + small[115] = 0b1110011; + small[116] = 0b1110100; + small[117] = 0b1110101; + small[118] = 0b1110110; + small[119] = 0b1110111; + small[120] = 0b1111000; + small[121] = 0b1111001; + small[122] = 0b1111010; + small[123] = 0b1111011; + small[124] = 0b1111100; + small[125] = 0b1111101; + small[126] = 0b1111110; + small[127] = 0b1111111; + small[128] = 0b10000000; + small[129] = 0b10000001; + small[130] = 0b10000010; + small[131] = 0b10000011; + small[132] = 0b10000100; + small[133] = 0b10000101; + small[134] = 0b10000110; + small[135] = 0b10000111; + small[136] = 0b10001000; + small[137] = 0b10001001; + small[138] = 0b10001010; + small[139] = 0b10001011; + small[140] = 0b10001100; + small[141] = 0b10001101; + small[142] = 0b10001110; + small[143] = 0b10001111; + small[144] = 0b10010000; + small[145] = 0b10010001; + small[146] = 0b10010010; + small[147] = 0b10010011; + small[148] = 0b10010100; + small[149] = 0b10010101; + small[150] = 0b10010110; + small[151] = 0b10010111; + small[152] = 0b10011000; + small[153] = 0b10011001; + small[154] = 0b10011010; + small[155] = 0b10011011; + small[156] = 0b10011100; + small[157] = 0b10011101; + small[158] = 0b10011110; + small[159] = 0b10011111; + small[160] = 0b10100000; + small[161] = 0b10100001; + small[162] = 0b10100010; + small[163] = 0b10100011; + small[164] = 0b10100100; + small[165] = 0b10100101; + small[166] = 0b10100110; + small[167] = 0b10100111; + small[168] = 0b10101000; + small[169] = 0b10101001; + small[170] = 0b10101010; + small[171] = 0b10101011; + small[172] = 0b10101100; + small[173] = 0b10101101; + small[174] = 0b10101110; + small[175] = 0b10101111; + small[176] = 0b10110000; + small[177] = 0b10110001; + small[178] = 0b10110010; + small[179] = 0b10110011; + small[180] = 0b10110100; + small[181] = 0b10110101; + small[182] = 0b10110110; + small[183] = 0b10110111; + small[184] = 0b10111000; + small[185] = 0b10111001; + small[186] = 0b10111010; + small[187] = 0b10111011; + small[188] = 0b10111100; + small[189] = 0b10111101; + small[190] = 0b10111110; + small[191] = 0b10111111; + small[192] = 0b11000000; + small[193] = 0b11000001; + small[194] = 0b11000010; + small[195] = 0b11000011; + small[196] = 0b11000100; + small[197] = 0b11000101; + small[198] = 0b11000110; + small[199] = 0b11000111; + small[200] = 0b11001000; + small[201] = 0b11001001; + small[202] = 0b11001010; + small[203] = 0b11001011; + small[204] = 0b11001100; + small[205] = 0b11001101; + small[206] = 0b11001110; + small[207] = 0b11001111; + small[208] = 0b11010000; + small[209] = 0b11010001; + small[210] = 0b11010010; + small[211] = 0b11010011; + small[212] = 0b11010100; + small[213] = 0b11010101; + small[214] = 0b11010110; + small[215] = 0b11010111; + small[216] = 0b11011000; + small[217] = 0b11011001; + small[218] = 0b11011010; + small[219] = 0b11011011; + small[220] = 0b11011100; + small[221] = 0b11011101; + small[222] = 0b11011110; + small[223] = 0b11011111; + small[224] = 0b11100000; + small[225] = 0b11100001; + small[226] = 0b11100010; + small[227] = 0b11100011; + small[228] = 0b11100100; + small[229] = 0b11100101; + small[230] = 0b11100110; + small[231] = 0b11100111; + small[232] = 0b11101000; + small[233] = 0b11101001; + small[234] = 0b11101010; + small[235] = 0b11101011; + small[236] = 0b11101100; + small[237] = 0b11101101; + small[238] = 0b11101110; + small[239] = 0b11101111; + small[240] = 0b11110000; + small[241] = 0b11110001; + small[242] = 0b11110010; + small[243] = 0b11110011; + small[244] = 0b11110100; + small[245] = 0b11110101; + small[246] = 0b11110110; + small[247] = 0b11110111; + small[248] = 0b11111000; + small[249] = 0b11111001; + small[250] = 0b11111010; + small[251] = 0b11111011; + small[252] = 0b11111100; + small[253] = 0b11111101; + small[254] = 0b11111110; + small[255] = 0b11111111; - for (i = 0; i < 256; i++) { - if (small[i] != i) - return 1; - } + for (i = 0; i < 256; i++) { + if (small[i] != i) + return 1; + } - big[0] = 0b1111111100000000; - big[1] = 0b1111111100000001; - big[2] = 0b1111111100000010; - big[3] = 0b1111111100000011; - big[4] = 0b1111111100000100; - big[5] = 0b1111111100000101; - big[6] = 0b1111111100000110; - big[7] = 0b1111111100000111; - big[8] = 0b1111111100001000; - big[9] = 0b1111111100001001; - big[10] = 0b1111111100001010; - big[11] = 0b1111111100001011; - big[12] = 0b1111111100001100; - big[13] = 0b1111111100001101; - big[14] = 0b1111111100001110; - big[15] = 0b1111111100001111; - big[16] = 0b1111111100010000; - big[17] = 0b1111111100010001; - big[18] = 0b1111111100010010; - big[19] = 0b1111111100010011; - big[20] = 0b1111111100010100; - big[21] = 0b1111111100010101; - big[22] = 0b1111111100010110; - big[23] = 0b1111111100010111; - big[24] = 0b1111111100011000; - big[25] = 0b1111111100011001; - big[26] = 0b1111111100011010; - big[27] = 0b1111111100011011; - big[28] = 0b1111111100011100; - big[29] = 0b1111111100011101; - big[30] = 0b1111111100011110; - big[31] = 0b1111111100011111; - big[32] = 0b1111111100100000; - big[33] = 0b1111111100100001; - big[34] = 0b1111111100100010; - big[35] = 0b1111111100100011; - big[36] = 0b1111111100100100; - big[37] = 0b1111111100100101; - big[38] = 0b1111111100100110; - big[39] = 0b1111111100100111; - big[40] = 0b1111111100101000; - big[41] = 0b1111111100101001; - big[42] = 0b1111111100101010; - big[43] = 0b1111111100101011; - big[44] = 0b1111111100101100; - big[45] = 0b1111111100101101; - big[46] = 0b1111111100101110; - big[47] = 0b1111111100101111; - big[48] = 0b1111111100110000; - big[49] = 0b1111111100110001; - big[50] = 0b1111111100110010; - big[51] = 0b1111111100110011; - big[52] = 0b1111111100110100; - big[53] = 0b1111111100110101; - big[54] = 0b1111111100110110; - big[55] = 0b1111111100110111; - big[56] = 0b1111111100111000; - big[57] = 0b1111111100111001; - big[58] = 0b1111111100111010; - big[59] = 0b1111111100111011; - big[60] = 0b1111111100111100; - big[61] = 0b1111111100111101; - big[62] = 0b1111111100111110; - big[63] = 0b1111111100111111; - big[64] = 0b1111111101000000; - big[65] = 0b1111111101000001; - big[66] = 0b1111111101000010; - big[67] = 0b1111111101000011; - big[68] = 0b1111111101000100; - big[69] = 0b1111111101000101; - big[70] = 0b1111111101000110; - big[71] = 0b1111111101000111; - big[72] = 0b1111111101001000; - big[73] = 0b1111111101001001; - big[74] = 0b1111111101001010; - big[75] = 0b1111111101001011; - big[76] = 0b1111111101001100; - big[77] = 0b1111111101001101; - big[78] = 0b1111111101001110; - big[79] = 0b1111111101001111; - big[80] = 0b1111111101010000; - big[81] = 0b1111111101010001; - big[82] = 0b1111111101010010; - big[83] = 0b1111111101010011; - big[84] = 0b1111111101010100; - big[85] = 0b1111111101010101; - big[86] = 0b1111111101010110; - big[87] = 0b1111111101010111; - big[88] = 0b1111111101011000; - big[89] = 0b1111111101011001; - big[90] = 0b1111111101011010; - big[91] = 0b1111111101011011; - big[92] = 0b1111111101011100; - big[93] = 0b1111111101011101; - big[94] = 0b1111111101011110; - big[95] = 0b1111111101011111; - big[96] = 0b1111111101100000; - big[97] = 0b1111111101100001; - big[98] = 0b1111111101100010; - big[99] = 0b1111111101100011; - big[100] = 0b1111111101100100; - big[101] = 0b1111111101100101; - big[102] = 0b1111111101100110; - big[103] = 0b1111111101100111; - big[104] = 0b1111111101101000; - big[105] = 0b1111111101101001; - big[106] = 0b1111111101101010; - big[107] = 0b1111111101101011; - big[108] = 0b1111111101101100; - big[109] = 0b1111111101101101; - big[110] = 0b1111111101101110; - big[111] = 0b1111111101101111; - big[112] = 0b1111111101110000; - big[113] = 0b1111111101110001; - big[114] = 0b1111111101110010; - big[115] = 0b1111111101110011; - big[116] = 0b1111111101110100; - big[117] = 0b1111111101110101; - big[118] = 0b1111111101110110; - big[119] = 0b1111111101110111; - big[120] = 0b1111111101111000; - big[121] = 0b1111111101111001; - big[122] = 0b1111111101111010; - big[123] = 0b1111111101111011; - big[124] = 0b1111111101111100; - big[125] = 0b1111111101111101; - big[126] = 0b1111111101111110; - big[127] = 0b1111111101111111; - big[128] = 0b1111111110000000; - big[129] = 0b1111111110000001; - big[130] = 0b1111111110000010; - big[131] = 0b1111111110000011; - big[132] = 0b1111111110000100; - big[133] = 0b1111111110000101; - big[134] = 0b1111111110000110; - big[135] = 0b1111111110000111; - big[136] = 0b1111111110001000; - big[137] = 0b1111111110001001; - big[138] = 0b1111111110001010; - big[139] = 0b1111111110001011; - big[140] = 0b1111111110001100; - big[141] = 0b1111111110001101; - big[142] = 0b1111111110001110; - big[143] = 0b1111111110001111; - big[144] = 0b1111111110010000; - big[145] = 0b1111111110010001; - big[146] = 0b1111111110010010; - big[147] = 0b1111111110010011; - big[148] = 0b1111111110010100; - big[149] = 0b1111111110010101; - big[150] = 0b1111111110010110; - big[151] = 0b1111111110010111; - big[152] = 0b1111111110011000; - big[153] = 0b1111111110011001; - big[154] = 0b1111111110011010; - big[155] = 0b1111111110011011; - big[156] = 0b1111111110011100; - big[157] = 0b1111111110011101; - big[158] = 0b1111111110011110; - big[159] = 0b1111111110011111; - big[160] = 0b1111111110100000; - big[161] = 0b1111111110100001; - big[162] = 0b1111111110100010; - big[163] = 0b1111111110100011; - big[164] = 0b1111111110100100; - big[165] = 0b1111111110100101; - big[166] = 0b1111111110100110; - big[167] = 0b1111111110100111; - big[168] = 0b1111111110101000; - big[169] = 0b1111111110101001; - big[170] = 0b1111111110101010; - big[171] = 0b1111111110101011; - big[172] = 0b1111111110101100; - big[173] = 0b1111111110101101; - big[174] = 0b1111111110101110; - big[175] = 0b1111111110101111; - big[176] = 0b1111111110110000; - big[177] = 0b1111111110110001; - big[178] = 0b1111111110110010; - big[179] = 0b1111111110110011; - big[180] = 0b1111111110110100; - big[181] = 0b1111111110110101; - big[182] = 0b1111111110110110; - big[183] = 0b1111111110110111; - big[184] = 0b1111111110111000; - big[185] = 0b1111111110111001; - big[186] = 0b1111111110111010; - big[187] = 0b1111111110111011; - big[188] = 0b1111111110111100; - big[189] = 0b1111111110111101; - big[190] = 0b1111111110111110; - big[191] = 0b1111111110111111; - big[192] = 0b1111111111000000; - big[193] = 0b1111111111000001; - big[194] = 0b1111111111000010; - big[195] = 0b1111111111000011; - big[196] = 0b1111111111000100; - big[197] = 0b1111111111000101; - big[198] = 0b1111111111000110; - big[199] = 0b1111111111000111; - big[200] = 0b1111111111001000; - big[201] = 0b1111111111001001; - big[202] = 0b1111111111001010; - big[203] = 0b1111111111001011; - big[204] = 0b1111111111001100; - big[205] = 0b1111111111001101; - big[206] = 0b1111111111001110; - big[207] = 0b1111111111001111; - big[208] = 0b1111111111010000; - big[209] = 0b1111111111010001; - big[210] = 0b1111111111010010; - big[211] = 0b1111111111010011; - big[212] = 0b1111111111010100; - big[213] = 0b1111111111010101; - big[214] = 0b1111111111010110; - big[215] = 0b1111111111010111; - big[216] = 0b1111111111011000; - big[217] = 0b1111111111011001; - big[218] = 0b1111111111011010; - big[219] = 0b1111111111011011; - big[220] = 0b1111111111011100; - big[221] = 0b1111111111011101; - big[222] = 0b1111111111011110; - big[223] = 0b1111111111011111; - big[224] = 0b1111111111100000; - big[225] = 0b1111111111100001; - big[226] = 0b1111111111100010; - big[227] = 0b1111111111100011; - big[228] = 0b1111111111100100; - big[229] = 0b1111111111100101; - big[230] = 0b1111111111100110; - big[231] = 0b1111111111100111; - big[232] = 0b1111111111101000; - big[233] = 0b1111111111101001; - big[234] = 0b1111111111101010; - big[235] = 0b1111111111101011; - big[236] = 0b1111111111101100; - big[237] = 0b1111111111101101; - big[238] = 0b1111111111101110; - big[239] = 0b1111111111101111; - big[240] = 0b1111111111110000; - big[241] = 0b1111111111110001; - big[242] = 0b1111111111110010; - big[243] = 0b1111111111110011; - big[244] = 0b1111111111110100; - big[245] = 0b1111111111110101; - big[246] = 0b1111111111110110; - big[247] = 0b1111111111110111; - big[248] = 0b1111111111111000; - big[249] = 0b1111111111111001; - big[250] = 0b1111111111111010; - big[251] = 0b1111111111111011; - big[252] = 0b1111111111111100; - big[253] = 0b1111111111111101; - big[254] = 0b1111111111111110; - big[255] = 0b1111111111111111; + big[0] = 0b1111111100000000; + big[1] = 0b1111111100000001; + big[2] = 0b1111111100000010; + big[3] = 0b1111111100000011; + big[4] = 0b1111111100000100; + big[5] = 0b1111111100000101; + big[6] = 0b1111111100000110; + big[7] = 0b1111111100000111; + big[8] = 0b1111111100001000; + big[9] = 0b1111111100001001; + big[10] = 0b1111111100001010; + big[11] = 0b1111111100001011; + big[12] = 0b1111111100001100; + big[13] = 0b1111111100001101; + big[14] = 0b1111111100001110; + big[15] = 0b1111111100001111; + big[16] = 0b1111111100010000; + big[17] = 0b1111111100010001; + big[18] = 0b1111111100010010; + big[19] = 0b1111111100010011; + big[20] = 0b1111111100010100; + big[21] = 0b1111111100010101; + big[22] = 0b1111111100010110; + big[23] = 0b1111111100010111; + big[24] = 0b1111111100011000; + big[25] = 0b1111111100011001; + big[26] = 0b1111111100011010; + big[27] = 0b1111111100011011; + big[28] = 0b1111111100011100; + big[29] = 0b1111111100011101; + big[30] = 0b1111111100011110; + big[31] = 0b1111111100011111; + big[32] = 0b1111111100100000; + big[33] = 0b1111111100100001; + big[34] = 0b1111111100100010; + big[35] = 0b1111111100100011; + big[36] = 0b1111111100100100; + big[37] = 0b1111111100100101; + big[38] = 0b1111111100100110; + big[39] = 0b1111111100100111; + big[40] = 0b1111111100101000; + big[41] = 0b1111111100101001; + big[42] = 0b1111111100101010; + big[43] = 0b1111111100101011; + big[44] = 0b1111111100101100; + big[45] = 0b1111111100101101; + big[46] = 0b1111111100101110; + big[47] = 0b1111111100101111; + big[48] = 0b1111111100110000; + big[49] = 0b1111111100110001; + big[50] = 0b1111111100110010; + big[51] = 0b1111111100110011; + big[52] = 0b1111111100110100; + big[53] = 0b1111111100110101; + big[54] = 0b1111111100110110; + big[55] = 0b1111111100110111; + big[56] = 0b1111111100111000; + big[57] = 0b1111111100111001; + big[58] = 0b1111111100111010; + big[59] = 0b1111111100111011; + big[60] = 0b1111111100111100; + big[61] = 0b1111111100111101; + big[62] = 0b1111111100111110; + big[63] = 0b1111111100111111; + big[64] = 0b1111111101000000; + big[65] = 0b1111111101000001; + big[66] = 0b1111111101000010; + big[67] = 0b1111111101000011; + big[68] = 0b1111111101000100; + big[69] = 0b1111111101000101; + big[70] = 0b1111111101000110; + big[71] = 0b1111111101000111; + big[72] = 0b1111111101001000; + big[73] = 0b1111111101001001; + big[74] = 0b1111111101001010; + big[75] = 0b1111111101001011; + big[76] = 0b1111111101001100; + big[77] = 0b1111111101001101; + big[78] = 0b1111111101001110; + big[79] = 0b1111111101001111; + big[80] = 0b1111111101010000; + big[81] = 0b1111111101010001; + big[82] = 0b1111111101010010; + big[83] = 0b1111111101010011; + big[84] = 0b1111111101010100; + big[85] = 0b1111111101010101; + big[86] = 0b1111111101010110; + big[87] = 0b1111111101010111; + big[88] = 0b1111111101011000; + big[89] = 0b1111111101011001; + big[90] = 0b1111111101011010; + big[91] = 0b1111111101011011; + big[92] = 0b1111111101011100; + big[93] = 0b1111111101011101; + big[94] = 0b1111111101011110; + big[95] = 0b1111111101011111; + big[96] = 0b1111111101100000; + big[97] = 0b1111111101100001; + big[98] = 0b1111111101100010; + big[99] = 0b1111111101100011; + big[100] = 0b1111111101100100; + big[101] = 0b1111111101100101; + big[102] = 0b1111111101100110; + big[103] = 0b1111111101100111; + big[104] = 0b1111111101101000; + big[105] = 0b1111111101101001; + big[106] = 0b1111111101101010; + big[107] = 0b1111111101101011; + big[108] = 0b1111111101101100; + big[109] = 0b1111111101101101; + big[110] = 0b1111111101101110; + big[111] = 0b1111111101101111; + big[112] = 0b1111111101110000; + big[113] = 0b1111111101110001; + big[114] = 0b1111111101110010; + big[115] = 0b1111111101110011; + big[116] = 0b1111111101110100; + big[117] = 0b1111111101110101; + big[118] = 0b1111111101110110; + big[119] = 0b1111111101110111; + big[120] = 0b1111111101111000; + big[121] = 0b1111111101111001; + big[122] = 0b1111111101111010; + big[123] = 0b1111111101111011; + big[124] = 0b1111111101111100; + big[125] = 0b1111111101111101; + big[126] = 0b1111111101111110; + big[127] = 0b1111111101111111; + big[128] = 0b1111111110000000; + big[129] = 0b1111111110000001; + big[130] = 0b1111111110000010; + big[131] = 0b1111111110000011; + big[132] = 0b1111111110000100; + big[133] = 0b1111111110000101; + big[134] = 0b1111111110000110; + big[135] = 0b1111111110000111; + big[136] = 0b1111111110001000; + big[137] = 0b1111111110001001; + big[138] = 0b1111111110001010; + big[139] = 0b1111111110001011; + big[140] = 0b1111111110001100; + big[141] = 0b1111111110001101; + big[142] = 0b1111111110001110; + big[143] = 0b1111111110001111; + big[144] = 0b1111111110010000; + big[145] = 0b1111111110010001; + big[146] = 0b1111111110010010; + big[147] = 0b1111111110010011; + big[148] = 0b1111111110010100; + big[149] = 0b1111111110010101; + big[150] = 0b1111111110010110; + big[151] = 0b1111111110010111; + big[152] = 0b1111111110011000; + big[153] = 0b1111111110011001; + big[154] = 0b1111111110011010; + big[155] = 0b1111111110011011; + big[156] = 0b1111111110011100; + big[157] = 0b1111111110011101; + big[158] = 0b1111111110011110; + big[159] = 0b1111111110011111; + big[160] = 0b1111111110100000; + big[161] = 0b1111111110100001; + big[162] = 0b1111111110100010; + big[163] = 0b1111111110100011; + big[164] = 0b1111111110100100; + big[165] = 0b1111111110100101; + big[166] = 0b1111111110100110; + big[167] = 0b1111111110100111; + big[168] = 0b1111111110101000; + big[169] = 0b1111111110101001; + big[170] = 0b1111111110101010; + big[171] = 0b1111111110101011; + big[172] = 0b1111111110101100; + big[173] = 0b1111111110101101; + big[174] = 0b1111111110101110; + big[175] = 0b1111111110101111; + big[176] = 0b1111111110110000; + big[177] = 0b1111111110110001; + big[178] = 0b1111111110110010; + big[179] = 0b1111111110110011; + big[180] = 0b1111111110110100; + big[181] = 0b1111111110110101; + big[182] = 0b1111111110110110; + big[183] = 0b1111111110110111; + big[184] = 0b1111111110111000; + big[185] = 0b1111111110111001; + big[186] = 0b1111111110111010; + big[187] = 0b1111111110111011; + big[188] = 0b1111111110111100; + big[189] = 0b1111111110111101; + big[190] = 0b1111111110111110; + big[191] = 0b1111111110111111; + big[192] = 0b1111111111000000; + big[193] = 0b1111111111000001; + big[194] = 0b1111111111000010; + big[195] = 0b1111111111000011; + big[196] = 0b1111111111000100; + big[197] = 0b1111111111000101; + big[198] = 0b1111111111000110; + big[199] = 0b1111111111000111; + big[200] = 0b1111111111001000; + big[201] = 0b1111111111001001; + big[202] = 0b1111111111001010; + big[203] = 0b1111111111001011; + big[204] = 0b1111111111001100; + big[205] = 0b1111111111001101; + big[206] = 0b1111111111001110; + big[207] = 0b1111111111001111; + big[208] = 0b1111111111010000; + big[209] = 0b1111111111010001; + big[210] = 0b1111111111010010; + big[211] = 0b1111111111010011; + big[212] = 0b1111111111010100; + big[213] = 0b1111111111010101; + big[214] = 0b1111111111010110; + big[215] = 0b1111111111010111; + big[216] = 0b1111111111011000; + big[217] = 0b1111111111011001; + big[218] = 0b1111111111011010; + big[219] = 0b1111111111011011; + big[220] = 0b1111111111011100; + big[221] = 0b1111111111011101; + big[222] = 0b1111111111011110; + big[223] = 0b1111111111011111; + big[224] = 0b1111111111100000; + big[225] = 0b1111111111100001; + big[226] = 0b1111111111100010; + big[227] = 0b1111111111100011; + big[228] = 0b1111111111100100; + big[229] = 0b1111111111100101; + big[230] = 0b1111111111100110; + big[231] = 0b1111111111100111; + big[232] = 0b1111111111101000; + big[233] = 0b1111111111101001; + big[234] = 0b1111111111101010; + big[235] = 0b1111111111101011; + big[236] = 0b1111111111101100; + big[237] = 0b1111111111101101; + big[238] = 0b1111111111101110; + big[239] = 0b1111111111101111; + big[240] = 0b1111111111110000; + big[241] = 0b1111111111110001; + big[242] = 0b1111111111110010; + big[243] = 0b1111111111110011; + big[244] = 0b1111111111110100; + big[245] = 0b1111111111110101; + big[246] = 0b1111111111110110; + big[247] = 0b1111111111110111; + big[248] = 0b1111111111111000; + big[249] = 0b1111111111111001; + big[250] = 0b1111111111111010; + big[251] = 0b1111111111111011; + big[252] = 0b1111111111111100; + big[253] = 0b1111111111111101; + big[254] = 0b1111111111111110; + big[255] = 0b1111111111111111; - for (i = 0; i < 256; i++) { - if (big[i] != i + 65280U) - return 1; - } + for (i = 0; i < 256; i++) { + if (big[i] != i + 65280U) + return 1; + } - return 0; + return 0; } diff --git a/test/val/bitfield-union.c b/test/val/bitfield-union.c new file mode 100644 index 000000000..1fd201456 --- /dev/null +++ b/test/val/bitfield-union.c @@ -0,0 +1,71 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of union of bit-fields; see https://sourceforge.net/p/cc65/mailman/message/36152700/ +*/ + +#include <stdio.h> + +typedef union { + unsigned int bf; + + struct { + unsigned int a : 1; + unsigned int b : 1; + unsigned int c : 1; + }; +} bitfield_t; + +static unsigned char failures = 0; + +int main (void) +{ + bitfield_t bitfield = {0}; + + printf ("Bitfield: %u\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.a = bitfield.a ^ 1; + printf ("a=1: %u\n", bitfield.bf); + if (bitfield.bf != 1) failures++; + + bitfield.a = bitfield.a ^ 1; + printf ("a=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.b = bitfield.b ^ 1; + printf ("b=1: %u\n", bitfield.bf); + if (bitfield.bf != 2) failures++; + + bitfield.b = bitfield.b ^ 1; + printf ("b=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.c = bitfield.c ^ 1; + printf ("c=1: %u\n", bitfield.bf); + if (bitfield.bf != 4) failures++; + + bitfield.c = bitfield.c ^ 1; + printf ("c=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + return failures; +} diff --git a/test/val/bitfield.c b/test/val/bitfield.c new file mode 100644 index 000000000..939d90dff --- /dev/null +++ b/test/val/bitfield.c @@ -0,0 +1,285 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of bit-field packing; see https://github.com/cc65/cc65/issues/1054 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct four_bits { + unsigned int x : 4; +} fb = {1}; + +static void test_four_bits(void) +{ + if (sizeof(struct four_bits) != 1) { + printf("Got sizeof(struct four_bits) = %zu, expected 1.\n", + sizeof(struct four_bits)); + failures++; + } + + if (fb.x != 1) { + printf("Got fb.x = %u, expected 1.\n", fb.x); + failures++; + } + + fb.x = 3; + + if (fb.x != 3) { + printf("Got fb.x = %u, expected 3.\n", fb.x); + failures++; + } +} + +/* + Logic is somewhat diferent for bit-fields that end a struct vs + having additional fields. +*/ + +static struct four_bits_with_int { + unsigned int x : 4; + unsigned int y; +} fbi = {1, 2}; + +static void test_four_bits_with_int(void) +{ + /* The first 4-bit bit-field just takes one byte, so the size is 3. */ + if (sizeof(struct four_bits_with_int) != 3) { + printf("Got sizeof(struct four_bits_with_int) = %zu, expected 3.\n", + sizeof(struct four_bits_with_int)); + failures++; + } + + if (fbi.x != 1) { + printf("Got fbi.x = %u, expected 1.\n", fbi.x); + failures++; + } + + if (fbi.y != 2) { + printf("Got fbi.y = %u, expected 2.\n", fbi.y); + failures++; + } + + fbi.x = 3; + fbi.y = 17; + + if (fbi.x != 3) { + printf("Got fbi.x = %u, expected 3.\n", fbi.x); + failures++; + } + + if (fbi.y != 17) { + printf("Got fbi.y = %u, expected 17.\n", fbi.y); + failures++; + } +} + +static struct overlap { + unsigned int x : 10; + unsigned int y : 10; +} o = {11, 22}; + +/* Tests that bit-fields can share allocation units. */ +static void test_overlap(void) +{ + if (sizeof(struct overlap) != 3) { + printf("Got sizeof(struct overlap) = %zu, expected 3.\n", + sizeof(struct overlap)); + failures++; + } + + if (o.x != 11) { + printf("Got o.x = %u, expected 11.\n", o.x); + failures++; + } + + if (o.y != 22) { + printf("Got o.y = %u, expected 22.\n", o.y); + failures++; + } + + o.x = 33; + o.y = 44; + + if (o.x != 33) { + printf("Got o.x = %u, expected 33.\n", o.x); + failures++; + } + + if (o.y != 44) { + printf("Got o.y = %u, expected 44.\n", o.y); + failures++; + } +} + +static struct overlap_with_int { + unsigned int x : 10; + unsigned int y : 10; + unsigned int z; +} oi = {111, 222, 333}; + +static void test_overlap_with_int(void) +{ + /* First two fields in 3 bytes, then another 2 bytes. */ + if (sizeof(struct overlap_with_int) != 5) { + printf("Got sizeof(struct overlap_with_int) = %zu, expected 5.\n", + sizeof(struct overlap_with_int)); + failures++; + } + + if (oi.x != 111) { + printf("Got oi.x = %u, expected 111.\n", oi.x); + failures++; + } + + if (oi.y != 222) { + printf("Got oi.y = %u, expected 222.\n", oi.y); + failures++; + } + + if (oi.z != 333) { + printf("Got oi.z = %u, expected 333.\n", oi.z); + failures++; + } + + oi.x = 444; + oi.y = 555; + oi.z = 666; + + if (oi.x != 444) { + printf("Got oi.x = %u, expected 444.\n", oi.x); + failures++; + } + + if (oi.y != 555) { + printf("Got oi.y = %u, expected 555.\n", oi.y); + failures++; + } + + if (oi.z != 666) { + printf("Got oi.z = %u, expected 666.\n", oi.z); + failures++; + } +} + +static struct full_width { + unsigned int x : 8; + unsigned int y : 16; +} fw = {255, 17}; + +static void test_full_width(void) +{ + if (sizeof(struct full_width) != 3) { + printf("Got sizeof(struct full_width) = %zu, expected 3.\n", + sizeof(struct full_width)); + failures++; + } + + if (fw.x != 255) { + printf("Got fw.x = %u, expected 255.\n", fw.x); + failures++; + } + + if (fw.y != 17) { + printf("Got fw.y = %u, expected 17.\n", fw.y); + failures++; + } + + fw.x = 42; + fw.y = 1023; + + if (fw.x != 42) { + printf("Got fw.x = %u, expected 42.\n", fw.x); + failures++; + } + + if (fw.y != 1023) { + printf("Got fw.y = %u, expected 1023.\n", fw.y); + failures++; + } +} + +static struct aligned_end { + unsigned int : 2; + unsigned int x : 6; + unsigned int : 3; + unsigned int y : 13; + /* z crosses a byte boundary, but fits in a byte when shifted. */ + unsigned int : 6; + unsigned int z : 7; +} ae = {63, 17, 100}; + +static void test_aligned_end(void) +{ + if (sizeof(struct aligned_end) != 5) { + printf("Got sizeof(struct aligned_end) = %zu, expected 5.\n", + sizeof(struct aligned_end)); + failures++; + } + + if (ae.x != 63) { + printf("Got ae.x = %u, expected 63.\n", ae.x); + failures++; + } + + if (ae.y != 17) { + printf("Got ae.y = %u, expected 17.\n", ae.y); + failures++; + } + + if (ae.z != 100) { + printf("Got ae.z = %u, expected 100.\n", ae.z); + failures++; + } + + ae.x = 42; + ae.y = 1023; + ae.z = 66; + + if (ae.x != 42) { + printf("Got ae.x = %u, expected 42.\n", ae.x); + failures++; + } + + if (ae.y != 1023) { + printf("Got ae.y = %u, expected 1023.\n", ae.y); + failures++; + } + + if (ae.z != 66) { + printf("Got ae.z = %u, expected 66.\n", ae.z); + failures++; + } +} + +int main(void) +{ + test_four_bits(); + test_four_bits_with_int(); + test_overlap(); + test_overlap_with_int(); + test_full_width(); + test_aligned_end(); + printf("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1047.c b/test/val/bug1047.c new file mode 100644 index 000000000..3f1d3cf63 --- /dev/null +++ b/test/val/bug1047.c @@ -0,0 +1,82 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of char bit-fields; see https://github.com/cc65/cc65/issues/1047 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct chars { + unsigned char a : 3; + unsigned char b : 3; + unsigned char c : 3; +} cs = {4, 1, 3}; + +static void test_char_bitfield(void) +{ + if (sizeof (cs) != 2) { + printf("Got sizeof (cs) = %zu, expected 2.\n", sizeof (cs)); + failures++; + } + + if (cs.a != 4) { + printf("Got cs.a = %u, expected 4.\n", cs.a); + failures++; + } + + if (cs.b != 1) { + printf("Got cs.b = %u, expected 1.\n", cs.b); + failures++; + } + + if (cs.c != 3) { + printf("Got cs.c = %u, expected 3.\n", cs.c); + failures++; + } + + cs.a = -1; + cs.b = 6; + cs.c = 1; + + if (cs.a != 7) { + printf("Got cs.a = %u, expected 7.\n", cs.a); + failures++; + } + + if (cs.b != 6) { + printf("Got cs.b = %u, expected 6.\n", cs.b); + failures++; + } + + if (cs.c != 1) { + printf("Got cs.c = %u, expected 1.\n", cs.c); + failures++; + } +} + +int main(void) +{ + test_char_bitfield(); + printf("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1050.c b/test/val/bug1050.c new file mode 100644 index 000000000..af9e4f6b2 --- /dev/null +++ b/test/val/bug1050.c @@ -0,0 +1,32 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests enum sizes; see https://github.com/cc65/cc65/issues/1050 +*/ + +enum e { k }; +_Static_assert(sizeof(enum e) == 1, "This should fit in a byte."); +_Static_assert(sizeof(k) == 2, "Enumerators are still ints."); + +int main (void) +{ + return 0; +} diff --git a/test/val/bug1071.c b/test/val/bug1071.c new file mode 100644 index 000000000..66e298b25 --- /dev/null +++ b/test/val/bug1071.c @@ -0,0 +1,87 @@ + +/* test related to issue #1071 */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +struct ImageStruct +{ + uint8_t _imageData; + #if !defined(NO_COLOR) + uint8_t _color; + #endif +}; + +typedef struct ImageStruct Image; + +struct CharacterStruct +{ + // character coordinates + uint8_t _x; + uint8_t _y; + + // _status decides whether the character is active + uint8_t _status; + + Image* _imagePtr; +}; + +typedef struct CharacterStruct Character; + +uint16_t ghostLevel; +uint8_t level; +uint16_t loop; + +#define GHOSTS_NUMBER 10 +#define BOMBS_NUMBER 3 + +#define MAX_GHOST_LEVEL_SCALE 3 +#define MAX_GHOST_LEVEL (1400/MAX_GHOST_LEVEL_SCALE) + +#define MAX_GHOST_LOOP_SCALE 3 +#define MAX_GHOST_LOOP (1800/MAX_GHOST_LOOP_SCALE) + +#define INITIAL_GHOST_SLOWDOWN 16000 + +#define ACTION_GHOST_MIN_SLOWDOWN_SCALE 1 +#define GHOST_MIN_SLOWDOWN_SCALE ACTION_GHOST_MIN_SLOWDOWN_SCALE +#define GHOST_MIN_SLOWDOWN (3000/GHOST_MIN_SLOWDOWN_SCALE) + +Character ghosts[GHOSTS_NUMBER]; +Character bombs[BOMBS_NUMBER]; + +uint16_t test1(void) +{ + if((loop<MAX_GHOST_LOOP) && (ghostLevel<MAX_GHOST_LEVEL)) + { + return INITIAL_GHOST_SLOWDOWN-(uint16_t)level*256-ghostLevel*8; + } + return GHOST_MIN_SLOWDOWN; +} + +uint16_t test2(void) +{ + if((loop<MAX_GHOST_LOOP) && (ghostLevel<MAX_GHOST_LEVEL)) + { + return INITIAL_GHOST_SLOWDOWN-(uint16_t)level*256-ghostLevel*16; + } + return GHOST_MIN_SLOWDOWN; +} + +uint16_t res = 0; + +int main(void) +{ + loop = 7; + ghostLevel = 13; + level = 3; + + res = test1(); + printf("test1 res: %d\n", res); + if (res != 15128) return -1; + res = test2(); + printf("test2 res: %d\n", res); + if (res != 15024) return -1; + return 0; +} diff --git a/test/val/bug1075.c b/test/val/bug1075.c new file mode 100644 index 000000000..6ff5ec8e7 --- /dev/null +++ b/test/val/bug1075.c @@ -0,0 +1,31 @@ +/* bug #1075 Internal compiler error */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +long rhs; + +int test(void) +{ + /* the whole lhs is errorneously treated as an absolute address (integer + constant) neglecting its dereference */ + return *(char *)0xD77C + rhs; +} + +int res; + +int main(void) +{ + memset(*(char *)0xD76C, 11, 0x80); + rhs = 0x10; + *(char *)(0xD77C + rhs) = 13; + *(char *)0xD77C = 23; + *(char *)0xD78C = 42; + res = test(); + printf("res: %d\n", res); + if (res != (23 + 0x10)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/test/val/bug1077.c b/test/val/bug1077.c new file mode 100644 index 000000000..2fb3ef168 --- /dev/null +++ b/test/val/bug1077.c @@ -0,0 +1,31 @@ +/* bug #1077 - Wrong code: a non-array constant address with a non-constant subscript */ + +#include <stdlib.h> +#include <stdio.h> + +int test1(void) +{ + int a = 0; + (&a)[a] = 42; + return a; +} +int test2(void) +{ + int a = 0; + int b = 0; + (&a)[b] = 42; + return a; +} + +int main(void) +{ + int res, ret = EXIT_SUCCESS; + res = test1(); + printf("%d\n", res); + if (res != 42) ret = EXIT_FAILURE; + res = test2(); + printf("%d\n", res); + if (res != 42) ret = EXIT_FAILURE; + + return ret; +} diff --git a/test/val/bug1094.c b/test/val/bug1094.c new file mode 100644 index 000000000..05e3e6430 --- /dev/null +++ b/test/val/bug1094.c @@ -0,0 +1,99 @@ + +/* bug #1094 - Nested struct/union initializers don't compile */ + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> + +typedef uint16_t u16; +typedef uint8_t u8; + +struct WW { + int a : 4; + struct { + unsigned int b : 4; + unsigned int c : 8; + } x[2]; +} wwqq = { 0, {2, 5, {3, 4}}, }; + +typedef struct { + u16 quot; + u16 rem; +} udiv_t; + +typedef struct { + u16 quot; + u16 rem; + char m[8]; +} big_t; + +union U { + struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; + }; + int u; +}; + +union U g = { 5, 3, 1 }; + +struct S { + struct { + unsigned int a : 3; + unsigned int b : 3; + unsigned int c : 3; + }; +}; + +struct S h = { 5, 3, 1 }; + +union X { + struct { + uint16_t a : 3; + union { + struct { + uint16_t b : 3; + uint16_t c : 3; + }; + uint16_t d; + }; + }; + uint16_t e; +} x = { 4, {5, 6} }; + + +udiv_t div3(udiv_t u) +{ + udiv_t v = {}; + + u.quot = 341 + u.quot; + u.rem = 1 + u.rem; + + v = u; + + return v; +} + +int main(void) +{ + udiv_t v = { 141, 32 }; + big_t b = { 141, 32 }; + + v = div3(*(udiv_t*)&b); + + printf("%d %d %d\n", (int)wwqq.a, wwqq.x[0].b, wwqq.x[0].c); + printf("%d %d %d\n", (int)wwqq.a, wwqq.x[1].b, wwqq.x[1].c); + printf("quot = %u, rem = %u\n", div3(v).quot, div3(v).rem); + printf("quot = %u, rem = %u\n", v.quot, v.rem); + printf("quot = %u, rem = %u\n", b.quot, b.rem); + printf("g.a = %u, g.b = %u, g.c = %d\n", g.a, g.b, g.c); + x.e = 1467; + printf("x.a = %d, x.b = %d, x.c = %d\n", x.a, x.b, x.c); + printf("(long)x.b = %ld, sizeof(x) = %u, sizeof((long)x.a) = %u\n", (long)x.b, sizeof(x), sizeof((long)x.a)); + printf("-x.d = %d, (long)(-x.c + 1) = %ld\n", -x.d, (long)(-x.c + 1)); + printf("h.a = %u, h.b = %u, h.c = %u\n", h.a, h.b, h.c); + + return 0; +} diff --git a/test/val/bug1095.c b/test/val/bug1095.c new file mode 100644 index 000000000..cecbf0329 --- /dev/null +++ b/test/val/bug1095.c @@ -0,0 +1,162 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of signed bit-fields; see https://github.com/cc65/cc65/issues/1095 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct signed_ints { + signed int a : 3; + signed int b : 3; + signed int c : 3; + signed int d : 10; + signed int : 0; + signed int e : 8; + signed int f : 16; +} si = {-4, -1, 3, -500, -100, -5000}; + +static void test_signed_bitfield(void) +{ + if (si.a >= 0) { + printf("Got si.a = %d, expected negative.\n", si.a); + failures++; + } + if (si.a != -4) { + printf("Got si.a = %d, expected -4.\n", si.a); + failures++; + } + + if (si.b >= 0) { + printf("Got si.b = %d, expected negative.\n", si.b); + failures++; + } + if (si.b != -1) { + printf("Got si.b = %d, expected -1.\n", si.b); + failures++; + } + + if (si.c <= 0) { + printf("Got si.c = %d, expected positive.\n", si.c); + failures++; + } + if (si.c != 3) { + printf("Got si.c = %d, expected 3.\n", si.c); + failures++; + } + + if (si.d >= 0) { + printf("Got si.d = %d, expected negative.\n", si.d); + failures++; + } + if (si.d != -500) { + printf("Got si.d = %d, expected -500.\n", si.d); + failures++; + } + + if (si.e >= 0) { + printf("Got si.e = %d, expected negative.\n", si.e); + failures++; + } + if (si.e != -100) { + printf("Got si.e = %d, expected -100.\n", si.e); + failures++; + } + + if (si.f >= 0) { + printf("Got si.f = %d, expected negative.\n", si.f); + failures++; + } + if (si.f != -5000) { + printf("Got si.f = %d, expected -5000.\n", si.f); + failures++; + } + + si.a = -3; + si.b = 1; + si.c = -2; + si.d = 500; + si.e = 100; + si.f = 5000; + + if (si.a >= 0) { + printf("Got si.a = %d, expected negative.\n", si.a); + failures++; + } + if (si.a != -3) { + printf("Got si.a = %d, expected -3.\n", si.a); + failures++; + } + + if (si.b <= 0) { + printf("Got si.b = %d, expected positive.\n", si.b); + failures++; + } + if (si.b != 1) { + printf("Got si.b = %d, expected 1.\n", si.b); + failures++; + } + + if (si.c >= 0) { + printf("Got si.c = %d, expected negative.\n", si.c); + failures++; + } + if (si.c != -2) { + printf("Got si.c = %d, expected -2.\n", si.c); + failures++; + } + + if (si.d <= 0) { + printf("Got si.d = %d, expected positive.\n", si.d); + failures++; + } + if (si.d != 500) { + printf("Got si.d = %d, expected 500.\n", si.d); + failures++; + } + + if (si.e <= 0) { + printf("Got si.e = %d, expected positive.\n", si.e); + failures++; + } + if (si.e != 100) { + printf("Got si.e = %d, expected 100.\n", si.e); + failures++; + } + + if (si.f <= 0) { + printf("Got si.f = %d, expected positive.\n", si.f); + failures++; + } + if (si.f != 5000) { + printf("Got si.f = %d, expected 5000.\n", si.f); + failures++; + } +} + +int main(void) +{ + test_signed_bitfield(); + printf("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1108.c b/test/val/bug1108.c new file mode 100644 index 000000000..1cd23c8e5 --- /dev/null +++ b/test/val/bug1108.c @@ -0,0 +1,37 @@ + +/* bug #1108 - Problem with static locals? */ + +#include <stdio.h> + +#pragma static-locals (on) + +unsigned char x = 0; + +static unsigned char PrintVar1(void) +{ + unsigned char cx = x + 1; + + printf("cx:%d x:%d\n", cx, x); + return cx == 0; +} + +static unsigned char PrintVar2(void) +{ + unsigned char cx = x + 1; + unsigned char cy; + + cy = x + 1; + printf("cx:%d cy:%d x:%d\n", cx, cy, x); + return cx != cy; +} + +static unsigned char ret = 0; + +int main(void) +{ + do { + ret |= PrintVar1(); + ret |= PrintVar2(); + } while (++x < 10); + return ret; +} diff --git a/test/val/bug1139.c b/test/val/bug1139.c new file mode 100644 index 000000000..5aef3924d --- /dev/null +++ b/test/val/bug1139.c @@ -0,0 +1,353 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests bit-field in if condition; see https://github.com/cc65/cc65/issues/1139 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct four_bits { + unsigned int x : 4; +} fb = {1}; + +static struct overlap { + unsigned int x : 10; + unsigned int y : 10; +} o = {11, 22}; + +static struct full_width { + unsigned int x : 8; + unsigned int y : 16; +} fw = {255, 17}; + +static struct aligned_end { + unsigned int : 2; + unsigned int x : 6; + unsigned int : 3; + unsigned int y : 13; + /* z crosses a byte boundary, but fits in a byte when shifted. */ + unsigned int : 6; + unsigned int z : 7; +} ae = {63, 17, 100}; + +/* Test using bit-fields in if conditions. */ +static void test_if(void) +{ + /* Original test case for the bug. */ + o.x = 0; + o.y = 44; + if (o.x) { + printf("Bad, o.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* Additionally, test most fields from bitfield.c to try to cover all start/end situations. */ + /* four_bits */ + fb.x = 1; + if (fb.x) { + printf("Good\n"); + } else { + printf("Bad, fb.x is true (1)\n"); + failures++; + } + + fb.x = 0; + if (fb.x) { + printf("Bad, fb.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* overlap */ + o.x = 123; + if (o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is true (123)\n"); + failures++; + } + + o.x = 0; + if (o.x) { + printf("Bad, o.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + o.y = 321; + if (o.y) { + printf("Good\n"); + } else { + printf("Bad, o.y is true (321)\n"); + failures++; + } + + o.y = 0; + if (o.y) { + printf("Bad, o.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* full_width */ + fw.x = 117; + if (fw.x) { + printf("Good\n"); + } else { + printf("Bad, fw.x is true (117)\n"); + failures++; + } + + fw.x = 0; + if (fw.x) { + printf("Bad, fw.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.y = 32123; + if (fw.y) { + printf("Good\n"); + } else { + printf("Bad, fw.y is true (32123)\n"); + failures++; + } + + fw.y = 0; + if (fw.y) { + printf("Bad, fw.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* aligned_end */ + ae.x = 2; + if (ae.x) { + printf("Good\n"); + } else { + printf("Bad, ae.x is true (2)\n"); + failures++; + } + + ae.x = 0; + if (ae.x) { + printf("Bad, ae.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.y = 2222; + if (ae.y) { + printf("Good\n"); + } else { + printf("Bad, ae.y is true (2222)\n"); + failures++; + } + + ae.y = 0; + if (ae.y) { + printf("Bad, ae.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.z = 111; + if (ae.z) { + printf("Good\n"); + } else { + printf("Bad, ae.z is true (111)\n"); + failures++; + } + + ae.z = 0; + if (ae.z) { + printf("Bad, ae.z is false\n"); + failures++; + } else { + printf("Good\n"); + } +} + +/* Test using bit-fields in inverted if conditions. */ +static void test_if_not(void) +{ + /* Original test case for the bug, inverted. */ + o.x = 0; + o.y = 44; + if (!o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is false\n"); + failures++; + } + + /* Additionally, test most fields from bitfield.c to try to cover all start/end situations. */ + /* four_bits */ + fb.x = 1; + if (!fb.x) { + printf("Bad, fb.x is true (1)\n"); + failures++; + } else { + printf("Good\n"); + } + + fb.x = 0; + if (!fb.x) { + printf("Good\n"); + } else { + printf("Bad, fb.x is false\n"); + failures++; + } + + /* overlap */ + o.x = 123; + if (!o.x) { + printf("Bad, o.x is true (123)\n"); + failures++; + } else { + printf("Good\n"); + } + + o.x = 0; + if (!o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is false\n"); + failures++; + } + + o.y = 321; + if (!o.y) { + printf("Bad, o.y is true (321)\n"); + failures++; + } else { + printf("Good\n"); + } + + o.y = 0; + if (!o.y) { + printf("Good\n"); + } else { + printf("Bad, o.y is false\n"); + failures++; + } + + /* full_width */ + fw.x = 117; + if (!fw.x) { + printf("Bad, fw.x is true (117)\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.x = 0; + if (!fw.x) { + printf("Good\n"); + } else { + printf("Bad, fw.x is false\n"); + failures++; + } + + fw.y = 32123; + if (!fw.y) { + printf("Bad, fw.y is true (32123)\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.y = 0; + if (!fw.y) { + printf("Good\n"); + } else { + printf("Bad, fw.y is false\n"); + failures++; + } + + /* aligned_end */ + ae.x = 2; + if (!ae.x) { + printf("Bad, ae.x is true (2)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.x = 0; + if (!ae.x) { + printf("Good\n"); + } else { + printf("Bad, ae.x is false\n"); + failures++; + } + + ae.y = 2222; + if (!ae.y) { + printf("Bad, ae.y is true (2222)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.y = 0; + if (!ae.y) { + printf("Good\n"); + } else { + printf("Bad, ae.y is false\n"); + failures++; + } + + ae.z = 111; + if (!ae.z) { + printf("Bad, ae.z is true (111)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.z = 0; + if (!ae.z) { + printf("Good\n"); + } else { + printf("Bad, ae.z is false\n"); + failures++; + } +} + +int main(void) +{ + test_if(); + test_if_not(); + printf("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1143warn.c b/test/val/bug1143warn.c new file mode 100644 index 000000000..9bbc8ea8b --- /dev/null +++ b/test/val/bug1143warn.c @@ -0,0 +1,9 @@ + +/* bug #1143 - Multiple storage class specifiers in one declaration? */ + +static static void* y[1]; /* warning */ + +int main(void) +{ + return 0; +} diff --git a/test/val/bug1178.c b/test/val/bug1178.c new file mode 100644 index 000000000..043767e4c --- /dev/null +++ b/test/val/bug1178.c @@ -0,0 +1,81 @@ + +/* bug #1178 - copying structs/unions > 4 bytes is broken */ + +#include <stdio.h> +#include <stdlib.h> + +typedef struct +{ + unsigned char a; + unsigned char b; + unsigned char c; + unsigned char d; + unsigned char e; +} +TestStruct1; + +TestStruct1 StructArray1[2]; +TestStruct1 test1; + +typedef struct +{ + unsigned char a; + unsigned char b; + unsigned char c; + unsigned char d; +} +TestStruct2; + +TestStruct2 StructArray2[2]; +TestStruct2 test2; + +int res = EXIT_SUCCESS; + +void dotest1(void) +{ + test1.a = 42; + test1.b = 11; + test1.c = 23; + test1.d = 47; + test1.e = 13; + + StructArray1[0] = test1; + + printf ("test1: %d, %d, %d, %d, %d\n", + (int)StructArray1[0].a, (int)StructArray1[0].b, (int)StructArray1[0].c, + (int)StructArray1[0].d, (int)StructArray1[0].e); + if ((StructArray1[0].a != 42) || + (StructArray1[0].b != 11) || + (StructArray1[0].c != 23) || + (StructArray1[0].d != 47) || + (StructArray1[0].e != 13)) { + res = EXIT_FAILURE; + } +} + +void dotest2(void) +{ + test2.a = 42; + test2.b = 11; + test2.c = 23; + test2.d = 47; + + StructArray2[0] = test2; + + printf ("test2: %d, %d, %d, %d, %d\n", + (int)StructArray2[0].a, (int)StructArray2[0].b, + (int)StructArray2[0].c, (int)StructArray2[0].d); + if ((StructArray2[0].a != 42) || + (StructArray2[0].b != 11) || + (StructArray2[0].c != 23) || + (StructArray2[0].d != 47)) { + res = EXIT_FAILURE; + } +} + +int main(void) +{ + dotest1(); + dotest2(); + return res; +} diff --git a/test/val/bug1181.c b/test/val/bug1181.c new file mode 100644 index 000000000..4ea2d54bf --- /dev/null +++ b/test/val/bug1181.c @@ -0,0 +1,86 @@ + +/* bug #1181 - Testing struct member against NULL is broken */ + +#include <stdio.h> +#include <stdlib.h> + +struct { + int a; +} s = { 256 }, *ps = &s; + +int res = EXIT_SUCCESS; + +void test1(void) +{ + if (ps->a) { + printf("OK\n"); + } else { + printf("ERROR: %d\n", ps->a); + res = EXIT_FAILURE; + } +} + +typedef struct _MENUITEM +{ + char *name; +} MENUITEM; + +typedef struct _MENU +{ + struct _MENUITEM *items; +} MENU; + +/* note: the behaviour changes when these strings are changed! */ +static unsigned char oi31[] = {"Browser Exec Setup"}; +static unsigned char oi36[] = {"Browser auto sort"}; +static unsigned char oi47[] = {"Browser startup"}; +static unsigned char oi49[] = {"Browser charset"}; +static unsigned char oi55[] = {"Menu color scheme"}; +static unsigned char oi63[] = {"Menu input scheme"}; +static unsigned char oi35[] = {"back"}; + +MENUITEM optionsitems_menu[] = { + {oi31}, + {oi36}, + {oi47}, + {oi49}, + {oi55}, + {oi63}, + {oi35}, + {NULL} +}; + +static MENU optionsmenu_menu = { + &optionsitems_menu[0], +}; + +unsigned char __fastcall__ menu_getnumitems(MENU *menu) +{ +static unsigned char numitems; +MENUITEM *items; + numitems = 0; + items = menu->items; + while(items->name) + { + ++numitems; + ++items; + } + return numitems; +} + +int main(void) +{ + unsigned char i = 0; + + i = menu_getnumitems(&optionsmenu_menu); + printf("numitems (expected 7): %d\n", i); + + if (i != 7) { + printf("failed\n"); + res = EXIT_FAILURE; + } + printf("passed\n"); + + test1(); + return res; +} diff --git a/test/val/bug1201.c b/test/val/bug1201.c new file mode 100644 index 000000000..6d194fbee --- /dev/null +++ b/test/val/bug1201.c @@ -0,0 +1,25 @@ + +/* bug #1201 - The unary operators +, - and ~ should do integer promote on the result types. */ + +char a; +short b; +int c; +long d; +enum E { + Z +} e; +struct S { + int a : 1; +} f; + +_Static_assert(sizeof(+a) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+b) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+c) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+d) == sizeof(long), "Result type should be long"); +_Static_assert(sizeof(+e) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+f.a) == sizeof(int), "Result type should be int"); + +int main(void) +{ + return 0; +} diff --git a/test/val/bug1209-ind-goto-rev.c b/test/val/bug1209-ind-goto-rev.c new file mode 100644 index 000000000..ab8213a30 --- /dev/null +++ b/test/val/bug1209-ind-goto-rev.c @@ -0,0 +1,53 @@ +/* + Copyright 2020 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 indirect goto with dynamic labels and order label def, label ref, goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include <stdio.h> +#include <stdlib.h> + +/* When operating correctly, this returns 0. */ +int f (void) +{ + static void *x[1]; + /* Define the label before referencing it with indirect label syntax. */ +L: if (x[0] != 0) return 0; + x[0] = &&L; + goto *x[0]; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/val/bug1211-ice-move-refs-1.c b/test/val/bug1211-ice-move-refs-1.c new file mode 100644 index 000000000..9be1696ee --- /dev/null +++ b/test/val/bug1211-ice-move-refs-1.c @@ -0,0 +1,53 @@ +/* + Copyright 2020 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 indirect goto with label merge ICE. + https://github.com/cc65/cc65/issues/1211 + This test case works because CS_MergeLabels has a hack to keep the "unreferenced" label + (i.e. the one referenced in a data segment). +*/ + +#include <stdio.h> + +/* When operating correctly, f(0) = 31 and f(1) = 41. */ +int f (int x) +{ + static const void *const labels[] = {&&L0, &&L1}; + goto *labels[x]; +L0: if (labels[0] != labels[1]) return 31; + else return 13; +L1: return 41; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f (0) != 31) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/val/bug1221.c b/test/val/bug1221.c new file mode 100644 index 000000000..360a71162 --- /dev/null +++ b/test/val/bug1221.c @@ -0,0 +1,12 @@ +/* bug #1221 - Structs/unions as ternary operands */ + +int a; +struct S { int a; } s1, s2; +struct U { int a; } u1, u2; + +int main() +{ + a ? s1 : s2; /* BUG: should be OK */ + a ? u1 : u2; /* BUG: should be OK */ + return 0; +} diff --git a/test/val/bug1222.c b/test/val/bug1222.c new file mode 100644 index 000000000..5e47e452e --- /dev/null +++ b/test/val/bug1222.c @@ -0,0 +1,12 @@ +/* bug #1222 - 'sizeof' issues */ + +#include <stdlib.h> + +int a[1]; +int b[sizeof ++a[42]]; /* should work as '++a[42]' is actually unevaluated */ + +int main(void) +{ + return EXIT_SUCCESS; +} + diff --git a/test/val/bug1244.c b/test/val/bug1244.c new file mode 100644 index 000000000..fb499a0c8 --- /dev/null +++ b/test/val/bug1244.c @@ -0,0 +1,18 @@ + +/* bug #1244 - ICE for enum bit-fields */ + +#include <stdlib.h> + +enum E { + L = 65535L /* or U = 65535U */ +}; + +struct S { + enum E a : 16; +} s; + +int main(void) +{ + return EXIT_SUCCESS; +} + diff --git a/test/val/bug1245.c b/test/val/bug1245.c new file mode 100644 index 000000000..2dda93790 --- /dev/null +++ b/test/val/bug1245.c @@ -0,0 +1,12 @@ +/* bug #1245 - ICE for enums with int initializers */ + +#include <stdlib.h> + +enum E { + X = 1000, +} e = 3; + +int main(void) +{ + return EXIT_SUCCESS; +} diff --git a/test/val/bug1263.c b/test/val/bug1263.c new file mode 100644 index 000000000..740b19250 --- /dev/null +++ b/test/val/bug1263.c @@ -0,0 +1,22 @@ +/* bug #1263 - erroneous error for K & R function declaration */ + +enum E { I = 0 }; + +extern int f(); +int f(e) + enum E e; +{ + return e; +} + +extern int g(int); +int g(e) + enum E e; +{ + return e; +} + +int main(void) +{ + return f(I) + g(I); +} diff --git a/test/val/bug1267.c b/test/val/bug1267.c new file mode 100644 index 000000000..382903594 --- /dev/null +++ b/test/val/bug1267.c @@ -0,0 +1,79 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of bit-field signedness with typedefs; see https://github.com/cc65/cc65/issues/1267 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +typedef int i16; +typedef unsigned int u16; +typedef signed int s16; + +static struct ints { + i16 i : 4; + u16 u : 4; + s16 s : 4; +} si = {1, 2, 3}; + +static void test_bitfield_typedefs (void) +{ + if (si.i != 1) { + /* Note that this is another bug that i is signed. */ + printf ("Got si.a = %d, expected 1.\n", si.i); + failures++; + } + if (si.u != 2) { + printf ("Got si.u = %u, expected 2.\n", si.u); + failures++; + } + if (si.s != 3) { + printf ("Got si.s = %d, expected 3.\n", si.s); + failures++; + } + + si.i = -1; + si.u = -2; + si.s = -3; + + /* Note that this is another bug that i is signed. */ + if (si.i != -1) { + printf ("Got si.a = %d, expected -1.\n", si.i); + failures++; + } + if (si.u != 14) { + printf ("Got si.u = %u, expected 14.\n", si.u); + failures++; + } + if (si.s != -3) { + printf ("Got si.s = %d, expected -3.\n", si.s); + failures++; + } +} + +int main (void) +{ + test_bitfield_typedefs (); + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1310.c b/test/val/bug1310.c new file mode 100644 index 000000000..306c91fb6 --- /dev/null +++ b/test/val/bug1310.c @@ -0,0 +1,87 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of constant expressions. https://github.com/cc65/cc65/issues/1310 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +int main (void) +{ + /* 255 * 255 is signed integer overflow, so UB, but it would be nice if + ** (1) there were a warning, and (2) it did the "obvious" thing. + */ + const int two_fifty_five = 255; + const int two_fifty_five_squared = 255 * 255; + + /* Unsigned overflow is not UB, but has similar problems with comparison. */ + const int two_fifty_six = 256U; + const int two_fifty_six_squared = 256U * 256U; + + if (255 * 255 != -511) { + fprintf (stderr, "Expected 255 * 255 == -511, got: %d\n", 255 * 255); + failures++; + } + if (two_fifty_five * two_fifty_five != -511) { + fprintf (stderr, "Expected two_fifty_five * two_fifty_five == -511, got: %d\n", + two_fifty_five * two_fifty_five); + failures++; + } + if (two_fifty_five_squared != -511) { + fprintf (stderr, "Expected two_fifty_five_squared == -511, got: %d\n", + two_fifty_five_squared); + failures++; + } + + if (256U * 256U != 0) { + fprintf (stderr, "Expected 256U * 256U == 0, got: %d\n", 256U * 256U); + failures++; + } + if (two_fifty_six * two_fifty_six != 0) { + fprintf (stderr, "Expected two_fifty_six * two_fifty_six == 0, got: %d\n", + two_fifty_six * two_fifty_six); + failures++; + } + if (two_fifty_six_squared != 0) { + fprintf (stderr, "Expected two_fifty_six_squared == 0, got: %d\n", + two_fifty_six_squared); + failures++; + } + + if (-32768U != 32768U) { + fprintf (stderr, "Expected -32768U == 32768U, got: %ld\n", (long)-32768U); + failures++; + } + if (~32767U != 32768U) { + fprintf (stderr, "Expected ~32767U == 32768U, got: %ld\n", (long)~32767U); + failures++; + } + + if ((long*)0x1000 - (long*)0x2000 >= 0) { + fprintf (stderr, "Expected (long*)0x1000 - (long*)0x2000 < 0, got: %ld\n", (long*)0x1000 - (long*)0x2000); + failures++; + } + printf ("failures: %u\n", failures); + + return failures; +} diff --git a/test/val/bug1320.c b/test/val/bug1320.c new file mode 100644 index 000000000..824b50071 --- /dev/null +++ b/test/val/bug1320.c @@ -0,0 +1,44 @@ +/* + Copyright 2020, 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 a post-counted pointer argument, + followed by a (nested) function-call argument. + Test that compiling it doesn't cause a seg-fault. + + https://github.com/cc65/cc65/issues/1320 +*/ + +static char *var; + +static void foo (char *, char) +{ +} + +static char bar (void) +{ + return 'b'; +} + +int main (void) +{ + foo (var++, bar ()); + return 0; +} diff --git a/test/val/bug1332.c b/test/val/bug1332.c new file mode 100644 index 000000000..9714e0782 --- /dev/null +++ b/test/val/bug1332.c @@ -0,0 +1,72 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests bit-field bug. https://github.com/cc65/cc65/issues/1332 +*/ + +#include <stdio.h> +#include <string.h> + +static unsigned char failures = 0; + +static const struct bitfield { + unsigned lsb : 1; + unsigned pad : 14; + unsigned msb : 1; +} b = {1, 0, 1}; + +static unsigned v; + +void test_if_val (void) +{ + /* Gets appropriate garbage (0x12) into .X so the test fails. */ + v = 0x1234; + + if (b.msb == 1) { + fprintf (stderr, "if (msb == 1) OK\n"); + } else { + fprintf (stderr, "if (msb == 1) FAILED\n"); + failures++; + } +} + +void test_sprintf (void) +/* This test case is similar to the original bug report. */ +{ + char buf[10]; + snprintf (buf, sizeof (buf), "%u", b.msb); + + if (strcmp (buf, "1") != 0) { + fprintf (stderr, "Expected: sprintf (msb) == \"1\", got: %s\n", buf); + failures++; + } else { + fprintf (stderr, "sprintf (msb) OK\n"); + } +} + +int main (void) +{ + test_if_val (); + test_sprintf (); + + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/bug1348.c b/test/val/bug1348.c new file mode 100644 index 000000000..c40d9ac84 --- /dev/null +++ b/test/val/bug1348.c @@ -0,0 +1,54 @@ +/* bug #1348, wrongly optimized integer promotion in comparison */ + +#include <stdio.h> + +static const int notrandtab[] = { + 0xffff, 0x7fff, 0x3fff, 0x1fff, + 0x0fff, 0x07ff, 0x03ff, 0x01ff, + 0x00ff, 0x007f, 0x003f, 0x001f, + 0x000f, 0x0007, 0x0003, 0x0001 +}; + +static unsigned char notrandcount = 0; + +static int notrand(void) +{ + return notrandtab[notrandcount & 0x0f]; +} + +static unsigned char n1, n2; +static unsigned char i, ii, s; +static unsigned char err = 0; + +static const unsigned char cmptab[] = { + 0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01, + 0x80, 0x40, 0x20, 0x10, + 0x08, 0x04, 0x02, 0x01 +}; + +int main(void) +{ + for (ii = 0; ii < 16; ++ii) { + s = cmptab[ii]; + for (i = 0; i < 16; ++i) { + n1 = n2 = 0; + if ((notrand() & 0xff) > s) { + n1 = 1; + } + if ((notrand() & 0xffu) > s) { + n2 = 1; + } + printf("%5d > %3d %u(%02x) %u(%02x) %s\n", + notrandtab[i], s, + n1, (notrand() & 0xff), + n2, (notrand() & 0xffu), + n1 == n2 ? "=" : "!="); + if (n1 != n2) { + err = 1; + } + notrandcount++; + } + } + return err; +} diff --git a/test/val/bug1374.c b/test/val/bug1374.c new file mode 100644 index 000000000..b55e75fee --- /dev/null +++ b/test/val/bug1374.c @@ -0,0 +1,83 @@ + +/* test for bug#1374 */ + +#include <stdint.h> +#include <stdio.h> + +static int res = 0; + +int test1(void) +{ + uint8_t x = 0x89; + uint8_t y = 0xab; + uint16_t z = (x << 8) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test1b(void) +{ + uint8_t x = 0x89; + uint8_t y = 0xab; + uint16_t z = (x * 256) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test2(void) +{ + uint16_t x = 0x8900; + uint8_t y = 0xab; + uint16_t z = x | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test3(void) +{ + uint16_t x = 0x89; + uint8_t y = 0xab; + uint16_t z = (x << 8) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test3b(void) +{ + uint16_t x = 0x89; + uint8_t y = 0xab; + uint16_t z = (x * 256) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test4(void) +{ + uint8_t x = 0x89; + uint16_t y = 0xab; + uint16_t z = (x << 8) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int test4b(void) +{ + uint8_t x = 0x89; + uint16_t y = 0xab; + uint16_t z = (x * 256) | y; + printf("%x\n", z); + return (z == 0x89ab) ? 0 : 1; +} + +int main(void) +{ + res |= test1(); + res |= test2(); + res |= test3(); + res |= test4(); + res |= test1b(); + res |= test3b(); + res |= test4b(); + printf("res: %d\n", res); + return res; +} diff --git a/test/val/bug1397.c b/test/val/bug1397.c new file mode 100644 index 000000000..191093efa --- /dev/null +++ b/test/val/bug1397.c @@ -0,0 +1,54 @@ + +/* bug #1937 - Incorrect Behavior Related to OptBoolTrans */ + +#include <stdio.h> + +unsigned char c; +int *p; + +void f1(void) { + int i = 1; + int *pa = (int *)0xaaaa; + int *pb = (int *)0xbbbb; + + p = (i == 0) ? pa : pb; + c = 0x5a; +} + + +struct data_t { + unsigned char c; + int *p; +}; + +struct data_t data; + +void f2(void) { + int i = 1; + int *pa = (int *)0xcccc; + int *pb = (int *)0xdddd; + struct data_t *po = &data; + + po->p = (i == 0) ? pa : pb; + po->c = 0xa5; +} + +int ret = 0; + +int main(void) { + f1(); + if (c != 0x5a) { + ret++; + } + printf("c: %hhx\n", c); + printf("p: %p\n", p); + f2(); + if (data.c != 0xa5) { + ret++; + } + printf("c: %hhx\n", data.c); + printf("p: %p\n", data.p); + + printf("failures: %d\n", ret); + return ret; +} diff --git a/test/val/bug1408.c b/test/val/bug1408.c new file mode 100644 index 000000000..8ecc1be68 --- /dev/null +++ b/test/val/bug1408.c @@ -0,0 +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; +} diff --git a/test/val/bug1431.c b/test/val/bug1431.c new file mode 100644 index 000000000..9eae3bdbd --- /dev/null +++ b/test/val/bug1431.c @@ -0,0 +1,33 @@ +/* +$ cl65 -Osir --codesize 180 -S -o main.s main.c +main.c(9): Internal compiler error: +Code generation messed up: StackPtr is -2, should be -4 + +Input: if (wcnt > btw) { + +$ git bisect bad +aa6fdf58b8a17b747090fb521f3d9106e0c56d1c is the first bad commit +commit aa6fdf58b8a17b747090fb521f3d9106e0c56d1c +Author: acqn <acqn163@outlook.com> +Date: Mon Feb 8 09:03:19 2021 +0800 + + Addresses in constant subtraction expressions now work. + Fixed codegen for cast type subtraction in constant expressions. +*/ + +unsigned long fptr = 0x40001; + +int main(void) +{ + unsigned int btw = 500; + unsigned int wcnt; + + wcnt = 512U - (fptr % 512U); + + if (wcnt > btw) { + wcnt = btw; + } + + return wcnt == 500 ? 0 : 1; +} + diff --git a/test/val/bug1437.c b/test/val/bug1437.c new file mode 100644 index 000000000..3424079e1 --- /dev/null +++ b/test/val/bug1437.c @@ -0,0 +1,35 @@ + +/* bug #1437 enum declaration in a struct/union is invisible in the scope where the struct/union is declared */ + +struct nodelist1 { + struct { + enum { DEAD1, LIVE1, ONCE1, TWICE1 } live1; + } s; +} firstnode1 = {ONCE1}; + +enum nodestate2 { DEAD2, LIVE2, ONCE2, TWICE2 } live2; + +union nodelist2 { + enum nodestate2 live2; +} firstnode2 = { {TWICE2} }; + +struct T { + int I; + int; + enum E { + I + }; +}; + +int failures = 0; + +int main (void) +{ + if (firstnode1.s.live1 != ONCE1) { + ++failures; + } + if (firstnode2.live2 != TWICE2) { + ++failures; + } + return failures; +} diff --git a/test/val/bug1438.c b/test/val/bug1438.c new file mode 100644 index 000000000..3894f87f1 --- /dev/null +++ b/test/val/bug1438.c @@ -0,0 +1,35 @@ + +/* Issue #1438 fix #1439 - crash in cc65, related to delayed post-counting + + this is an odd issue, the compile would crash *sometimes*, perhaps in one + of ten compilation runs. +*/ + +/* #define __fastcall__ */ + +unsigned short a[10] = {0,1,2,3,4,5,6,7,8,9}; + +unsigned short __fastcall__ func2(void) +{ + return 42; +} + +void func1(unsigned short *wp) +{ + *wp++ = func2(); +} + +int main(void) +{ + func1(&a[3]); + if (a[2] != 2) { + return 1; + } + if (a[3] != 42) { + return 1; + } + if (a[4] != 4) { + return 1; + } + return 0; +} diff --git a/test/val/bug1451.c b/test/val/bug1451.c new file mode 100644 index 000000000..f9cca2561 --- /dev/null +++ b/test/val/bug1451.c @@ -0,0 +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; +} diff --git a/test/val/bug1462-2.c b/test/val/bug1462-2.c new file mode 100644 index 000000000..df94cfc59 --- /dev/null +++ b/test/val/bug1462-2.c @@ -0,0 +1,51 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* even the = operation is buggy in certain ways */ + +#include <stdio.h> + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; +} T; + +int failures = 0; + +T *f(T *t) +{ + t->a = 0; + t->c = 0; + return t; +} + +void test(void) +{ + T a = { 7, 0, 7 }; + T *p = &a; + + a.b = f(p)->a; + + if (a.a != 0) { + ++failures; + } + printf("%d\n", a.a); + + if (p->b != 0) { + ++failures; + } + printf("%d\n", p->b); + + if ((&a)->c != 0) { + ++failures; + } + printf("%d\n", (&a)->c); + + printf("Failures: %d\n", failures); +} + +int main(void) +{ + test(); + return failures; +} diff --git a/test/val/bug1462-3.c b/test/val/bug1462-3.c new file mode 100644 index 000000000..12559b94f --- /dev/null +++ b/test/val/bug1462-3.c @@ -0,0 +1,95 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More tests on "op= expression result value" that a naive fix might fail with */ + +#include <stdio.h> + +typedef struct { + signed int a : 3; + unsigned int b : 3; + signed int c : 3; + unsigned int d : 3; +} T1; + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; + signed int d : 3; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 3, 3, 3, 3 }; + int i; + + i = a.a -= a.b + a.c; + if (i != -3 || a.a != -3) { + ++failures1; + } + printf("i = %d, a.a = %d\n", i, a.a); + + a.b = i = a.b / -1; + if (i != -3 || a.b != 5) { + ++failures1; + } + printf("i = %d, a.b = %d\n", i, a.b); + + i = a.c = 0; + if (i != 0 || a.c != 0) { + ++failures1; + } + printf("i = %d, a.c = %d\n", i, a.c); + + i = a.d /= -1; + if (i != 5 || a.d != 5) { + ++failures1; + } + printf("i = %d, a.d = %d\n", i, a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T2 b = { 3, 3, 4, 4 }; + int i; + + i = b.a++; + if (i != 3 || b.a != -4) { + ++failures2; + } + printf("i = %d, b.a = %d\n", i, b.a); + + i = ++b.b; + if (i != -4 || b.b != -4) { + ++failures2; + } + printf("i = %d, b.b = %d\n", i, b.b); + + i = b.c--; + if (i != -4 || b.c != 3) { + ++failures2; + } + printf("i = %d, b.c = %d\n", i, b.c); + + i = --b.d; + if (i != 3 || b.d != 3) { + ++failures2; + } + printf("i = %d, b.d = %d\n", i, b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + diff --git a/test/val/bug1462-4.c b/test/val/bug1462-4.c new file mode 100644 index 000000000..f811ddbd6 --- /dev/null +++ b/test/val/bug1462-4.c @@ -0,0 +1,85 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More tests on "op= expression result value" that a naive fix might fail with */ + +#include <stdio.h> +#include <limits.h> + +#define SMALL_WIDTH 4 +#define LARGE_WIDTH (CHAR_BIT * sizeof (unsigned int)) + +typedef struct { + unsigned int a : SMALL_WIDTH; + unsigned int b : SMALL_WIDTH; + unsigned int c : SMALL_WIDTH; + unsigned int d : SMALL_WIDTH; +} T1; + +typedef struct { + unsigned int a : LARGE_WIDTH; + unsigned int b : LARGE_WIDTH; + unsigned int c : LARGE_WIDTH; + unsigned int d : LARGE_WIDTH; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", SMALL_WIDTH); + if (!(~a.a < 0)) { + ++failures1; + } + printf("~a.a < 0 : %d\n", ~a.a < 0); + if (!(0 > ~a.b)) { + ++failures1; + } + printf("0 > ~a.b : %d\n",0 > ~a.b); + if (!(a.c > -1)) { + ++failures1; + } + printf("a.c > -1 : %d\n", a.c > -1); + if (!(-1 < a.d)) { + ++failures1; + } + printf("-1 < a.d : %d\n", -1 < a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T1 b = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", LARGE_WIDTH); + if (!(~b.a < 0)) { + ++failures2; + } + printf("~b.a < 0 : %d\n", ~b.a < 0); + if (!(0 > ~b.b)) { + ++failures2; + } + printf("0 > ~b.b : %d\n", 0 > ~b.b); + if (!(b.c > -1)) { + ++failures2; + } + printf("b.c > -1 : %d\n", b.c > -1); + if (!(-1 < b.d)) { + ++failures2; + } + printf("-1 < b.d : %d\n", -1 < b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + diff --git a/test/val/bug1462.c b/test/val/bug1462.c new file mode 100644 index 000000000..aec990cde --- /dev/null +++ b/test/val/bug1462.c @@ -0,0 +1,67 @@ + +/* issue #1462 - Bit-fields are still broken */ + +#include <stdio.h> + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; +} T; + +int failures = 0; + +void test() +{ + T a = {2, 5, -1}; + T b = {1, 4, -1}; + T m[1] = {{6, 3, -1}}; + T *p = &a; + + a.c += b.a; + p->c += b.b; + m->c += b.c; + + if (a.c != -4) { + ++failures; + } + printf("%d\n", a.c); + + if (p->c != -4) { + ++failures; + } + printf("%d\n", p->c); + + if (m->c != -2) { + ++failures; + } + printf("%d\n", m->c); + + ++a.a; + p->b++; + m->c--; + + if (a.a != 3) { + ++failures; + } + printf("%d\n", a.a); + + if (p->b != -2) { + ++failures; + } + printf("%d\n", p->b); + + if (m->c != -3) { + ++failures; + } + printf("%d\n", m->c); + + printf("Failures: %d\n", failures); +} + +int main(void) +{ + test(); + return failures; +} + diff --git a/test/val/bug1504.c b/test/val/bug1504.c new file mode 100644 index 000000000..bd93c7387 --- /dev/null +++ b/test/val/bug1504.c @@ -0,0 +1,13 @@ + +/* bug #1504 - Some compilation failures */ + +#include <stdio.h> + +int main(void) +{ + int i = 0, *p = &i; + switch (i) case 0: case 1: i = 21; /* Should be OK but fails */ + p++[0] += 21; /* Should be OK but fails */ + printf("%d\n", i); + return i != 42; +} diff --git a/test/val/bug1552.c b/test/val/bug1552.c new file mode 100644 index 000000000..42f39eec6 --- /dev/null +++ b/test/val/bug1552.c @@ -0,0 +1,42 @@ + +/* + bug #1552 - crash in fuzix xec.c + + cc65 -t none -O bug1552.c +*/ + +#include <stdio.h> + +typedef struct trenod *TREPTR; +typedef struct whnod *WHPTR; + +struct trenod { + int tretyp; +}; + +struct whnod { + int whtyp; + TREPTR whtre; +}; + +int execute(TREPTR argt, int execflg, int *pf1, int *pf2) +{ + register TREPTR t; + int type; + switch (type) + { + case 6: + { + while ((execute(((WHPTR) t)->whtre, 0, NULL, NULL) == 0) == (type == 5)) { + + } + break; + } + } + return 0; +} + +int main(void) +{ + return execute((TREPTR)42, 2, (int *)3, (int *)4); +} diff --git a/test/val/bug1562.c b/test/val/bug1562.c new file mode 100644 index 000000000..7e6c1751e --- /dev/null +++ b/test/val/bug1562.c @@ -0,0 +1,30 @@ + +/* bug 1562: cc65 generates incorrect code for logical expression with -O */ + +#include <stdio.h> +#include <string.h> + +int failures = 0; + +char input[256]; + +#define DEBUGTRUE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 0 : 1 + +#define DEBUGFALSE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 1 : 0 + +int main(void) { + char* r; + strcpy(input, "\"XYZ\""); + r = input+4; + DEBUGFALSE(*r != '"'); // = false + DEBUGTRUE(*r == '"'); // = true + DEBUGFALSE(*(r+1) == '"'); // = false + // Next answer should be false because + // (false || true && false) is false, but it is true with -O. + DEBUGFALSE(*r != '"' || *r == '"' && *(r+1) == '"'); + // Adding parens fixes it even with -O. + DEBUGFALSE(*r != '"' || (*r == '"' && *(r+1) == '"')); + + printf("failures: %d\n", failures); + return failures; +} diff --git a/test/val/bug170.c b/test/val/bug170.c new file mode 100644 index 000000000..ac54d54d8 --- /dev/null +++ b/test/val/bug170.c @@ -0,0 +1,40 @@ +/* bug #170 - Wrong implicit conversion of integers */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +int main(void) +{ + uint8_t c = 2; + uint32_t u = 2; + int16_t a = -2; + int32_t l = -2; + + /* Generated code should use tosmulax but uses tosumulax */ + int16_t r = c * a; + /* Generated code should use tosmuleax but uses tosumuleax */ + int32_t lr = u * l; + + int32_t n = -95; + uint16_t d = 3; + int16_t r1 = n / d; // produces 21813 instead of -31 + + int16_t r2 = n / (int32_t) d; // workaround + + printf("r: %d (-4)\n", r); +#ifdef REFERENCE + printf("lr: %d (-4)\n", lr); +#else + printf("lr: %ld (-4)\n", lr); +#endif + printf("r1: %d (-31)\n", r1); + printf("r2: %d (-31)\n", r2); + + if (r != -4) { return EXIT_FAILURE; } + if (lr != -4) { return EXIT_FAILURE; } + if (r1 != -31) { return EXIT_FAILURE; } + if (r2 != -31) { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} diff --git a/test/val/bug250.c b/test/val/bug250.c new file mode 100644 index 000000000..60f3c633d --- /dev/null +++ b/test/val/bug250.c @@ -0,0 +1,13 @@ +/* bug #250 - Array size compile-time optimization stops halfway */ + +#include <stdlib.h> + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +unsigned char c[2*4]; +unsigned char b[2*LZO_MAX(8,sizeof(int))]; // this will not compile + +int main(void) +{ + /* FIXME: add some runtime check */ + return EXIT_SUCCESS; +} diff --git a/test/val/bug263.c b/test/val/bug263.c new file mode 100644 index 000000000..6f3f8f57e --- /dev/null +++ b/test/val/bug263.c @@ -0,0 +1,49 @@ + +/* issue #263 - cc65 miscompiles w/ a static variable and -O */ + +#include <stdint.h> +#include <stdio.h> + +int failures = 0; + +void __fastcall__ set_vram_update(unsigned char *ptr) +{ + printf("set_vram_update: %04x\n", ptr); + if (ptr != NULL) { + failures++; + } +} + +unsigned char __fastcall__ ppu_wait_nmi(void) +{ + // we need to make sure somehow the akku is not zero before the break + return 0x1234; +} + +unsigned char ctrl, ret, i; + +unsigned char gameloop (void) +{ + ctrl = 0; + ret = 0; + while(1) { + if (ctrl & 1) { + while (--i) { + ppu_wait_nmi(); + } + break; + } + ctrl = 1; + } + // This will pass garbage, not NULL. + set_vram_update(NULL); + return ret; +} + +int main(void) +{ + gameloop(); + printf("failures: %d\n", failures); + return failures; +} + diff --git a/test/val/bug327.c b/test/val/bug327.c new file mode 100644 index 000000000..f6a79962b --- /dev/null +++ b/test/val/bug327.c @@ -0,0 +1,34 @@ +/* bug #327 - Promoting u8 to s16 gives wrong result */ + +#include <stdio.h> +#include <stdint.h> + +static const uint8_t arr[2] = { + 0, + 255 +}; + +static int16_t get16() { + return -arr[1]; +} + +static int16_t get16_2() { + return -(int16_t) arr[1]; +} + +char res = 0; + +int main() { + + printf("Value %d, should be -255\n", get16()); + printf("Value %d, should be -255\n", get16_2()); + + if (get16() != -255) { + res++; + } + if (get16_2() != -255) { + res++; + } + + return res; +} diff --git a/test/val/bug897.c b/test/val/bug897.c new file mode 100644 index 000000000..eaf751441 --- /dev/null +++ b/test/val/bug897.c @@ -0,0 +1,52 @@ + +/* issue #897 - __asm__()-referenced code-labels are generated for only branches and jumps */ + +#include <stdlib.h> +#include <stdio.h> + +static unsigned char *srcptr, *dstptr; + +#define COPY_LEN 16 + +void test(void) +{ + asm("lda %v", srcptr); + asm("sta %g+1", s2b_copy_from); + asm("lda %v+1", srcptr); + asm("sta %g+2", s2b_copy_from); + + asm("lda %v", dstptr); + asm("sta %g+1", s2b_copy_to); + asm("lda %v+1", dstptr); + asm("sta %g+2", s2b_copy_to); + + asm("ldy #%b", COPY_LEN-1); +s2b_copy_from: + asm("lda $FFFF,y"); +s2b_copy_to: + asm("sta $FFFF,y"); + asm("dey"); + asm("bpl %g", s2b_copy_from); +} + +unsigned char src[16] = "0123456789abcdef"; +unsigned char dest[16]; + +int failures = 0; + +unsigned char i; + +int main(void) +{ + srcptr = src; + dstptr = dest; + test(); + for (i = 0; i < COPY_LEN; i++) { + printf("%d %02x %02x\n", i, src[i], dest[i]); + if (src[i] != dest[i]) { + failures++; + } + } + printf("failures: %d\n", failures); + return failures; +} diff --git a/test/val/bug975.c b/test/val/bug975.c new file mode 100644 index 000000000..458524a5a --- /dev/null +++ b/test/val/bug975.c @@ -0,0 +1,18 @@ +/* bug #975 - Forward array reference fails to compile */ + +#include <stdlib.h> + +// this works +static const unsigned char array2[3]; +int test2(void) { + return array2[0]; +} +static const unsigned char array2[] = { 0, 1, 2 }; + +// this should work, but does not compile +static const unsigned char array[]; +int main() { + if (test2() != 0) return EXIT_FAILURE; + return array[0]; +} +static const unsigned char array[] = { 0, 1, 2 }; diff --git a/test/val/casttochar.c b/test/val/casttochar.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141002.c b/test/val/cc65141002.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141011.c b/test/val/cc65141011.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141022.c b/test/val/cc65141022.c old mode 100755 new mode 100644 diff --git a/test/val/char-bitfield.c b/test/val/char-bitfield.c new file mode 100644 index 000000000..0d611f127 --- /dev/null +++ b/test/val/char-bitfield.c @@ -0,0 +1,280 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of char bit-fields; see https://github.com/cc65/cc65/issues/1047 +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct four_bits { + unsigned char x : 4; +} fb = {1}; + +static void test_four_bits (void) +{ + if (sizeof (struct four_bits) != 1) { + printf ("Got sizeof (struct four_bits) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fb.x != 1) { + printf ("Got fb.x = %u, expected 1.\n", fb.x); + failures++; + } + + fb.x = 3; + + if (fb.x != 3) { + printf ("Got fb.x = %u, expected 3.\n", fb.x); + failures++; + } +} + +static struct four_bits_signed { + signed char x : 4; +} fbs = {1}; + +static void test_four_bits_signed (void) +{ + if (sizeof (struct four_bits_signed) != 1) { + printf ("Got sizeof (struct four_bits_signed) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fbs.x != 1) { + printf ("Got fbs.x = %d, expected 1.\n", fbs.x); + failures++; + } + + fbs.x = 3; + + if (fbs.x != 3) { + printf ("Got fbs.x = %d, expected 3.\n", fbs.x); + failures++; + } +} + +static struct four_bits_plain { + char x : 4; +} fbp = {1}; + +static void test_four_bits_plain (void) +{ + if (sizeof (struct four_bits_plain) != 1) { + printf ("Got sizeof (struct four_bits_plain) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fbp.x != 1) { + printf ("Got fbp.x = %d, expected 1.\n", fbp.x); + failures++; + } + + fbp.x = 3; + + if (fbp.x != 3) { + printf ("Got fbp.x = %d, expected 3.\n", fbp.x); + failures++; + } +} + +/* + Logic is somewhat diferent for bit-fields that end a struct vs + having additional fields. +*/ + +static struct four_bits_with_char { + unsigned char x : 4; + unsigned char y; +} fbi = {1, 2}; + +static void test_four_bits_with_char (void) +{ + if (sizeof (struct four_bits_with_char) != 2) { + printf ("Got sizeof (struct four_bits_with_char) = %zu, expected 2.\n", + sizeof (struct four_bits_with_char)); + failures++; + } + + if (fbi.x != 1) { + printf ("Got fbi.x = %u, expected 1.\n", fbi.x); + failures++; + } + + if (fbi.y != 2) { + printf ("Got fbi.y = %u, expected 2.\n", fbi.y); + failures++; + } + + fbi.x = 3; + fbi.y = 17; + + if (fbi.x != 3) { + printf ("Got fbi.x = %u, expected 3.\n", fbi.x); + failures++; + } + + if (fbi.y != 17) { + printf ("Got fbi.y = %u, expected 17.\n", fbi.y); + failures++; + } +} + +static struct two_chars { + unsigned char x : 4; + unsigned char y : 4; +} o = {11, 7}; + +/* Tests that bit-fields can share allocation units. */ +static void test_two_chars (void) +{ + if (sizeof (struct two_chars) != 1) { + printf ("Got sizeof (struct two_chars) = %zu, expected 1.\n", + sizeof (struct two_chars)); + failures++; + } + + if (o.x != 11) { + printf ("Got o.x = %u, expected 11.\n", o.x); + failures++; + } + + if (o.y != 7) { + printf ("Got o.y = %u, expected 7.\n", o.y); + failures++; + } + + o.x = 3; + o.y = 4; + + if (o.x != 3) { + printf ("Got o.x = %u, expected 3.\n", o.x); + failures++; + } + + if (o.y != 4) { + printf ("Got o.y = %u, expected 4.\n", o.y); + failures++; + } +} + +static struct full_width { + unsigned char x : 8; +} fw = {255}; + +static void test_full_width (void) +{ + if (sizeof (struct full_width) != 1) { + printf ("Got sizeof (struct full_width) = %zu, expected 1.\n", + sizeof (struct full_width)); + failures++; + } + + if (fw.x != 255) { + printf ("Got fw.x = %u, expected 255.\n", fw.x); + failures++; + } + + fw.x = 42; + + if (fw.x != 42) { + printf ("Got fw.x = %u, expected 42.\n", fw.x); + failures++; + } +} + +static struct aligned_end { + unsigned char : 2; + unsigned char x : 6; + unsigned char : 3; + unsigned char y : 5; +} ae = {63, 17}; + +static void test_aligned_end (void) +{ + if (sizeof (struct aligned_end) != 2) { + printf ("Got sizeof (struct aligned_end) = %zu, expected 2.\n", + sizeof (struct aligned_end)); + failures++; + } + + if (ae.x != 63) { + printf ("Got ae.x = %u, expected 63.\n", ae.x); + failures++; + } + + if (ae.y != 17) { + printf ("Got ae.y = %u, expected 17.\n", ae.y); + failures++; + } + + ae.x = 42; + ae.y = 15; + + if (ae.x != 42) { + printf ("Got ae.x = %u, expected 42.\n", ae.x); + failures++; + } + + if (ae.y != 15) { + printf ("Got ae.y = %u, expected 15.\n", ae.y); + failures++; + } +} + +struct { signed char x : 1; } sc = {-1}; +struct { unsigned char x : 1; } uc = {1}; +struct { char x : 1; } pc = {1}; + +static void test_signedness (void) +{ + if (sc.x != -1) { + printf ("Got sc.x = %d, expected -1.\n", sc.x); + failures++; + } + + if (uc.x != 1) { + printf ("Got uc.x = %u, expected 1.\n", uc.x); + failures++; + } + + if (pc.x != 1) { + printf ("Got pc.x = %u, expected 1.\n", pc.x); + failures++; + } +} + +int main (void) +{ + test_four_bits (); + test_four_bits_with_char (); + test_two_chars (); + test_full_width (); + test_aligned_end (); + test_signedness (); + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/char-promote.c b/test/val/char-promote.c new file mode 100644 index 000000000..0d2dad04e --- /dev/null +++ b/test/val/char-promote.c @@ -0,0 +1,176 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of promotions of character types. +*/ + +#include <stdio.h> + +typedef unsigned char u8; + +static unsigned char failures = 0; + +void test_sub (void) +{ + const u8 one = 1, two = 2; + + /* For any unsigned type other than unsigned char, (T) 1 - (T) 2 > 0. */ + if (1U - 2U < 0) { + fprintf (stderr, "Expected 1U - 2U > 0\n"); + failures++; + } + + /* The unsigned chars get promoted to int, so this is negative. */ + if (one - two > 0) { + fprintf (stderr, "Expected one - two < 0\n"); + failures++; + } + + /* Test the constant expression code paths. */ + if ((u8) 1 - (u8) 2 > 0) { + fprintf (stderr, "Expected (u8) 1 - (u8) 2 < 0\n"); + failures++; + } +} + +void test_mul (void) +{ + const u8 two_fifty_five = 255; + const u8 sixteen = 16; + int x; + + if (255U * 255U != 65025U) { + fprintf (stderr, "Expected 255U * 255U == 65025U\n"); + failures++; + } +#if 0 + /* Disabled pending fix of #1310. */ + if (255 * 255 != -511) { + fprintf (stderr, "Expected 255 * 255 == -511, got: %d\n", 255 * 255); + failures++; + } +#endif + + /* The unsigned chars get promoted to int, so this is -511. + ** We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses umul, not mul. + */ + if (two_fifty_five * two_fifty_five != -511) { + fprintf (stderr, "Expected two_fifty_five * two_fifty_five == -511\n"); + failures++; + } +#if 0 + /* Disabled pending fix of #1310. */ + if ((u8) 255 * (u8) 255 != -511) { + fprintf (stderr, "Expected (u8) 255 * (u8) 255 == -511, got: %d\n", + (u8) 255 * (u8) 255); + failures++; + } +#endif + + /* This should compile to a shift. */ + x = sixteen * 4; + if (x != 64) { + fprintf (stderr, "Expected sixteen * 4 == 64, got: %d\n", x); + failures++; + } +} + +void test_div (void) +{ + const u8 seventeen = 17; + const u8 three = 3; + int x; + + /* We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses udiv, not div. + */ + if (seventeen / three != 5) { + fprintf (stderr, "Expected seventeen / three == 5, got: %d\n", seventeen / three); + failures++; + } + if ((u8) 17 / (u8) 3 != 5) { + fprintf (stderr, "Expected (u8) 17 / (u8) 3 == 5, got: %d\n", (u8) 17 / (u8) 3); + failures++; + } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = seventeen / 4; + if (x != 4) { + fprintf (stderr, "Expected seventeen / 4 == 4, got: %d\n", x); + failures++; + } +} + +void test_mod (void) +{ + const u8 seventeen = 17; + /* Ideally, this would compile to a bitwise and, but that does not happen currently. */ + int x = seventeen % 4; + if (x != 1) { + fprintf (stderr, "Expected seventeen %% 4 == 1, got: %d\n", x); + failures++; + } +} + +void test_shr (void) +{ + const unsigned int forty_two = 42; + const unsigned int two = 2; + int x; + + /* We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses shr, not asr. + */ + if (forty_two >> two != 10) { + fprintf (stderr, "Expected forty_two >> two == 10, got: %d\n", forty_two >> two); + failures++; + } + if ((u8) 42 >> (u8) 2 != 10) { + fprintf (stderr, "Expected (u8) 42 >> (u8) 2 == 10, got: %d\n", (u8) 42 >> (u8) 3); + failures++; + } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = forty_two >> 2; + if (x != 10) { + fprintf (stderr, "Expected forty_two >> 2 == 10, got: %d\n", x); + failures++; + } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = 42 >> two; + if (x != 10) { + fprintf (stderr, "Expected 42 >> two == 10, got: %d\n", x); + failures++; + } +} + +int main (void) +{ + test_sub (); + test_mul (); + test_div (); + test_mod (); + test_shr (); + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/compare4.c b/test/val/compare4.c index 47948c3a3..8e3baad5d 100644 --- a/test/val/compare4.c +++ b/test/val/compare4.c @@ -6,11 +6,14 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> /* compare4.c */ +/*#define SUPPORT_BIT_TYPES */ + /*#define COMPARE_OUT_OF_RANGE 1*/ unsigned char success = 0; @@ -20,22 +23,9 @@ unsigned char dummy = 0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -short int0 = 0; -short int1 = 0; -#else -int int0 = 0; -int int1 = 0; - -#endif - -#else -int int0 = 0; -int int1 = 0; - -#endif +int16_t int0 = 0; +int16_t int1 = 0; signed char char0 = 0; signed char char1 = 0; diff --git a/test/val/constexpr.c b/test/val/constexpr.c new file mode 100644 index 000000000..4338717f4 --- /dev/null +++ b/test/val/constexpr.c @@ -0,0 +1,111 @@ + +/* + +This tests a couple of expressions which yield constant results. While we cant +really check if the compiler figures out they are constant, we can still check +if they are being compiled/evaluated correctly. + +related: + +pr #1424 - More compile-time constant expressions regarding object addresses +issue #1196 - Constant expressions in general + +*/ + +#include <stdio.h> + +int fails = 0; + +#define TESTEXPR(expr) \ + if (!(expr)) { \ + printf("fail line %d\n", __LINE__); \ + fails++; \ + } + +#define TESTEXPRFALSE(expr) \ + if (expr) { \ + printf("fail line %d\n", __LINE__); \ + fails++; \ + } + +int a; +volatile int b; +const int c = 1; +#define d 1 +enum { e = 1 }; +int f() { return 1; } + +/* we cant really test these at runtime (because the result is constant, but not + * compile-time known), so compile only */ +void test0(void) +{ + TESTEXPR(a); /* Pure: Yes; Static: No; Immutable: No; Compile-Time-Known: No */ + TESTEXPR(b); /* Pure: No?; Static: No; Immutable: No; Compile-Time-Known: No */ + TESTEXPR(&a > &b); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */ +} + +void test1(void) +{ + TESTEXPR(1); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPR(c); /* Pure: Yes; Static: ???; Immutable: ???; Compile-Time-Known: ??? */ + TESTEXPR(d); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPR(e); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPR(c == c); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPRFALSE(c != c); + TESTEXPR(f() == f()); /* Pure: Yes; Static: Yes?; Immutable: Yes; Compile-Time-Known: Yes? */ + TESTEXPRFALSE(f() != f()); + TESTEXPR(&a == &a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPRFALSE(&a != &a); + TESTEXPR(&a != 0); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes* */ + TESTEXPRFALSE(&a == 0); +/* in a real program we cant rely on these, but in this test we can */ + TESTEXPR(&a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */ + TESTEXPR((int)&a != 0); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */ + TESTEXPRFALSE((int)&a == 0); + TESTEXPR(&a != &b); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: ??** */ + TESTEXPRFALSE(&a == &b); +/* this may fail in a real world program, but here we can rely on it anyway */ + TESTEXPR(b == b); /* Pure: No?; Static: No; Immutable: No; Compile-Time-Known: No */ + TESTEXPRFALSE(b != b); +/* NOT detected by the compiler as constant */ + TESTEXPR(a == a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPRFALSE(a != a); + TESTEXPR(f()); /* Pure: Yes; Static: Yes?; Immutable: Yes; Compile-Time-Known: Yes? */ +} + +/* Taken from #1196 reply */ +struct S { + int a; + int b; +} s[2]; + +void test2(void) +{ + TESTEXPR((void*)&s == (void*)&s[0]); + TESTEXPRFALSE((void*)&s != (void*)&s[0]); + TESTEXPR(&s[0] < &s[1]); + TESTEXPR(&s[0].b > &s[0].a); + TESTEXPR(&s[0].b < &s[1].a); +} + +/* we abuse the close function here, close(-1) will return -1 */ +extern int close(int fd); + +void test3(void) +{ + TESTEXPR(close(-1)); /* Pure: No; Static: No; Immutable: No; Compile-Time-Known: No */ + TESTEXPR((close(-1), 1)) /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */ +/* Error: Scalar expression expected */ +// TESTEXPR((void)close(-1)); /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */ + TESTEXPR(sizeof(close(-1))); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */ + /* NOT detected by the compiler as constant */ + TESTEXPRFALSE(close(-1) * 0); /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */ +} + +int main(void) +{ + test1(); + test2(); + test3(); + return fails; +} diff --git a/test/val/div-char-char.c b/test/val/div-char-char.c new file mode 100644 index 000000000..aa86bcd19 --- /dev/null +++ b/test/val/div-char-char.c @@ -0,0 +1,86 @@ +#include <stdint.h> +#include <stdio.h> +#include "div-common.h" + +int res = 0; + +/* we check A_8 and B_8 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_8(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 32, 64, 0) +TEST(12, -32, 64, 0) +TEST(13, 32, -64, 0) +TEST(14, -32, -64, 0) +TEST(15, 64, 32, 2) +TEST(16, -64, 32, -2) +TEST(17, 64, -32, -2) +TEST(18, -64, -32, 2) + +/* +128 can't be tested for 8-bit signed char */ +TEST(101, 127, -128, 0) +TEST(102, -127, -128, 0) +TEST(103, -128, -128, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + + return res; +} diff --git a/test/val/div-common.h b/test/val/div-common.h new file mode 100644 index 000000000..b67de54dc --- /dev/null +++ b/test/val/div-common.h @@ -0,0 +1,52 @@ +#ifndef DIV_COMMON_H +#define DIV_COMMON_H + +/* check if the result is correct */ +#define TEST_A_T(type, _n,_a,_b,_r) \ + int test##_n##a(void) { \ + typedef type int_t; \ + int_t a = ((int_t)_a), b = ((int_t)_b); \ + if (((a/((int_t)_b)) == ((int_t)_r)) && ((a/b) == ((int_t)_r))) { \ + return 0; \ + } else { \ + printf("%d\tincorrect: a/%ld = %ld, a/b = %ld\n\texpected: %ld/%ld = %ld\n", \ + (_n), (long)((int_t)_b), (long)(a/((int_t)_b)), (long)(a/b), (long)((int_t)_a), (long)((int_t)_b), (long)((int_t)_r)); \ + return 1; \ + } \ + } + +/* check if the results are equal */ +#define TEST_B_T(type, _n,_a,_b,_r) \ + int test##_n##b(void) { \ + typedef type int_t; \ + int_t a = ((int_t)_a), b = ((int_t)_b); \ + if (((a/((int_t)_b)) == (a/b))) { \ + return 0; \ + } else { \ + printf("%d\tnot equal: %ld != %ld, a = %ld, b = %ld\n\texpected: %ld/%ld = %ld\n", \ + (_n), (long)(a/((int_t)_b)), (long)(a/b), (long)(a), (long)(b), (long)((int_t)_a), (long)((int_t)_b), (long)((int_t)_r)); \ + return 1; \ + } \ + } + +#define TEST_A_8(_n,_a,_b,_r) TEST_A_T(int8_t, _n,_a,_b,_r) +#define TEST_B_8(_n,_a,_b,_r) TEST_B_T(int8_t, _n,_a,_b,_r) +#define TEST_A_16(_n,_a,_b,_r) TEST_A_T(int16_t, _n,_a,_b,_r) +#define TEST_B_16(_n,_a,_b,_r) TEST_B_T(int16_t, _n,_a,_b,_r) +#define TEST_A_32(_n,_a,_b,_r) TEST_A_T(int32_t, _n,_a,_b,_r) +#define TEST_B_32(_n,_a,_b,_r) TEST_B_T(int32_t, _n,_a,_b,_r) + +/* A and B */ +#define TEST_AB_8(_n,_a,_b,_r) \ + TEST_A_8(_n,_a,_b,_r) \ + TEST_B_8(_n,_a,_b,_r) + +#define TEST_AB_16(_n,_a,_b,_r) \ + TEST_A_16(_n,_a,_b,_r) \ + TEST_B_16(_n,_a,_b,_r) + +#define TEST_AB_32(_n,_a,_b,_r) \ + TEST_A_32(_n,_a,_b,_r) \ + TEST_B_32(_n,_a,_b,_r) + +#endif diff --git a/test/val/div-int-int.c b/test/val/div-int-int.c new file mode 100644 index 000000000..efcb12a48 --- /dev/null +++ b/test/val/div-int-int.c @@ -0,0 +1,114 @@ +#include <stdint.h> +#include <stdio.h> +#include "div-common.h" + +int res = 0; + +/* we check A_16 and B_16 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_16(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 2048, 512, 4) +TEST(12, -2048, 512, -4) +TEST(13, 2048, -512, -4) +TEST(14, -2048, -512, 4) +TEST(15, 512, 2048, 0) +TEST(16, -512, 2048, 0) +TEST(17, 512, -2048, 0) +TEST(18, -512, -2048, 0) + +/* values that are around min/max of the type(s) */ +TEST(101, 127, 128, 0) +TEST(102, -127, 128, 0) +TEST(103, 127, -128, 0) +TEST(104, -127, -128, 0) +TEST(105, 128, 128, 1) +TEST(106, 128, -128, -1) +TEST(107, -128, 128, -1) +TEST(108, -128, -128, 1) + +/* +32768 can't be tested for 16-bit signed short */ +TEST(201, 32767L, -32768L, 0) +TEST(202, 32767L, -32768L, 0) +TEST(203, -32768L, -32768L, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + DO_TEST_A(104); + DO_TEST_A(105); + DO_TEST_A(106); + DO_TEST_A(107); + DO_TEST_A(108); + + DO_TEST_A(201); + DO_TEST_A(202); + DO_TEST_A(203); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + DO_TEST_B(104); + DO_TEST_B(105); + DO_TEST_B(106); + DO_TEST_B(107); + DO_TEST_B(108); + + DO_TEST_B(201); + DO_TEST_B(202); + DO_TEST_B(203); + + return res; +} diff --git a/test/val/div-long-long.c b/test/val/div-long-long.c new file mode 100644 index 000000000..d2553e3c3 --- /dev/null +++ b/test/val/div-long-long.c @@ -0,0 +1,141 @@ +#include <stdint.h> +#include <stdio.h> +#include "div-common.h" + +int res = 0; + +/* we check A_32 and B_32 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_32(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 2048, 512, 4) +TEST(12, -2048, 512, -4) +TEST(13, 2048, -512, -4) +TEST(14, -2048, -512, 4) +TEST(15, 512, 2048, 0) +TEST(16, -512, 2048, 0) +TEST(17, 512, -2048, 0) +TEST(18, -512, -2048, 0) + +/* values that are around min/max of the type(s) */ +TEST(101, 127, 128, 0) +TEST(102, -127, 128, 0) +TEST(103, 127, -128, 0) +TEST(104, -127, -128, 0) +TEST(105, 128, 128, 1) +TEST(106, 128, -128, -1) +TEST(107, -128, 128, -1) +TEST(108, -128, -128, 1) + +TEST(201, 32767L, 32768L, 0) +TEST(202, -32767L, 32768L, 0) +TEST(203, 32767L, -32768L, 0) +TEST(204, 32767L, -32768L, 0) +TEST(205, 32768L, 32768L, 1) +TEST(206, 32768L, -32768L, -1) +TEST(207, -32768L, 32768L, -1) +TEST(208, -32768L, -32768L, 1) + +/* +2147483648 can't be tested for 32-bit signed long */ +TEST(401, 2147483647UL, -2147483648UL, 0) +TEST(402, -2147483647UL, -2147483648UL, 0) +TEST(403, -2147483648UL, -2147483648UL, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + DO_TEST_A(104); + DO_TEST_A(105); + DO_TEST_A(106); + DO_TEST_A(107); + DO_TEST_A(108); + + DO_TEST_A(201); + DO_TEST_A(202); + DO_TEST_A(203); + DO_TEST_A(204); + DO_TEST_A(205); + DO_TEST_A(206); + DO_TEST_A(207); + DO_TEST_A(208); + + DO_TEST_A(401); + DO_TEST_A(402); + DO_TEST_A(403); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + DO_TEST_B(104); + DO_TEST_B(105); + DO_TEST_B(106); + DO_TEST_B(107); + DO_TEST_B(108); + + DO_TEST_B(201); + DO_TEST_B(202); + DO_TEST_B(203); + DO_TEST_B(204); + DO_TEST_B(205); + DO_TEST_B(206); + DO_TEST_B(207); + DO_TEST_B(208); + + DO_TEST_B(401); + DO_TEST_B(402); + DO_TEST_B(403); + + return res; +} diff --git a/test/val/enum-bitfield.c b/test/val/enum-bitfield.c new file mode 100644 index 000000000..5669978c9 --- /dev/null +++ b/test/val/enum-bitfield.c @@ -0,0 +1,284 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests of enum bit-fields; see https://github.com/cc65/cc65/issues/1244 +*/ + +#include <stdio.h> +#include <limits.h> + +static unsigned char failures = 0; + +/* Enum with underlying type unsigned int. */ +enum e10u { + E10U_200 = 200, + E10U_1000 = 1000, +}; + +static struct enum_bitfield_uint { + enum e10u x : 1; + enum e10u y : 8; + enum e10u z : CHAR_BIT * sizeof (enum e10u); +} e10ubf = {0, E10U_200, E10U_1000}; + +static void test_enum_bitfield_uint(void) +{ + if (sizeof (struct enum_bitfield_uint) != 4) { + printf ("Got sizeof(struct enum_bitfield_uint) = %zu, expected 4.\n", + sizeof(struct enum_bitfield_uint)); + failures++; + } + + if (e10ubf.x != 0) { + printf ("Got e10ubf.x = %u, expected 0.\n", e10ubf.x); + failures++; + } + if (e10ubf.y != 200) { + printf ("Got e10ubf.y = %u, expected 200.\n", e10ubf.y); + failures++; + } + if (e10ubf.z != 1000) { + printf ("Got e10ubf.z = %u, expected 1000.\n", e10ubf.z); + failures++; + } + + e10ubf.x = 1; + e10ubf.y = 17; + e10ubf.z = 1023; + + if (e10ubf.x != 1) { + printf ("Got e10ubf.x = %u, expected 1.\n", e10ubf.x); + failures++; + } + + /* Check signedness, should be signed. */ + { + long v = e10ubf.x - 2; + if (v >= 0) { + printf ("Got non-negative v (= e10ubf.x - 2) = %ld, expected negative.\n", v); + failures++; + } + } + + if (e10ubf.y != 17) { + printf ("Got e10ubf.y = %u, expected 17.\n", e10ubf.y); + failures++; + } + if (e10ubf.z != 1023) { + printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z); + failures++; + } + + /* Check signedness, should be unsigned. */ + { + long v = e10ubf.z - 1024; + if (v < 0) { + printf ("Got negative v (= e10ubf.z - 1024) = %ld, expected positive.\n", v); + failures++; + } + } +} + +/* Enum with underlying type signed int. */ +enum e11i { + E11I_M1 = -1, + E11I_100 = 100, + E11I_1000 = 1000, +}; + +static struct enum_bitfield_int { + enum e11i x : 2; + enum e11i y : 8; + enum e11i z : CHAR_BIT * sizeof (enum e11i); +} e11ibf = {E11I_M1, E11I_100, E11I_1000}; + +static void test_enum_bitfield_int(void) +{ + if (sizeof (struct enum_bitfield_int) != 4) { + printf ("Got sizeof(struct enum_bitfield_int) = %zu, expected 4.\n", + sizeof(struct enum_bitfield_int)); + failures++; + } + + if (e11ibf.x != -1) { + printf ("Got e11ibf.x = %d, expected -1.\n", e11ibf.x); + failures++; + } + if (e11ibf.y != 100) { + printf ("Got e11ibf.y = %d, expected 100.\n", e11ibf.y); + failures++; + } + if (e11ibf.z != 1000) { + printf ("Got e11ibf.z = %d, expected 1000.\n", e11ibf.z); + failures++; + } + + e11ibf.x = 1; + e11ibf.y = 17; + e11ibf.z = 1023; + + if (e11ibf.x != 1) { + printf ("Got e11ibf.x = %d, expected 1.\n", e11ibf.x); + failures++; + } + + /* Check signedness, should be signed. */ + { + long v = e11ibf.x - 2; + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.x - 2) = %ld, expected negative.\n", v); + failures++; + } + } + + if (e11ibf.y != 17) { + printf ("Got e11ibf.y = %d, expected 17.\n", e11ibf.y); + failures++; + } + if (e11ibf.z != 1023) { + printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z); + failures++; + } + + /* Check signedness, should be signed. */ + { + long v = e11ibf.z - 1024; + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.z - 1024) = %ld, expected negative.\n", v); + failures++; + } + } +} + +/* Enum with underlying type unsigned char. */ +enum e7uc { + E7UC_100 = 100, +}; + +static struct enum_bitfield_uchar { + enum e7uc x : 1; + enum e7uc y : 4; + enum e7uc z : CHAR_BIT; +} e7ucbf = {0, 10, E7UC_100}; + +static void test_enum_bitfield_uchar(void) +{ + if (sizeof (struct enum_bitfield_uchar) != 2) { + printf ("Got sizeof(struct enum_bitfield_uchar) = %zu, expected 2.\n", + sizeof(struct enum_bitfield_uchar)); + failures++; + } + + if (e7ucbf.x != 0) { + printf ("Got e7ucbf.x = %u, expected 0.\n", e7ucbf.x); + failures++; + } + if (e7ucbf.y != 10) { + printf ("Got e7ucbf.y = %u, expected 10.\n", e7ucbf.y); + failures++; + } + if (e7ucbf.z != 100) { + printf ("Got e7ucbf.z = %u, expected 100.\n", e7ucbf.z); + failures++; + } + + e7ucbf.x = -1; /* Will store 1. */ + e7ucbf.y = -1; /* Will store 15. */ + e7ucbf.z = 127; + + /* Both signed char and unsigned char are converted to int in arithmetic expressions, + ** so we write this test differently to enum_bitfield_int. + */ + if (e7ucbf.x != 1) { + printf ("Got e7ucbf.x = %u, expected 1.\n", e7ucbf.x); + failures++; + } + + if (e7ucbf.y != 15) { + printf ("Got e7ucbf.y = %u, expected 15.\n", e7ucbf.y); + failures++; + } + if (e7ucbf.z != 127) { + printf ("Got e7ucbf.z = %u, expected 127.\n", e7ucbf.z); + failures++; + } +} + +/* Enum with underlying type signed char. */ +enum e8sc { + E8SC_M1 = -1, + E8SC_100 = 100, +}; + +static struct enum_bitfield_char { + enum e8sc x : 1; + enum e8sc y : 4; + enum e8sc z : CHAR_BIT; +} e8scbf = {0, 5, E8SC_100}; + +static void test_enum_bitfield_char(void) +{ + if (sizeof (struct enum_bitfield_char) != 2) { + printf ("Got sizeof(struct enum_bitfield_char) = %zu, expected 2.\n", + sizeof(struct enum_bitfield_char)); + failures++; + } + + if (e8scbf.x != 0) { + printf ("Got e8scbf.x = %d, expected 0.\n", e8scbf.x); + failures++; + } + if (e8scbf.y != 5) { + printf ("Got e8scbf.y = %d, expected 10.\n", e8scbf.y); + failures++; + } + if (e8scbf.z != 100) { + printf ("Got e8scbf.z = %d, expected 100.\n", e8scbf.z); + failures++; + } + + e8scbf.x = -1; + e8scbf.y = -3; + e8scbf.z = 127; + + if (e8scbf.x != -1) { + printf ("Got e8scbf.x = %d, expected -1.\n", e8scbf.x); + failures++; + } + if (e8scbf.y != -3) { + printf ("Got e8scbf.y = %d, expected -3.\n", e8scbf.y); + failures++; + } + if (e8scbf.z != 127) { + printf ("Got e8scbf.z = %d, expected 127.\n", e8scbf.z); + failures++; + } +} + +int main(void) +{ + test_enum_bitfield_uint(); + test_enum_bitfield_int(); + test_enum_bitfield_uchar(); + test_enum_bitfield_char(); + printf("failures: %u\n", failures); + return failures; +} diff --git a/test/misc/fields.c b/test/val/fields.c similarity index 100% rename from test/misc/fields.c rename to test/val/fields.c diff --git a/test/val/opsize.c b/test/val/opsize.c new file mode 100644 index 000000000..20c7f0511 --- /dev/null +++ b/test/val/opsize.c @@ -0,0 +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; +} diff --git a/test/val/plain-int-bitfield.c b/test/val/plain-int-bitfield.c new file mode 100644 index 000000000..4d158eca9 --- /dev/null +++ b/test/val/plain-int-bitfield.c @@ -0,0 +1,63 @@ +/* + Copyright 2020 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. +*/ + +/* + Tests that plain int bit-fields are unsigned. +*/ + +#include <stdio.h> + +static unsigned char failures = 0; + +static struct plain_ints { + int x : 4; + int y : 10; +} pi = {15, 700}; + +static void test_plain_int_bitfields (void) +{ + if (pi.x != 15) { + printf ("Got pi.x = %u, expected 15.\n", pi.x); + failures++; + } + if (pi.y != 700) { + printf ("Got pi.y = %u, expected 700.\n", pi.y); + failures++; + } + + pi.x = 3; + pi.y = 1023; + + if (pi.x != 3) { + printf ("Got pi.x = %u, expected 3.\n", pi.x); + failures++; + } + if (pi.y != 1023) { + printf ("Got pi.y = %u, expected 1023.\n", pi.y); + failures++; + } +} + +int main (void) +{ + test_plain_int_bitfields (); + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/pptest1.c b/test/val/pptest1.c new file mode 100644 index 000000000..52b381b0b --- /dev/null +++ b/test/val/pptest1.c @@ -0,0 +1,27 @@ + +/* preprocessor test #1 */ + +#define hash_hash # ## # +#define mkstr(a) # a +#define in_between(a) mkstr(a) +#define join(c, d) in_between(c hash_hash d) + +#define x "first" +#define y "second" + +char p[] = join(x, y); // Comment + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + printf("expected: %s\n", "\"first\" ## \"second\""); + printf("p: %s\n", p); + if (!strcmp(p, "\"first\" ## \"second\"")) { + return EXIT_SUCCESS; + } + printf("all fine\n"); + return EXIT_FAILURE; +} diff --git a/test/val/pptest3.c b/test/val/pptest3.c new file mode 100644 index 000000000..b48677703 --- /dev/null +++ b/test/val/pptest3.c @@ -0,0 +1,44 @@ + +/* preprocessor test #3 */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +char *x1 = "123"; +char *x2 = "456"; +FILE *s; +char *str = "789"; + +#define str(s) # s +#define xstr(s) str(s) +#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ + x ## s, x ## t) +#define INCFILE(n) vers ## n // Comment +#define glue(a,b) a ## b +#define xglue(a,b) glue(a,b) +#define HIGHLOW "hello" +#define LOW LOW ", world" + +int main(void) { + s = stdout; + + debug (1, 2); + + fputs (str (strncmp("abc\0d", "abc", '\4') // Comment + == 0) str (: @\n), s); + + str = glue (HIGH, LOW); + printf("str: %s\n", str); + if (strcmp(str, "hello") != 0) { + return EXIT_FAILURE; + } + + str = xglue (HIGH, LOW); + printf("str: %s\n", str); + if (strcmp(str, "hello, world") != 0) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/test/val/pptest4.c b/test/val/pptest4.c new file mode 100644 index 000000000..827be7200 --- /dev/null +++ b/test/val/pptest4.c @@ -0,0 +1,24 @@ + +/* preprocessor test #4 */ + +#define t(x,y,z) x ## y ## z +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; + +int e[] = { 123, 45, 67, 89, 10, 11, 12, }; + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +unsigned char i; + +int main(void) +{ + for (i = 0; i < 7; ++i) { + printf("j: %d expect: %d\n", j[i], e[i]); + if (j[i] != e[i]) return EXIT_FAILURE; + } + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/test/val/pptest5.c b/test/val/pptest5.c new file mode 100644 index 000000000..82f642c8e --- /dev/null +++ b/test/val/pptest5.c @@ -0,0 +1,24 @@ + +/* preprocessor test #5 */ + +#define t(x,y,z) x ## y ## z +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; + +int e[] = { 123, 45, 67, 89, 10, 11, 12, }; + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +unsigned char i; + +int main(void) +{ + for (i = 0; i < 7; ++i) { + printf("j: %d expect: %d\n", j[i], e[i]); + if (j[i] != e[i]) return EXIT_FAILURE; + } + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/test/val/pr1102.c b/test/val/pr1102.c new file mode 100644 index 000000000..2a78e997b --- /dev/null +++ b/test/val/pr1102.c @@ -0,0 +1,77 @@ + +/* pr#1102 - Passing structs/unions <= 4 bytes to/from functions */ + +#include <stdlib.h> +#include <stdio.h> + +typedef struct test1_s { + char a; + int b; + char c; +} test1_t; + +test1_t test1(int a, int b, int c) +{ + test1_t res; + res.a = a; + res.b = b; + res.c = c; + return res; +} + +int test2(test1_t s) +{ + printf("a: %d b: %d c: %d\n", s.a, s.b, s.c); + if ((s.a != 13) || (s.b != 4711) || (s.c != 99)) { + return 0; + } + return 1; +} + +typedef struct test2_s { + char a; + char b; + char c; + char d; +} test2_t; + +test2_t test3(test1_t s) +{ + test2_t ret; + printf("a: %d b: %d c: %d\n", s.a, s.b, s.c); + ret.a = s.c; + ret.b = s.b & 0xff; + ret.c = s.b >> 8; + ret.d = s.a; + return ret; +} + +int main(void) { + test1_t t1; + test2_t t2; + + t1 = test1(12, 1842, 23); + printf("a: %d b: %d c: %d\n", t1.a, t1.b, t1.c); + if ((t1.a != 12) || (t1.b != 1842) || (t1.c != 23)) { + return EXIT_FAILURE; + } + + t1.a = 13; + t1.b = 4711; + t1.c = 99; + if (test2(t1) != 1) { + return EXIT_FAILURE; + } + + t1.a = 66; + t1.b = 0x7788; + t1.c = 22; + t2 = test3(t1); + printf("a: %d b: %d c: %d d: %d\n", t2.a, t2.b, t2.c, t2.d); + if ((t2.a != 22) || (t2.b != 0x88) || (t2.c != 0x77) || (t2.d != 66)) { + return EXIT_FAILURE; + } + + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/test/val/pr1110a.c b/test/val/pr1110a.c new file mode 100644 index 000000000..0e0d91f85 --- /dev/null +++ b/test/val/pr1110a.c @@ -0,0 +1,19 @@ + +/* pr #1110 - the part of the redefinition that should compile */ + +static const unsigned char array[3]; /* OK */ +static const unsigned char array[] = { 0, 1, 2 }; /* OK - complete definition*/ +static const unsigned char array[3]; /* OK */ +static const unsigned char array[]; /* OK */ + +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + printf("%u %u %u\n", array[0], array[1], array[2]); + if ((array[0] != 0) || (array[1] != 1) || (array[2] != 2)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/test/val/pr1423.c b/test/val/pr1423.c new file mode 100644 index 000000000..3135b64a3 --- /dev/null +++ b/test/val/pr1423.c @@ -0,0 +1,41 @@ +/* pr #1423 - Codegen fix for certain cases of object addresses as boolean */ + +unsigned char fails = 0; + +void test1(void) +{ + int a; + while (&a) { + return; + } + fails++; + return; +} + +void test2(void) +{ + int a; + do { + return; + } while (&a); + fails++; + return; +} + +void test3(void) +{ + int a; + for (;&a;) { + return; + } + fails++; + return; +} + +int main(void) +{ + test1(); + test2(); + test3(); + return fails; +} diff --git a/test/val/pr1425.c b/test/val/pr1425.c new file mode 100644 index 000000000..e3ae23ba3 --- /dev/null +++ b/test/val/pr1425.c @@ -0,0 +1,45 @@ + +/* pr #1425 - Ternary fixes */ + +unsigned char fails = 0; + +void test1(void) +{ + int x = 0; + x ? (void)x-- : (void)1; + if (x != 0) { + fails++; + } +} + +int test2(void) +{ + int x = 0, y = 0; + x ? (void)x--, (void)y++ : (void)1; + if (x != 0) { + fails++; + } + if (y != 0) { + fails++; + } +} + +void test3(void) +{ + int x = 0, y = 0; + x ? ((void)x--, (void)y++) : (void)1; + if (x != 0) { + fails++; + } + if (y != 0) { + fails++; + } +} + +int main(void) +{ + test1(); + test2(); + test3(); + return fails; +} diff --git a/test/val/pr1461.c b/test/val/pr1461.c new file mode 100644 index 000000000..dae6b2999 --- /dev/null +++ b/test/val/pr1461.c @@ -0,0 +1,53 @@ + +/* pr#1461 Fixed pointer subtraction in certain very rare cases */ + +#include <stdlib.h> +#include <stdio.h> + +static int err = 0; + +static int a[1], *p; +static unsigned int i1, i2; + +int test1(void) +{ + p = a - (int)a; + printf("a: %p - (int)a: 0x%x = p: %p\n", a, (int)a, p); + printf("i1: 0x%x - i2: 0x%x = p: %p\n", i1, i2, i1 - i2); + if ((int)p != (i1 - i2)) { + printf("-> failed\n"); + return 1; + } + return 0; +} + +int test2(void) +{ + p = p - (int)a; + printf("p: %p - (int)a: 0x%x = p: %p\n", p, (int)a, p); + printf("p: %p - i2: 0x%x = p: %p\n", p, i2, 0x1234 - i2); + if ((int)p != (0x1234 - i2)) { + printf("-> failed\n"); + return 1; + } + return 0; +} + +int main(void) +{ + a[0] = 0x4711; + i1 = (int)a; + i2 = i1 << 1; + + p = (int*)0x1234; + printf("p: %p &a[0]: %p a: %p (int)a: 0x%x i1: 0x%x i2: 0x%x\n", p, &a[0], a, (int)a, i1, i2); + + err += test1(); + + p = (int*)0x1234; + printf("p: %p &a[0]: %p a: %p (int)a: 0x%x i1: 0x%x i2: 0x%x\n", p, &a[0], a, (int)a, i1, i2); + + err += test2(); + + return err; +} diff --git a/test/val/ptrfunc.c b/test/val/ptrfunc.c index 4aa23b77b..78e53e254 100644 --- a/test/val/ptrfunc.c +++ b/test/val/ptrfunc.c @@ -6,7 +6,7 @@ #include <stdio.h> -#define NO_IMPLICIT_FUNCPTR_CONV +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -33,15 +33,9 @@ volatile unsigned char uchar1 = 0; volatile unsigned char uchar2 = 0; #endif -#ifdef NO_IMPLICIT_FUNCPTR_CONV void (*pfunc)(void); void (*p1func)(void); unsigned char (*pcfunc)(void); -#else -void (*pfunc)(); -void (*p1func)(); -unsigned char (*pcfunc)(); -#endif void done() { @@ -79,11 +73,7 @@ void docall1() } } -#ifdef NO_IMPLICIT_FUNCPTR_CONV -void docall2( void(*pf)(void) ) -#else void docall2( void(*pf)() ) -#endif { unsigned char i; for(i = 0; i < 2; i++) { diff --git a/test/val/rand.c b/test/val/rand.c new file mode 100644 index 000000000..ac2ab0d17 --- /dev/null +++ b/test/val/rand.c @@ -0,0 +1,110 @@ +/* This test verifies that the assembly implementation of rand() matches its + * theoretical high level equivalent. + * + * This does about 3000 tests from various starting srand() seeds. + * A more thorough test might visit the entire sequence with 2^32 tests, but + * that takes hours to simulate, and this should be a sufficient sampling. + * + * This will also fail if rand() is ever altered, which might be a warning to + * tread carefully. Some past discussion of RNG here: + * https://github.com/cc65/cc65/pull/951 + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +/* for faster execution */ +#pragma static-locals (on) + +/* values tested per seed */ +#define SUBTESTS 50 + +/* increments used between tested seeds */ +/* 653 is prime and divides 32768 by ~50 */ +#define TESTINC 653 + +static uint32_t seed; + +int ref_rand() +{ + uint16_t output; + /* seed follows the LCG sequence * 0x01010101 + 0xB3B3B3B3 */ + seed = seed * 0x01010101UL + 0xB3B3B3B3UL; + /* output uses the top two bytes (reversed) XOR with bottom two bytes */ + { + uint16_t s0 = (seed >> 0) & 0xFF; + uint16_t s1 = (seed >> 8) & 0xFF; + uint16_t s2 = (seed >> 16) & 0xFF; + uint16_t s3 = (seed >> 24) & 0xFF; + uint16_t o0 = s3 ^ s1; + uint16_t o1 = s2 ^ s0; + output = o0 | (o1 << 8); + } + return (int)(output & 0x7FFF); +} + +void ref_srand(int ax) +{ + uint32_t s = (unsigned int)ax; + seed = s | (s << 16); /* low 16 bits is convenient filler for high 16 bits */ + ref_rand(); /* one pre-call "shuffles" the first rand() result so it isn't too predictable */ +} + +int main(void) +{ + unsigned int i,j; + int a,b; + + /* test that startup state is equivalent to srand(1) */ + { + //srand(1); // implied + ref_srand(1); + for (j=0; j<SUBTESTS; ++j) + { + a = rand(); + b = ref_rand(); + if (a != b) + { + printf("failed startup seed at test %d. rand()=%d reference=%d\n",j,a,b); + return EXIT_FAILURE; + } + } + } + + /* test every power of 2 seed */ + for (i = 0; i < 16; ++i) + { + srand(1<<i); + ref_srand(1<<i); + for (j=0; j<SUBTESTS; ++j) + { + a = rand(); + b = ref_rand(); + if (a != b) + { + printf("failed seed %d at test %d. rand()=%d reference=%d\n",(1<<i),j,a,b); + return EXIT_FAILURE; + } + } + } + + /* test a sampling of seeds*/ + for (i = 0; i < 32768UL; i += TESTINC) + { + srand(i); + ref_srand(i); + for (j=0; j<SUBTESTS; ++j) + { + a = rand(); + b = ref_rand(); + if (a != b) + { + printf("failed seed %d at test %d. rand()=%d reference=%d\n",(1<<i),j,a,b); + return EXIT_FAILURE; + } + } + } + + return EXIT_SUCCESS; +} diff --git a/test/val/return-struct.c b/test/val/return-struct.c new file mode 100644 index 000000000..a114939a4 --- /dev/null +++ b/test/val/return-struct.c @@ -0,0 +1,62 @@ +/* bug #264 - cc65 fails to warn about a function returning struct */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +typedef uint16_t u16; +typedef uint8_t u8; + +typedef struct { + u16 quot; + u16 rem; +} udiv_t; + +udiv_t div3(u16 in) { + + udiv_t u; + u16 q = 0; + + while (in >= 300) { + in -= 300; + q += 100; + } + + while (in >= 30) { + in -= 30; + q += 10; + } + + while (in >= 3) { + in -= 3; + ++q; + } + + u.quot = q; + u.rem = in; + + return u; +} + +int res = 0; + +int main(void) { + + u16 i; + div_t d; + udiv_t u; + + for (i = 1024; i; i--) { + d = div(i, 3); + u = div3(i); + + if (d.quot != u.quot || d.rem != u.rem) { + printf("Mismatch at %u/3, div %u %u, div3 %u %u\n", i, + d.quot, d.rem, u.quot, u.rem); + res++; + } + } + + return res; +} + diff --git a/test/val/rotate3.c b/test/val/rotate3.c index 21b2dc370..8ac2581e9 100644 --- a/test/val/rotate3.c +++ b/test/val/rotate3.c @@ -6,6 +6,9 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; - -#endif unsigned char achar0 = 0; unsigned char achar1 = 0; unsigned char achar2 = 0; diff --git a/test/val/rotate4.c b/test/val/rotate4.c index 09b1ebf4c..4b50b0d1c 100644 --- a/test/val/rotate4.c +++ b/test/val/rotate4.c @@ -6,6 +6,9 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; - -#endif unsigned char uchar0 = 0; unsigned char uchar1 = 0; unsigned char uchar2 = 0; diff --git a/test/val/rotate5.c b/test/val/rotate5.c index 501e2e567..9de897321 100644 --- a/test/val/rotate5.c +++ b/test/val/rotate5.c @@ -6,6 +6,9 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; -#else -signed int aint0 = 0; -signed int aint1 = 0; +int16_t aint0 = 0; +int16_t aint1 = 0; -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif signed char achar0 = 0; signed char achar1 = 0; signed char achar2 = 0; diff --git a/test/val/rotate6.c b/test/val/rotate6.c index 9109e124e..93a770d99 100644 --- a/test/val/rotate6.c +++ b/test/val/rotate6.c @@ -6,6 +6,9 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; -#else -signed int aint0 = 0; -signed int aint1 = 0; +int16_t aint0 = 0; +int16_t aint1 = 0; -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif signed char achar0 = 0; signed char achar1 = 0; signed char achar2 = 0; diff --git a/test/val/rotate7.c b/test/val/rotate7.c index 2b30b86dd..4a044c16a 100644 --- a/test/val/rotate7.c +++ b/test/val/rotate7.c @@ -6,27 +6,14 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> unsigned char success=0; unsigned char failures=0; unsigned char dummy=0; -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif +int16_t aint0 = 0; +int16_t aint1 = 0; /* signed char achar0 = 0; diff --git a/testcode/lib/shift-test.c b/test/val/shift-test.c similarity index 100% rename from testcode/lib/shift-test.c rename to test/val/shift-test.c diff --git a/testcode/lib/signal-test.c b/test/val/signal-test.c similarity index 64% rename from testcode/lib/signal-test.c rename to test/val/signal-test.c index 4e34a281d..31dd8ed5c 100644 --- a/testcode/lib/signal-test.c +++ b/test/val/signal-test.c @@ -1,12 +1,16 @@ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> +int signalcounter = 0; + void __fastcall__ sighandler (int sig) { printf ("Got signal #%d\n", sig); + signalcounter++; } @@ -15,15 +19,18 @@ int main (void) { if (signal (SIGSEGV, sighandler) == SIG_ERR) { printf ("signal failure %d: %s\n", errno, strerror (errno)); - return 1; + return EXIT_FAILURE; } printf ("About to raise SIGSEGV...\n"); raise (SIGSEGV); printf ("Back from signal handler\n"); printf ("About to raise SIGILL...\n"); raise (SIGILL); - printf ("Back from signal handler\n"); - return 0; + printf ("Back from signal handler, signalcounter = %d\n", signalcounter); + if (signalcounter != 1) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/testcode/lib/snprintf-test.c b/test/val/snprintf-test.c similarity index 97% rename from testcode/lib/snprintf-test.c rename to test/val/snprintf-test.c index d3af47d78..e3a60d99b 100644 --- a/testcode/lib/snprintf-test.c +++ b/test/val/snprintf-test.c @@ -137,8 +137,9 @@ unsigned char main(void) } else { printf("There were no"); } - printf(" failures.\nTap a key. "); + printf(" failures.\nTap a key.\n"); +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc(); - +#endif return failures; } diff --git a/test/val/static-1.c b/test/val/static-1.c index ae2ba6289..6918e0033 100644 --- a/test/val/static-1.c +++ b/test/val/static-1.c @@ -13,6 +13,7 @@ static int n = 0; extern int n; /* should not give an error */ +static int n; /* should not give an error */ int main(void) { diff --git a/test/val/staticassert.c b/test/val/staticassert.c new file mode 100644 index 000000000..e43eeec8d --- /dev/null +++ b/test/val/staticassert.c @@ -0,0 +1,72 @@ +/* + Copyright 2020 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. +*/ + +/* +** Tests of passing _Static_asserts. +**/ + + + +#include <assert.h> + +_Static_assert (1, "1 should be true."); +_Static_assert (1); /* Support C2x syntax with no message. */ +_Static_assert (!0, "!0 should be true."); +_Static_assert (1 == 1, "1 == 1 should be true."); +_Static_assert (1 == 1L, "1 == 1L should be true."); +_Static_assert (1 != 0, "1 != 0 should be true."); +_Static_assert (sizeof (char) == 1, "sizeof (char) should be 1."); +_Static_assert (sizeof (int) == 2, "sizeof (int) should be 2."); + +/* Make sure we can also do structs. */ +struct sc { char a; }; +_Static_assert (sizeof (struct sc) == 1, "sizeof (struct sc) should be 1."); +struct si { int a; }; +_Static_assert (sizeof (struct si) == 2, "sizeof (struct si) should be 2."); + +/* Try enums. */ +enum { k = 1 }; +_Static_assert (k == 1, "k should be 1."); + +/* Just test the macro version once. */ +static_assert (1, "1 should be true."); +static_assert (1); + +/* _Static_assert can appear anywhere a declaration can. */ +void f (void) +{ + _Static_assert (1, "1 should still be true."); + if (1) { + _Static_assert (1, "1 should still be true."); + } +} + +/* _Static_assert can also appear in structs. */ +struct S { + int a; + _Static_assert (1, "1 should still be true."); + int b; +}; + + +int main (void) +{ + return 0; +} diff --git a/testcode/lib/strncmp-test.c b/test/val/strncmp-test.c similarity index 59% rename from testcode/lib/strncmp-test.c rename to test/val/strncmp-test.c index b15565036..7dbbb4b8c 100644 --- a/testcode/lib/strncmp-test.c +++ b/test/val/strncmp-test.c @@ -10,15 +10,19 @@ static const char S2[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0', 'B' }; - - +int fails = 0; int main (void) { char I; + int ret; for (I = 0; I < 20; ++I) { - printf ("%02d: %d\n", I, strncmp (S1, S2, I)); + ret = strncmp (S1, S2, I); + printf ("%02d: %d\n", I, ret); + if ((ret != 0) && (I < 7)) { + fails++; + } } - return 0; + printf("fails: %d\n", fails); + return fails; } - diff --git a/testcode/lib/strnicmp-test.c b/test/val/strnicmp-test.c similarity index 56% rename from testcode/lib/strnicmp-test.c rename to test/val/strnicmp-test.c index b2d942a97..6376a39bb 100644 --- a/testcode/lib/strnicmp-test.c +++ b/test/val/strnicmp-test.c @@ -3,70 +3,79 @@ #include <string.h> #include <conio.h> +int fails = 0; + static int do_test(const char *s1, const char *s2, size_t n) { printf("strnicmp(\"%s\", \"%s\", %d): ", s1, s2, (int)n); return strncasecmp(s1, s2, n); } +static void printresult(int ret) +{ + if (ret) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + +static void printresultgt(int ret) +{ + if (ret >= 0) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + +static void printresultlt(int ret) +{ + if (ret <= 0) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + int main(void) { int ret; ret = do_test("Wurzl", "wURZL", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzl", "wURZL", 6); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzl", "wURZL", 10); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzla", "wURZLB", 10); - if (ret >= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultgt(ret); ret = do_test("Wurzla", "wURZLb", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("BLI", "bla", 5); - if (ret <= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultlt(ret); ret = do_test("", "bla", 5); - if (ret >= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultgt(ret); ret = do_test("BLI", "", 5); - if (ret <= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultlt(ret); ret = do_test("", "", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); - + printresult(ret); + + printf("fails: %d\n", fails); + +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc(); - return 0; +#endif + return fails; } diff --git a/testcode/lib/strpbrk-test.c b/test/val/strpbrk-test.c similarity index 78% rename from testcode/lib/strpbrk-test.c rename to test/val/strpbrk-test.c index 25c2c2fbe..6a688732d 100644 --- a/testcode/lib/strpbrk-test.c +++ b/test/val/strpbrk-test.c @@ -3,24 +3,32 @@ static const char fox[] = "The quick brown fox jumped over the lazy dogs."; -void main (void) +int fails = 0; + +int main (void) { printf ("Testing strpbrk():\n"); if (strpbrk (fox, "qwerty") != &fox[2]) { printf ("\nThe first 'e' wasn't found.\n"); + fails++; } if (strpbrk (fox, "QWERTY") != &fox[0]) { printf ("The 'T' wasn't found.\n"); + fails++; } if (strpbrk (fox, "asdfg") != &fox[16]) { printf ("The 'f' wasn't found.\n"); + fails++; } if (strpbrk (fox, "nxv,zmb") != &fox[10]) { printf ("The 'b' wasn't found.\n"); + fails++; } if (strpbrk (fox, "!@#$%^&*()-+=[];:',/?<>.") != &fox[45]) { printf ("The '.' wasn't found.\n"); + fails++; } - printf ("\nFinished.\n"); + printf ("\nFinished. fails = %d\n", fails); + return fails; } diff --git a/testcode/lib/strtol-test.c b/test/val/strtol-test.c similarity index 100% rename from testcode/lib/strtol-test.c rename to test/val/strtol-test.c diff --git a/testcode/lib/strtoul-test.c b/test/val/strtoul-test.c similarity index 100% rename from testcode/lib/strtoul-test.c rename to test/val/strtoul-test.c diff --git a/test/val/sub1.c b/test/val/sub1.c index f1ae9394f..5dbba97df 100644 --- a/test/val/sub1.c +++ b/test/val/sub1.c @@ -6,6 +6,10 @@ #include <stdio.h> #include <limits.h> +#include <stdint.h> + +/* #define SUPPORT_BIT_TYPES */ +/* #define SUPPORT_BIT_ARITHMETIC */ unsigned char success=0; unsigned char failures=0; @@ -28,19 +32,9 @@ bit bit11 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -#endif +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -#endif unsigned char achar0 = 0; unsigned char achar1 = 0; unsigned char achar2 = 0; diff --git a/test/val/time-test.c b/test/val/time-test.c new file mode 100644 index 000000000..304238fa0 --- /dev/null +++ b/test/val/time-test.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> + +#define EXPECTSTR "3DD173D1 - Tue Nov 12 21:34:09 2002\n" +char result[0x100]; + +int fails = 0; + +int main (void) +{ + struct tm tm; + time_t t; + char buf[64]; + + + tm.tm_sec = 9; + tm.tm_min = 34; + tm.tm_hour = 21; + tm.tm_mday = 12; + tm.tm_mon = 10; /* 0..11, so this is november */ + tm.tm_year = 102; /* year - 1900, so this is 2002 */ + tm.tm_wday = 2; /* Tuesday */ + tm.tm_isdst = 0; + + /* Convert this broken down time into a time_t and back */ + t = mktime (&tm); + printf ("Test passes if the following lines are\n" + "all identical:\n"); + printf (EXPECTSTR); + + sprintf (result, "%08lX - %s", t, asctime (&tm)); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + sprintf (result, "%08lX - %s", t, asctime (gmtime (&t))); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + strftime (buf, sizeof (buf), "%c", &tm); + sprintf (result, "%08lX - %s\n", t, buf); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + strftime (buf, sizeof (buf), "%a %b %d %H:%M:%S %Y", &tm); + sprintf (result, "%08lX - %s\n", t, buf); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + printf("fails: %d\n", fails); + + return fails; +} diff --git a/test/val/uneval.c b/test/val/uneval.c new file mode 100644 index 000000000..50e00973a --- /dev/null +++ b/test/val/uneval.c @@ -0,0 +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; +} diff --git a/testcode/compiler/pptest1.c b/testcode/compiler/pptest1.c deleted file mode 100644 index e42135688..000000000 --- a/testcode/compiler/pptest1.c +++ /dev/null @@ -1,6 +0,0 @@ -#define hash_hash # ## # -#define mkstr(a) # a -#define in_between(a) mkstr(a) -#define join(c, d) in_between(c hash_hash d) - -char p[] = join(x, y); // Comment diff --git a/testcode/compiler/pptest2.c b/testcode/compiler/pptest2.c deleted file mode 100644 index e127d53fb..000000000 --- a/testcode/compiler/pptest2.c +++ /dev/null @@ -1,19 +0,0 @@ -#define x 3 -#define f(a) f(x * (a)) -#undef x -#define x 2 -#define g f -#define z z[0] -#define h g(~ -#define m(a) a(w) -#define w 0,1 -#define t(a) a -#define p() int -#define q(x) x -#define r(x,y) x ## y -#define str(x) # x - -f(y+1) + f(f(z)) % t(t(g) (0) + t)(1); -g(x+(3,4)-w) | h 5) & m(f)^m(m); -p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; -char c[2][6] = { str(hello), str() }; diff --git a/testcode/compiler/pptest3.c b/testcode/compiler/pptest3.c deleted file mode 100644 index 62aa7f705..000000000 --- a/testcode/compiler/pptest3.c +++ /dev/null @@ -1,16 +0,0 @@ -#define str(s) # s -#define xstr(s) str(s) -#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ - x ## s, x ## t) -#define INCFILE(n) vers ## n // Comment -#define glue(a,b) a ## b -#define xglue(a,b) glue(a,b) -#define HIGHLOW "hello" -#define LOW LOW ", world" - -debug (1, 2); -fputs (str (strncmp("abc\0d", "abc", '\4') // Comment - == 0) str (: @\n), s); -glue (HIGH, LOW); -xglue (HIGH, LOW); - diff --git a/testcode/compiler/pptest4.c b/testcode/compiler/pptest4.c deleted file mode 100644 index b8540b5c5..000000000 --- a/testcode/compiler/pptest4.c +++ /dev/null @@ -1,3 +0,0 @@ -#define t(x,y,z) x ## y ## z -int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), - t(10,,), t(,11,), t(,,12), t(,,) }; diff --git a/testcode/compiler/pptest5.c b/testcode/compiler/pptest5.c deleted file mode 100644 index 1f0bd4328..000000000 --- a/testcode/compiler/pptest5.c +++ /dev/null @@ -1,3 +0,0 @@ -#define t(x,y,z) x ## y ## z -int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), - t(10,,), t(,11,), t(,,12), t(,,) }; diff --git a/testcode/disasm/bank0.dai b/testcode/disasm/bank0.dai deleted file mode 100644 index 2d865e77c..000000000 --- a/testcode/disasm/bank0.dai +++ /dev/null @@ -1,33 +0,0 @@ -# 1 "bank0.da" -# 1 "<built-in>" -# 1 "<command-line>" -# 1 "bank0.da" - - - - -global { - inputoffs $00010; - inputsize $4000; - startaddr $8000; - cpu "6502"; -}; - -# 1 "fixed.da" 1 -# 18 "fixed.da" -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; -# 13 "bank0.da" 2 - -label { addr $8000; name "Bank0ProcA"; }; -label { addr $8123; name "Bank0ProcB"; }; -range { start $A000; end $BFFF; name "Bank0Data"; type ByteTable; }; diff --git a/testcode/disasm/bank1.dai b/testcode/disasm/bank1.dai deleted file mode 100644 index 2b5b68532..000000000 --- a/testcode/disasm/bank1.dai +++ /dev/null @@ -1,33 +0,0 @@ -# 1 "bank1.da" -# 1 "<built-in>" -# 1 "<command-line>" -# 1 "bank1.da" - - - - -global { - inputoffs $04010; - inputsize $4000; - startaddr $8000; - cpu "6502"; -}; - -# 1 "fixed.da" 1 -# 18 "fixed.da" -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; -# 13 "bank1.da" 2 - -range { start $8000; end $AFFF; name "Bank1Data"; type ByteTable; }; -label { addr $B000; name "Bank1ProcA"; }; -label { addr $B123; name "Bank1ProcB"; }; diff --git a/testcode/disasm/fixed.dai b/testcode/disasm/fixed.dai deleted file mode 100644 index d73155cf0..000000000 --- a/testcode/disasm/fixed.dai +++ /dev/null @@ -1,25 +0,0 @@ -# 1 "fixed.da" -# 1 "<built-in>" -# 1 "<command-line>" -# 1 "fixed.da" -# 9 "fixed.da" -global { - inputoffs $1C010; - inputsize $4000; - startaddr $C000; - cpu "6502"; -}; - - - -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; diff --git a/testcode/disasm/sample-unix.mk b/testcode/disasm/sample-unix.mk deleted file mode 100644 index 0ef64a5e5..000000000 --- a/testcode/disasm/sample-unix.mk +++ /dev/null @@ -1,28 +0,0 @@ -# Sample makefile using a preprocessor against info files -# and the --sync-lines option - -CPP = env LANG=C cpp -CPPFLAGS = # -DTEST_ERROR - -ASMS = fixed.s bank0.s bank1.s -DAIS = fixed.dai bank0.dai bank1.dai - -.SUFFIXES: .da .dai .s -.PHONY: all clean maintainer-clean -.SECONDARY: $(DAIS) - -.da.dai: - $(CPP) -o $@ $(CPPFLAGS) $< - -.dai.s: - da65 --sync-lines -o $@ -i $< image.bin - -all: $(ASMS) - -clean: - rm -f $(ASMS) - -maintainer-clean: clean - rm -f $(DAIS) - -$(DAIS): fixed.da diff --git a/testcode/lib/.gitignore b/testcode/lib/.gitignore deleted file mode 100644 index 9bb8eaa3e..000000000 --- a/testcode/lib/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.o -em-test-* diff --git a/testcode/lib/atari/multi.xex b/testcode/lib/atari/multi.xex deleted file mode 100644 index 7da39ad47..000000000 Binary files a/testcode/lib/atari/multi.xex and /dev/null differ diff --git a/testcode/lib/gamate/Makefile b/testcode/lib/gamate/Makefile deleted file mode 100644 index 2cf98d189..000000000 --- a/testcode/lib/gamate/Makefile +++ /dev/null @@ -1,25 +0,0 @@ - -all: audiotest.bin lcdtest.bin ctest.bin - -audiotest.bin: audiotest.s - ../../../bin/cl65 -l audiotest.lst -t gamate -o audiotest.bin audiotest.s -lcdtest.bin: lcdtest.s - ../../../bin/cl65 -l lcdtest.lst -t gamate -o lcdtest.bin lcdtest.s -ctest.bin: ctest.c - ../../../bin/cl65 -l ctest.lst -t gamate -o ctest.bin ctest.c -nachtm.bin: nachtm.c - ../../../bin/cl65 -Os -l nachtm.lst -t gamate -o nachtm.bin nachtm.c - gamate-fixcart nachtm.bin - -test1: lcdtest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/lcdtest.bin -test2: audiotest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/audiotest.bin -testc: ctest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/ctest.bin -testn: nachtm.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/nachtm.bin - -clean: - rm -f lcdtest.o audiotest.o ctest.o - rm -f lcdtest.bin audiotest.bin ctest.bin nachtm.bin diff --git a/testcode/lib/pce/Makefile b/testcode/lib/pce/Makefile deleted file mode 100644 index a4a495c9a..000000000 --- a/testcode/lib/pce/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -.PHONY: all clean test - -# Size of cartridge to generate. -# Possible values: -# 8K = 0x2000 -# 16K = 0x4000 -# 32K = 0x8000 -CARTSIZE := 0x2000 - -ifeq (${CARTSIZE},0x8000) -COUNT := 3 -else -COUNT := 1 -endif - -all: conio.pce - -%.pce: %.bin - dd if=$< bs=8K skip=${COUNT} > $@ - dd if=$< bs=8K count=${COUNT} >> $@ - -%.bin: %.c ../../../lib/pce.lib - ../../../bin/cl65 -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ - -clean: - $(RM) conio.o conio.??? - -test: conio.pce - mednafen -force_module pce $< diff --git a/testcode/lib/time-test.c b/testcode/lib/time-test.c deleted file mode 100644 index 99d16be01..000000000 --- a/testcode/lib/time-test.c +++ /dev/null @@ -1,38 +0,0 @@ -#include <stdio.h> -#include <time.h> - - - -int main (void) -{ - struct tm tm; - time_t t; - char buf[64]; - - - tm.tm_sec = 9; - tm.tm_min = 34; - tm.tm_hour = 21; - tm.tm_mday = 12; - tm.tm_mon = 10; /* 0..11, so this is november */ - tm.tm_year = 102; /* year - 1900, so this is 2002 */ - tm.tm_wday = 2; /* Tuesday */ - tm.tm_isdst = 0; - - /* Convert this broken down time into a time_t and back */ - t = mktime (&tm); - printf ("Test passes if the following lines are\n" - "all identical:\n"); - printf ("3DD173D1 - Tue Nov 12 21:34:09 2002\n"); - printf ("%08lX - %s", t, asctime (&tm)); - printf ("%08lX - %s", t, asctime (gmtime (&t))); - strftime (buf, sizeof (buf), "%c", &tm); - printf ("%08lX - %s\n", t, buf); - strftime (buf, sizeof (buf), "%a %b %d %H:%M:%S %Y", &tm); - printf ("%08lX - %s\n", t, buf); - - return 0; -} - - - diff --git a/util/cbm/cbmcvt.c b/util/cbm/cbmcvt.c deleted file mode 100644 index 11c0325e4..000000000 --- a/util/cbm/cbmcvt.c +++ /dev/null @@ -1,55 +0,0 @@ -/* cbmcvt.c -- PetSCII <--> ISO-8859-1 Conversion Filter Tool */ -/* 2010-09-06, Greg King */ - -#include <stdio.h> -#include <unistd.h> - -/* Translation table ISO-8859-1 -> PetSCII */ -static const unsigned char CTPET[256] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x14,0x09,0x0D,0x11,0x93,0x0A,0x0E,0x0F, - 0x10,0x0B,0x12,0x13,0x08,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, - 0x40,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, - 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0x5B,0xBF,0x5D,0x5E,0xA4, - 0xAD,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, - 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0xB3,0xDD,0xAB,0xB1,0xDF, - 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, - 0x90,0x91,0x92,0x0C,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, - 0xA0,0xA1,0xA2,0xA3,0x5F,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0x7D,0xAC,0x60,0xAE,0xAF, - 0xB0,0x7E,0xB2,0x7B,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0x5C, - 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, - 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0xDC,0x7C,0xDE,0x7F, - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, - 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF -}; - -static unsigned char CTISO[256]; - - -int main (int argc, char *argv[]) { - int C; - size_t I = 0u; - - if (isatty(fileno(stdin))) { - fputs("cbmcvt v2.1 -- Conversion Filter (stdin --> stdout)\n" - " -p converts ISO-8859-1 to PetSCII\n" - " else, converts in other direction.\n", stderr); - return 0; - } - if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'p') { - while ((C = fgetc (stdin)) != EOF) { - fputc (CTPET[C], stdout); - } - } else { - /* Create translation table PetSCII -> ISO-8859-1 */ - for (; I < sizeof CTPET; ++I) { - CTISO[CTPET[I]] = I; - } - - while ((C = fgetc (stdin)) != EOF) { - fputc (CTISO[C], stdout); - } - } - return 0; -}