diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 045bc048dd..146964a30d 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -19,7 +19,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -44,7 +44,7 @@ jobs: shell: bash run: make -j2 doc - name: Upload a documents snapshot. - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: ./html @@ -54,7 +54,7 @@ jobs: make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=x86_64-w64-mingw32- build_windows: - name: Build (Windows) + name: Build and Test (Windows) runs-on: windows-latest steps: @@ -62,10 +62,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (x86 debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32 @@ -79,4 +79,14 @@ jobs: - name: Build app (x64 release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64 + - name: Build utils (MinGW) + shell: cmd + run: make -j2 util SHELL=cmd + - name: Build the platform libraries (make lib) + shell: cmd + run: make -j2 lib QUIET=1 SHELL=cmd + + - name: Run the regression tests (make test) + shell: cmd + run: make test QUIET=1 SHELL=cmd diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 571947c9bd..d58bff8edd 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -18,10 +18,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug @@ -44,7 +44,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -86,18 +86,18 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip - name: Get the online documents repo. - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: cc65/doc # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 @@ -120,7 +120,7 @@ jobs: - name: Package offline documents. run: 7z a cc65-snapshot-docs.zip ./html/*.* - name: Upload a Documents Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-docs path: cc65-snapshot-docs.zip diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 451b37f792..f722542733 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -30,7 +30,7 @@ jobs: run: mkdir ~/.cache-sha - name: Cache SHA - uses: actions/cache@v3 + uses: actions/cache@v4 id: check-sha with: path: ~/.cache-sha @@ -43,11 +43,11 @@ jobs: - name: Checkout source if: steps.check-sha.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH if: steps.check-sha.outputs.cache-hit != 'true' - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (MSVC debug) if: steps.check-sha.outputs.cache-hit != 'true' diff --git a/Contributing.md b/Contributing.md index 25c6217aab..1fde873f22 100644 --- a/Contributing.md +++ b/Contributing.md @@ -1,4 +1,6 @@ -This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete. +This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete. + +Also, before you put a lot of work into implementing something you want to contribute, please get in touch with one of the developers and ask if what you are going to do is actually wanted and has a chance of being merged. Perhaps someone else is already working on it, or perhaps what you have in mind is not how we'd expect it to be - talking to us before you start might save you a lot of work in those cases. (''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.) diff --git a/README.md b/README.md index 11c3bb0ffd..dce9a07bca 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For details look at the [Website](https://cc65.github.io). Project founders: -* John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted +* John R. Dunning: [original implementation](https://public.websites.umich.edu/~archive/atari/8bit/Languages/Cc65/) of the C compiler and runtime library, Atari hosted * Ullrich von Bassewitz: * move the code to modern systems * rewrite most parts of the compiler diff --git a/cfg/kim1-mtu60k.cfg b/cfg/kim1-mtu60k.cfg new file mode 100644 index 0000000000..4f24a4bf45 --- /dev/null +++ b/cfg/kim1-mtu60k.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $2000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/kim1-mtuE000.cfg b/cfg/kim1-mtuE000.cfg new file mode 100644 index 0000000000..5f93cc13f8 --- /dev/null +++ b/cfg/kim1-mtuE000.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $E000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $2000, size = $E000 - $2000 - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/lynx-uploader.cfg b/cfg/lynx-uploader.cfg index 476b3c5dea..62269de909 100644 --- a/cfg/lynx-uploader.cfg +++ b/cfg/lynx-uploader.cfg @@ -5,16 +5,17 @@ SYMBOLS { __BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size __EXEHDR__: type = import; __BOOTLDR__: type = import; - __DEFDIR__: type = import; __UPLOADER__: type = import; + __UPLOADERSIZE__: type = export, value = $61; + __HEADERSIZE__: type = export, value = 64; } MEMORY { ZP: file = "", define = yes, start = $0000, size = $0100; - HEADER: file = %O, start = $0000, size = $0040; + HEADER: file = %O, start = $0000, size = __HEADERSIZE__; BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__; - DIR: file = %O, start = $0000, size = 8; - MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__; - UPLDR: file = %O, define = yes, start = $BFDC, size = $005C; + DIR: file = %O, start = $0000, size = 16; + MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__; + UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; @@ -30,8 +31,8 @@ SEGMENTS { RODATA: load = MAIN, type = ro, define = yes; DATA: load = MAIN, type = rw, define = yes; BSS: load = MAIN, type = bss, define = yes; - UPCODE: load = UPLDR, type = ro, define = yes; - UPDATA: load = UPLDR, type = rw, define = yes; + UPCODE: load = UPLOAD, type = ro, define = yes; + UPDATA: load = UPLOAD, type = rw, define = yes; } FEATURES { CONDES: type = constructor, diff --git a/doc/apple2.sgml b/doc/apple2.sgml index f1603c4286..e6ec870eec 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -62,7 +62,7 @@ Special locations:

While running _dos_type _filetype _datetime +beep get_ostype +gmtime_dt +mktime_dt rebootafterexit ser_apple2_slot tgi_apple2_mix +Apple IIgs specific functions in accelerator.h

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

There's currently no support for direct hardware access. This does not mean diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index e275015777..d47820cc74 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -63,7 +63,7 @@ Special locations:

While running _dos_type _filetype _datetime +beep get_ostype +gmtime_dt +mktime_dt rebootafterexit ser_apple2_slot tgi_apple2_mix @@ -340,6 +343,20 @@ usage. +Apple IIgs specific functions in accelerator.h

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

There's currently no support for direct hardware access. This does not mean diff --git a/doc/funcref.sgml b/doc/funcref.sgml index c8a8182952..81c63a38bf 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -71,18 +71,21 @@ function. + + + @@ -92,6 +95,7 @@ function. _dos_type + @@ -103,7 +107,10 @@ function. _dos_type + + + rebootafterexit @@ -1766,10 +1773,11 @@ used in presence of a prototype. / +/ -The function is specific to the Sym-1. +The function is specific to the Sym-1 and Apple2 platforms. +detect_iigs

+ + + +/ + +The function is specific to the Apple2 and Apple2enh platforms. + +, +, + + + + detect_scpu

@@ -4167,6 +4195,27 @@ header files define constants that can be used to check the return code. +get_iigs_speed

+ + + +/ + +The function is specific to the Apple2 and Apple2enh platforms. +See the accelerator.h header for the speed definitions. + +, +, + + + + get_scpu_speed

@@ -6985,6 +7034,30 @@ clean-up when exiting the program. +set_iigs_speed

+ + + +/ + +The function is specific to the Apple2 and Apple2enh platforms. +See the accelerator.h header for the speed definitions. +Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are +considered SPEED_FAST). + +, +, + + + + set_scpu_speed

diff --git a/include/accelerator.h b/include/accelerator.h index b5d8d01948..0137a7feda 100644 --- a/include/accelerator.h +++ b/include/accelerator.h @@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void); * 0x01 : C64 Turbo Master cartridge present */ +unsigned char __fastcall__ set_iigs_speed (unsigned char speed); + +/* Set the speed of the Apple IIgs CPU. + * + * Possible values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + * + * Any other value will be interpreted as SPEED_FAST. + */ + +unsigned char get_iigs_speed (void); + +/* Get the speed of the Apple IIgs CPU. + * + * Possible return values: + * SPEED_SLOW : 1 Mhz mode + * SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of + * an accelerator) + */ + +unsigned char detect_iigs (void); + +/* Check whether we are running on an Apple IIgs. + * + * Possible return values: + * 0x00 : No + * 0x01 : Yes + */ + /* End of accelerator.h */ #endif - diff --git a/include/apple2.h b/include/apple2.h index f59c29b56c..a5310ebc40 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -197,6 +197,9 @@ extern unsigned char a2_lo_tgi[]; +void beep (void); +/* Beep beep. */ + unsigned char get_ostype (void); /* Get the machine type. Returns one of the APPLE_xxx codes. */ diff --git a/include/stdio.h b/include/stdio.h index 012b8e2baa..35ebd77845 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -86,6 +86,10 @@ extern FILE* stderr; # define FILENAME_MAX (80+1) #elif defined(__TELESTRAT__) # define FILENAME_MAX (50+1) +#elif defined(__SIM6502__) +# define FILENAME_MAX (1024+1) +#elif defined(__SIM65C02__) +# define FILENAME_MAX (1024+1) #else # define FILENAME_MAX (16+1) #endif diff --git a/libsrc/apple2/beep.s b/libsrc/apple2/beep.s new file mode 100644 index 0000000000..ebebb6e3fd --- /dev/null +++ b/libsrc/apple2/beep.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; void beep(void) +; + + .export _beep + .import BELL + + .include "apple2.inc" + + .segment "LOWCODE" + +_beep: + lda CH ; Bell scrambles CH in 80col mode on IIgs, storing + pha ; it in OURCH and resetting CH to 0. Save it. + jsr BELL + pla + sta CH ; Restore CH + rts diff --git a/libsrc/apple2/bell.s b/libsrc/apple2/bell.s new file mode 100644 index 0000000000..28e6d24f8f --- /dev/null +++ b/libsrc/apple2/bell.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; BELL routine +; + + .export BELL + + .include "apple2.inc" + + .segment "LOWCODE" + +BELL: + ; Switch in ROM and call BELL + bit $C082 + jsr $FF3A ; BELL + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts diff --git a/libsrc/apple2/crt0.s b/libsrc/apple2/crt0.s index 60a8516d1f..c129cdbf89 100644 --- a/libsrc/apple2/crt0.s +++ b/libsrc/apple2/crt0.s @@ -40,12 +40,15 @@ _exit: ldx #exit jsr reset ; Setup RESET vector - ; Switch in ROM, in case it wasn't already switched in by a RESET. - bit $C082 + ; Switch in LC bank 2 for R/O in case it was switched out by a RESET. + bit $C080 ; Call the module destructors. jsr donelib + ; Switch in ROM. + bit $C082 + ; Restore the original RESET vector. exit: ldx #$02 : lda rvsave,x diff --git a/libsrc/apple2/detect_iigs.s b/libsrc/apple2/detect_iigs.s new file mode 100644 index 0000000000..f82a464ac8 --- /dev/null +++ b/libsrc/apple2/detect_iigs.s @@ -0,0 +1,17 @@ +; +; Colin Leroy-Mira , 2024 +; +; void __fastcall__ detect_iigs(void) +; + + .export _detect_iigs + .import ostype, return0, return1 + + .include "apple2.inc" + + ; Returns 1 if running on IIgs, 0 otherwise +_detect_iigs: + lda ostype + bpl :+ + jmp return1 +: jmp return0 diff --git a/libsrc/apple2/get_iigs_speed.s b/libsrc/apple2/get_iigs_speed.s new file mode 100644 index 0000000000..1915d7773d --- /dev/null +++ b/libsrc/apple2/get_iigs_speed.s @@ -0,0 +1,22 @@ +; +; Colin Leroy-Mira , 2024 +; +; unsigned char __fastcall__ get_iigs_speed(void) +; + + .export _get_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_get_iigs_speed: + lda ostype ; Return SLOW if not IIgs + bpl :+ + lda CYAREG ; Check current setting + bpl :+ + lda #SPEED_FAST + ldx #$00 + rts + .assert SPEED_SLOW = 0, error +: jmp return0 ; SPEED_SLOW diff --git a/libsrc/apple2/get_ostype.s b/libsrc/apple2/get_ostype.s index a1b1eb5bef..ea9ff25cc5 100644 --- a/libsrc/apple2/get_ostype.s +++ b/libsrc/apple2/get_ostype.s @@ -5,7 +5,7 @@ ; .constructor initostype, 9 - .export _get_ostype + .export _get_ostype, ostype ; Identify machine according to: ; Apple II Miscellaneous TechNote #7, Apple II Family Identification diff --git a/libsrc/apple2/set_iigs_speed.s b/libsrc/apple2/set_iigs_speed.s new file mode 100644 index 0000000000..5e2f2f7226 --- /dev/null +++ b/libsrc/apple2/set_iigs_speed.s @@ -0,0 +1,29 @@ +; +; Colin Leroy-Mira , 2024 +; +; unsigned char __fastcall__ detect_iigs(unsigned char speed) +; + + .export _set_iigs_speed + .import ostype, return0 + + .include "apple2.inc" + .include "accelerator.inc" + +_set_iigs_speed: + tax ; Keep parameter + lda ostype ; Return if not IIgs + bmi :+ + jmp return0 + +: lda CYAREG + cpx #SPEED_SLOW + beq :+ + ora #%10000000 + bne set_speed +: and #%01111111 +set_speed: + sta CYAREG + txa + ldx #$00 + rts diff --git a/libsrc/apple2/sleep.s b/libsrc/apple2/sleep.s new file mode 100644 index 0000000000..43873d9f4a --- /dev/null +++ b/libsrc/apple2/sleep.s @@ -0,0 +1,54 @@ +; +; Colin Leroy-Mira , 2024 +; +; void __fastcall__ sleep(unsigned s) +; +; + + .export _sleep + .import _get_iigs_speed + .import _set_iigs_speed + .import WAIT + .importzp tmp1 + + .include "accelerator.inc" + + ; This functions uses the Apple2 WAIT ROM routine to waste a certain + ; amount of cycles and returns approximately after the numbers of + ; seconds passed in AX. + ; + ; It takes 1023730 cycles when called with AX=1 (1,0007s), + ; 10236364 cycles when called with AX=10 (10,006 seconds), + ; 306064298 cycles with AX=300 (299.2 seconds). + ; + ; Caveat: IRQs firing during calls to sleep will make the sleep longer + ; by the amount of cycles it takes to handle the IRQ. + ; +_sleep: + stx tmp1 ; High byte of s in X + tay ; Low byte in A + ora tmp1 + bne :+ + rts +: jsr _get_iigs_speed ; Save current CPU speed + pha + lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT + jsr _set_iigs_speed +sleep_1s: + ldx #$0A ; Loop 10 times +sleep_100ms: + lda #$C7 ; Sleep about 99ms + jsr WAIT + lda #$0D ; About 1ms + jsr WAIT + dex + bne sleep_100ms + dey + bne sleep_1s + dec tmp1 + bmi done + dey ; Down to #$FF + bne sleep_1s +done: + pla ; Restore CPU speed + jmp _set_iigs_speed diff --git a/libsrc/apple2/statvfs.s b/libsrc/apple2/statvfs.s index 6274bb52bd..8fcf46af83 100644 --- a/libsrc/apple2/statvfs.s +++ b/libsrc/apple2/statvfs.s @@ -6,7 +6,7 @@ .export _statvfs .import _dio_query_sectsize - .import mli_file_info, pushax, popax, popptr1 + .import mli_file_info, pushax, popax, popptr1, pushptr1 .include "zeropage.inc" .include "apple2.inc" .include "errno.inc" @@ -45,9 +45,7 @@ _statvfs: sty vol_sep ; Register '/' index lda #$00 sta (ptr1),y ; Cut pathname at first slash -: lda ptr1 - ldx ptr1+1 - jsr pushax +: jsr pushptr1 jsr mli_file_info diff --git a/libsrc/apple2/wait.s b/libsrc/apple2/wait.s new file mode 100644 index 0000000000..3b569215b6 --- /dev/null +++ b/libsrc/apple2/wait.s @@ -0,0 +1,20 @@ +; +; Colin Leroy-Mira, 2024 +; +; WAIT routine +; + + .export WAIT + + .include "apple2.inc" + + .segment "LOWCODE" + +WAIT: + ; Switch in ROM and call WAIT + bit $C082 + jsr $FCA8 ; Vector to WAIT routine + + ; Switch in LC bank 2 for R/O and return + bit $C080 + rts diff --git a/libsrc/apple2/waitvsync.s b/libsrc/apple2/waitvsync.s index a4ab5ebb37..1697622deb 100644 --- a/libsrc/apple2/waitvsync.s +++ b/libsrc/apple2/waitvsync.s @@ -5,21 +5,11 @@ ; .ifdef __APPLE2ENH__ - .constructor initvsync .export _waitvsync - .import _get_ostype + .import ostype .include "apple2.inc" - .segment "ONCE" - -initvsync: - jsr _get_ostype - sta ostype - rts - - .code - _waitvsync: bit ostype bmi iigs ; $8x @@ -53,8 +43,4 @@ iic: sei cli rts - .segment "INIT" - -ostype: .res 1 - .endif ; __APPLE2ENH__ diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s index ed3e40b2f2..e7e55c54c0 100644 --- a/libsrc/atari/open.s +++ b/libsrc/atari/open.s @@ -19,7 +19,7 @@ .import findfreeiocb .import incsp4 .import ldaxysp,addysp - .import ___oserror + .import ___oserror, returnFFFF .ifdef UCASE_FILENAME .import ucase_fn .endif @@ -39,9 +39,7 @@ parmok: jsr findfreeiocb lda # -** -*/ - - - -#ifndef __IS_LEAP_YEAR_H -#define __IS_LEAP_YEAR_H - - - -unsigned char __fastcall__ IsLeapYear (unsigned char Year); -/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206, - * without 1900 added */ - - - -/* End of _is_leap_year.h */ -#endif diff --git a/libsrc/common/_is_leap_year.s b/libsrc/common/_is_leap_year.s deleted file mode 100644 index d3136c1c84..0000000000 --- a/libsrc/common/_is_leap_year.s +++ /dev/null @@ -1,23 +0,0 @@ -; -; Colin Leroy-Mira, 2024 -; -; unsigned char __fastcall__ IsLeapYear (unsigned char Year) -; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206, -; without 1900 added. -; - - .export _IsLeapYear - -_IsLeapYear: - ldx #$00 ; Prepare X for rts - cmp #$00 ; Y 0 (1900) is not a leap year - beq NotLeap - cmp #$C8 ; Y 200 (2100) is not a leap year - beq NotLeap - and #$03 ; Year % 4 == 0 means leap year - bne NotLeap - lda #$01 ; Return 1 - rts -NotLeap: - lda #$00 ; Return 0 - rts diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index a0074583e3..d7eeb072dc 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -13,6 +13,7 @@ .import _strlower, _strlen .macpack generic + .macpack cpu ; ---------------------------------------------------------------------------- ; We will store variables into the register bank in the zeropage. Define @@ -37,7 +38,11 @@ FCount = ptr2 GetFormatChar: ldy #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (Format) + .else lda (Format),y + .endif IncFormatPtr: inc Format bne @L1 @@ -110,7 +115,11 @@ GetIntArg: lda (ArgList),y tax dey + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (ArgList) + .else lda (ArgList),y + .endif rts ; ---------------------------------------------------------------------------- @@ -135,9 +144,9 @@ ReadInt: pha ; Save digit value lda ptr1 ldx ptr1+1 - asl ptr1 + asl a rol ptr1+1 ; * 2 - asl ptr1 + asl a rol ptr1+1 ; * 4, assume carry clear adc ptr1 sta ptr1 @@ -265,10 +274,16 @@ Save: lda regbank,y ; Initialize the output counter in the output descriptor to zero lda #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (OutData) + ldy #$01 + sta (OutData),y + .else tay sta (OutData),y iny sta (OutData),y + .endif ; Get the output function from the output descriptor and remember it @@ -338,7 +353,11 @@ MainLoop: sta (sp),y dey lda FCount + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) + .else sta (sp),y + .endif jsr CallOutFunc ; Call the output function ; We're back from out(), or we didn't call it. Check for end of string. @@ -551,10 +570,16 @@ CheckCount: jsr GetIntArg sta ptr1 stx ptr1+1 ; Get user supplied pointer + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (OutData) ; Low byte of OutData->ccount + sta (ptr1) + ldy #1 + .else ldy #0 lda (OutData),y ; Low byte of OutData->ccount sta (ptr1),y iny + .endif lda (OutData),y ; High byte of OutData->ccount sta (ptr1),y jmp MainLoop ; Done diff --git a/libsrc/common/_time_t_to_tm.c b/libsrc/common/_time_t_to_tm.c deleted file mode 100644 index 684cff752b..0000000000 --- a/libsrc/common/_time_t_to_tm.c +++ /dev/null @@ -1,64 +0,0 @@ -/*****************************************************************************/ -/* */ -/* gmtime.c */ -/* */ -/* Convert calendar time into broken down time in UTC */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - -struct tm* __fastcall__ _time_t_to_tm (const time_t t) -{ - static struct tm timebuf; - /* Since our ints are just 16 bits, split the given time into seconds, - ** hours and days. Each of the values will fit in a 16 bit variable. - ** The mktime routine will then do the rest. - */ - timebuf.tm_sec = t % 3600; - timebuf.tm_min = 0; - timebuf.tm_hour = (t / 3600) % 24; - timebuf.tm_mday = (t / (3600UL * 24UL)) + 1; - timebuf.tm_mon = 0; - timebuf.tm_year = 70; /* Base value is 1/1/1970 */ - - /* Call mktime to do the final conversion */ - mktime (&timebuf); - - /* Return the result */ - return &timebuf; -} diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s new file mode 100644 index 0000000000..9bcf841842 --- /dev/null +++ b/libsrc/common/_time_t_to_tm.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ _time_t_to_tm (const time_t t) +; +; Helper to gmtime and localtime. Breaks down a number of +; seconds since Jan 1, 1970 into days, hours and seconds, +; so that each of them fits in 16 bits; passes the +; result to _mktime which fixes all values in the struct, +; and returns a pointer to the struct to callers. +; + + .export __time_t_to_tm + .import udiv32, _mktime + .importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4 + + .include "time.inc" + + .macpack cpu + +__time_t_to_tm: + ; Divide number of seconds since epoch, in ptr1:sreg, + ; by 86400 to get the number of days since epoch, and + ; the number of seconds today in the remainder. + + ; Load t as dividend (sreg is already set by the caller) + sta ptr1 + stx ptr1+1 + + ; Load 86400 as divisor + lda #$80 + sta ptr3 + lda #$51 + sta ptr3+1 + lda #$01 + sta ptr4 + lda #$00 + sta ptr4+1 + + ; Clear TM buf while we have zero in A + ldx #.sizeof(tm)-1 +: sta TM,x + dex + bpl :- + + ; Divide t/86400 + jsr udiv32 + + ; Store the quotient (the number of full days), and increment + ; by one as epoch starts at day 1. + clc + lda ptr1 + adc #1 + sta TM + tm::tm_mday + lda ptr1+1 + adc #0 + sta TM + tm::tm_mday+1 + + ; Now divide the number of remaining seconds by 3600, + ; to get the number of hours, and the seconds in the + ; current hour, in neat 16-bit integers. + + ; Load the previous division's remainder (in ptr2:tmp3:tmp4) + ; as dividend + lda ptr2 + sta ptr1 + lda ptr2+1 + sta ptr1+1 + lda tmp3 + sta sreg + ; We ignore the high byte stored in tmp4 because it will be + ; zero. We'll zero sreg+1 right below, when we'll have + ; a convenient zero already in A. + + ; Load divisor + lda #<3600 + sta ptr3 + lda #>3600 + sta ptr3+1 + + ; Zero the two high bytes of the divisor and the high byte + ; of the dividend. + .if .cpu .bitand CPU_ISET_65SC02 + stz ptr4 + stz ptr4+1 + stz sreg+1 + .else + lda #$00 + sta ptr4 + sta ptr4+1 + sta sreg+1 + .endif + + ; Do the division + jsr udiv32 + + ; Store year + lda #70 + sta TM + tm::tm_year + + ; Store hours (the quotient of the last division) + lda ptr1 + sta TM + tm::tm_hour + lda ptr1+1 + sta TM + tm::tm_hour+1 + + ; Store seconds (the remainder of the last division) + lda ptr2 + sta TM + tm::tm_sec + lda ptr2+1 + sta TM + tm::tm_sec+1 + + ; The rest of the struct tm fields are zero. mktime + ; will take care of shifting extra seconds to minutes, + ; and extra days to months and years. + + ; Call mktime + lda #TM + jsr _mktime + + ; And return our pointer + lda #TM + rts + + .bss + +TM: .tag tm diff --git a/libsrc/common/asctime.c b/libsrc/common/asctime.c deleted file mode 100644 index b46f291281..0000000000 --- a/libsrc/common/asctime.c +++ /dev/null @@ -1,59 +0,0 @@ -/*****************************************************************************/ -/* */ -/* asctime.c */ -/* */ -/* Convert a broken down time into a string */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include -#include - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - -/* - CAUTION: we need to reserve enough space to be able to hold the maximum - length string: - - 1234567890123456789012345678901234567 - "Wednesday September ..1 00:00:00 1970" -*/ - -char* __fastcall__ asctime (const struct tm* timep) -{ - static char buf[38]; - - /* Format into given buffer and return the result */ - return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0; -} diff --git a/libsrc/common/asctime.s b/libsrc/common/asctime.s new file mode 100644 index 0000000000..efcf34b411 --- /dev/null +++ b/libsrc/common/asctime.s @@ -0,0 +1,81 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ asctime (const struct tm* timep) +; + + .export _asctime + .import _strftime, pushax + .importzp ptr1 + .include "time.inc" + + .macpack cpu + +; ------------------------------------------------------------------------ +; Special values + +; We need to be able to store up to 38 bytes: +; 1234567890123456789012345678901234567 +; "Wednesday September ..1 00:00:00 1970" +MAX_BUF_LEN = 38 + +; ------------------------------------------------------------------------ +; Code + +_asctime: + ; Backup timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + pha + phx + .else + sta ptr1 + stx ptr1+1 + .endif + + ; Push buf + lda #buf + jsr pushax + + ; Push sizeof(buf) + lda #MAX_BUF_LEN + jsr pushax + + ; Push format string + lda #fmt + jsr pushax + + ; Restore timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + plx + pla + .else + lda ptr1 + ldx ptr1+1 + .endif + + ; Call formatter + jsr _strftime + + ; Check return status + bne :+ + cpx #$00 + bne :+ + rts + +: lda #buf + rts + + .data + +fmt: .byte '%' + .byte 'c' + .byte $0A + .byte $00 + + .bss + +buf: .res MAX_BUF_LEN diff --git a/libsrc/common/checkferror.s b/libsrc/common/checkferror.s new file mode 100644 index 0000000000..736fa3ccd7 --- /dev/null +++ b/libsrc/common/checkferror.s @@ -0,0 +1,24 @@ +; +; Colin Leroy-Mira, 2024 +; +; Helper to check for file opened, not eof, not ferror +; Expects file pointer in ptr1, +; Returns with Z flag set if everything is OK, +; Destroys A, X, Y, +; Sets file flags in A +; + + .export checkferror + .importzp ptr1 + + .include "_file.inc" + +checkferror: + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #(_FOPEN|_FERROR|_FEOF); Check for file open, error/eof + tay + txa + cpy #_FOPEN + rts diff --git a/libsrc/common/divt.s b/libsrc/common/divt.s index 7f2b4e1bb5..52b6efd04f 100644 --- a/libsrc/common/divt.s +++ b/libsrc/common/divt.s @@ -3,7 +3,7 @@ ; 2002-10-22, Greg King ; ; This signed-division function returns both the quotient and the remainder, -; in this structure: +; in this structure: (quotient in sreg, remainder in AX) ; ; typedef struct { ; int rem, quot; diff --git a/libsrc/common/fclose.s b/libsrc/common/fclose.s index 2368bf9f65..f6c57841eb 100644 --- a/libsrc/common/fclose.s +++ b/libsrc/common/fclose.s @@ -7,7 +7,7 @@ .export _fclose - .import _close + .import _close, ___directerrno .importzp ptr1 .include "errno.inc" @@ -31,10 +31,7 @@ ; File is not open lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno ; File is open. Reset the flags and close the file. @@ -47,4 +44,3 @@ jmp _close ; Will set errno and return an error flag .endproc - diff --git a/libsrc/common/fgetc.c b/libsrc/common/fgetc.c deleted file mode 100644 index b4ba18d732..0000000000 --- a/libsrc/common/fgetc.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -** fgetc.c -** -** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org) -** -*/ - - - -#include -#include -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fgetc (register FILE* f) -{ - unsigned char c; - - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* If we have a pushed back character, return it */ - if (f->f_flags & _FPUSHBACK) { - f->f_flags &= ~_FPUSHBACK; - return f->f_pushback; - } - - /* Read one byte */ - switch (read (f->f_fd, &c, 1)) { - - case -1: - /* Error */ - f->f_flags |= _FERROR; - return EOF; - - case 0: - /* EOF */ - f->f_flags |= _FEOF; - return EOF; - - default: - /* Char read */ - return c; - - } -} - - - diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s new file mode 100644 index 0000000000..34d4df3aa3 --- /dev/null +++ b/libsrc/common/fgetc.s @@ -0,0 +1,94 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fgetc (register FILE* f) +; + + .export _fgetc + .import _read, checkferror + .import pusha0, pushax, popptr1, incsp2, returnFFFF + .importzp ptr1 + + .include "stdio.inc" + .include "_file.inc" + + .macpack cpu + +_fgetc: + sta ptr1 + stx ptr1+1 + jsr pushax ; Backup our ptr + + jsr checkferror + bne ret_eof + + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FPUSHBACK ; Check for pushed back char + beq do_read + .else + tax + and #_FPUSHBACK ; Check for pushed back char + beq do_read + txa + .endif + + and #<(~_FPUSHBACK) ; Reset flag + sta (ptr1),y + + .assert _FILE::f_pushback = _FILE::f_flags+1, error + iny + jsr incsp2 ; Drop our ptr copy + lda (ptr1),y ; Return pushed back char + ldx #$00 + rts + +do_read: + ; Push _read parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #c + jsr pushax + + lda #$01 + ldx #$00 + + ; Read + jsr _read + + ; Check for errors + cmp #$00 + beq set_feof + + cmp #<(-1) + beq set_ferror + + jsr incsp2 + ; Return char + ldx #$00 + lda c + rts + +ret_eof: + jsr incsp2 + jmp returnFFFF + +set_ferror: + lda #_FERROR + bne set_err +set_feof: + lda #_FEOF +set_err: + pha + jsr popptr1 + pla + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 1 diff --git a/libsrc/common/fgets.c b/libsrc/common/fgets.c deleted file mode 100644 index 21a991fd6f..0000000000 --- a/libsrc/common/fgets.c +++ /dev/null @@ -1,68 +0,0 @@ -/* -** Ullrich von Bassewitz, 11.08.1998 -** -** char* fgets (char* s, int size, FILE* f); -*/ - - - -#include -#include -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) -{ - register char* p = s; - unsigned i; - int c; - - if (size == 0) { - /* Invalid size */ - return (char*) _seterrno (EINVAL); - } - - /* Read input */ - i = 0; - while (--size) { - - /* Get next character */ - if ((c = fgetc (f)) == EOF) { - /* Error or EOF */ - if ((f->f_flags & _FERROR) != 0 || i == 0) { - /* ERROR or EOF on first char */ - *p = '\0'; - return 0; - } else { - /* EOF with data already read */ - break; - } - } - - /* One char more */ - *p = c; - ++p; - ++i; - - /* Stop at end of line */ - if ((char)c == '\n') { - break; - } - } - - /* Terminate the string */ - *p = '\0'; - - /* Done */ - return s; -} - - - diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s new file mode 100644 index 0000000000..172ca10dd0 --- /dev/null +++ b/libsrc/common/fgets.s @@ -0,0 +1,117 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ fgets (char* s, unsigned size, register FILE* f) +; + + .export _fgets + .import _fgetc, popptr1, pushptr1, popax, pushax, return0, ___errno + .importzp ptr1, ptr4 + + .include "errno.inc" + .include "stdio.inc" + .include "_file.inc" + + .macpack cpu + +terminate_ptr: + lda #$00 + tax + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr4) + .else + tay + sta (ptr4),y + .endif + rts + +_fgets: + sta ptr1 + stx ptr1+1 + + jsr popax + sta size + stx size+1 + + jsr popax + sta ptr4 + stx ptr4+1 + sta buf + stx buf+1 + + .if (.cpu .bitand ::CPU_ISET_65SC02) + stz didread + .else + lda #$00 ; We have read nothing yet + sta didread + .endif + + ; Check size + lda size + ora size+1 + bne read_loop + lda #EINVAL + sta ___errno + jmp return0 + +read_loop: + lda size ; Dec size + bne :+ + dec size+1 +: dec size + + bne :+ ; Check bound + ldx size+1 + beq done + +: jsr pushptr1 ; Push ptr1 for backup and load it to AX for fgetc + jsr _fgetc ; Read a char + + pha + jsr popptr1 ; Get ptr1 back + pla + + cpx #buf + lda # -#include -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fputc (int c, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - goto ReturnEOF; - } - - /* Write the byte */ - if (write (f->f_fd, &c, 1) != 1) { - /* Error */ - f->f_flags |= _FERROR; -ReturnEOF: - return EOF; - } - - /* Return the byte written */ - return c & 0xFF; -} - - - diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s new file mode 100644 index 0000000000..358723538d --- /dev/null +++ b/libsrc/common/fputc.s @@ -0,0 +1,66 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputc (int c, FILE* f); +; + + .export _fputc + .importzp ptr1 + .import _write, checkferror + .import pushax, pusha0, popax, incsp2 + .import pushptr1, popptr1, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputc: + sta ptr1 + stx ptr1+1 + + jsr popax ; Get char, as we'll have + sta c ; to return it anyway + stx c+1 + + jsr checkferror + bne ret_eof + + jsr pushptr1 ; Backup fp pointer + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #c + jsr pushax + + lda #$01 + ldx #$00 + + ; Write + jsr _write + + ; Check for errors + cmp #$01 + bne set_ferror + + ; Return char + lda c + ldx #$00 + jmp incsp2 ; Drop fp pointer copy + +ret_eof: + jmp returnFFFF + +set_ferror: + jsr popptr1 + lda #_FERROR + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 2 diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c deleted file mode 100644 index be476a3f0b..0000000000 --- a/libsrc/common/fputs.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -** int fputs (const char* s, FILE* f); -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include -#include -#include -#include "_file.h" - - - -int __fastcall__ fputs (const char* s, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* Write the string */ - return write (f->f_fd, s, strlen (s)); -} - - - diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s new file mode 100644 index 0000000000..b79a4707fc --- /dev/null +++ b/libsrc/common/fputs.s @@ -0,0 +1,36 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputs (const char* s, register FILE* f) +; + + .export _fputs + .importzp ptr1, ptr2 + .import _write, _strlen, checkferror + .import swapstk, pushax, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputs: + sta ptr1 + stx ptr1+1 + + jsr checkferror + bne ret_eof + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + ldx #$00 + jsr swapstk ; Push fd, get s + + jsr pushax ; Push s + + jsr _strlen ; Get length + + ; Write + jmp _write + +ret_eof: + jmp returnFFFF diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s index 647a7f2c83..b39b9d7486 100644 --- a/libsrc/common/fread.s +++ b/libsrc/common/fread.s @@ -20,6 +20,7 @@ .include "_file.inc" .macpack generic + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -47,13 +48,21 @@ ldy #_FILE::f_flags lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN ; Is the file open? + .else and #_FOPEN ; Is the file open? + .endif beq @L1 ; Branch if no ; Check if the stream is in an error state + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FERROR + .else lda (file),y ; get file->f_flags again and #_FERROR + .endif beq @L2 ; File not open or in error state @@ -65,11 +74,19 @@ ; Remember if we have a pushed back character and reset the flag. -@L2: tax ; X = 0 +@L2: .if (.cpu .bitand ::CPU_ISET_65SC02) + ldx #$00 + bit #_FPUSHBACK + .else + tax ; X = 0 lda (file),y and #_FPUSHBACK + .endif beq @L3 + + .if (.not .cpu .bitand ::CPU_ISET_65SC02) lda (file),y + .endif and #<~_FPUSHBACK sta (file),y ; file->f_flags &= ~_FPUSHBACK; inx ; X = 1 @@ -118,12 +135,20 @@ ; Copy the buffer pointer into ptr1, and increment the pointer value passed ; to read() by one, so read() starts to store data at buf+1. + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) + sta ptr1 + add #1 + sta (sp) + ldy #1 + .else ldy #0 lda (sp),y sta ptr1 add #1 sta (sp),y iny + .endif lda (sp),y sta ptr1+1 adc #0 @@ -134,8 +159,12 @@ ldy #_FILE::f_pushback lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr1) ; *buf = file->f_pushback; + .else ldy #0 sta (ptr1),y ; *buf = file->f_pushback; + .endif ; Restore the low byte of count and decrement count by one. This may result ; in count being zero, so check for that. @@ -210,4 +239,3 @@ .bss save: .res 2 pb: .res 1 - diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s index 861feb1202..e7151da953 100644 --- a/libsrc/common/fwrite.s +++ b/libsrc/common/fwrite.s @@ -8,7 +8,7 @@ .export _fwrite .import _write - .import pushax, incsp6, addysp, ldaxysp, pushwysp, return0 + .import pushax, pusha0, incsp6, addysp, ldaxysp, pushwysp, return0 .import tosumulax, tosudivax .importzp ptr1 @@ -16,6 +16,7 @@ .include "errno.inc" .include "_file.inc" + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -33,7 +34,11 @@ ldy #_FILE::f_flags lda (ptr1),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN + .else and #_FOPEN ; Is the file open? + .endif bne @L2 ; Branch if yes ; File not open @@ -45,7 +50,9 @@ ; Check if the stream is in an error state -@L2: lda (ptr1),y ; get file->f_flags again +@L2: .if (.not .cpu .bitand ::CPU_ISET_65SC02) + lda (ptr1),y ; get file->f_flags again + .endif and #_FERROR bne @L1 @@ -53,8 +60,7 @@ ldy #_FILE::f_fd lda (ptr1),y - ldx #$00 - jsr pushax ; file->f_fd + jsr pusha0 ; file->f_fd ldy #9 jsr pushwysp ; buf @@ -123,4 +129,3 @@ .bss file: .res 2 - diff --git a/libsrc/common/gets.c b/libsrc/common/gets.c deleted file mode 100644 index 2936c70de1..0000000000 --- a/libsrc/common/gets.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -** gets.c -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -char* __fastcall__ gets (char* s) -{ - register char* p = s; - int c; - unsigned i = 0; - - while (1) { - - /* Get next character */ - if ((c = fgetc (stdin)) == EOF) { - /* Error or EOF */ - *p = '\0'; - if (stdin->f_flags & _FERROR) { - /* ERROR */ - return 0; - } else { - /* EOF */ - if (i) { - return s; - } else { - return 0; - } - } - } - - /* One char more. Newline ends the input */ - if ((char) c == '\n') { - *p = '\0'; - break; - } else { - *p = c; - ++p; - ++i; - } - - } - - /* Done */ - return s; -} - - - - diff --git a/libsrc/common/gets.s b/libsrc/common/gets.s new file mode 100644 index 0000000000..dfaf2def37 --- /dev/null +++ b/libsrc/common/gets.s @@ -0,0 +1,47 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ gets (char* s) +; + + .export _gets + .import _fgets, _stdin, popax, pushax + .importzp ptr4 + +_gets: + ; Push buffer + sta ptr4 + stx ptr4+1 + jsr pushax + + ; Push size (there's no limit!) + lda #$FF + tax + jsr pushax + + lda _stdin + ldx _stdin+1 + + jsr _fgets + + ; Check return value + bne :+ + cpx #$00 + bne :+ + rts + +: ; At least one byte written. + jsr pushax ; Store returned pointer + + ; Remove \n if there is one. + lda ptr4 ; _fgets returns with ptr4 at + bne :+ ; end of buffer + dec ptr4+1 +: dec ptr4 + lda (ptr4),y ; _fgets returns with Y=0 + cmp #$0A + bne :+ + tya + sta (ptr4),y ; Set terminator over \n + +: jmp popax diff --git a/libsrc/common/malloc.s b/libsrc/common/malloc.s index 6872f1f2e4..72c4aedaad 100644 --- a/libsrc/common/malloc.s +++ b/libsrc/common/malloc.s @@ -131,6 +131,7 @@ _malloc: sta ptr1 bcc @L1 inc ptr1+1 + beq OutOfHeapSpace ; if high byte's 0, we overflowed! @L1: ldx ptr1+1 bne @L2 cmp #HEAP_MIN_BLOCKSIZE+1 @@ -336,4 +337,3 @@ RetUserPtr: bcc @L9 inx @L9: rts - diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c deleted file mode 100644 index c9ac1652c4..0000000000 --- a/libsrc/common/mktime.c +++ /dev/null @@ -1,184 +0,0 @@ -/*****************************************************************************/ -/* */ -/* mktime.c */ -/* */ -/* Make calendar time from broken down time and cleanup */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include -#include -#include -#include "_is_leap_year.h" - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -#define JANUARY 0 -#define FEBRUARY 1 -#define DECEMBER 11 -#define JAN_1_1970 4 /* 1/1/1970 is a thursday */ - - - -static const unsigned char MonthLength [] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; -static const unsigned MonthDays [] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -time_t __fastcall__ mktime (register struct tm* TM) -/* Make a time in seconds since 1/1/1970 from the broken down time in TM. -** A call to mktime does also correct the time in TM to contain correct -** values. -*/ -{ - register div_t D; - static int Max; - static unsigned DayCount; - - /* Check if TM is valid */ - if (TM == 0) { - /* Invalid data */ - return (time_t) -1L; - } - - /* Adjust seconds. */ - D = div (TM->tm_sec, 60); - TM->tm_sec = D.rem; - - /* Adjust minutes */ - TM->tm_min += D.quot; - D = div (TM->tm_min, 60); - TM->tm_min = D.rem; - - /* Adjust hours */ - TM->tm_hour += D.quot; - D = div (TM->tm_hour, 24); - TM->tm_hour = D.rem; - - /* Adjust days */ - TM->tm_mday += D.quot; - - /* Adjust year */ - while (1) { - Max = 365UL + IsLeapYear (TM->tm_year); - if ((unsigned int)TM->tm_mday > Max) { - ++TM->tm_year; - TM->tm_mday -= Max; - } else { - break; - } - } - - /* Adjust month and year. This is an iterative process, since changing - ** the month will change the allowed days for this month. - */ - while (1) { - - /* Make sure, month is in the range 0..11 */ - D = div (TM->tm_mon, 12); - TM->tm_mon = D.rem; - TM->tm_year += D.quot; - - /* Now check if mday is in the correct range, if not, correct month - ** and eventually year and repeat the process. - */ - if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) { - Max = 29; - } else { - Max = MonthLength[TM->tm_mon]; - } - if ((unsigned int)TM->tm_mday > Max) { - /* Must correct month and eventually, year */ - if (TM->tm_mon == DECEMBER) { - TM->tm_mon = JANUARY; - ++TM->tm_year; - } else { - ++TM->tm_mon; - } - TM->tm_mday -= Max; - } else { - /* Done */ - break; - } - } - - /* Ok, all time/date fields are now correct. Calculate the days in this - ** year. - */ - TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1; - if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) { - ++TM->tm_yday; - } - - /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to - ** somewhere in 2106) all years dividable by 4 are leap years(1), - ** so dividing by 4 gives the days that must be added because of leap years. - ** (and the last leap year before 1970 was 1968) - ** (1): Exception on 2100, which is not leap, and handled just after. - */ - DayCount = ((unsigned) (TM->tm_year-70)) * 365U + - (((unsigned) (TM->tm_year-(68+1))) / 4) + - TM->tm_yday; - - /* Handle the 2100 exception */ - if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) { - DayCount--; - } else if (TM->tm_year > 200) { - DayCount--; - } - - /* Calculate the weekday */ - TM->tm_wday = (JAN_1_1970 + DayCount) % 7; - - /* No (US) daylight saving (for now) */ - TM->tm_isdst = 0; - - /* Return seconds since 1970 */ - return DayCount * 86400UL + - ((unsigned) TM->tm_hour) * 3600UL + - ((unsigned) TM->tm_min) * 60U + - ((unsigned) TM->tm_sec) - - _tz.timezone; -} diff --git a/libsrc/common/mktime.s b/libsrc/common/mktime.s new file mode 100644 index 0000000000..ac5755a457 --- /dev/null +++ b/libsrc/common/mktime.s @@ -0,0 +1,476 @@ +; +; Colin Leroy-Mira, 2024 +; +; time_t __fastcall__ mktime (register struct tm* TM) +; +; Converts a struct tm to a time_t timestamp, making sure +; day, month, year, hour, minute and seconds are in the +; correct range. +; + + .export _mktime + .import __tz + .import pushax, pusha0, pusheax + .import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax + .importzp ptr2, tmp3, sreg + + .include "time.inc" + +; ------------------------------------------------------------------------ +; Special values + +FEBRUARY = 1 +MARCH = 2 +JAN_1_1970 = 4 +N_SEC = 60 +N_MIN = 60 +N_HOUR = 24 +N_MON = 12 +N_DAY_YEAR = 365 +; ------------------------------------------------------------------------ +; Helpers + + ; Helper to shift overflows from one field to the next + ; Current field in Y, divisor in A + ; Keeps remainder in current field, and adds the quotient + ; to the next one +adjust_field: + pha ; Push divisor + iny ; Point to high byte of current field + lda (ptr2),y + tax + dey + sty tmp3 ; Store current field (_div will mess with + lda (ptr2),y ; tmp1 and tmp2) + jsr pushax + pla ; Load divisor + ldx #$00 + + jsr _div + + ldy tmp3 ; Store remainder in current field + sta (ptr2),y + iny + txa + sta (ptr2),y + + lda sreg ; Add quotient to next field + iny + clc + adc (ptr2),y + sta (ptr2),y + iny + lda sreg+1 + adc (ptr2),y + sta (ptr2),y + rts + + ; Returns 1 in A if the given year is a leap year. Expects a year + ; from 0 to 206, without 1900 added. +is_leap_year: + cmp #$00 ; Y 0 (1900) is not a leap year + beq not_leap + cmp #$C8 ; Y 200 (2100) is not a leap year + beq not_leap + and #$03 ; Year % 4 == 0 means leap year + bne not_leap + lda #$01 ; Return 1 + rts +not_leap: + lda #$00 ; Return 0 + rts + + ; Returns the number of days in the current month/year in A +get_days_in_month: + ldy #tm::tm_mon + lda (ptr2),y + tax + lda months_len,x + cpx #FEBRUARY + beq :+ + rts +: tax + ldy #tm::tm_year ; Adjust for leap years + lda (ptr2),y + jsr is_leap_year + beq :+ + inx +: txa + rts + + ; Add AX to counter +addaxcounter: + clc + adc Counter + sta Counter ; Store in Counter + txa + adc Counter+1 + sta Counter+1 + rts + + ; Helpers for long chain of arithmetic on day counter. + ; Reload Counter and push it on the stack +load_and_push_counter: + lda Counter+3 + sta sreg+1 + lda Counter+2 + sta sreg + lda Counter + ldx Counter+1 + jsr pusheax + rts + + ; Store result in AX:sreg to Counter +store_counter: + sta Counter + stx Counter+1 + lda sreg + sta Counter+2 + lda sreg+1 + sta Counter+3 + rts + +; ------------------------------------------------------------------------ +; Code + +_mktime: + sta ptr2 ; Store struct to ptr2, which arithmetic + stx ptr2+1 ; functions won't touch + + ; Check pointer validity + ora ptr2+1 + bne :+ + lda #$FF + tax + sta sreg + sta sreg+1 + rts + + ; Adjust seconds +: ldy #tm::tm_sec + lda #N_SEC + jsr adjust_field + + ; Adjust minutes + ldy #tm::tm_min + lda #N_MIN + jsr adjust_field + + ; Adjust hours + ldy #tm::tm_hour + lda #N_HOUR + jsr adjust_field + + ;Shift one year as long as tm_mday is more than a year + ldy #tm::tm_year + lda (ptr2),y + +dec_by_year: + jsr is_leap_year ; Compute max numbers of days in year + clc + adc #N_DAY_YEAR + beq :+ ; High byte equal, check low byte + bcs do_year_dec ; High byte greater, decrement + bcc dec_by_month ; Low byte lower, we're done +: dey + lda (ptr2),y + cmp Max + bcc dec_by_month + beq dec_by_month + +do_year_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sbc Max ; Carry already set + sta (ptr2),y + iny + lda (ptr2),y + sbc #>N_DAY_YEAR + sta (ptr2),y + + ; Increment year + ldy #tm::tm_year + lda (ptr2),y + clc + adc #1 + sta (ptr2),y ; No carry possible here either + bcc dec_by_year ; bra, go check next year + +dec_by_month: + ; We're done decrementing days by full years, now do it + ; month per month. + ldy #tm::tm_mon + lda #N_MON + jsr adjust_field + + ; Get max day for this month + jsr get_days_in_month + sta Max + + ; So, do we have more days than this month? + ldy #tm::tm_mday+1 + lda (ptr2),y + bne do_month_dec ; High byte not zero, sure we do + dey + lda (ptr2),y + cmp Max + bcc calc_tm_yday ; No + beq calc_tm_yday + +do_month_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sec + sbc Max + sta (ptr2),y + iny + lda (ptr2),y + sbc #$00 + sta (ptr2),y + + ; Increment month + ldy #tm::tm_mon + lda (ptr2),y + clc + adc #1 + sta (ptr2),y + + bne dec_by_month ; Check next month + +calc_tm_yday: + ; We finished decrementing tm_mday and have put it in the correct + ; year/month range. Now compute the day of the year. + ldy #tm::tm_mday ; Get current day of month + lda (ptr2),y + sta Counter ; Store it in Counter + + lda #$00 ; Init counter high bytes + sta Counter+1 + sta Counter+2 + sta Counter+3 + + ldy #tm::tm_mon ; Get current month + lda (ptr2),y + asl + tax + clc + lda yday_by_month,x ; Get yday for this month's start + adc Counter ; Add it to counter + sta Counter + inx + lda yday_by_month,x + adc Counter+1 + sta Counter+1 + + ldy #tm::tm_year ; Adjust for leap years (if after feb) + lda (ptr2),y + jsr is_leap_year + beq dec_counter + ldy #tm::tm_mon ; Leap year, get current month + lda (ptr2),y + cmp #MARCH + bcs store_yday + +dec_counter: + lda Counter ; Decrease counter by one (yday starts at 0), + bne :+ ; unless we're after february in a leap year + dec Counter+1 +: dec Counter + +store_yday: + ldy #tm::tm_yday ; Store tm_yday + lda Counter + sta (ptr2),y + iny + lda Counter+1 + sta (ptr2),y + + ; Now calculate total day count since epoch with the formula: + ; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970) + ; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970) + ; TM->tm_yday (number of days in this year) + + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #70 + ldx #0 + jsr pushax + lda #N_DAY_YEAR + + jsr tosumulax + jsr addaxcounter + + ; Add one day per leap year + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #69 + ldx #0 + jsr shrax2 ; Divide by 4 + + jsr addaxcounter + + ; Handle the 2100 exception (which was considered leap by "Add one day + ; per leap year" just before) + ldy #tm::tm_year ; Get full years + lda (ptr2),y + cmp #201 + bcc finish_calc ; <= 200, nothing to do + + lda Counter + bne :+ + dec Counter+1 +: dec Counter + +finish_calc: + ; Now we can compute the weekday. + lda Counter + clc + adc #JAN_1_1970 + pha + lda Counter+1 + adc #0 + tax + pla + jsr pushax + + lda #7 ; Modulo 7 + ldx #0 + jsr tosumodax + + ldy #tm::tm_wday ; Store tm_wday + sta (ptr2),y + iny + txa + sta (ptr2),y + + ; DST + lda #$00 ; Store tm_isdst + ldy #tm::tm_isdst + sta (ptr2),y + iny + sta (ptr2),y + + ; Our struct tm is all fixed and every field calculated. + ; We can finally count seconds according to this formula: + ; seconds = (full days since epoch) * 86400UL + + ; ((unsigned) TM->tm_hour) * 3600UL + + ; ((unsigned) TM->tm_min) * 60U + + ; ((unsigned) TM->tm_sec) - + ; _tz.timezone; + + ; We already have the number of days since epoch in our counter, + ; from just before when we computed tm_wday. Reuse it. + jsr load_and_push_counter + lda #$00 ; Multiply by 86400 + sta sreg+1 + lda #$01 + sta sreg + lda #$80 + ldx #$51 + jsr tosumuleax + jsr store_counter ; Store into counter + + ; Push counter to add 3600 * hours to it + jsr load_and_push_counter + + ldx #$00 ; Load hours + stx sreg + stx sreg+1 + ldy #tm::tm_hour + lda (ptr2),y + jsr pusheax ; Push + ldx #$00 ; Load 3600 + stx sreg + stx sreg+1 + lda #<3600 + ldx #>3600 + jsr tosumuleax ; Multiply (pops the pushed hours) + jsr tosaddeax ; Add to counter (pops the pushed counter) + jsr store_counter ; Store counter + + ; Push counter to add 60 * min to it + jsr load_and_push_counter + + ldy #tm::tm_min ; Load minutes + lda (ptr2),y + jsr pusha0 ; Push + lda #N_MIN + ldx #0 + stx sreg + stx sreg+1 + jsr tosumulax ; Multiply + jsr tosaddeax ; Add to pushed counter + jsr store_counter ; Store + + ; Add seconds + jsr load_and_push_counter + + ldy #tm::tm_sec ; Load seconds + lda (ptr2),y + ldx #0 + stx sreg + stx sreg+1 + jsr tosaddeax ; Simple addition there + + ; No need to store/load/push the counter here, simply to push it + ; for the last substraction + jsr pusheax + + ; Substract timezone + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tossubeax + + ; And we're done! + rts + + .data + +months_len: + .byte 31 + .byte 28 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + +yday_by_month: + .word 0 + .word 31 + .word 59 + .word 90 + .word 120 + .word 151 + .word 181 + .word 212 + .word 243 + .word 273 + .word 304 + .word 334 + + + .bss + +Max: .res 1 ; We won't need a high byte +Counter: + .res 4 diff --git a/libsrc/common/pmemalign.c b/libsrc/common/pmemalign.c index 52adb240d3..4499084d13 100644 --- a/libsrc/common/pmemalign.c +++ b/libsrc/common/pmemalign.c @@ -50,7 +50,6 @@ */ - int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Allocate a block of memory with the given "size", which is aligned to a ** memory address that is a multiple of "alignment". "alignment" MUST NOT be @@ -64,20 +63,27 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) size_t rawsize; size_t uppersize; size_t lowersize; + char err; register struct usedblock* b; /* points to raw Block */ register struct usedblock* u; /* points to User block */ register struct usedblock* p; /* Points to upper block */ /* Handle requests for zero-sized blocks */ if (size == 0) { +err_einval: + err = EINVAL; +err_out: *memptr = NULL; - return EINVAL; + return err; } - /* Test alignment: is it a power of two? There must be only one bit set. */ - if (alignment == 0 || (alignment & (alignment - 1)) != 0) { - *memptr = NULL; - return EINVAL; + /* Test alignment: is it a power of two? There must be one and only one bit set. */ + if (alignment == 0) { + goto err_einval; + } + + if (alignment & (alignment - 1)) { + goto err_einval; } /* Augment the block size up to the alignment, and allocate memory. @@ -90,8 +96,8 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size) /* Handle out-of-memory */ if (b == NULL) { - *memptr = NULL; - return ENOMEM; + err = ENOMEM; + goto err_out; } /* Create (and return) a new pointer that points to the user-visible diff --git a/libsrc/common/putenv.s b/libsrc/common/putenv.s index 5febcc71ec..13f0e7dc4f 100644 --- a/libsrc/common/putenv.s +++ b/libsrc/common/putenv.s @@ -10,7 +10,7 @@ .import _malloc, _free .import searchenv, copyenvptr .import __environ, __envcount, __envsize - .import return0 + .import return0, ___directerrno .import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp .include "errno.inc" @@ -169,10 +169,7 @@ addentry: ; Error entries nomem: lda #ENOMEM -error: jsr ___seterrno - lda #$FF ; Return -1 - tax - rts +error: jmp ___directerrno .endproc @@ -184,5 +181,3 @@ error: jsr ___seterrno name: .addr 0 ; Pointer to name newsize: .byte 0 ; New environment size - - diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c deleted file mode 100644 index eeb1eeea59..0000000000 --- a/libsrc/common/realloc.c +++ /dev/null @@ -1,112 +0,0 @@ -/*****************************************************************************/ -/* */ -/* realloc.c */ -/* */ -/* Change the size of an allocated memory block */ -/* */ -/* */ -/* */ -/* (C) 1998-2004 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include -#include -#include <_heap.h> - - - -void* __fastcall__ realloc (void* block, register size_t size) -{ - register struct usedblock* b; - struct usedblock* newblock; - unsigned oldsize; - unsigned newhptr; - - /* Check the block parameter */ - if (!block) { - /* Block is NULL, same as malloc */ - return malloc (size); - } - - /* Check the size parameter */ - if (size == 0) { - /* Block is not NULL, but size is: free the block */ - free (block); - return 0; - } - - /* Make the internal used size from the given size */ - size += HEAP_ADMIN_SPACE; - if (size < sizeof (struct freeblock)) { - size = sizeof (struct freeblock); - } - - /* The word below the user block contains a pointer to the start of the - ** raw memory block. The first word of this raw memory block is the full - ** size of the block. Get a pointer to the real block, get the old block - ** size. - */ - b = (((struct usedblock*) block) - 1)->start; - oldsize = b->size; - - /* Is the block at the current heap top? */ - if (((unsigned) b) + oldsize == ((unsigned) __heapptr)) { - /* Check if we've enough memory at the heap top */ - newhptr = ((unsigned) __heapptr) - oldsize + size; - if (newhptr <= ((unsigned) __heapend)) { - /* Ok, there's space enough */ - __heapptr = (unsigned*) newhptr; - b->size = size; - b->start = b; - return block; - } - } - - /* The given block was not located on top of the heap, or there's no - ** room left. Try to allocate a new block and copy the data. - */ - if (newblock = malloc (size)) { - - /* Adjust the old size to the user visible portion */ - oldsize -= HEAP_ADMIN_SPACE; - - /* If the new block is larger than the old one, copy the old - ** data only - */ - if (size > oldsize) { - size = oldsize; - } - - /* Copy the block data */ - memcpy (newblock, block, size); - free (block); - } - return newblock; -} - - - diff --git a/libsrc/common/realloc.s b/libsrc/common/realloc.s new file mode 100644 index 0000000000..925ac3d19e --- /dev/null +++ b/libsrc/common/realloc.s @@ -0,0 +1,213 @@ +; +; Colin Leroy-Mira, 2024 +; +; void* __fastcall__ realloc (void* block, register size_t size) +; + + .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp + .import _malloc, _memcpy, _free + .import pushax, popptr1, return0 + .import incsp2, decsp2 + .export _realloc + + .include "_heap.inc" + + .macpack generic + +;---------------------------------------------------------------------------- +; Aliases for clarity + +block = ptr1 +size = ptr2 +ublock = ptr3 +oldsize = ptr4 +newblock = tmp1 ; (and tmp2) +orgblock = tmp3 ; (and tmp4) + +;---------------------------------------------------------------------------- +; Code + +_realloc: + sta size ; Store size + stx size+1 + + jsr popptr1 ; Pop block + + lda block+1 ; Is block null? + tax + ora block + bne :+ + + lda size ; Block is null, just malloc + ldx size+1 + jmp _malloc + +: lda size ; Is size 0? + ora size+1 + bne :+ + + lda block ; It is: free block (high byte already in X) + jsr _free + jmp return0 + +: clc ; Add internal used size + lda size + adc #HEAP_ADMIN_SPACE + sta size + bcc :+ + inc size+1 + bne :+ + + lda #$00 ; Size high byte now 0: We overflowed! + tax + rts + +: ldx size+1 ; Should we round size up? + bne :+ + cmp #.sizeof (freeblock) + bcs :+ + + lda #.sizeof (freeblock) + sta size ; (we presuppose that sizeof (freeblock) is < 256) + +: lda block ; Get pointer to raw memory block + sta orgblock ; Store original pointer + sec + sbc #.sizeof(usedblock) + sta ublock + lda block+1 + sta orgblock+1 ; Finish storing original pointer + sbc #0 + sta ublock+1 ; We have our usedblock struct + + ; Get block start + ldy #usedblock::start+1 + lda (ublock),y + tax ; Backup ublock high + dey + lda (ublock),y + + sta ublock ; Store ublock + stx ublock+1 + + ; Remember oldsize + ldy #usedblock::size+1 + lda (ublock),y + sta oldsize+1 + dey + lda (ublock),y + sta oldsize + + clc ; Is the block at heap top? + adc ublock + tay + lda ublock+1 + adc oldsize+1 + cmp ___heapptr+1 + bne must_malloc_new + cpy ___heapptr + bne must_malloc_new + + tya ; Put ___heapptr back in A + sec ; Check if we have enough memory at heap top + sbc oldsize ; Substract oldsize + sta newblock + lda ___heapptr+1 + sbc oldsize+1 + sta newblock+1 + clc + lda newblock ; And add size + adc size + sta newblock + lda newblock+1 + adc size+1 + sta newblock+1 + bcs must_malloc_new ; If we have a carry there we overflowed + + cmp ___heapend+1 + bne :+ + lda newblock + cmp ___heapend +: bcc :+ + bne must_malloc_new + +: lda newblock ; There is enough space + sta ___heapptr ; Update heapptr + lda newblock+1 + sta ___heapptr+1 + + ldy #usedblock::start+1 + lda ublock+1 + sta (ublock),y ; Update block start + dey + lda ublock + sta (ublock),y + dey + + .assert usedblock::size = usedblock::start-2, error + lda size+1 + sta (ublock),y ; Update block size + dey + lda size + sta (ublock),y + + lda orgblock ; Return original block + ldx orgblock+1 + rts + +must_malloc_new: ; The block is not at heap top, or too big + lda size+1 + pha ; Backup new size (at this point the only ptr + tax ; we'll need after malloc). tmp* are safe + lda size ; from malloc, memcpy and free. + pha + jsr _malloc + + cmp #$00 ; Did malloc succeed? + bne :+ + cpx #$00 + bne :+ + pla ; Pop size backup and return NULL + pla + txa ; X already 0 + rts ; No + +: sta newblock ; Yes, store newblock + stx newblock+1 + jsr pushax ; Push newblock for memcpy + + lda orgblock ; Push orgblock for memcpy + ldx orgblock+1 + jsr pushax + + sec ; Remove admin space from oldsize + lda oldsize + sbc #HEAP_ADMIN_SPACE + sta oldsize+1 + + pla ; Restore new size to AX + tay + pla + tax + tya + + cmp oldsize ; Find the smallest size + bcc :+ + cpx oldsize+1 + bcc :+ + + lda oldsize + ldx oldsize+1 + +: jsr _memcpy ; And copy data + + lda orgblock ; Free old block + ldx orgblock+1 + jsr _free + + lda newblock ; Return new block + ldx newblock+1 + rts diff --git a/libsrc/common/ungetc.s b/libsrc/common/ungetc.s index 7e8c1f94f0..c661600afa 100644 --- a/libsrc/common/ungetc.s +++ b/libsrc/common/ungetc.s @@ -62,10 +62,6 @@ ; File is not open or the character is invalid error: lda #EINVAL - jsr ___seterrno - lda #$FF ; Return -1 - tax - rts + jmp ___directerrno .endproc - diff --git a/libsrc/geos-common/graph/bitotherclip.s b/libsrc/geos-common/graph/bitotherclip.s index 020139da81..fba00d9664 100644 --- a/libsrc/geos-common/graph/bitotherclip.s +++ b/libsrc/geos-common/graph/bitotherclip.s @@ -6,7 +6,7 @@ ; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy, ; struct iconpic *myGfx); -; both proc1, proc2 should be: char __fastcall something (void); +; both proc1, proc2 should be: char foo (void); ; proc1 is called before reading a byte (.A returns next data) ; proc2 is called before reading each byte which is not pattern (code >219) diff --git a/libsrc/lynx/lseek.s b/libsrc/lynx/lseek.s index 4b4f94d7c2..04d8169452 100644 --- a/libsrc/lynx/lseek.s +++ b/libsrc/lynx/lseek.s @@ -18,7 +18,7 @@ .import ldeaxysp, decsp2, pushax, incsp8 .import tosandeax,decax1,tosdiveax,axlong,ldaxysp .import lynxskip0, lynxblock,tosasreax - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .importzp _FileCurrBlock .segment "CODE" @@ -32,15 +32,15 @@ jsr ldeaxysp jsr pusheax ldx #$00 - lda #<(__BLOCKSIZE__/1024 + 9) + lda #<(__BANK0BLOCKSIZE__/1024 + 9) jsr tosasreax sta _FileCurrBlock jsr lynxblock ldy #$05 jsr ldeaxysp jsr pusheax - lda #<(__BLOCKSIZE__-1) - ldx #>(__BLOCKSIZE__-1) + lda #<(__BANK0BLOCKSIZE__-1) + ldx #>(__BANK0BLOCKSIZE__-1) jsr axlong jsr tosandeax eor #$FF diff --git a/libsrc/lynx/lynx-cart.s b/libsrc/lynx/lynx-cart.s index 94edff6771..d1f3e33ebf 100644 --- a/libsrc/lynx/lynx-cart.s +++ b/libsrc/lynx/lynx-cart.s @@ -17,7 +17,7 @@ .include "extzp.inc" .export lynxskip0, lynxread0 .export lynxblock - .import __BLOCKSIZE__ + .import __BANK0BLOCKSIZE__ .code @@ -88,7 +88,7 @@ lynxblock: lda __iodat sta IODAT stz _FileBlockByte - lda #<($100-(>__BLOCKSIZE__)) + lda #<($100-(>__BANK0BLOCKSIZE__)) sta _FileBlockByte+1 ply plx diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s index f16a1721ab..df3e5df403 100644 --- a/libsrc/lynx/uploader.s +++ b/libsrc/lynx/uploader.s @@ -33,7 +33,7 @@ loop1: cont1: jsr read_byte sta (load_ptr2),y - sta PALETTE ; feedback ;-) + sta PALETTE + 1 ; feedback ;-) iny bne loop1 inc load_ptr2+1 @@ -69,6 +69,8 @@ again: ; last action : clear interrupt ; exit: + lda #$10 + sta INTRST clc rts diff --git a/libsrc/runtime/aslax7.s b/libsrc/runtime/aslax7.s new file mode 100644 index 0000000000..533ee55e6d --- /dev/null +++ b/libsrc/runtime/aslax7.s @@ -0,0 +1,21 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shlax7, aslax7 + +aslax7: +shlax7: ; XXXXXXXL AAAAAAAl + tay + txa + lsr ; XXXXXXXL -> 0XXXXXXX, L->C + tya + ror ; AAAAAAAl -> LAAAAAAA, l->C + tax + lda #$00 ; LAAAAAAA 00000000 + ror ; LAAAAAAA l0000000 + rts + + ; 10 bytes, 16 cycles + rts diff --git a/libsrc/runtime/asrax7.s b/libsrc/runtime/asrax7.s new file mode 100644 index 0000000000..3c9cce681c --- /dev/null +++ b/libsrc/runtime/asrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, signed +; + + .export asrax7 + +asrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + dex ; 11111111 XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/runtime/popptr1.s b/libsrc/runtime/popptr1.s index 1d04330ab4..b54bb9eb3a 100644 --- a/libsrc/runtime/popptr1.s +++ b/libsrc/runtime/popptr1.s @@ -8,12 +8,18 @@ .import incsp2 .importzp sp, ptr1 + .macpack cpu + .proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved) ldy #1 lda (sp),y ; get hi byte sta ptr1+1 ; into ptr hi - dey ; no optimization for 65C02 here to have Y=0 at exit! + dey ; dey even for for 65C02 here to have Y=0 at exit! +.if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) ; get lo byte +.else lda (sp),y ; get lo byte +.endif sta ptr1 ; to ptr lo jmp incsp2 .endproc diff --git a/libsrc/runtime/pushax.s b/libsrc/runtime/pushax.s index ac181b9948..27ddf641dd 100644 --- a/libsrc/runtime/pushax.s +++ b/libsrc/runtime/pushax.s @@ -7,6 +7,8 @@ .export push0, pusha0, pushax .importzp sp + .macpack cpu + push0: lda #0 pusha0: ldx #0 @@ -29,7 +31,11 @@ pusha0: ldx #0 sta (sp),y ; (27) pla ; (31) dey ; (33) +.if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) ; (37) +.else sta (sp),y ; (38) - rts ; (44) +.endif + rts ; (44/43) .endproc diff --git a/libsrc/runtime/pushptr1.s b/libsrc/runtime/pushptr1.s new file mode 100644 index 0000000000..ca934d6c1f --- /dev/null +++ b/libsrc/runtime/pushptr1.s @@ -0,0 +1,14 @@ +; +; Colin Leroy-Mira, 2024 +; +; CC65 runtime: Push ptr1 to stack. +; A/X destroyed (set to ptr1) + + .export pushptr1 + .import pushax + .importzp ptr1 + +pushptr1: + lda ptr1 + ldx ptr1+1 + jmp pushax diff --git a/libsrc/runtime/returnFFFF.s b/libsrc/runtime/returnFFFF.s new file mode 100644 index 0000000000..41180651b8 --- /dev/null +++ b/libsrc/runtime/returnFFFF.s @@ -0,0 +1,15 @@ +; +; Ullrich von Bassewitz, 25.10.2000 +; +; CC65 runtime: Return -1 in a/x +; + + .export returnFFFF + +.proc returnFFFF + + lda #$FF + tax + rts + +.endproc diff --git a/libsrc/runtime/shrax7.s b/libsrc/runtime/shrax7.s new file mode 100644 index 0000000000..31712ca721 --- /dev/null +++ b/libsrc/runtime/shrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shrax7 + +shrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + inx ; 0000000H XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/sim6502/paravirt.s b/libsrc/sim6502/paravirt.s index 0d8e528b17..3bd40fbe4d 100644 --- a/libsrc/sim6502/paravirt.s +++ b/libsrc/sim6502/paravirt.s @@ -8,10 +8,15 @@ ; .export exit, args, _open, _close, _read, _write + .export __sysremove, ___osmaperrno +__sysremove := $FFF2 +___osmaperrno := $FFF3 _open := $FFF4 _close := $FFF5 _read := $FFF6 _write := $FFF7 args := $FFF8 exit := $FFF9 + + ; $FFFA-FFFF are hardware vectors, extend before not after! diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile index 74c415fdc1..08bb2a7809 100644 --- a/samples/kim1/Makefile +++ b/samples/kim1/Makefile @@ -31,9 +31,12 @@ else LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) endif -EXELIST_kim1 = \ - kimHello.bin \ - kimSieve.bin +EXELIST_kim1 = \ + kimHello.bin \ + kimSieve.bin \ + kimLife.bin \ + kimTest.bin \ + kimGFX.bin ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -50,13 +53,65 @@ else @echo > $(NULLDEV) endif +subs.o: subs.asm + $(AS) subs.asm -o subs.o + +ramfont.o: ramfont.asm + $(AS) ramfont.asm -o ramfont.o + +kimLife.bin: kimLife.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimLife.bin kimLife.c + +kimTest.bin: kimTest.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimTest.bin kimTest.c + +kimGFX.bin: kimGFX.c subs.o ramfont.o + $(CL) -t kim1 --listing kimGFX.lst -C kim1-mtuE000.cfg -o kimGFX.bin kimGFX.c subs.o ramfont.o -Ln kimgfx.lbl + kimSieve.bin: kimSieve.c $(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c kimHello.bin: kimHello.c $(CL) -t kim1 -O -o kimHello.bin kimHello.c +# To build an intel-format file for the CORSHAM SD card reader + +kimLife.hex: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.hex -Intel -address-length=2 + +kimTest.hex: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.hex -Intel -address-length=2 + +kimGFX.hex: kimGFX.bin ramfont.o + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.hex -Intel -address-length=2 + +# To build a paper tape file for uploading to the KIM-1 via terminal + +kimLife.ptp: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.ptp -MOS_Technologies + +kimGFX.ptp: kimGFX.bin + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.ptp -MOS_Technologies + +kimTest.ptp: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.ptp -MOS_Technologies + clean: @$(DEL) kimSieve.bin 2>$(NULLDEV) @$(DEL) kimHello.bin 2>$(NULLDEV) + @$(DEL) kimLife.bin 2>$(NULLDEV) + @$(DEL) kimLife.ptp 2>$(NULLDEV) + @$(DEL) kimLife.hex 2>$(NULLDEV) + @$(DEL) kimTest.bin 2>$(NULLDEV) + @$(DEL) kimTest.ptp 2>$(NULLDEV) + @$(DEL) kimTest.hex 2>$(NULLDEV) + @$(DEL) kimGFX.bin 2>$(NULLDEV) + @$(DEL) kimGFX.ptp 2>$(NULLDEV) + @$(DEL) kimGFX.hex 2>$(NULLDEV) + @$(DEL) kimgfx.lbl 2>$(NULLDEV) + @$(DEL) kimGFX.lst 2>$(NULLDEV) + @$(DEL) subs.o 2>$(NULLDEV) + @$(DEL) ramfont.o 2>$(NULLDEV) + + diff --git a/samples/kim1/font.rom b/samples/kim1/font.rom new file mode 100644 index 0000000000..e69de29bb2 diff --git a/samples/kim1/kimGFX.c b/samples/kim1/kimGFX.c new file mode 100644 index 0000000000..45daafa1e5 --- /dev/null +++ b/samples/kim1/kimGFX.c @@ -0,0 +1,290 @@ +// -------------------------------------------------------------------------- +// Simple Graphics Test for KIM-1 with MTU Visible Memory Board +// +// Assumes the MTU Visible Memory Board mapped at 0xA000 for 8K of video RAM +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy +#include + +typedef unsigned char byte; + +extern void ClearScreen(void); // In subs.asm +extern void ScrollScreen(void); +extern void DrawCircle(void); +extern void SetPixel(void); +extern void ClearPixel(void); +extern void DrawChar(void); +extern void Demo(void); +extern void __fastcall__ Delay(byte loops); +extern void __fastcall__ DrawLine(byte bSet); +extern byte __fastcall__ AscToPet(byte in); +extern byte __fastcall__ PetToAsc(byte in); +extern byte __fastcall__ ReverseBits(byte in); +extern void __fastcall__ CharOut(byte asci_char); +extern byte __fastcall__ getch(); +extern unsigned char font8x8_basic[256][8]; + +extern int x1cord; +extern int y1cord; +extern int x2cord; +extern int y2cord; +extern int cursorX; +extern int cursorY; + +// If in zeropage: +// +// #pragma zpsym("x1cord") +// #pragma zpsym("x2cord") +// #pragma zpsym("y1cord") +// #pragma zpsym("y2cord") + +// Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte + +byte * screen = (byte *) 0xA000; + +// Cursor position + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define CHARWIDTH 8 +#define CHARHEIGHT 8 +#define BYTESPERROW (SCREEN_WIDTH / 8) +#define BYTESPERCHARROW (BYTESPERROW * 8) +#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH) +#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT) + +// SETPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Draws a pixel on the screen in white or black at pixel pos x, y + +void SETPIXEL(int x, int y, byte b) +{ + x1cord = x; + y1cord = y; + + if (b) + SetPixel(); + else + ClearPixel(); +} + +// DRAWPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Turns on a screen pixel at pixel pos x,y + +void DRAWPIXEL(int x, int y) +{ + x1cord = x; + y1cord = y; + SetPixel(); +} + +int c; + +void DrawText(char * psz) +{ + while (*psz) + { + while (cursorX >= CHARSPERROW) + { + cursorX -= CHARSPERROW; + cursorY += 1; + } + + // If we've gone off the bottom of the screen, we scroll the screen and back up to the last line again + + if (cursorY >= ROWSPERCOLUMN) + { + cursorY = ROWSPERCOLUMN - 1; + ScrollScreen(); + } + + // If we output a newline we advanced the cursor down one line and reset it to the left + + if (*psz == 0x0A) + { + cursorX = 0; + cursorY++; + psz++; + } + else + { + c = *psz; + + __asm__ ("ldx %v", cursorX); + __asm__ ("ldy %v", cursorY); + __asm__ ("lda %v", c); + DrawChar(); + cursorX++; + psz++; + } + } +} + +void DrawTextAt(int x, int y, char * psz) +{ + cursorX = x; + cursorY = y; + DrawText(psz); +} + +// Something like Bresenham's algorithm for drawing a line +/* +void DrawLine(int x0, int y0, int x1, int y1, byte val) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + while (1) + { + SETPIXEL(x0, y0, val); + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 > -dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} +*/ + +// DrawCircle +// +// Draw a circle without sin, cos, or floating point! + +void DrawCircleC(int x0, int y0, int radius, byte) +{ + x1cord = x0; + y1cord = y0; + y2cord = radius; + DrawCircle(); +} + +void DrawLineC(int x1, int y1, int x2, int y2, byte bSet) +{ + x1cord = x1; + y1cord = y1; + x2cord = x2; + y2cord = y2; + DrawLine(bSet); +} + +// MirrorFont +// +// RAM font is backwards left-right relative to the way memory is laid out on the KIM-1, so we swap all the +// bytes in place by reversing the order of the bits in every byte + +void MirrorFont() +{ + int c; + byte * pb = (byte *) font8x8_basic; + + for (c = 0; c < 128 * 8; c++) + pb[c] = ReverseBits(pb[c]); +} + +// DrawScreenMoire +// +// Draws a moire pattern on the screen without clearing it first + +void DrawMoire(int left, int top, int right, int bottom, byte pixel) +{ + int x, y; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, pixel); + + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, pixel); +} + +void DrawScreenMoire(int left, int top, int right, int bottom) +{ + int x, y; + + DrawLineC(left, top, right, top, 1); + DrawLineC(left, bottom, right, bottom, 1); + DrawLineC(left, top, left, bottom, 1); + DrawLineC(right, top, right, bottom, 1); + + left++; top++; right--; bottom--; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 1); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 1); + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 0); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 0); + +} + +int main (void) +{ + + int i; + int c = 0; + + Demo(); + + CharOut('R'); + CharOut('E'); + CharOut('A'); + CharOut('D'); + CharOut('Y'); + CharOut('.'); + CharOut('\n'); + + + while(1) + { + c = toupper(getch()); + if (c != EOF) + CharOut(c); + } + + // Clear the screen memory + while(1) + { + Demo(); + DrawScreenMoire(0,30, 319, 199); + Delay(10); + + Demo(); + for (i = 5; i < 80; i+=5) + { + DrawCircleC(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH/4, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH*3/4, SCREEN_HEIGHT/2 + 20, i, 1); + } + + Delay(10); + + } + + printf("Done, exiting...\r\n"); + return 0; +} diff --git a/samples/kim1/kimLife.c b/samples/kim1/kimLife.c new file mode 100644 index 0000000000..fb1696b9eb --- /dev/null +++ b/samples/kim1/kimLife.c @@ -0,0 +1,144 @@ +// -------------------------------------------------------------------------- +// Conway's Game of Life for KIM-1 +// +// Assumes the MTU Visible Memory Board mapped at 0x8000 for 8K of video RAM +// +// Dave Plummer on a rainy Thursday +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy + +typedef unsigned char byte; + +// World size + +#define WIDTH 320 +#define HEIGHT 200 +#define NUMBITS 64000 +#define NUMBYTES 8000 +#define DENSITY 50 + +// Screen memory is placed at 8000, our world copy at A000, and they use the same layout so +// that we can memcpy from one to the other without translating + +byte * world = (byte *) 0x8000; +byte * new_world = (byte *) 0xA000; + +// BITARRAY +// +// Access individual bits in a block of memory + +// Access to the screen bitmap + +byte GETBIT(byte *p, int n) +{ + return (p[n >> 3] & (1 << (n & 7))) ? 1 : 0; +} + +void SETBIT(byte *p, int n) +{ + p[n >> 3] |= (1 << (n & 7)); +} + +void CLRBIT(byte *p, int n) +{ + p[n >> 3] &= ~(1 << (n & 7)); +} + +void SETPIXEL(byte * p, int x, int y, byte b) +{ + if (b) + SETBIT(p, y * WIDTH + x); + else + CLRBIT(p, y * WIDTH + x); +} + +byte GETPIXEL(byte *p, int x, int y) +{ + return GETBIT(p, y * WIDTH + x); +} + +// RandomFillWorld +// +// Populates the initial world with random cells + +void RandomFillWorld() +{ + int x, y; + + // I need a better way to see the RNG or it'll be the same game every time! + srand(0); + for (x = 0; x < WIDTH; x++) + { + for (y = 0; y < HEIGHT; y++) + { + byte b = ((rand() % 100) < DENSITY) ? 1 : 0; + SETPIXEL(world, x, y, b); + } + } +} + +// CountNeighbors +// +// Count the number of live cells around the given spot, excluding the actual spot specified + +int CountNeighbors(int x, int y) +{ + int i, j, nx, ny, count = 0; + + for (j = -1; j <= 1; j++) + { + for (i = -1; i <= 1; i++) + { + if (i != 0 || j != 0) + { + nx = (x + i + WIDTH) % WIDTH; + ny = (y + j + HEIGHT) % HEIGHT; + count += GETPIXEL(world, nx, ny) ? 1 : 0; + } + } + } + return count; +} + +// UpdateWorld +// +// Applies the rules of Conway's Game of Life to the cells + +void UpdateWorld() +{ + int x, y; + + for (y = 0; y < HEIGHT; y++) + { + for (x = 0; x < WIDTH; x++) + { + int neighbors = CountNeighbors(x, y); + if (GETPIXEL(world, x, y)) + SETPIXEL(new_world, x, y, (neighbors == 2 || neighbors == 3)); + else + SETPIXEL(new_world, x, y, (neighbors == 3)); + } + } +} + +int main (void) +{ + printf("\r\nStarting Conway's Game of Life: Randomizing World...\r\n"); + RandomFillWorld(); + printf("World Ready, Running!\r\n"); + + for (;;) + { + UpdateWorld(); + printf("["); + memcpy(world, new_world, NUMBYTES); + printf("]"); + } + + return 0; +} diff --git a/samples/kim1/kimTest.c b/samples/kim1/kimTest.c new file mode 100644 index 0000000000..273aeb5843 --- /dev/null +++ b/samples/kim1/kimTest.c @@ -0,0 +1,262 @@ +// -------------------------------------------------------------------------- +// Diagnostics Test for KIM-1 +// +// Dave Plummer +// davepl@davepl.com +// +// Memory test examples by Michael Barr +// +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy + +typedef unsigned char byte; + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +/********************************************************************** + * + * Function: memTestDataBus() + * + * Description: Test the data bus wiring in a memory region by + * performing a walking 1's test at a fixed address + * within that region. The address (and hence the + * memory region) is selected by the caller. + * + * Returns: 0 if the test succeeds. + * A non-zero result is the first pattern that failed. + * + **********************************************************************/ + +byte memTestDataBus(volatile byte * address) +{ + byte pattern; + + // Perform a walking 1's test at the given address. + + for (pattern = 1; pattern != 0; pattern <<= 1) + { + // Write the test pattern. + *address = pattern; + + // Read it back and check it + if (*address != pattern) + { + printf("\r\nmemTestDataBus: FAILED at %04x with pattern %02x\r\n", address, pattern); + return (pattern); + } + } + + return (0); +} + +/********************************************************************** + * + * Function: memTestAddressBus() + * + * Description: Test the address bus wiring in a memory region by + * performing a walking 1's test on the relevant bits + * of the address and checking for aliasing. This test + * will find single-bit address failures such as stuck + * -high, stuck-low, and shorted pins. The base address + * and size of the region are selected by the caller. + * + * Notes: For best results, the selected base address should + * have enough LSB 0's to guarantee single address bit + * changes. For example, to test a 64-Kbyte region, + * select a base address on a 64-Kbyte boundary. Also, + * select the region size as a power-of-two--if at all + * possible. + * + * Returns: NULL if the test succeeds. + * A non-zero result is the first address at which an + * aliasing problem was uncovered. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestAddressBus(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long addressMask = (nBytes/sizeof(byte) - 1); + unsigned long offset; + unsigned long testOffset; + + byte pattern = (byte) 0xAAAAAAAA; + byte antipattern = (byte) 0x55555555; + + + //Write the default pattern at each of the power-of-two offsets. + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + baseAddress[offset] = pattern; + } + + // Check for address bits stuck high. + + testOffset = 0; + baseAddress[testOffset] = antipattern; + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[offset]); + } + if (offset % 1024 == 0) + printf("."); + } + + baseAddress[testOffset] = pattern; + + + // Check for address bits stuck low or shorted. + + for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1) + { + baseAddress[testOffset] = antipattern; + + if (baseAddress[0] != pattern) + { + return ((byte *) &baseAddress[testOffset]); + } + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if ((baseAddress[offset] != pattern) && (offset != testOffset)) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[testOffset]); + } + } + baseAddress[testOffset] = pattern; + } + return (NULL); +} + +/********************************************************************** + * + * Function: memTestDevice() + * + * Description: Test the integrity of a physical memory device by + * performing an increment/decrement test over the + * entire region. In the process every storage bit + * in the device is tested as a zero and a one. The + * base address and the size of the region are + * selected by the caller. + * + * Returns: NULL if the test succeeds. + * + * A non-zero result is the first address at which an + * incorrect value was read back. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestDevice(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long offset; + unsigned long nWords = nBytes / sizeof(byte); + + byte pattern; + byte antipattern; + + + // Fill memory with a known pattern. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + baseAddress[offset] = pattern; + + // Check each location and invert it for the second pass. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with pattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + + antipattern = ~pattern; + baseAddress[offset] = antipattern; + + } + + // Check each location for the inverted pattern and zero it. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + antipattern = ~pattern; + if (baseAddress[offset] != antipattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with antipattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + } + + return (NULL); +} + +// TestMemory +// +// Run all three memory tests + +byte TestMemory(byte * startAddress, unsigned long size) +{ + if ((memTestDataBus(startAddress) != 0) || + (memTestAddressBus(startAddress, size) != NULL) || + (memTestDevice(startAddress, size) != NULL)) + { + return (-1); + } + else + { + return (0); + } +} + +int main (void) +{ + printf("\r\nTesting KIM-1...\r\n"); + RepeatChar('-', 39); + + printf("\r\nTesting RIOT RAM: 1780-17BF\r\n"); + if (TestMemory((byte *)0x1780, 0x17BF - 0x1780)) + return 0; + + printf("\r\nTesting RIOT RAM: 17C0-17E6\r\n"); + if (TestMemory((byte *)0x17C0, 0x17E6 - 0x17C0)) + return 0; + + printf("\r\nTesting Memory: 0400-13FF\r\n"); + if (TestMemory((byte *)0x0400, 0x13FF - 0x0400)) + return 0; + + printf("\r\nTesting Memory: 4000-DFFF\r\n"); + if (TestMemory((byte *)0x4000, 0xDFFF - 0x4000)) + return 0; + + printf("\r\nPASS!\r\n"); + return 1; +} + + diff --git a/samples/kim1/ramfont.asm b/samples/kim1/ramfont.asm new file mode 100644 index 0000000000..ac0b2c9cda --- /dev/null +++ b/samples/kim1/ramfont.asm @@ -0,0 +1,272 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: ramfont.s +; Magnetic OCR (check number style) Font data +; +;----------------------------------------------------------------------------------- + +.segment "CODE" +.export _font8x8_basic + +_font8x8_basic: + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 0 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 1 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 2 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 3 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 4 + .byte $7e, $40, $40, $7c, $60, $60, $7e, $00 ; PETSCII code 5 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 6 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 7 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 8 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 9 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 10 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 11 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 12 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 13 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 14 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 15 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 16 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 17 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 18 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 19 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 20 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 21 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 22 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 23 + .byte $42, $42, $66, $18, $66, $62, $62, $00 ; PETSCII code 24 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 25 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 26 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 27 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 28 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 29 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 30 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 31 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 32 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 33 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 34 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 35 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 36 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 37 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 38 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 39 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 40 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 41 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 42 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 43 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 44 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 45 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 46 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 47 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 48 + .byte $18, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 49 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 50 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 51 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 52 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 53 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 54 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 55 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 56 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 57 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 58 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 59 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 60 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 61 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 62 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 63 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 64 + .byte $08, $1c, $3e, $7f, $7f, $1c, $3e, $00 ; PETSCII code 65 + .byte $10, $10, $10, $10, $10, $10, $10, $10 ; PETSCII code 66 + .byte $00, $00, $00, $ff, $00, $00, $00, $00 ; PETSCII code 67 + .byte $00, $00, $ff, $00, $00, $00, $00, $00 ; PETSCII code 68 + .byte $00, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 69 + .byte $00, $00, $00, $00, $00, $ff, $00, $00 ; PETSCII code 70 + .byte $20, $20, $20, $20, $20, $20, $20, $20 ; PETSCII code 71 + .byte $04, $04, $04, $04, $04, $04, $04, $04 ; PETSCII code 72 + .byte $00, $00, $00, $00, $e0, $10, $08, $08 ; PETSCII code 73 + .byte $08, $08, $08, $04, $03, $00, $00, $00 ; PETSCII code 74 + .byte $08, $08, $08, $10, $e0, $00, $00, $00 ; PETSCII code 75 + .byte $80, $80, $80, $80, $80, $80, $80, $ff ; PETSCII code 76 + .byte $80, $40, $20, $10, $08, $04, $02, $01 ; PETSCII code 77 + .byte $01, $02, $04, $08, $10, $20, $40, $80 ; PETSCII code 78 + .byte $ff, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 79 + .byte $ff, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 80 + .byte $00, $3c, $7e, $7e, $7e, $7e, $3c, $00 ; PETSCII code 81 + .byte $00, $00, $00, $00, $00, $00, $ff, $00 ; PETSCII code 82 + .byte $36, $7f, $7f, $7f, $3e, $1c, $08, $00 ; PETSCII code 83 + .byte $40, $40, $40, $40, $40, $40, $40, $40 ; PETSCII code 84 + .byte $00, $00, $00, $00, $03, $04, $08, $08 ; PETSCII code 85 + .byte $81, $42, $24, $18, $18, $24, $42, $81 ; PETSCII code 86 + .byte $00, $3c, $42, $42, $42, $42, $3c, $00 ; PETSCII code 87 + .byte $08, $1c, $2a, $77, $2a, $08, $08, $00 ; PETSCII code 88 + .byte $02, $02, $02, $02, $02, $02, $02, $02 ; PETSCII code 89 + .byte $08, $1c, $3e, $7f, $3e, $1c, $08, $00 ; PETSCII code 90 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 91 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 92 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 93 + .byte $00, $00, $01, $3e, $54, $14, $14, $00 ; PETSCII code 94 + .byte $ff, $7f, $3f, $1f, $0f, $07, $03, $01 ; PETSCII code 95 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 96 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 97 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 98 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 99 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 100 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 101 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 102 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 103 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 104 + .byte $ff, $fe, $fc, $f8, $f0, $e0, $c0, $80 ; PETSCII code 105 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 106 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 107 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 108 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 109 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 110 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 111 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 112 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 113 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 114 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 115 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 116 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 117 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 118 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 119 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 120 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 121 + .byte $01, $01, $01, $01, $01, $01, $01, $ff ; PETSCII code 122 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 123 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 124 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 125 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 126 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 127 + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 128 + .byte $00, $00, $3c, $04, $7c, $64, $7c, $00 ; PETSCII code 129 + .byte $40, $40, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 130 + .byte $00, $00, $7e, $42, $60, $62, $7e, $00 ; PETSCII code 131 + .byte $02, $02, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 132 + .byte $00, $00, $7e, $42, $7e, $60, $7e, $00 ; PETSCII code 133 + .byte $1e, $12, $10, $7c, $18, $18, $18, $00 ; PETSCII code 134 + .byte $00, $00, $7e, $42, $62, $7e, $02, $7e ; PETSCII code 135 + .byte $40, $40, $7e, $42, $62, $62, $62, $00 ; PETSCII code 136 + .byte $18, $00, $10, $10, $18, $18, $18, $00 ; PETSCII code 137 + .byte $0c, $00, $08, $0c, $0c, $0c, $44, $7c ; PETSCII code 138 + .byte $40, $40, $44, $48, $78, $64, $64, $00 ; PETSCII code 139 + .byte $10, $10, $10, $10, $18, $18, $18, $00 ; PETSCII code 140 + .byte $00, $00, $7f, $49, $6d, $6d, $6d, $00 ; PETSCII code 141 + .byte $00, $00, $7e, $42, $62, $62, $62, $00 ; PETSCII code 142 + .byte $00, $00, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 143 + .byte $00, $00, $7e, $42, $62, $7e, $40, $40 ; PETSCII code 144 + .byte $00, $00, $7e, $42, $46, $7e, $02, $02 ; PETSCII code 145 + .byte $00, $00, $7e, $40, $60, $60, $60, $00 ; PETSCII code 146 + .byte $00, $00, $7e, $40, $7e, $06, $7e, $00 ; PETSCII code 147 + .byte $10, $10, $7c, $10, $18, $18, $18, $00 ; PETSCII code 148 + .byte $00, $00, $42, $42, $62, $62, $7e, $00 ; PETSCII code 149 + .byte $00, $00, $62, $62, $66, $24, $3c, $00 ; PETSCII code 150 + .byte $00, $00, $49, $49, $6d, $6d, $7f, $00 ; PETSCII code 151 + .byte $00, $00, $42, $42, $3c, $62, $62, $00 ; PETSCII code 152 + .byte $00, $00, $62, $62, $42, $7e, $02, $7e ; PETSCII code 153 + .byte $00, $00, $7e, $06, $18, $60, $7e, $00 ; PETSCII code 154 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 155 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 156 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 157 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 158 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 159 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 160 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 161 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 162 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 163 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 164 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 165 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 166 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 167 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 168 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 169 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 170 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 171 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 172 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 173 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 174 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 175 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 176 + .byte $38, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 177 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 178 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 179 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 180 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 181 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 182 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 183 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 184 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 185 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 186 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 187 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 188 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 189 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 190 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 191 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 192 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 193 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 194 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 195 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 196 + .byte $7e, $40, $40, $78, $60, $60, $7e, $00 ; PETSCII code 197 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 198 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 199 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 200 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 201 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 202 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 203 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 204 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 205 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 206 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 207 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 208 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 209 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 210 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 211 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 212 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 213 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 214 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 215 + .byte $42, $42, $66, $3c, $66, $62, $62, $00 ; PETSCII code 216 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 217 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 218 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 219 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 220 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 221 + .byte $cc, $cc, $33, $33, $cc, $cc, $33, $33 ; PETSCII code 222 + .byte $cc, $66, $33, $99, $cc, $66, $33, $99 ; PETSCII code 223 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 224 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 225 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 226 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 227 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 228 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 229 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 230 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 231 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 232 + .byte $99, $33, $66, $cc, $99, $33, $66, $cc ; PETSCII code 233 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 234 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 235 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 236 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 237 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 238 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 239 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 240 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 241 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 242 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 243 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 244 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 245 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 246 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 247 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 248 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 249 + .byte $01, $02, $44, $48, $50, $60, $40, $00 ; PETSCII code 250 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 251 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 252 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 253 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 254 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 255 diff --git a/samples/kim1/subs.asm b/samples/kim1/subs.asm new file mode 100644 index 0000000000..5b525749ea --- /dev/null +++ b/samples/kim1/subs.asm @@ -0,0 +1,1140 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: subs.asm Assembly language subroutines for KIMGFX +; +;----------------------------------------------------------------------------------- + + +.SETCPU "6502" + +.export _ClearScreen +.export _ScrollScreen +.export _SetPixel +.export _ClearPixel +.export _DrawCircle +.export _DrawLine +.export _AscToPet +.export _ReverseBits +.export _DrawChar +.export _CharOut +.export _Demo +.export _Delay +.export _getch + + +.import _font8x8_basic + +; This is the assumed location of the MTU visible memory board's 8K of memory. You can adjust this +; constant to refelct other locations as needed. + +SCREEN = $A000 + +; Note that even though these constants are defined here and respected, there are still going to be +; logic assumptions in GetPixelAddress that assume a 320x200 screen. If you change these, you'll +; need to adjust GetPixelAddress to match. + +SCREEN_WIDTH = 320 +SCREEN_HEIGHT = 200 +SCREEN_BYTES = SCREEN_WIDTH * SCREEN_HEIGHT / 8 +CHARWIDTH = 8 +CHARHEIGHT = 8 +BYTESPERROW = (SCREEN_WIDTH / 8) +BYTESPERCHARROW = (BYTESPERROW * 8) +CHARSPERROW = (SCREEN_WIDTH / CHARWIDTH) +ROWSPERCOLUMN = (SCREEN_HEIGHT / CHARHEIGHT) +LASTROW = SCREEN + SCREEN_BYTES - BYTESPERCHARROW + +.segment "ZEROPAGE" + +btpt: .res 1 + +dest: +dest_lo: .res 1 +dest_hi: .res 1 + +src: +src_lo: .res 1 +src_hi: .res 1 + +adp1: +adp1_lo: .res 1 +adp1_hi: .res 1 + +adp2: +adp2_lo: .res 1 +adp2_hi: .res 1 + +scroll_src: +scroll_src_lo: .res 1 +scroll_src_hi: .res 1 + +scroll_dest: +scroll_dest_lo: .res 1 +scroll_dest_hi: .res 1 + + +.segment "DATA" + +; Arguments for graphics functions + +_x1cord: .res 2 +_x2cord: .res 2 +_y1cord: .res 2 +_y2cord: .res 2 +_cursorX: .res 1 +_cursorY: .res 1 + +; Linedraw + +dx: .res 2 +dy: .res 2 +e2: .res 2 +sx: .res 1 +sy: .res 1 +dltemp: .res 2 +pixel: .res 1 + +; DrawCircle + +xval: .res 2 ; These could move to zeropage for perf, but presume we +yval: .res 2 ; we want to minimize the amount we grow zero page use +err: .res 2 +temp: .res 2 +tempa: .res 1 +tempx: .res 1 +tempy: .res 1 +temp2: .res 2 +x0: .res 2 +y0: .res 2 + +; CharOut + +tempstr: .res 2 + +.export _x1cord ; Make sure these show up on the C side as zero page +.export _x2cord +.export _y1cord +.export _y2cord +.export _cursorX +.export _cursorY + +.segment "CODE" + +;----------------------------------------------------------------------------------- +; GetPixelAddress - Calculate the address of a pixel in the video memory +;----------------------------------------------------------------------------------- +; Based on MTU PIXADR code +;----------------------------------------------------------------------------------- +; In: _x1cord (16-bit) +; _y1cord (16-bit) +; Out: adp1 (16-bit) Address of pixel to set +;----------------------------------------------------------------------------------- + +_GetPixelAddress: + lda _x1cord ; compute bit address first + sta adp1 ; also transfer x1cord to adp1 + and #$07 ; + which is simply the low 3 bits of x + sta btpt + lda _x1cord+1 ; finish transferring x1cord to adp1 + sta adp1+1 + lsr adp1+1 ; double shift adp1 right 3 to get + ror adp1 ; int(xcord/8 ) + lsr adp1+1 + ror adp1 + lsr adp1+1 + ror adp1 + sec ; and temporary storage + lda _y1cord + sta adp2 + sta temp + lda #0 + sbc _y1cord+1 + sta adp2+1 + sta temp+1 + asl adp2 ; compute 40*(y1cord) + rol adp2+1 ; 2*(y1cord) + asl adp2 + rol adp2+1 ; 4*(y1cord) + lda adp2 ; add in temporary save of (y1cord) + clc ; to make 5*(y1cord) + adc temp + sta adp2 + lda adp2+1 + adc temp+1 + sta adp2+1 ; 5*(y1cord) + asl adp2 ; 10*(1cord) + rol adp2+1 + asl adp2 ; 20#(y1cord) + rol adp2+1 + asl adp2 ; 40*(y1cord) + rol adp2+1 + lda adp2 ; add in int(x1cord/8) computed earlier + clc + adc adp1 + sta adp1 + lda adp2+1 + adc adp1+1 + adc #>SCREEN ; add in vmorg*256 + sta adp1+1 ; final result + rts ; return + +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +; +; MSKTB1 is a table of 1 bits corresponding to bit numbers +; MSKTB2 is a table of 0 bits corresponding to bit numbers +;----------------------------------------------------------------------------------- + +msktb1: .byte $80,$40,$20,$10,$08,$04,$02,$01 +msktb2: .byte $7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE + +_Delay: pha + sta temp + txa + pha + tya + pha + +@loopa: ldx #$ff +@loopx: ldy #$ff +@loopy: dey + bne @loopy + dex + bne @loopx + dec temp + bne @loopa + + pla + tay + pla + tax + pla + rts + +;----------------------------------------------------------------------------------- +; SetPixel - Set a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +;----------------------------------------------------------------------------------- + +_SetPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb1,y ; get a byte with that bit =1, others =0 + ldy #0 + ora (adp1),y ; combine the bit with the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearPixel - Clears a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- + +_ClearPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb2,y ; get a byte with that bit =0, others =1 + ldy #0 + and (adp1),y ; remove the bit from the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearScreen - Clears the entire video memory (and thus the screen) +;----------------------------------------------------------------------------------- + +_ClearScreen: + lda #$00 + + ldx #SCREEN + stx dest_hi + + ldy #0 +: sta (dest), y ; Loop unwound by a factor of 8, which means our iny before the branchh + iny ; will still work as it's on a page crossing boundary. + sta (dest), y ; This will avoid most of the overhead of the branch. + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + bne :- + + inc dest_hi + ldx dest_hi + cpx #>SCREEN + $20 + bne :- + + rts + +;----------------------------------------------------------------------------------- +; ScrollScreen - Scrolls the entire video memory (and thus the screen) up one row +;----------------------------------------------------------------------------------- + +BYTES_TO_MOVE = SCREEN_BYTES - BYTESPERCHARROW +PAGES_TO_MOVE = BYTES_TO_MOVE / 256 + +_ScrollScreen: + pha + tya + pha + txa + pha + + ; Load the source (A140) and destination (A000) addresses + lda #<(SCREEN+BYTESPERCHARROW) + sta scroll_src_lo + lda #>(SCREEN+BYTESPERCHARROW) + sta scroll_src_hi + lda #SCREEN + sta scroll_dest_hi + + ldx #PAGES_TO_MOVE +@outerLoop: + ldy #0 +@innerLoop: ; + ; I could do this faster in self-modifying code (avoiding the zero page overhead) but then it + ; couldn't go into ROM + + lda (scroll_src),y ; I've unwound the loop to do 8 bytes at a time. Since we're doing full pages + sta (scroll_dest),y ; as long as we unwind the loop to do 8 bytes at a time, we know we'll still + iny ; do the final increment on a page boundary. + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + bne @innerLoop ; If Y overflows, it will be 0, so won't branch + inc scroll_src_hi + inc scroll_dest_hi + dex + bne @outerLoop + + ; Clear the last line + lda #LASTROW + sta scroll_dest_hi + lda #$00 + ldy #0 +fullPageLoop: + sta (scroll_dest_lo),y + iny + bne fullPageLoop + inc scroll_dest_hi +partialPageLoop: + sta (scroll_dest_lo),y + iny + cpy #BYTESPERCHARROW - 256 ; Only clear up to the 64th byte (256 + 64 == 320) + bne partialPageLoop + + pla + tax + pla + tay + pla + rts + +;----------------------------------------------------------------------------------- +; DrawCircle - Draws a circle in video memory of a given radius at a given coord +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +; radius - _y2cord (16-bit) +;----------------------------------------------------------------------------------- +; Implements the midpoint circle algorithm without floating point or trig functions +;----------------------------------------------------------------------------------- +; int x = radius; +; int y = 0; +; int err = 0; +; +; while (x >= y) +; { +; SETPIXEL(x0 + x, y0 + y, val); +; SETPIXEL(x0 + y, y0 + x, val); +; SETPIXEL(x0 - y, y0 + x, val); +; SETPIXEL(x0 - x, y0 + y, val); +; SETPIXEL(x0 - x, y0 - y, val); +; SETPIXEL(x0 - y, y0 - x, val); +; SETPIXEL(x0 + y, y0 - x, val); +; SETPIXEL(x0 + x, y0 - y, val); +; +; y++; +; err += 1 + 2 * y; +; if (2 * (err - x) + 1 > 0) { +; x--; +; err += 1 - 2 * x; +; } +; } +;----------------------------------------------------------------------------------- + +_DrawCircle: lda _x1cord ; x0 = _x1cord + sta x0 + lda _x1cord+1 + sta x0+1 + lda _y1cord ; y0 = _y1cord + sta y0 + lda _y1cord+1 + sta y0+1 + + lda _y2cord ; x = radius + sta xval + lda _y2cord+1 + sta xval+1 + + lda #$0 ; yval = 0; + sta yval + sta yval+1 + sta err ; err = 0; + sta err+1 +circleloop: + lda xval+1 ; if (xval < yval) we're done; + sec + cmp yval+1 + bcc doneCircle ; if high byteof yval is greater, we can draw + bne doCircle ; it not greater and not equal, is less, so done + lda xval ; in other cases we need to compare the LSB next + cmp yval + bcs doCircle ; if it's less, but MSB was equal, we go draw + +doneCircle: rts + +doCircle: lda x0 ; Draw the first of 8 symmetric quadrant copies + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 - x, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 - x, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 - y, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 + y, val); + + lda x0 + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 + x, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 + y, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 - y, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 + x, val); + + inc yval ; yval++; + bne :+ + inc yval+1 + +: lda yval ; temp = 2 * yval + 1; + asl + sta temp + lda yval+1 + rol + sta temp+1 + inc temp + bne :+ + inc temp+1 +: + lda err ; err += temp + clc + adc temp + sta err + lda err+1 + adc temp+1 + sta err+1 + ; if (2 * (err - xval) + 1 > 0) then dec xval + lda err ; temp = err-xval + sec + sbc xval + sta temp + lda err+1 + sbc xval+1 + sta temp+1 + + asl temp ; temp = 2*(err-xval)+1 + rol temp+1 + inc temp + bne :+ + inc temp+1 +: + lda temp+1 ; if (temp > 0) we'll dec xval + bmi doneLoop ; less than zero, so no dec + bne decxval ; if not zero, go ahead and dec + + lda temp ; MSB is zero so now check the LSB + beq doneLoop ; both bytes are zero, so no dec + +decxval: lda xval ; xval-- + bne :+ + dec xval+1 +: dec xval + +updateerr: lda xval ; temp = xval * 2 + asl + sta temp + lda xval+1 + rol + sta temp+1 + + lda #1 ; temp2 == 1-temp == 1-(xval*2) + sec + sbc temp + sta temp2 + lda #0 + sbc temp+1 + sta temp2+1 + + lda err ; err += 1-(xval*2) + clc + adc temp2 + sta err + lda err+1 + adc temp2+1 + sta err+1 + +doneLoop: jmp circleloop + +;----------------------------------------------------------------------------------- +; Character set translation tables +;----------------------------------------------------------------------------------- + +ascToPetTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f + .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f + .byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef + .byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff + +; PETSCI to Ascii lookup table - not current used, so commented out, but can be used to map fonts +; +; + petToAscTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f + .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + +;----------------------------------------------------------------------------------- +; PetToAsc - Convert a PETSCII character to ASCII +;----------------------------------------------------------------------------------- +; A - Character to convert +;----------------------------------------------------------------------------------- + +_AscToPet: tay + lda ascToPetTable, y + rts + +_PetToAsc: tay + lda petToAscTable, Y + rts + +;----------------------------------------------------------------------------------- +; ReverseBits - Reverse the bits in a byte +;----------------------------------------------------------------------------------- +; A = octet to be reversed +;----------------------------------------------------------------------------------- + +_ReverseBits: + ldx #8 ; set counter to 8 (for 8 bits) + lda #0 ; initialize A to 0 + sta temp ; clear result byte (accumulator for the reversed octet) +: asl ; shift leftmost bit of input into carry + lda temp ; load the temporary result byte into A + ror ; rotate carry into leftmost bit of result + sta temp ; store the updated result back into memory + dex ; decrement counter + bne :- ; repeat until all bits are processed + lda temp ; load the final reversed byte into A + rts ; return with result in A + +;----------------------------------------------------------------------------------- +; LoadFont - Makes sure the font data is ready to use. This usually requires +; reversing the bits so that they match the bit order of the screen +;----------------------------------------------------------------------------------- + +_LoadFont: ldx #3 + lda #<_font8x8_basic + sta adp1_lo + lda #>_font8x8_basic + sta adp1_hi + ldy #0 +@loop: lda (adp1), y + jsr _ReverseBits + sta (adp1), y + iny + bne @loop + + inc adp1_lo + bne :+ + inc adp1_hi +: dex + bne @loop + rts + +ScreenLineAddresses: + .word SCREEN + 0 * BYTESPERCHARROW, SCREEN + 1 * BYTESPERCHARROW + .word SCREEN + 2 * BYTESPERCHARROW, SCREEN + 3 * BYTESPERCHARROW + .word SCREEN + 4 * BYTESPERCHARROW, SCREEN + 5 * BYTESPERCHARROW + .word SCREEN + 6 * BYTESPERCHARROW, SCREEN + 7 * BYTESPERCHARROW + .word SCREEN + 8 * BYTESPERCHARROW, SCREEN + 9 * BYTESPERCHARROW + .word SCREEN + 10 * BYTESPERCHARROW, SCREEN + 11 * BYTESPERCHARROW + .word SCREEN + 12 * BYTESPERCHARROW, SCREEN + 13 * BYTESPERCHARROW + .word SCREEN + 14 * BYTESPERCHARROW, SCREEN + 15 * BYTESPERCHARROW + .word SCREEN + 16 * BYTESPERCHARROW, SCREEN + 17 * BYTESPERCHARROW + .word SCREEN + 18 * BYTESPERCHARROW, SCREEN + 19 * BYTESPERCHARROW + .word SCREEN + 20 * BYTESPERCHARROW, SCREEN + 21 * BYTESPERCHARROW + .word SCREEN + 22 * BYTESPERCHARROW, SCREEN + 23 * BYTESPERCHARROW + .word SCREEN + 24 * BYTESPERCHARROW + .assert( (* - ScreenLineAddresses) = ROWSPERCOLUMN * 2), error + +;----------------------------------------------------------------------------------- +; DrawChar - Draws an ASCII character at char location x, y +;----------------------------------------------------------------------------------- +; 0 <= x < 40 +; 0 <= y < 25 +; Preserves all registers, but its not very threadsafe or reentrant +;----------------------------------------------------------------------------------- + +_DrawChar: sty tempy + stx tempx + sta tempa + + tya ; Get the address in screen memory where this + asl ; character X/Y cursor pos should be drawn + tay + txa + clc + adc ScreenLineAddresses, y + sta dest_lo + lda ScreenLineAddresses+1, y + adc #0 + sta dest_hi + + lda #0 ; Get the address in font memory where this + sta src_hi ; Petscii chracter lives (after conversion from + lda tempa ; ascii) + + sty temp2 + jsr _AscToPet + ldy temp2 + + asl + rol src_hi + asl + rol src_hi + asl + rol src_hi + clc + adc #<_font8x8_basic ; Add the base address of the font table to the offset + sta src_lo + lda src_hi + adc #>_font8x8_basic + sta src_hi + + ldy #0 ; opy the character def to the screen, one byte at a time + ldx #0 +: lda (src), y ; Copy this byte from the character def to the screen target + sta (dest, x) + lda dest_lo ; Advance to the next "scanline", or pixel row, down + clc + adc #BYTESPERROW + sta dest_hi + + iny + cpy #8 + bne :- + + ldy tempy + ldx tempx + lda tempa + rts + +;----------------------------------------------------------------------------------- +; CursorOn - Turns on the text cursor and draws it at the current cursor pos +;----------------------------------------------------------------------------------- + +CursorOn: ldx _cursorX + ldy _cursorY + lda #'_' + jsr _DrawChar + rts + +CursorOff: ldx _cursorX + ldy _cursorY + lda #' ' + jsr _DrawChar + rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII char in A at the current cursor pos, saves all regs +;----------------------------------------------------------------------------------- + +_CharOut: sta temp + lda #0 + sta temp+1 + txa + pha + tya + pha + + ldx #temp + jsr _DrawText + + pla + tay + pla + tax + rts + +;----------------------------------------------------------------------------------- +; Backspace - Erase the current character and move back one position. Does not +; move back up to previous line +;----------------------------------------------------------------------------------- + +Backspace: lda _cursorX + beq colzero + jsr CursorOff + dec _cursorX + jsr CursorOn +colzero: rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII string at the current cursor position +;----------------------------------------------------------------------------------- +; XY - Pointer to the string to draw, stops on NUL or 255 chars later +;----------------------------------------------------------------------------------- + +_DrawText: stx adp1_lo + sty adp1_hi + jsr CursorOff + + ldy #0 +checkHWrap: lda _cursorX + cmp #CHARSPERROW + bcc checkVWrap + lda #0 + sta _cursorX + inc _cursorY + +checkVWrap: lda _cursorY + cmp #ROWSPERCOLUMN + bcc loadChar + jsr _ScrollScreen + lda #ROWSPERCOLUMN-1 + sta _cursorY + +loadChar: lda (adp1), y + beq doneText + + cmp #$0a + bne :+ + + lda #0 ; Back to the left edge + sta _cursorX + inc _cursorY ; Advance to the next line + iny + bne checkHWrap + +: sty temp + ldx _cursorX + ldy _cursorY + jsr _DrawChar + ldy temp + inc _cursorX + iny + bne checkHWrap + +doneText: jsr CursorOn + rts + +demoText1: .byte " *** COMMODORE KIM-1 SHELL V0.1 ***", $0A, $0A + .byte " 60K RAM SYSTEM. 49152 BYTES FREE.", $0A, $0A, $00 +readyText: .byte $0A,"READY.", $0A, 00 + +_Demo: jsr _ClearScreen + lda #0 + sta _cursorX + sta _cursorY + ldx #demoText1 + jsr _DrawText + rts + +_Ready: ldx #readyText + jsr _DrawText + rts + + +;----------------------------------------------------------------------------------- +; DrawLine - Draws a line between two points +;----------------------------------------------------------------------------------- +; _x1cord (16-bit) +; _y1cord ( 8-bit) +; _x2cord (16-bit) +; _y2cord ( 8-bit) +;----------------------------------------------------------------------------------- +; Implements something like Bresenham's algorithm for drawing a line: +;----------------------------------------------------------------------------------- +; void DrawLine(int x0, int y0, int x1, int y1, byte val) +; { +; int dx = abs(_x2cord - _x1cord), sx = _x1cord < _x2cord ? 1 : -1; +; int dy = abs(_y2cord - _y1cord), sy = _y1cord < _y2cord ? 1 : -1; +; int err = (dx > dy ? dx : -dy) / 2, e2; +; +; while (1) +; { +; SETPIXEL(_x1cord, _y1cord, val); +; +; if (_x1cord == _x2cord && _y1cord == _y2cord) +; break; +; +; e2 = err; +; +; if (e2 > -dx) +; { +; err -= dy; +; _x1cord += sx; +; } +; if (e2 < dy) +; { +; err += dx; +; _y1cord += sy; +; } +; } +; } +;----------------------------------------------------------------------------------- + +_DrawLine: sta pixel + + ldx #$01 ; positive x-step for now + stx sx + + ; Calculate dx = (x2cord - X1cord) and see if its positive or not + + lda _x2cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x1cord + sta dx + lda _x2cord+1 + sbc _x1cord+1 + sta dx+1 + bpl calcdy ; dx is positive (dx >= 0), so we're good + + ; dx was negative (dx < 0), so we set sx to -1 and get the absolute + ; value by subtracting the other direction + + ldx #$FF ; negative x-step + stx sx + lda _x1cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x2cord + sta dx + lda _x1cord+1 + sbc _x2cord+1 + sta dx+1 + + ; Calculate dy = (y2cord - y1cord) and see if its positive or not + +calcdy: ldx #$01 ; positive y-step for now + stx sy + lda _y2cord + sec + sbc _y1cord + sta dy + bcs positivedy ; If y2cord > y1cord, then dy is positive and we're good + + ; dy was negative (dy < 0), so we set sy to -1 and get the absolute value + + ldx #$FF ; negative y-step + stx sy + lda _y1cord + sec + sbc _y2cord + sta dy + + ; Now we have dx and dy, so we can calculate err, but first we need + ; to see if dx > dy or not + +positivedy: lda dx+1 ; Check if dx > dy (both are always positive now) + bne dxgt ; If MSB of dx is greater than zero, then dx > dy since dy is 8-bits + lda dy + cmp dx + bcs dygte + +dxgt: lda dx ; We found dx>dy so set err = dx / 2 + sta err + lda dx+1 + lsr + sta err+1 ; err = dx/2 + ror err + jmp loop + +dygte: lda #0 ; we found dx <= dy so set err = -dy / 2 + sec + sbc dy ; else err = -dy / 2 + ror + ora #$80 + sta err + lda #$FF + sta err+1 + + ; Now we have dx, dy, and err, so we can start drawing pixels + +loop: lda pixel + beq clearpixel + jsr _SetPixel ; Plot the current _x1cord, _y1cord + jmp next +clearpixel: jsr _ClearPixel ; Clear the current _x1cord, _y1cord + +next: lda _x1cord ; if (_x1cord == _x2cord && _y1cord == _y2cord) then we rts + cmp _x2cord + bne noteq + lda _y1cord + cmp _y2cord + bne noteq + lda _x1cord+1 + cmp _x2cord+1 + bne noteq + + rts + +noteq: lda err ; e2 = err + sta e2 + lda err+1 + sta e2+1 + + ; Check the two update conditions for x and y, and update if needed + + lda e2 ; if (e2 > -dx) is the same as if (e2 + dx > 0), so we test that because its easier + clc ; If its true then we dec err and inc _x1cord + adc dx + sta temp + lda e2+1 + adc dx+1 + bmi doneupdatex ; If result is negative, then e2 + dx < 0, so we don't dec err or inc _x1cord + bne stepx ; If MSB is non-zero, then e2 + dx > 0, so we DO dec err and inc _x1cord + lda temp ; If result is zero in MSB, then we check the LSB here + beq doneupdatex ; If LSB is zero, then we don't dec err or inc _x1cord + ; We already know e2 + dx > 0, so LSB can't be negative +stepx: lda sx + bmi decx +incxval: inc _x1cord ; _x1cord += 1 because sx == 1 + bne updatexerr + inc _x1cord+1 + jmp updatexerr + +decx: lda _x1cord ; _x1cord += 1 because sx == 1 + sec + sbc #1 + sta _x1cord + lda _x1cord+1 + sbc #0 + sta _x1cord+1 + +updatexerr: lda err ; err -= dy + sec + sbc dy + sta err + lda err+1 + sbc #0 + sta err+1 + +doneupdatex: lda e2+1 ; if (e2 < dy) then we inc err and inc _y1cord + bmi updateerry ; If MSB is negative, then e2 < dy, so we inc err and inc _y1cord + bne noupdatey ; If the MSB of e2 is set and positive, then we know e2 > dy, so we don't inc err or inc _y1cord + lda e2 + sec + sbc dy + beq noupdatey ; e2 - dy == 0 so we don't inc err or inc _y1cord + bcs noupdatey ; if e2 was large enough that carry never cleared, then e2 > dy do no update + +updateerry: lda err ; err += dx + clc + adc dx + sta err + lda err+1 + adc dx+1 + sta err+1 + +stepy: lda _y1cord + clc + adc sy + sta _y1cord + +noupdatey: jmp loop + +_getch: jsr $1E5A ; Get character using Monitor ROM call + and #$7F ; Clear top bit + cmp #$0D ; Check for '\r' + bne gotch ; ...if CR character + lda #$0A ; Replace with '\n' +gotch: rts diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 166d054343..8521f0a817 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -80,9 +80,9 @@ static void AsmErrorSkip (void) -static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) -/* Find the symbol with the name currently in NextTok. The symbol must be of -** the given type. On errors, NULL is returned. +static SymEntry* AsmGetSym (unsigned Arg, int OnStack) +/* Find the symbol with the name currently in NextTok. The symbol must be on +** the stack if OnStack is true. On errors, NULL is returned. */ { SymEntry* Sym; @@ -110,8 +110,8 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) /* We found the symbol - skip the name token */ NextToken (); - /* Check if we have a global symbol */ - if ((Sym->Flags & Type) != Type) { + /* Check if the symbol is on the stack */ + if ((Sym->Flags & SC_STORAGEMASK) != SC_AUTO ? OnStack : !OnStack) { Error ("Type of argument %u differs from format specifier", Arg); AsmErrorSkip (); return 0; @@ -218,23 +218,24 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) */ { /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); + SymEntry* Sym = AsmGetSym (Arg, 0); if (Sym == 0) { /* Some sort of error */ return; } - /* Check for external linkage */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) { - /* External linkage or a function */ + /* Get the correct asm name */ + if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC || SymIsGlobal (Sym)) { + /* External or internal linkage or a function */ SB_AppendChar (T, '_'); SB_AppendStr (T, Sym->Name); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + /* Register variable */ char Buf[32]; xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs); SB_AppendStr (T, Buf); } else { - /* Static variable */ + /* Local static variable */ SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label)); } } @@ -248,7 +249,7 @@ static void ParseLVarArg (StrBuf* T, unsigned Arg) char Buf [16]; /* Parse the symbol name parameter and check the type */ - SymEntry* Sym = AsmGetSym (Arg, SC_AUTO); + SymEntry* Sym = AsmGetSym (Arg, 1); if (Sym == 0) { /* Some sort of error */ return; diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index c2bdfcd63b..69dcc1c6c2 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3260,6 +3260,14 @@ void g_asr (unsigned flags, unsigned long val) } val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shrax7"); + } else { + AddCodeLine ("jsr asrax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shrax4"); @@ -3402,6 +3410,14 @@ void g_asl (unsigned flags, unsigned long val) AddCodeLine ("lda #$00"); val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shlax7"); + } else { + AddCodeLine ("jsr aslax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shlax4"); diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 88f8a51387..427bfc52b2 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -99,6 +99,7 @@ static const FuncInfo FuncInfoTable[] = { { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax7", REG_AX, PSTATE_ALL | REG_AXY }, { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -108,6 +109,7 @@ static const FuncInfo FuncInfoTable[] = { { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax7", REG_AX, PSTATE_ALL | REG_AX }, { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -245,6 +247,7 @@ static const FuncInfo FuncInfoTable[] = { { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax7", REG_AX, PSTATE_ALL | REG_AXY }, { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -254,6 +257,7 @@ static const FuncInfo FuncInfoTable[] = { { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax7", REG_AX, PSTATE_ALL | REG_AX }, { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index 9f1bf4cc52..f4970b5867 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -1471,7 +1471,7 @@ void CS_Output (CodeSeg* S) /* Add line debug info */ if (DebugInfo) { WriteOutput ("\t.dbg\tline, \"%s\", %u\n", - GetInputName (LI), GetInputLine (LI)); + GetActualFileName (LI), GetActualLineNum (LI)); } } /* Output the code */ diff --git a/src/cc65/compile.c b/src/cc65/compile.c index aaa0174531..e08a829e6f 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -121,15 +121,13 @@ static void Parse (void) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE); /* Don't accept illegal storage classes */ - if ((Spec.StorageClass & SC_TYPEMASK) == 0) { - if ((Spec.StorageClass & SC_AUTO) != 0 || - (Spec.StorageClass & SC_REGISTER) != 0) { - Error ("Illegal storage class"); - Spec.StorageClass = SC_EXTERN | SC_STATIC; - } + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO || + (Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Error ("Illegal storage class"); + Spec.StorageClass &= ~SC_STORAGEMASK; } /* Check if this is only a type declaration */ @@ -165,33 +163,32 @@ static void Parse (void) break; } - /* Check if we must reserve storage for the variable. We do this, - ** - ** - if it is not a typedef or function, - ** - if we don't had a storage class given ("int i") - ** - if the storage class is explicitly specified as static, - ** - or if there is an initialization. - ** - ** This means that "extern int i;" will not get storage allocated. - */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + /* The symbol is now visible in the file scope */ + if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - if ((Spec.Flags & DS_DEF_STORAGE) != 0 || - (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || - ((Decl.StorageClass & SC_EXTERN) != 0 && + /* Check if we must reserve storage for the variable. We do this, + ** + ** - if it is not a typedef or function, + ** - if we don't had a storage class given ("int i") + ** - if the storage class is explicitly specified as static, + ** - or if there is an initialization. + ** + ** This means that "extern int i;" will not get storage allocated + ** in this translation unit. + */ + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE || + (Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC || + ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN && CurTok.Tok == TOK_ASSIGN)) { - /* We will allocate storage */ - Decl.StorageClass |= SC_STORAGE; - } else { - /* It's a declaration */ - Decl.StorageClass |= SC_DECL; + /* We will allocate storage in this translation unit */ + Decl.StorageClass |= SC_TU_STORAGE; } } /* If this is a function declarator that is not followed by a comma ** or semicolon, it must be followed by a function body. */ - if ((Decl.StorageClass & SC_FUNC) != 0) { + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { if (CurTok.Tok == TOK_LCURLY) { /* A definition */ Decl.StorageClass |= SC_DEF; @@ -205,8 +202,6 @@ static void Parse (void) } } else { /* Just a declaration */ - Decl.StorageClass |= SC_DECL; - FuncDef = GetFuncDesc (Decl.Type); if ((FuncDef->Flags & (FD_EMPTY | FD_OLDSTYLE)) == FD_OLDSTYLE) { /* A parameter list without types is only allowed in a @@ -224,7 +219,7 @@ static void Parse (void) SymUseAttr (Sym, &Decl); /* Reserve storage for the variable if we need to */ - if (Decl.StorageClass & SC_STORAGE) { + if (Decl.StorageClass & SC_TU_STORAGE) { /* Get the size of the variable */ unsigned Size = SizeOf (Decl.Type); @@ -327,9 +322,13 @@ static void Parse (void) } /* Make the symbol zeropage according to the segment address size */ - if ((Sym->Flags & SC_STATIC) != 0) { - if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { - Sym->Flags |= SC_ZEROPAGE; + if ((Sym->Flags & SC_TYPEMASK) == SC_NONE) { + if (SymIsGlobal (Sym) || + (Sym->Flags & SC_STORAGEMASK) == SC_STATIC || + (Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { + if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { + Sym->Flags |= SC_ZEROPAGE; + } } } @@ -517,7 +516,10 @@ void Compile (const char* FileName) ** global variables. */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { - if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { + /* Is it a global (with or without static) tentative declaration of + ** an uninitialized variable? + */ + if ((Entry->Flags & (SC_TU_STORAGE | SC_DEF)) == SC_TU_STORAGE) { /* Assembly definition of uninitialized global variable */ SymEntry* TagSym = GetESUTagSym (Entry->Type); unsigned Size = SizeOf (Entry->Type); @@ -552,11 +554,15 @@ void Compile (const char* FileName) Entry->Name, GetFullTypeName (Entry->Type)); } - } else if (!SymIsDef (Entry) && (Entry->Flags & SC_FUNC) == SC_FUNC) { + } else if (!SymIsDef (Entry) && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { /* Check for undefined functions */ - if ((Entry->Flags & (SC_EXTERN | SC_STATIC)) == SC_STATIC && SymIsRef (Entry)) { + if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) { Warning ("Static function '%s' used but never defined", Entry->Name); + } else if ((Entry->Flags & SC_INLINE) != 0) { + Warning ("Inline function '%s' %s but never defined", + Entry->Name, + SymIsRef (Entry) ? "used" : "declared"); } } } diff --git a/src/cc65/coptlong.c b/src/cc65/coptlong.c index 29cf4d353e..23c30875a6 100644 --- a/src/cc65/coptlong.c +++ b/src/cc65/coptlong.c @@ -87,6 +87,7 @@ unsigned OptLongAssign (CodeSeg* S) L[0] = CS_GetEntry (S, I); if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry* N; if (/* Check the opcode sequence */ L[0]->OPC == OP65_LDA && L[1]->OPC == OP65_STA && @@ -119,11 +120,13 @@ unsigned OptLongAssign (CodeSeg* S) !RegXUsed (S, I+12) && !CS_RangeHasLabel(S, I, 12)) { - L[1]->AM = L[11]->AM; - CE_SetArg(L[1], L[11]->Arg); + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); - L[3]->AM = L[9]->AM; - CE_SetArg(L[3], L[9]->Arg); + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); CS_DelEntries (S, I+8, 4); @@ -179,6 +182,7 @@ unsigned OptLongCopy (CodeSeg* S) L[0] = CS_GetEntry (S, I); if (CS_GetEntries (S, L+1, I+1, 12)) { + CodeEntry *N; if (L[0]->OPC == OP65_LDA && !strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) && !strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") && @@ -210,11 +214,13 @@ unsigned OptLongCopy (CodeSeg* S) !RegXUsed (S, I+11) && !CS_RangeHasLabel(S, I, 12)) { - L[1]->AM = L[11]->AM; - CE_SetArg(L[1], L[11]->Arg); + N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI); + CS_DelEntry (S, I+1); + CS_InsertEntry (S, N, I+1); - L[3]->AM = L[9]->AM; - CE_SetArg(L[3], L[9]->Arg); + N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI); + CS_DelEntry (S, I+3); + CS_InsertEntry (S, N, I+3); CS_DelEntries (S, I+8, 4); diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 7e024ae889..402f16b97f 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1488,7 +1488,7 @@ static const OptFuncDesc FuncTable[] = { }; static const OptFuncDesc FuncRegATable[] = { - { "tosandax", Opt_a_tosand, REG_NONE, OP_NONE }, + { "tosandax", Opt_a_tosand, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, @@ -1496,13 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, - { "tosorax", Opt_a_tosor, REG_NONE, OP_NONE }, + { "tosorax", Opt_a_tosor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, - { "tosxorax", Opt_a_tosxor, REG_NONE, OP_NONE }, + { "tosxorax", Opt_a_tosxor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, }; #define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9c82e67734..4d6cb25a5c 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -989,6 +989,24 @@ int IsIncompleteESUType (const Type* T) +int IsAnonESUType (const Type* T) +/* Return true if this is an anonymous ESU type */ +{ + SymEntry* TagSym = GetESUTagSym (T); + + return TagSym != 0 && SymHasAnonName (TagSym); +} + + + +int IsAnonStructClass (const Type* T) +/* Return true if this is an anonymous struct or union type */ +{ + return IsClassStruct (T) && IsAnonESUType (T); +} + + + int IsPassByRefType (const Type* T) /* Return true if this is a large struct/union type that doesn't fit in the ** primary. This returns false for the void value extension type since it is diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index dbe0eedaa1..8446fb914a 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -792,6 +792,12 @@ int IsESUType (const Type* T); int IsIncompleteESUType (const Type* T); /* Return true if this is an incomplete ESU type */ +int IsAnonESUType (const Type* T); +/* Return true if this is an anonymous ESU type */ + +int IsAnonStructClass (const Type* T); +/* Return true if this is an anonymous struct or union type */ + int IsPassByRefType (const Type* T); /* Return true if this is a large struct/union type that doesn't fit in the ** primary. This returns false for the void value extension type since it is diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2666a8d312..e1e66ab859 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -91,7 +91,7 @@ static unsigned ParseOneStorageClass (void) switch (CurTok.Tok) { case TOK_EXTERN: - StorageClass = SC_EXTERN | SC_STATIC; + StorageClass = SC_EXTERN; NextToken (); break; @@ -101,7 +101,7 @@ static unsigned ParseOneStorageClass (void) break; case TOK_REGISTER: - StorageClass = SC_REGISTER | SC_STATIC; + StorageClass = SC_REGISTER; NextToken (); break; @@ -124,9 +124,37 @@ static unsigned ParseOneStorageClass (void) +static unsigned ParseOneFuncSpec (void) +/* Parse and return a function specifier */ +{ + unsigned FuncSpec = 0; + + /* Check the function specifier given */ + switch (CurTok.Tok) { + + case TOK_INLINE: + FuncSpec = SC_INLINE; + NextToken (); + break; + + case TOK_NORETURN: + FuncSpec = SC_NORETURN; + NextToken (); + break; + + default: + break; + } + + return FuncSpec; +} + + + static int ParseStorageClass (DeclSpec* Spec) /* Parse storage class specifiers. Return true if a specifier is read even if -** it was duplicated or disallowed. */ +** it was duplicated or disallowed. +*/ { /* Check the storage class given */ unsigned StorageClass = ParseOneStorageClass (); @@ -136,9 +164,9 @@ static int ParseStorageClass (DeclSpec* Spec) } while (StorageClass != 0) { - if (Spec->StorageClass == 0) { - Spec->StorageClass = StorageClass; - } else if (Spec->StorageClass == StorageClass) { + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { + Spec->StorageClass |= StorageClass; + } else if ((Spec->StorageClass & SC_STORAGEMASK) == StorageClass) { Warning ("Duplicate storage class specifier"); } else { Error ("Conflicting storage class specifier"); @@ -151,6 +179,31 @@ static int ParseStorageClass (DeclSpec* Spec) +static int ParseFuncSpecClass (DeclSpec* Spec) +/* Parse function specifiers. Return true if a specifier is read even if it +** was duplicated or disallowed. +*/ +{ + /* Check the function specifiers given */ + unsigned FuncSpec = ParseOneFuncSpec (); + + if (FuncSpec == 0) { + return 0; + } + + while (FuncSpec != 0) { + if ((Spec->StorageClass & FuncSpec) != 0) { + Warning ("Duplicate function specifier"); + } + Spec->StorageClass |= FuncSpec; + FuncSpec = ParseOneFuncSpec (); + } + + return 1; +} + + + static void DuplicateQualifier (const char* Name) /* Print an error message */ { @@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t */ { TypeCode Q = T_QUAL_NONE; - int Continue; + int HasStorageClass; + int HasFuncSpec; do { /* There may be type qualifiers *before* any storage class specifiers */ @@ -311,11 +365,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t *Qualifiers |= Q; /* Parse storage class specifiers anyway then check */ - Continue = ParseStorageClass (Spec); - if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) { + HasStorageClass = ParseStorageClass (Spec); + if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) { Error ("Unexpected storage class specified"); } - } while (Continue || Q != T_QUAL_NONE); + + /* Parse function specifiers anyway then check */ + HasFuncSpec = ParseFuncSpecClass (Spec); + if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) { + Error ("Unexpected function specifiers"); + } + } while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec); } @@ -618,12 +678,12 @@ static SymEntry* ForwardESU (const char* Name, unsigned Flags, unsigned* DSFlags */ SymEntry* TagEntry = FindTagSym (Name); if (TagEntry == 0) { - if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { + if ((Flags & SC_TYPEMASK) != SC_ENUM) { TagEntry = AddStructSym (Name, Flags, 0, 0, DSFlags); } else { TagEntry = AddEnumSym (Name, Flags, 0, 0, DSFlags); } - } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { + } else if ((TagEntry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { /* Already defined, but not the same type class */ Error ("Symbol '%s' is already different kind", Name); } @@ -798,7 +858,7 @@ static SymEntry* ParseEnumSpec (const char* Name, unsigned* DSFlags) } /* Add an entry of the enumerator to the symbol table */ - AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); + AddConstSym (Ident, NewType, SC_DEF | SC_ENUMERATOR, EnumVal); /* Use this type for following members */ MemberType = NewType; @@ -997,7 +1057,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* Parse union fields */ UnionSize = 0; - while (CurTok.Tok != TOK_RCURLY) { + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { /* Get the type of the entry */ DeclSpec Spec; @@ -1020,7 +1080,8 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); /* Check if this is only a type declaration */ - if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { CheckEmptyDecl (&Spec); NextToken (); continue; @@ -1061,22 +1122,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags) /* In cc65 mode, we allow anonymous structs/unions within ** a union. */ - SymEntry* TagEntry; - if (IS_Get (&Standard) >= STD_CC65 && - IsClassStruct (Decl.Type) && - (TagEntry = GetESUTagSym (Decl.Type)) && - SymHasAnonName (TagEntry)) { - /* This is an anonymous struct or union */ - AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); - - /* Ignore CVR qualifiers */ - if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { - Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); - Decl.Type[0].C &= ~T_QUAL_CVR; - } - } else { - /* Invalid member */ - goto NextMember; + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; } } else if (FieldWidth > 0) { /* A bit-field without a name will get an anonymous one */ @@ -1217,7 +1268,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) FlexibleMember = 0; StructSize = 0; BitOffs = 0; - while (CurTok.Tok != TOK_RCURLY) { + while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { /* Get the type of the entry */ DeclSpec Spec; @@ -1240,7 +1291,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE); /* Check if this is only a type declaration */ - if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) { + if (CurTok.Tok == TOK_SEMI && + !(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) { CheckEmptyDecl (&Spec); NextToken (); continue; @@ -1308,22 +1360,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags) /* In cc65 mode, we allow anonymous structs/unions within ** a struct. */ - SymEntry* TagEntry; - if (IS_Get (&Standard) >= STD_CC65 && - IsClassStruct (Decl.Type) && - (TagEntry = GetESUTagSym (Decl.Type)) && - SymHasAnonName (TagEntry)) { - /* This is an anonymous struct or union */ - AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); - - /* Ignore CVR qualifiers */ - if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { - Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); - Decl.Type[0].C &= ~T_QUAL_CVR; - } - } else { - /* Invalid member */ - goto NextMember; + AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount); + + /* Ignore CVR qualifiers */ + if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) { + Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type)); + Decl.Type[0].C &= ~T_QUAL_CVR; } } else if (FieldWidth > 0) { /* A bit-field without a name will get an anonymous one */ @@ -1814,7 +1856,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) } /* An optional list of type specifications follows */ - while (CurTok.Tok != TOK_LCURLY) { + while (CurTok.Tok != TOK_LCURLY && CurTok.Tok != TOK_CEOF) { DeclSpec Spec; int NeedClean; @@ -1825,8 +1867,8 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) /* We accept only auto and register as storage class specifiers, but ** we ignore all this, since we use auto anyway. */ - if ((Spec.StorageClass & SC_AUTO) == 0 && - (Spec.StorageClass & SC_REGISTER) == 0) { + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO && + (Spec.StorageClass & SC_STORAGEMASK) != SC_REGISTER) { Error ("Illegal storage class"); } @@ -1854,7 +1896,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused))) } /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } @@ -1942,12 +1984,12 @@ static void ParseAnsiParamList (FuncDesc* F) ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); /* We accept only auto and register as storage class specifiers */ - if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { - Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; - } else if ((Spec.StorageClass & SC_REGISTER) == SC_REGISTER) { - Spec.StorageClass = SC_REGISTER | SC_STATIC | SC_PARAM | SC_DEF; + if ((Spec.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { + Spec.StorageClass = SC_REGISTER | SC_PARAM | SC_DEF; } else { - Error ("Illegal storage class"); + if ((Spec.StorageClass & SC_STORAGEMASK) != SC_AUTO) { + Error ("Illegal storage class"); + } Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } @@ -1957,7 +1999,7 @@ static void ParseAnsiParamList (FuncDesc* F) } /* Warn about new local type declaration */ - if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) { Warning ("'%s' will be invisible out of this function", GetFullTypeName (Spec.Type)); } @@ -2355,7 +2397,9 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) D->StorageClass = Spec->StorageClass; /* If we have a function, add a special symbol type */ - if (IsTypeFunc (D->Type)) { + if (Mode != DM_ACCEPT_PARAM_IDENT && + IsTypeFunc (D->Type) && + (D->StorageClass & SC_TYPEMASK) == SC_NONE) { D->StorageClass |= SC_FUNC; } @@ -2373,30 +2417,54 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) /* Parse attributes for this declarator */ ParseAttribute (D); - /* Check a few pre-C99 things */ - if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { - /* Check and warn about an implicit int return in the function */ - if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) { - /* Function has an implicit int return. Output a warning if we don't - ** have the C89 standard enabled explicitly. + /* Check a few things for the instance (rather than the type) */ + if (D->Ident[0] != '\0') { + /* Check a few pre-C99 things */ + if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE && IsRankInt (Spec->Type)) { + /* If the standard was not set explicitly to C89, print a warning + ** for typedefs with implicit int type specifier. */ if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' return type is an obsolete feature"); + if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + Warning ("Implicit 'int' type specifier is an obsolete feature"); + } else { + Warning ("Type specifier defaults to 'int' in typedef of '%s'", + D->Ident); + Note ("Implicit 'int' type specifier is an obsolete feature"); + } } - GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; } - /* For anything that is not a function or typedef, check for an implicit - ** int declaration. - */ - if (!IsTypeFunc (D->Type) && - (D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { - /* If the standard was not set explicitly to C89, print a warning - ** for variables with implicit int type. - */ - if (IS_Get (&Standard) >= STD_C99) { - Warning ("Implicit 'int' is an obsolete feature"); + /* Check other things depending on the "kind" of the instance */ + if ((D->StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Special handling for main() */ + if (strcmp (D->Ident, "main") == 0) { + /* main() cannot be a fastcall function */ + if (IsQualFastcall (D->Type)) { + Error ("'main' cannot be declared __fastcall__"); + } + + /* main() cannot be an inline function */ + if ((D->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'main' cannot be declared inline"); + D->StorageClass &= ~SC_INLINE; + } + + /* Check return type */ + if (GetUnqualRawTypeCode (GetFuncReturnType (D->Type)) != T_INT) { + /* If cc65 extensions aren't enabled, don't allow a main function + ** that doesn't return an int. + */ + if (IS_Get (&Standard) != STD_CC65) { + Error ("'main' must always return an int"); + } + } } + } else if (Mode != DM_ACCEPT_PARAM_IDENT && + (D->StorageClass & SC_INLINE) == SC_INLINE) { + /* 'inline' is only allowed on functions */ + Error ("'inline' on non-function declaration"); + D->StorageClass &= ~SC_INLINE; } } @@ -2476,12 +2544,12 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage) Spec->Flags &= ~DS_DEF_STORAGE; /* Parse the type specifiers */ - ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC); + ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC); /* If no explicit storage class is given, use the default */ - if (Spec->StorageClass == 0) { + if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { Spec->Flags |= DS_DEF_STORAGE; - Spec->StorageClass = DefStorage; + Spec->StorageClass |= DefStorage; } } @@ -2493,13 +2561,25 @@ void CheckEmptyDecl (const DeclSpec* Spec) ** warning if not. */ { - if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { + if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) { + Error ("'inline' on empty declaration"); + } else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) { /* No declaration at all */ } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { - Warning ("Declaration does not declare anything"); - } else if (IsClassStruct (Spec->Type) && - !IsIncompleteESUType (Spec->Type) && - SymHasAnonName (GetESUTagSym (Spec->Type))) { + /* Empty declaration of basic types */ + Warning ("Useless declaration"); + } else if (IsAnonStructClass (Spec->Type)) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field outside a struct/union. + */ Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type)); + } else if (GetLexicalLevel () == LEX_LEVEL_STRUCT) { + /* This could be that the user made a wrong attempt to declare an + ** anonymous struct/union field inside a struct/union. Perhaps just + ** paranoid since it is not so uncommon to do forward declarations. + */ + if (!IsTypeEnum (Spec->Type) || ((Spec->Flags & DS_NEW_TYPE_DEF) == 0)) { + Warning ("Declaration defines no instances"); + } } } diff --git a/src/cc65/error.c b/src/cc65/error.c index 5cd29b3889..d6e7d7cbdb 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -120,13 +120,46 @@ Collection DiagnosticStrBufs; +void PrintFileInclusionInfo (const LineInfo* LI) +/* Print hierarchy of file inclusion */ +{ + if (LI->IncFiles != 0) { + unsigned FileCount = CollCount (LI->IncFiles); + if (FileCount > 0) { + const char* Str = "In file included from %s:%u%c\n"; + + while (FileCount-- > 0) { + LineInfoFile* LIF = CollAtUnchecked (LI->IncFiles, FileCount); + char C = FileCount > 0 ? ',' : ':'; + + fprintf (stderr, Str, LIF->Name, LIF->LineNum, C); + Str = " from %s:%u%c\n"; + } + } + } +} + + + +static LineInfo* GetDiagnosticLI (void) +/* Get the line info where the diagnostic info refers to */ +{ + if (CurTok.LI) { + return CurTok.LI; + } else { + return GetCurLineInfo (); + } +} + + + static const char* GetDiagnosticFileName (void) /* Get the source file name where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputName (CurTok.LI); + return GetPresumedFileName (CurTok.LI); } else { - return GetCurrentFilename (); + return GetCurrentFileName (); } } @@ -136,7 +169,7 @@ static unsigned GetDiagnosticLineNum (void) /* Get the source line number where the diagnostic info refers to */ { if (CurTok.LI) { - return GetInputLine (CurTok.LI); + return GetPresumedLineNum (CurTok.LI); } else { return GetCurrentLineNum (); } @@ -199,10 +232,18 @@ void Internal (const char* Format, ...) -static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print an error message - internal function */ { - fprintf (stderr, "%s:%u: Error: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Error: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -229,23 +270,23 @@ static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const -void Error (const char* Format, ...) -/* Print an error message */ +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print an error message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntError (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntError (EC, LI, Format, ap); va_end (ap); } -void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) -/* Print an error message with the line info given explicitly */ +void Error (const char* Format, ...) +/* Print an error message */ { va_list ap; va_start (ap, Format); - IntError (EC, GetInputName (LI), GetInputLine (LI), Format, ap); + IntError (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -256,7 +297,7 @@ void PPError (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntError (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntError (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } @@ -268,17 +309,25 @@ void PPError (const char* Format, ...) -static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap) /* Print a warning message - internal function */ { if (IS_Get (&WarningsAreErrors)) { /* Treat the warning as an error */ - IntError (EC, Filename, LineNo, Msg, ap); + IntError (EC, LI, Msg, ap); } else if (IS_Get (&WarnEnable)) { - fprintf (stderr, "%s:%u: Warning: ", Filename, LineNo); + unsigned LineNo = GetPresumedLineNum (LI); + + /* Print file inclusion if appropriate */ + if (HasFileInclusionChanged (LI)) { + PrintFileInclusionInfo (LI); + } + RememberCheckedLI (LI); + + fprintf (stderr, "%s:%u: Warning: ", GetPresumedFileName (LI), LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); @@ -297,23 +346,23 @@ static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, cons -void Warning (const char* Format, ...) -/* Print a warning message */ +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) +/* Print a warning message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntWarning (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntWarning (EC, LI, Format, ap); va_end (ap); } -void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) -/* Print a warning message with the line info given explicitly */ +void Warning (const char* Format, ...) +/* Print a warning message */ { va_list ap; va_start (ap, Format); - IntWarning (EC, GetInputName (LI), GetInputLine (LI), Format, ap); + IntWarning (EC_PARSER, GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -324,7 +373,7 @@ void PPWarning (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntWarning (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntWarning (EC_PP, GetCurLineInfo (), Format, ap); va_end (ap); } @@ -365,33 +414,33 @@ void ListWarnings (FILE* F) -static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) +static void IntNote (const LineInfo* LI, const char* Msg, va_list ap) /* Print a note message - internal function */ { - fprintf (stderr, "%s:%u: Note: ", Filename, LineNo); + fprintf (stderr, "%s:%u: Note: ", GetPresumedFileName (LI), GetPresumedLineNum (LI)); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); } -void Note (const char* Format, ...) -/* Print a note message */ +void LINote (const LineInfo* LI, const char* Format, ...) +/* Print a note message with the line info given explicitly */ { va_list ap; va_start (ap, Format); - IntNote (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap); + IntNote (LI, Format, ap); va_end (ap); } -void LINote (const LineInfo* LI, const char* Format, ...) -/* Print a note message with the line info given explicitly */ +void Note (const char* Format, ...) +/* Print a note message */ { va_list ap; va_start (ap, Format); - IntNote (GetInputName (LI), GetInputLine (LI), Format, ap); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } @@ -402,7 +451,7 @@ void PPNote (const char* Format, ...) { va_list ap; va_start (ap, Format); - IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap); + IntNote (GetDiagnosticLI (), Format, ap); va_end (ap); } diff --git a/src/cc65/error.h b/src/cc65/error.h index 4dce6cf91d..5af862f9f4 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -100,6 +100,9 @@ struct StrBuf; +void PrintFileInclusionInfo (const LineInfo* LI); +/* Print hierarchy of file inclusion */ + void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2))); /* Print a message about a fatal error and die */ @@ -109,7 +112,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1, void Error (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print an error message */ -void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); +void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print an error message with the line info given explicitly */ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); @@ -118,7 +121,7 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2))); void Warning (const char* Format, ...) attribute ((format (printf, 1, 2))); /* Print a warning message */ -void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); +void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4))); /* Print a warning message with the line info given explicitly */ void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2))); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index ee26bd0e85..a855e5b3c1 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1223,8 +1223,8 @@ static void Primary (ExprDesc* E) NextToken (); /* Check for illegal symbol types */ - CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL); - if (Sym->Flags & SC_ESUTYPEMASK) { + CHECK ((Sym->Flags & SC_TYPEMASK) != SC_LABEL); + if ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF) { /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ @@ -1244,7 +1244,7 @@ static void Primary (ExprDesc* E) /* Enum or some other numeric constant */ E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->IVal = Sym->V.ConstVal; - } else if ((Sym->Flags & SC_AUTO) == SC_AUTO) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { /* Local variable. If this is a parameter for a variadic ** function, we have to add some address calculations, and the ** address is not const. @@ -1258,26 +1258,25 @@ static void Primary (ExprDesc* E) E->Flags = E_LOC_STACK | E_RTYPE_LVAL; E->IVal = Sym->V.Offs; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* Function */ E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; E->Name = (uintptr_t) Sym->Name; - } else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable, zero page based */ E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL; E->Name = Sym->V.R.RegOffs; - } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { - /* Static variable */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { - E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; - E->Name = (uintptr_t) Sym->Name; - } else { - E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; - E->Name = Sym->V.L.Label; - } - } else { + } else if (SymIsGlobal (Sym)) { + /* Global variable */ + E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; + E->Name = (uintptr_t) Sym->Name; + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_STATIC) { /* Local static variable */ E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; + E->Name = Sym->V.L.Label; + } else { + /* Other */ + E->Flags = E_LOC_STATIC | E_RTYPE_LVAL; E->Name = Sym->V.Offs; } @@ -1311,7 +1310,7 @@ static void Primary (ExprDesc* E) } else { Warning ("Call to undeclared function '%s'", Ident); } - Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC); + Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_REF | SC_FUNC); E->Type = Sym->Type; E->Flags = E_LOC_GLOBAL | E_RTYPE_RVAL; E->Name = (uintptr_t) Sym->Name; @@ -1408,7 +1407,7 @@ static void Primary (ExprDesc* E) } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO); if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) { Error ("Mixed declarations and code are not supported in cc65"); diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index f6009a8a9e..d1ff99445f 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -98,7 +98,7 @@ enum { E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ - E_LOC_STATIC = 0x0004, /* Static variable */ + E_LOC_STATIC = 0x0004, /* Local static variable */ E_LOC_REGISTER = 0x0008, /* Register variable */ E_LOC_STACK = 0x0010, /* Value on the stack */ E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 4b4060f2ac..a4b8602516 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -450,7 +450,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { int ParamComplete; /* If all paramemters have complete types */ - int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ const Type* ReturnType; /* Return type */ @@ -513,28 +512,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Mark this as the main function */ CurrentFunc->Flags |= FF_IS_MAIN; - /* Main cannot be a fastcall function */ - if (IsQualFastcall (Func->Type)) { - Error ("'main' cannot be declared as __fastcall__"); - } - - /* Check return type */ - if (GetUnqualRawTypeCode (ReturnType) == T_INT) { - /* Determine if this is a main function in a C99 environment that - ** returns an int. - */ - if (IS_Get (&Standard) >= STD_C99) { - C99MainFunc = 1; - } - } else { - /* If cc65 extensions aren't enabled, don't allow a main function - ** that doesn't return an int. - */ - if (IS_Get (&Standard) != STD_CC65) { - Error ("'main' must always return an int"); - } - } - /* Add a forced import of a symbol that is contained in the startup ** code. This will force the startup code to be linked in. */ @@ -659,7 +636,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* If this is the main function in a C99 environment returning an int, ** let it always return zero. Otherwise output a warning. */ - if (C99MainFunc) { + if (IS_Get (&Standard) >= STD_C99 && GetUnqualRawTypeCode (ReturnType) == T_INT) { g_getimmed (CF_INT | CF_CONST, 0, 0); } else if (IS_Get (&WarnReturnType)) { Warning ("Control reaches end of non-void function [-Wreturn-type]"); @@ -685,9 +662,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Leave the lexical level */ LeaveFunctionLevel (); - /* Eat the closing brace */ - ConsumeRCurly (); - /* Restore the old literal pool, remembering the one for the function */ Func->V.F.LitPool = PopLiteralPool (); @@ -699,6 +673,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Switch back to the old segments */ PopSegContext (); + /* Eat the closing brace after we've done everything with the function + ** definition. This way we won't have troubles with pragmas right after + ** the closing brace. + */ + ConsumeRCurly(); + /* Reset the current function pointer */ FreeFunction (CurrentFunc); CurrentFunc = 0; diff --git a/src/cc65/goto.c b/src/cc65/goto.c index 44ae0595e6..e96ad6c4c7 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -92,7 +92,7 @@ void GotoStatement (void) /* Find array size */ if (!IsTypeArray (arr->Type) || SizeOf (arr->Type) == 0 || - !(arr->Flags & SC_STATIC) || + (arr->Flags & SC_STORAGEMASK) != SC_STATIC || SizeOf (GetElementType(arr->Type)) != 2) { Error ("Expected a static array"); } else if (GetElementCount (arr->Type) > 127) { diff --git a/src/cc65/input.c b/src/cc65/input.c index 89c471687d..fcf7f32f32 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -91,10 +91,11 @@ struct IFile { /* Struct that describes an active input file */ typedef struct AFile AFile; struct AFile { - unsigned Line; /* Line number for this file */ + unsigned LineNum; /* Actual line number for this file */ FILE* F; /* Input file stream */ IFile* Input; /* Points to corresponding IFile */ int SearchPath; /* True if we've added a path for this file */ + unsigned LineOffs; /* Offset to presumed line number for this file */ char* PName; /* Presumed name of the file */ PPIfStack IfStack; /* PP #if stack */ int MissingNL; /* Last input line was missing a newline */ @@ -111,6 +112,7 @@ static Collection* CurrentInputStack; /* Counter for the __COUNTER__ macro */ static unsigned MainFileCounter; +LineInfo* PrevDiagnosticLI; @@ -163,10 +165,11 @@ static AFile* NewAFile (IFile* IF, FILE* F) AFile* AF = (AFile*) xmalloc (sizeof (AFile)); /* Initialize the fields */ - AF->Line = 0; - AF->F = F; - AF->Input = IF; - AF->PName = 0; + AF->LineNum = 0; + AF->F = F; + AF->Input = IF; + AF->LineOffs = 0; + AF->PName = 0; AF->IfStack.Index = -1; AF->MissingNL = 0; @@ -285,7 +288,7 @@ void OpenMainFile (const char* Name) /* Update the line infos, so we have a valid line info even at start of ** the main file before the first line is read. */ - UpdateLineInfo (MainFile->Input, MainFile->Line, Line); + UpdateCurrentLineInfo (Line); /* Initialize the __COUNTER__ counter */ MainFileCounter = 0; @@ -553,7 +556,7 @@ int NextLine (void) if (!Input->MissingNL || SB_NotEmpty (Line)) { /* Accept files without a newline at the end */ - ++Input->Line; + ++Input->LineNum; /* Assume no new line */ Input->MissingNL = 1; @@ -569,7 +572,7 @@ int NextLine (void) if (C == '\n') { /* We got a new line */ - ++Input->Line; + ++Input->LineNum; /* If the \n is preceeded by a \r, remove the \r, so we can read ** DOS/Windows files under *nix. @@ -605,7 +608,7 @@ int NextLine (void) InitLine (Line); /* Create line information for this line */ - UpdateLineInfo (Input->Input, Input->Line, Line); + UpdateCurrentLineInfo (Line); /* Done */ return C != EOF || SB_NotEmpty (Line); @@ -645,15 +648,145 @@ int PreprocessNextLine (void) -const char* GetInputFile (const struct IFile* IF) -/* Return a filename from an IFile struct */ +static LineInfoFile* NewLineInfoFile (const AFile* AF) +{ + const char* Name = AF->PName == 0 ? AF->Input->Name : AF->PName; + unsigned Len = strlen (Name); + + /* Allocate memory for the file info and the file name */ + LineInfoFile* LIF = xmalloc (sizeof (LineInfoFile) + Len); + + /* Copy info */ + LIF->InputFile = AF->Input; + LIF->LineNum = AF->LineNum + AF->LineOffs; + memcpy (LIF->Name, Name, Len + 1); + + return LIF; +} + + + +void GetFileInclusionInfo (struct LineInfo* LI) +/* Get info about source file inclusion for LineInfo struct */ +{ + unsigned FileCount = CollCount (&AFiles); + + CHECK (FileCount > 0); + + /* Get the correct index */ + --FileCount; + + if (LI->IncFiles != 0) { + FreeFileInclusionInfo (LI); + } + LI->IncFiles = 0; + + if (LI->File != 0) { + xfree (LI->File); + } + + /* Copy info from the AFile */ + LI->File = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Remember the actual line number */ + LI->ActualLineNum = ((AFile*)CollAtUnchecked (&AFiles, FileCount))->LineNum; + + if (FileCount > 0) { + /* The file is included from another */ + + /* Always use a new collection */ + LI->IncFiles = NewCollection (); + + while (FileCount-- > 0) { + /* Copy info from the AFile */ + LineInfoFile* LIF = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount)); + + /* Add this file */ + CollAppend (LI->IncFiles, LIF); + } + } +} + + + +void FreeFileInclusionInfo (struct LineInfo* LI) +/* Free info about source file inclusion for LineInfo struct */ +{ + if (LI->File != 0) { + xfree (LI->File); + LI->File = 0; + } + + if (LI->IncFiles != 0) { + unsigned I; + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + CollAtUnchecked (LI->IncFiles, I); + } + FreeCollection (LI->IncFiles); + LI->IncFiles = 0; + } +} + + + +static int IsDifferentLineInfoFile (const LineInfoFile* Lhs, const LineInfoFile* Rhs) +/* Return true if the two files are different */ +{ + /* If the input files are the same but their presumed names are different, + ** we still consider the files same. + */ + return Lhs->InputFile != Rhs->InputFile || Lhs->LineNum != Rhs->LineNum; +} + + + +int HasFileInclusionChanged (const struct LineInfo* LI) +/* Return true if file inclusion has changed from last time */ +{ + if (LI->File != 0) { + LineInfo* PrevLI = GetPrevCheckedLI (); + + if (LI == PrevLI) { + return 0; + } + + if (PrevLI == 0) { + return 1; + } + + if (LI->IncFiles != 0) { + unsigned I; + + if (PrevLI->IncFiles == 0 || + CollCount (LI->IncFiles) != CollCount (PrevLI->IncFiles)) { + return 1; + } + + for (I = 0; I < CollCount (LI->IncFiles); ++I) { + /* If this refers to a different file, then the inclusion has changed */ + if (IsDifferentLineInfoFile (CollAtUnchecked (LI->IncFiles, I), + CollAtUnchecked (PrevLI->IncFiles, I))) { + return 1; + } + } + } + } + + /* Unchanged */ + return 0; +} + + + +const char* GetInputFileName (const struct IFile* IF) +/* Return the name of the file from an IFile struct */ { return IF->Name; } -const char* GetCurrentFilename (void) +const char* GetCurrentFileName (void) /* Return the name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); @@ -674,7 +807,7 @@ unsigned GetCurrentLineNum (void) unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { const AFile* AF = CollLast (&AFiles); - return AF->Line; + return AF->LineNum + AF->LineOffs; } else { /* No open file */ return 0; @@ -684,18 +817,18 @@ unsigned GetCurrentLineNum (void) void SetCurrentLineNum (unsigned LineNum) -/* Set the line number in the current input file */ +/* Set the presumed line number in the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { AFile* AF = CollLast (&AFiles); - AF->Line = LineNum; + AF->LineOffs = LineNum - AF->LineNum; } } -void SetCurrentFilename (const char* Name) +void SetCurrentFileName (const char* Name) /* Set the presumed name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); diff --git a/src/cc65/input.h b/src/cc65/input.h index 9457bdf9b1..9a5a769497 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -52,6 +52,10 @@ +/* Forwards */ +struct IFile; +struct LineInfo; + /* An enum that describes different types of input files. The members are ** choosen so that it is possible to combine them to bitsets */ @@ -61,9 +65,6 @@ typedef enum { IT_USRINC = 0x04, /* User include file (using "") */ } InputType; -/* Forward for an IFile structure */ -struct IFile; - /* The current input line */ extern StrBuf* Line; @@ -125,10 +126,19 @@ int PreprocessNextLine (void); ** main file. */ -const char* GetInputFile (const struct IFile* IF); -/* Return a filename from an IFile struct */ +void GetFileInclusionInfo (struct LineInfo* LI); +/* Get info about source file inclusion for LineInfo struct */ + +void FreeFileInclusionInfo (struct LineInfo* LI); +/* Free info about source file inclusion for LineInfo struct */ + +int HasFileInclusionChanged (const struct LineInfo* LI); +/* Return true if file inclusion has changed from last time */ + +const char* GetInputFileName (const struct IFile* IF); +/* Return the name of the file from an IFile struct */ -const char* GetCurrentFilename (void); +const char* GetCurrentFileName (void); /* Return the name of the current input file */ unsigned GetCurrentLineNum (void); @@ -137,7 +147,7 @@ unsigned GetCurrentLineNum (void); void SetCurrentLineNum (unsigned LineNum); /* Set the line number in the current input file */ -void SetCurrentFilename (const char* Name); +void SetCurrentFileName (const char* Name); /* Set the presumed name of the current input file */ unsigned GetCurrentCounter (void); diff --git a/src/cc65/lineinfo.c b/src/cc65/lineinfo.c index f5c2e2689c..8639d27e9c 100644 --- a/src/cc65/lineinfo.c +++ b/src/cc65/lineinfo.c @@ -56,6 +56,9 @@ /* Global pointer to line information for the current line */ static LineInfo* CurLineInfo = 0; +/* Global pointer to previously checked line information about file inclusion hierarchy */ +static LineInfo* PrevCheckedLI = 0; + /*****************************************************************************/ @@ -64,7 +67,7 @@ static LineInfo* CurLineInfo = 0; -static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) +static LineInfo* NewLineInfo (const StrBuf* Line) /* Create and return a new line info. Ref count will be 1. */ { unsigned Len; @@ -87,8 +90,9 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L /* Initialize the fields */ LI->RefCount = 1; - LI->InputFile = F; - LI->LineNum = LineNum; + LI->File = 0; + LI->IncFiles = 0; + GetFileInclusionInfo (LI); /* Copy the line, replacing tabs by spaces in the given line since tabs ** will give rather arbitrary results when used in the output later, and @@ -117,6 +121,7 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L static void FreeLineInfo (LineInfo* LI) /* Free a LineInfo structure */ { + FreeFileInclusionInfo (LI); xfree (LI); } @@ -156,8 +161,8 @@ LineInfo* GetCurLineInfo (void) -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line) +/* Update the current line info - called if a new line is read */ { /* If a current line info exists, release it */ if (CurLineInfo) { @@ -172,23 +177,60 @@ void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line) } /* Create a new line info */ - CurLineInfo = NewLineInfo (F, LineNum, Line); + CurLineInfo = NewLineInfo (Line); +} + + + +void RememberCheckedLI (LineInfo* LI) +/* Remember the latest checked line info struct */ +{ + if (PrevCheckedLI != LI) { + if (PrevCheckedLI != 0) { + ReleaseLineInfo (PrevCheckedLI); + } + PrevCheckedLI = UseLineInfo (LI); + } } -const char* GetInputName (const LineInfo* LI) -/* Return the file name from a line info */ +LineInfo* GetPrevCheckedLI (void) +/* Get the latest checked line info struct */ +{ + return PrevCheckedLI; +} + + + +const char* GetPresumedFileName (const LineInfo* LI) +/* Return the presumed file name from a line info */ { PRECONDITION (LI != 0); - return GetInputFile (LI->InputFile); + return LI->File->Name; } -unsigned GetInputLine (const LineInfo* LI) -/* Return the line number from a line info */ +unsigned GetPresumedLineNum (const LineInfo* LI) +/* Return the presumed line number from a line info */ { PRECONDITION (LI != 0); - return LI->LineNum; + return LI->File->LineNum; +} + + + +const char* GetActualFileName (const struct LineInfo* LI) +/* Return the actual name of the source file from a line info struct */ +{ + return LI->File != 0 ? GetInputFileName (LI->File->InputFile) : ""; +} + + + +unsigned GetActualLineNum (const struct LineInfo* LI) +/* Return the actual line number of the source file from a line info struct */ +{ + return LI->ActualLineNum; } diff --git a/src/cc65/lineinfo.h b/src/cc65/lineinfo.h index f365b4f013..02e77cd9cb 100644 --- a/src/cc65/lineinfo.h +++ b/src/cc65/lineinfo.h @@ -60,15 +60,24 @@ struct IFile; +/* Struct that describes an input file for line info */ +typedef struct LineInfoFile LineInfoFile; +struct LineInfoFile { + struct IFile* InputFile; /* Points to corresponding IFile */ + unsigned LineNum; /* Presumed line number for this file */ + char Name[1]; /* Presumed name of the file */ +}; + /* The text for the actual line is allocated at the end of the structure, so ** the size of the structure varies. */ typedef struct LineInfo LineInfo; struct LineInfo { - unsigned RefCount; /* Reference counter */ - struct IFile* InputFile; /* Input file for this line */ - unsigned LineNum; /* Line number */ - char Line[1]; /* Source code line */ + unsigned RefCount; /* Reference counter */ + LineInfoFile* File; /* Presumed input files for this line */ + unsigned ActualLineNum; /* Actual line number for this file */ + struct Collection* IncFiles; /* Presumed inclusion input files */ + char Line[1]; /* Text of source code line */ }; @@ -92,14 +101,26 @@ LineInfo* GetCurLineInfo (void); ** increased, use UseLineInfo for that purpose. */ -void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line); -/* Update the line info - called if a new line is read */ +void UpdateCurrentLineInfo (const StrBuf* Line); +/* Update the current line info - called if a new line is read */ + +void RememberCheckedLI (struct LineInfo* LI); +/* Remember the latest checked line info struct */ + +LineInfo* GetPrevCheckedLI (void); +/* Get the latest checked line info struct */ + +const char* GetPresumedFileName (const LineInfo* LI); +/* Return the presumed file name from a line info */ + +unsigned GetPresumedLineNum (const LineInfo* LI); +/* Return the presumed line number from a line info */ -const char* GetInputName (const LineInfo* LI); -/* Return the file name from a line info */ +const char* GetActualFileName (const struct LineInfo* LI); +/* Return the actual name of the source file from a line info struct */ -unsigned GetInputLine (const LineInfo* LI); -/* Return the line number from a line info */ +unsigned GetActualLineNum (const struct LineInfo* LI); +/* Return the actual line number of the source file from a line info struct */ diff --git a/src/cc65/locals.c b/src/cc65/locals.c index b8738992ff..34d7623242 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -304,7 +304,7 @@ static void ParseAutoDecl (Declarator* Decl) /* Static local variables. */ - Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; + Decl->StorageClass = (Decl->StorageClass & ~SC_STORAGEMASK) | SC_STATIC; /* Generate a label, but don't define it */ DataLabel = GetLocalDataLabel (); @@ -451,24 +451,27 @@ static int ParseOneDecl (DeclSpec* Spec) /* Read the declarator */ NeedClean = ParseDecl (Spec, &Decl, DM_IDENT_OR_EMPTY); - /* Check if there are any non-extern storage classes set for function - ** declarations. Function can only be declared inside functions with the - ** 'extern' storage class specifier or no storage class specifier at all. + /* Check if there are explicitly specified non-external storage classes + ** for function declarations. */ - if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { - - /* Check if there are explicitly specified non-external storage classes */ + if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { + /* Function can only be declared inside functions with the 'extern' + ** storage class specifier or no storage class specifier at all. + ** Note: this declaration is always checked for compatibility with + ** other declarations of the same symbol, but does not necessarily + ** make the symbol globally visible. This is tricky. + */ if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && - (Decl.StorageClass & SC_EXTERN) == 0 && + (Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN && (Decl.StorageClass & SC_STORAGEMASK) != 0) { Error ("Illegal storage class on function"); } /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; - - /* This is always an extern declaration */ - Decl.StorageClass |= SC_DECL | SC_EXTERN; + } else if ((Decl.StorageClass & SC_STORAGEMASK) != SC_EXTERN) { + /* If the symbol is not marked as external, it will be defined now */ + Decl.StorageClass |= SC_DEF; } /* If we don't have a name, this was flagged as an error earlier. @@ -478,12 +481,6 @@ static int ParseOneDecl (DeclSpec* Spec) AnonName (Decl.Ident, "param"); } - /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_DECL) == 0 && - (Decl.StorageClass & SC_EXTERN) == 0) { - Decl.StorageClass |= SC_DEF; - } - /* Handle anything that needs storage (no functions, no typdefs) */ if ((Decl.StorageClass & SC_DEF) == SC_DEF && (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { @@ -492,20 +489,20 @@ static int ParseOneDecl (DeclSpec* Spec) ** convert the declaration to "auto" if this is not possible. */ int Reg = 0; /* Initialize to avoid gcc complains */ - if ((Decl.StorageClass & SC_REGISTER) != 0 && + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER && (Reg = F_AllocRegVar (CurrentFunc, Decl.Type)) < 0) { /* No space for this register variable, convert to auto */ - Decl.StorageClass = (Decl.StorageClass & ~SC_REGISTER) | SC_AUTO; + Decl.StorageClass = (Decl.StorageClass & ~SC_STORAGEMASK) | SC_AUTO; } /* Check the variable type */ - if ((Decl.StorageClass & SC_REGISTER) == SC_REGISTER) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_REGISTER) { /* Register variable */ ParseRegisterDecl (&Decl, Reg); - } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_AUTO) { /* Auto variable */ ParseAutoDecl (&Decl); - } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { + } else if ((Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); } else { @@ -514,7 +511,7 @@ static int ParseOneDecl (DeclSpec* Spec) } else { - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN) { /* External identifier - may not get initialized */ if (CurTok.Tok == TOK_ASSIGN) { Error ("Cannot initialize extern variable '%s'", Decl.Ident); @@ -524,8 +521,8 @@ static int ParseOneDecl (DeclSpec* Spec) } } - if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || - (Decl.StorageClass & SC_FUNC) == SC_FUNC) { + if ((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN || + (Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) { /* Add the global symbol to both of the global and local symbol ** tables. */ @@ -554,7 +551,9 @@ void DeclareLocals (void) /* A place to store info about potential initializations of auto variables */ CollAppend (&CurrentFunc->LocalsBlockStack, 0); - /* Loop until we don't find any more variables */ + /* Loop until we don't find any more variables. EOF is handled in the loop + ** as well. + */ while (1) { DeclSpec Spec; int NeedClean; @@ -566,7 +565,7 @@ void DeclareLocals (void) } /* Read the declaration specifier */ - ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO); /* Check variable declarations. We need distinguish between a default ** int type and the end of variable declarations. So we will do the diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index db306040da..b9394494b3 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -630,7 +630,7 @@ static void WrappedCallPragma (pragma_scope_t Scope, StrBuf* B) Entry = FindSym(Name); /* Check if the name is valid */ - if (Entry && (Entry->Flags & SC_FUNC) == SC_FUNC) { + if (Entry && (Entry->Flags & SC_TYPEMASK) == SC_FUNC) { PushWrappedCall(Entry, (unsigned int) Val); Entry->Flags |= SC_REF; diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 0a9b94bf24..66cbb2a9d0 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -842,7 +842,7 @@ static void AddPreLine (StrBuf* Str) SB_AppendChar (Str, '\n'); } SB_Printf (&Comment, "#line %u \"%s\"\n", - GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ()); + GetCurrentLineNum () - ContinuedLines, GetCurrentFileName ()); SB_Append (Str, &Comment); } else { /* Output new lines */ @@ -2943,7 +2943,7 @@ static void DoLine (void) StrBuf Filename = AUTO_STRBUF_INITIALIZER; if (SB_GetString (Line, &Filename)) { SB_Terminate (&Filename); - SetCurrentFilename (SB_GetConstBuf (&Filename)); + SetCurrentFileName (SB_GetConstBuf (&Filename)); } else { PPError ("Invalid filename for #line directive"); LineNum = 0; @@ -3220,7 +3220,7 @@ void HandleSpecialMacro (Macro* M, const char* Name) } else if (strcmp (Name, "__FILE__") == 0) { /* Replace __FILE__ with the current filename */ StrBuf B = AUTO_STRBUF_INITIALIZER; - SB_InitFromString (&B, GetCurrentFilename ()); + SB_InitFromString (&B, GetCurrentFileName ()); SB_Clear (&M->Replacement); Stringize (&B, &M->Replacement); SB_Done (&B); @@ -3332,7 +3332,7 @@ void Preprocess (void) PLine = InitLine (PLine); if (Verbosity > 1 && SB_NotEmpty (Line)) { - printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (), + printf ("%s:%u: %.*s\n", GetCurrentFileName (), GetCurrentLineNum (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index ccf3a88050..6fc3e53701 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -76,6 +76,7 @@ typedef enum token_t { /* Function specifiers */ TOK_INLINE, + TOK_NORETURN, TOK_FASTCALL, TOK_CDECL, diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 30ebe7dd8c..56f6ffbb0e 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -88,7 +88,7 @@ void FreeSymEntry (SymEntry* E) TypeFree (E->Type); xfree (E->AsmName); - if (E->Flags & SC_LABEL) { + if ((E->Flags & SC_TYPEMASK) == SC_LABEL) { for (i = 0; i < CollCount (E->V.L.DefsOrRefs); i++) { xfree (CollAt (E->V.L.DefsOrRefs, i)); } @@ -109,21 +109,17 @@ void DumpSymEntry (FILE* F, const SymEntry* E) unsigned Val; } SCFlagTable; - static SCFlagTable ESUTypes[] = { - { "SC_TYPEDEF", SC_TYPEDEF }, - { "SC_UNION", SC_UNION }, + static SCFlagTable Types[] = { + { "SC_NONE", SC_NONE }, { "SC_STRUCT", SC_STRUCT }, + { "SC_UNION", SC_UNION }, { "SC_ENUM", SC_ENUM }, - }; - - static SCFlagTable Types[] = { + { "SC_LABEL", SC_LABEL }, { "SC_BITFIELD", SC_BITFIELD }, - { "SC_STRUCTFIELD", SC_STRUCTFIELD }, + { "SC_TYPEDEF", SC_TYPEDEF }, { "SC_ENUMERATOR", SC_ENUMERATOR }, - { "SC_CONST", SC_CONST }, - { "SC_LABEL", SC_LABEL }, - { "SC_PARAM", SC_PARAM }, { "SC_FUNC", SC_FUNC }, + { "SC_ARRAY", SC_ARRAY }, }; static SCFlagTable Storages[] = { @@ -131,11 +127,31 @@ void DumpSymEntry (FILE* F, const SymEntry* E) { "SC_REGISTER", SC_REGISTER }, { "SC_STATIC", SC_STATIC }, { "SC_EXTERN", SC_EXTERN }, - { "SC_STORAGE", SC_STORAGE }, + }; + + static SCFlagTable Properties[] = { + { "SC_CONST", SC_CONST }, + { "SC_STRUCTFIELD", SC_STRUCTFIELD }, + { "SC_PARAM", SC_PARAM }, + { "SC_DEFTYPE", SC_DEFTYPE }, { "SC_ZEROPAGE", SC_ZEROPAGE }, - { "SC_DECL", SC_DECL }, + { "SC_HAVEALIGN", SC_HAVEALIGN }, + { "SC_HAVEATTR", SC_HAVEATTR }, + { "SC_TU_STORAGE", SC_TU_STORAGE }, + { "SC_ASSIGN_INIT", SC_ASSIGN_INIT }, + { "SC_ALIAS", SC_ALIAS }, + { "SC_FICTITIOUS", SC_FICTITIOUS }, + { "SC_HAVEFAM", SC_HAVEFAM }, + { "SC_HAVECONST", SC_HAVECONST }, + }; + + static SCFlagTable Status[] = { { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, + { "SC_GOTO", SC_GOTO }, + { "SC_GOTO_IND", SC_GOTO_IND }, + { "SC_LOCALSCOPE", SC_LOCALSCOPE }, + { "SC_NOINLINEDEF", SC_NOINLINEDEF }, }; unsigned I; @@ -152,28 +168,38 @@ void DumpSymEntry (FILE* F, const SymEntry* E) /* Print the flags */ SymFlags = E->Flags; fprintf (F, " Flags:"); - /* Enum, struct, union and typedefs */ - if ((SymFlags & SC_ESUTYPEMASK) != 0) { - for (I = 0; I < sizeof (ESUTypes) / sizeof (ESUTypes[0]); ++I) { - if ((SymFlags & SC_ESUTYPEMASK) == ESUTypes[I].Val) { - SymFlags &= ~SC_ESUTYPEMASK; - fprintf (F, " %s", ESUTypes[I].Name); + /* Symbol types */ + if ((SymFlags & SC_TYPEMASK) != 0) { + for (I = 0; I < sizeof (Types) / sizeof (Types[0]); ++I) { + if ((SymFlags & SC_TYPEMASK) == Types[I].Val) { + SymFlags &= ~SC_TYPEMASK; + fprintf (F, " %s", Types[I].Name); + break; + } + } + } + /* Storage classes */ + if ((SymFlags & SC_STORAGEMASK) != 0) { + for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]); ++I) { + if ((SymFlags & SC_STORAGEMASK) == Storages[I].Val) { + SymFlags &= ~SC_STORAGEMASK; + fprintf (F, " %s", Storages[I].Name); break; } } } - /* 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); + /* Special property flags */ + for (I = 0; I < sizeof (Properties) / sizeof (Properties[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Properties[I].Val) == Properties[I].Val) { + SymFlags &= ~Properties[I].Val; + fprintf (F, " %s", Properties[I].Name); } } - /* 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); + /* Status flags */ + for (I = 0; I < sizeof (Status) / sizeof (Status[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Status[I].Val) == Status[I].Val) { + SymFlags &= ~Status[I].Val; + fprintf (F, " %s", Status[I].Name); } } if (SymFlags != 0) { @@ -199,9 +225,10 @@ int SymIsOutputFunc (const SymEntry* Sym) /* Symbol must be a function which is defined and either extern or ** static and referenced. */ - return IsTypeFunc (Sym->Type) && - SymIsDef (Sym) && - (Sym->Flags & (SC_REF | SC_EXTERN)); + return IsTypeFunc (Sym->Type) && + SymIsDef (Sym) && + ((Sym->Flags & SC_REF) || + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC); } @@ -272,7 +299,7 @@ void SymCvtRegVarToAuto (SymEntry* Sym) /* Convert a register variable to an auto variable */ { /* Change the storage class */ - Sym->Flags = (Sym->Flags & ~(SC_REGISTER | SC_STATIC | SC_EXTERN)) | SC_AUTO; + Sym->Flags = (Sym->Flags & ~SC_STORAGEMASK) | SC_AUTO; /* Transfer the stack offset from register save area to actual offset */ Sym->V.Offs = Sym->V.R.SaveOffs; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 639221625b..7871b9ade1 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -68,47 +68,92 @@ struct CodeEntry; -/* Storage classes and flags */ +/* Symbol types and flags */ #define SC_NONE 0x0000U /* Nothing */ -#define SC_STRUCT 0x0001U /* Struct */ -#define SC_UNION 0x0002U /* Union */ -#define SC_ENUM 0x0003U /* Enum */ -#define SC_TYPEDEF 0x0004U /* Typedef */ -#define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ -#define SC_ENUMERATOR 0x0008U /* An enumerator */ -#define SC_BITFIELD 0x0010U /* A bit-field inside a struct or union */ -#define SC_TYPEMASK 0x001FU /* Mask for above types */ -#define SC_FUNC 0x0020U /* A function */ -#define SC_LABEL 0x0040U /* A goto code label */ -#define SC_CONST 0x0080U /* A numeric constant with a type */ -#define SC_PARAM 0x0100U /* A function parameter */ -#define SC_DEFTYPE 0x0200U /* Parameter has default type (=int, old style) */ -#define SC_STRUCTFIELD 0x0400U /* Struct or union field */ - -#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_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_HAVEATTR 0x100000U /* Symbol has attributes */ - -#define SC_GOTO 0x200000U -#define SC_SPADJUSTMENT 0x400000U -#define SC_GOTO_IND 0x800000U /* Indirect goto */ +/* Types of symbols */ +#define SC_STRUCT 0x0001U /* Struct tag */ +#define SC_UNION 0x0002U /* Union tag */ +#define SC_ENUM 0x0003U /* Enum tag */ +#define SC_LABEL 0x0004U /* A goto code label */ +#define SC_BITFIELD 0x0005U /* A bit-field inside a struct or union */ +#define SC_TYPEDEF 0x0006U /* A typedef */ +#define SC_ENUMERATOR 0x0007U /* An enumerator */ + +/* Note: These symbol types might be checked as bit-flags occasionally. +** So don't share their unique bits with other symbol types. +*/ +#define SC_FUNC 0x0008U /* A function */ +#define SC_ARRAY 0x0010U /* UNUSED: An array */ +#define SC_TYPEMASK 0x001FU /* Mask for symbol types all above */ + +/* Additional property of the symbols */ +#define SC_CONST 0x0020U /* A numeric constant with a type */ +#define SC_STRUCTFIELD 0x0040U /* A struct or union field */ +#define SC_PARAM 0x0080U /* A function parameter */ +#define SC_DEFTYPE 0x0100U /* An old-style parameter with default type (=int) */ + +/* Address property of the symbol */ +#define SC_ZEROPAGE 0x0200U /* Symbol marked as on zeropage */ + +/* Additional attributes of the symbol */ +#define SC_HAVEALIGN 0x0400U /* UNUSED: Symbol has special alignment */ +#define SC_HAVEATTR 0x0800U /* Symbol has attributes */ + +/* Special property of declaration */ +#define SC_TU_STORAGE 0x1000U /* Symbol has allocated storage in the TU */ +#define SC_ASSIGN_INIT 0x2000U /* Symbol is to be initialized with assignment code */ + +#define SC_ALIAS 0x4000U /* Symbol is an alias */ +#define SC_FICTITIOUS 0x8000U /* Symbol is fictitious (for error recovery) */ +#define SC_HAVEFAM 0x010000U /* Struct/union has a Flexible Array Member */ +#define SC_HAVECONST 0x020000U /* Struct/union has a const member */ + +/* Status of the symbol */ +#define SC_DEF 0x040000U /* Symbol is defined */ +#define SC_REF 0x080000U /* Symbol is referenced */ +#define SC_GOTO 0x100000U /* Symbol is destination of a goto */ +#define SC_GOTO_IND 0x200000U /* Symbol is destination of an indirect goto */ +#define SC_LOCALSCOPE 0x400000U /* Symbol is invisible in file scope */ +#define SC_NOINLINEDEF 0x800000U /* Symbol may never have an inline definition */ + +/* To figure out the linkage of an object or function symbol Sym: +** - external linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) != SC_STATIC +** - internal linkage: +** SymIsGlobal (Sym) && (Sym->Flags & SC_STORAGEMASK) == SC_STATIC +** - no linkage: +** !SymIsGlobal (Sym) +** +** To figure out the storage class of a symbol by its SC_ flags: +** +** - no explicit storage class specifiers (in file scope): +** (flags & SC_STORAGEMASK) == SC_NONE +** - no explicit storage class specifiers (in block scope): +** (flags & SC_STORAGEMASK) == SC_AUTO +** - extern: +** (flags & SC_STORAGEMASK) == SC_EXTERN +** - static: +** (flags & SC_STORAGEMASK) == SC_STATIC +** - auto: +** (flags & SC_STORAGEMASK) == SC_AUTO +** - register: +** (flags & SC_STORAGEMASK) == SC_REGISTER +** - typedef (per ISO C): +** (flags & SC_TYPEMASK) == SC_TYPEDEF +** +** Note: SC_TYPEDEF can be also used as a flag. +*/ +#define SC_AUTO 0x01000000U /* Auto storage class */ +#define SC_REGISTER 0x02000000U /* Register storage class */ +#define SC_STATIC 0x03000000U /* Static storage class */ +#define SC_EXTERN 0x04000000U /* Extern storage class */ +#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */ +#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */ -#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */ -#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */ -#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ -#define SC_HAVECONST 0x08000000U /* Type has a const member */ +/* Function specifiers */ +#define SC_INLINE 0x10000000U /* Inline function */ +#define SC_NORETURN 0x20000000U /* Noreturn function */ @@ -214,14 +259,37 @@ void FreeSymEntry (SymEntry* E); void DumpSymEntry (FILE* F, const SymEntry* E); /* Dump the given symbol table entry to the file in readable form */ +int SymIsOutputFunc (const SymEntry* Sym); +/* Return true if this is a function that must be output */ + +#if defined(HAVE_INLINE) +INLINE int SymIsArray (const SymEntry* Sym) +/* Return true if the given entry is an array entry */ +{ + return ((Sym->Flags & SC_TYPEMASK) == SC_ARRAY); +} +#else +# define SymIsArray(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_ARRAY) +#endif + #if defined(HAVE_INLINE) INLINE int SymIsBitField (const SymEntry* Sym) /* Return true if the given entry is a bit-field entry */ { - return ((Sym->Flags & SC_BITFIELD) == SC_BITFIELD); + return ((Sym->Flags & SC_TYPEMASK) == SC_BITFIELD); +} +#else +# define SymIsBitField(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_BITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int SymIsLabel (const SymEntry* Sym) +/* Return true if the given entry is a label entry */ +{ + return ((Sym)->Flags & SC_TYPEMASK) == SC_LABEL; } #else -# define SymIsBitField(Sym) (((Sym)->Flags & SC_BITFIELD) == SC_BITFIELD) +# define SymIsLabel(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_LABEL) #endif #if defined(HAVE_INLINE) @@ -257,17 +325,13 @@ INLINE int SymIsRef (const SymEntry* Sym) #if defined(HAVE_INLINE) INLINE int SymIsRegVar (const SymEntry* Sym) /* Return true if the given entry is a register variable */ -/* ### HACK! Fix the ugly type flags! */ { - return ((Sym->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER); + return ((Sym->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_STORAGEMASK | SC_TYPEMASK)) == (SC_REGISTER | SC_NONE)) #endif -int SymIsOutputFunc (const SymEntry* Sym); -/* Return true if this is a function that must be output */ - #if defined(HAVE_INLINE) INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) /* Return true if the given entry has a flexible array member */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index c2c6bab270..07723699ff 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -165,22 +165,23 @@ static void CheckSymTable (SymTable* Tab) /* Ignore typedef entries */ if (!SymIsTypeDef (Entry)) { - /* Check if the symbol is one with storage, and it if it was - ** defined but not used. - */ - if (((Flags & SC_AUTO) || (Flags & SC_STATIC)) && (Flags & SC_EXTERN) == 0) { + /* Check if the symbol has non-external linkage and is defined but not used */ + if (!SymIsGlobal (Entry) || (Flags & SC_STORAGEMASK) == SC_STATIC) { if (SymIsDef (Entry) && !SymIsRef (Entry) && !SymHasAttr (Entry, atUnused)) { if (Flags & SC_PARAM) { - if (IS_Get (&WarnUnusedParam)) { + if (IS_Get (&WarnUnusedParam) && + !IsAnonName (Entry->Name)) { Warning ("Parameter '%s' is never used", Entry->Name); } - } else if (Flags & SC_FUNC) { + } else if ((Flags & SC_TYPEMASK) == SC_FUNC) { if (IS_Get (&WarnUnusedFunc)) { Warning ("Function '%s' is defined but never used", Entry->Name); } - } else if (!IsAnonName (Entry->Name)) { - if (IS_Get (&WarnUnusedVar)) { + } else if ((Flags & SC_TYPEMASK) == SC_NONE) { + if (IS_Get (&WarnUnusedVar) && + !IsAnonName (Entry->Name) && + (Flags & SC_CONST) != SC_CONST) { Warning ("Variable '%s' is defined but never used", Entry->Name); } } @@ -188,7 +189,7 @@ static void CheckSymTable (SymTable* Tab) } /* If the entry is a label, check if it was defined in the function */ - if (Flags & SC_LABEL) { + if ((Flags & SC_TYPEMASK) == SC_LABEL) { if (!SymIsDef (Entry)) { /* Undefined label */ Error ("Undefined label: '%s'", Entry->Name); @@ -557,8 +558,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H -static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) -/* Find the symbol with the given name in the table tree that starts with T */ +static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name) +/* Find the visible symbol with the given name in the table tree that starts +** with Tab. +*/ { /* Get the hash over the name */ unsigned Hash = HashStr (Name); @@ -574,7 +577,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) } /* Bail out if we found it */ - if (E != 0) { + if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) { return E; } @@ -589,9 +592,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name) SymEntry* FindSym (const char* Name) -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ { - return FindSymInTree (SymTab, Name); + return FindVisibleSymInTree (SymTab, Name); } @@ -613,9 +616,9 @@ SymEntry* FindLocalSym (const char* Name) SymEntry* FindTagSym (const char* Name) -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ { - return FindSymInTree (TagTab, Name); + return FindVisibleSymInTree (TagTab, Name); } @@ -716,7 +719,7 @@ static int HandleSymRedefinition (SymEntry* Sym, const Type* T, unsigned Flags) Sym = 0; } - } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { + } else if ((Sym->Flags & SC_TYPEMASK) == SC_FUNC) { /* In case of a function, use the new type descriptor, since it ** contains pointers to the new symbol tables that are needed if @@ -863,7 +866,6 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab } else { TagEntry->V.E.SymTab = Tab; TagEntry->V.E.Type = Type; - TagEntry->Flags &= ~SC_DECL; TagEntry->Flags |= SC_DEF; /* Remember this is the first definition of this type */ @@ -955,7 +957,13 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl TagEntry = 0; } else if (Size == 0) { /* Empty struct is not supported now */ - Error ("Empty %s type '%s' is not supported", SCType == SC_STRUCT ? "struct" : "union", Name); + if (!IsAnonName (Name)) { + Error ("Empty %s type '%s' is not supported", + SCType == SC_STRUCT ? "struct" : "union", Name); + } else { + Error ("Empty %s type is not supported", + SCType == SC_STRUCT ? "struct" : "union"); + } TagEntry = 0; } } @@ -1047,7 +1055,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); if (Entry) { - if ((Entry->Flags & SC_CONST) != SC_CONST) { + if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_TYPEMASK)) { Error ("Symbol '%s' is already different kind", Name); } else { Error ("Multiple definition for constant '%s'", Name); @@ -1056,7 +1064,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val } /* Create a new entry */ - Entry = NewSymEntry (Name, Flags); + Entry = NewSymEntry (Name, Flags | SC_CONST); /* We only have integer constants for now */ Entry->Type = TypeDup (T); @@ -1167,7 +1175,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Optimizer will need the information about the value of SP adjustment ** later, so let's preserve it. */ - E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); + E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), 0); E->V.SPAdjustment = StackPtr - DOR->StackPtr; AddSymEntry (SPAdjustTab, E); } @@ -1236,38 +1244,32 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { - int CheckExtern = 0; if ((Flags & SC_STRUCTFIELD) == 0) { while (Entry && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { /* Get the aliased entry */ Entry = Entry->V.A.Field; - /* Check for conflict with local storage class */ - CheckExtern = 1; } } /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { /* Redefinitions are not allowed */ if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { Error ("Multiple definition of '%s'", Entry->Name); Entry = 0; - } else if (CheckExtern) { - if ((Flags & (SC_AUTO | SC_REGISTER)) != 0) { - Error ("Declaration of '%s' with no linkage follows extern declaration", Name); - Entry = 0; - } else if ((Flags & SC_DEF) != 0 && (Flags & SC_EXTERN) == 0) { - /* If a static declaration follows a non-static declaration, - ** then it is an error. - */ - Error ("Static declaration of '%s' follows extern declaration", Name); - Entry = 0; - } } else if ((Flags & SC_STRUCTFIELD) != 0) { Error ("Duplicate member '%s'", Entry->Name); Entry = 0; + } else if (Entry->Owner == SymTab0) { + if ((Flags & SC_STORAGEMASK) == SC_AUTO || + (Flags & SC_STORAGEMASK) == SC_REGISTER || + (Flags & SC_STORAGEMASK) == SC_STATIC) { + Error ("Declaration of '%s' with no linkage follows extern declaration", + Name); + Entry = 0; + } } } @@ -1290,20 +1292,20 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry->Type = TypeDup (T); if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || - (Flags & SC_ESUTYPEMASK) == SC_TYPEDEF) { + (Flags & SC_TYPEMASK) == SC_TYPEDEF) { if ((Flags & SC_ALIAS) != SC_ALIAS) { Entry->V.Offs = Offs; } - } else if ((Flags & SC_AUTO) == SC_AUTO) { + } else if ((Flags & SC_STORAGEMASK) == SC_AUTO) { Entry->V.Offs = Offs; - } else if ((Flags & SC_REGISTER) == SC_REGISTER) { + } else if ((Flags & SC_STORAGEMASK) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; Entry->V.R.SaveOffs = StackPtr; - } else if ((Flags & SC_EXTERN) == SC_EXTERN || - (Flags & SC_FUNC) == SC_FUNC) { + } else if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { Entry->V.L.Label = Offs; SymSetAsmName (Entry); - } else if ((Flags & SC_STATIC) == SC_STATIC) { + } else if ((Flags & SC_STORAGEMASK) == SC_STATIC) { /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); @@ -1348,49 +1350,70 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Entry = FindGlobalSym (Name); } + /* Do we have a symbol with this name already? */ if (Entry) { - /* We have a symbol with this name already */ + /* Check if the symbol refers to some different type of things */ if (HandleSymRedefinition (Entry, T, Flags)) { Entry = 0; - } else if ((Entry->Flags & (SC_AUTO | SC_REGISTER)) != 0) { - /* Check for local storage class conflict */ + } else if (Entry->Owner != SymTab0) { + /* The previous declaration has no linkage. The current declaration + ** has either external or internal linkage. Either way it is an + ** error since the two declarations would be referring to different + ** objects with the same identifier. + */ Error ("Extern declaration of '%s' follows declaration with no linkage", Name); Entry = 0; - } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { - /* If a static declaration follows a non-static declaration, then - ** the result is undefined. - ** Most compilers choose to either give an error at compile time, - ** or remove the extern property for a link time error if used. + } else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) { + /* If we are adding the symbol in the file scope, it is now + ** visible there. */ - if (SymTab == SymTab0 && - (Flags & SC_EXTERN) == 0 && - (Entry->Flags & SC_EXTERN) != 0) { - Error ("Static declaration of '%s' follows non-static declaration", Name); - Entry = 0; - } else if ((Flags & SC_EXTERN) != 0 && - (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && - (Entry->Flags & SC_EXTERN) == 0) { - /* It is OK if a global extern declaration follows a global - ** non-static declaration, but an error if either of them is - ** local, as the two would be referring to different objects. - ** It is an error as well if a global non-static declaration - ** follows a global static declaration. + if (SymTab == SymTab0) { + Entry->Flags &= ~SC_LOCALSCOPE; + } + + /* The C standard specifies that the result is undefined if the + ** same thing has both internal and external linkage. Most + ** compilers choose to either give an error at compile time, or + ** remove the external linkage for a link time error if used + ** outside the current translation unit. We choose to give an + ** error at compile time in this case. + */ + if ((Entry->Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a non-static declaration of an + ** object or function that has external linkage. */ - if (Entry->Owner == SymTab0) { - if ((Flags & SC_STORAGE) == 0) { - /* The C standard specifies that a later extern declaration will keep - ** the previously declared internal or external linkage unchanged. - ** Though not required by the standard, we are warning on this case. - */ - Flags &= ~SC_EXTERN; - Warning ("Extern declaration of '%s' follows static declaration, linkage unchanged", Name); - } else { - Error ("Non-static declaration of '%s' follows static declaration", Name); - Entry = 0; - } - } else { - Error ("Extern declaration of '%s' follows static declaration", Name); + if ((Flags & SC_STORAGEMASK) == SC_STATIC) { + /* It is a static declaration of an object or function that + ** has internal linkage. Conflicted wih the previous one. + */ + Error ("Static declaration of '%s' follows non-static declaration", + Name); + Entry = 0; + } + } else if ((Flags & SC_STORAGEMASK) != SC_STATIC) { + /* The previous declaration is a static declaration of an + ** object or function that has internal linkage. + */ + if ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_TYPEMASK) == SC_FUNC) { + /* The C standard specifies that an extern declaration + ** shall keep the previously declared internal linkage + ** unchanged. For a function declaration with no storage + ** class specifiers, it is treated as if with 'extern'. + ** We give a warning although it is not required by the + ** standard. + */ + Flags &= ~SC_STORAGEMASK; + Warning ("Extern declaration of '%s' follows static declaration", + Name); + } else if ((Flags & SC_STORAGEMASK) == SC_NONE) { + /* It is a non-extern-or-static declaration of an object in + ** file scope that has external linkage. Conflicted wih the + ** previous one. + */ + Error ("Non-static declaration of '%s' follows static declaration", + Name); Entry = 0; } } @@ -1408,6 +1431,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } if (Entry == 0) { + /* Hide the symbol in the file scope if we are declaring it in a + ** local scope. + */ + if (Tab == SymTab0 && SymTab != SymTab0) { + Flags |= SC_LOCALSCOPE; + } + /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -1432,6 +1462,16 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) Entry->V.F.WrappedCall = WrappedCall; Entry->V.F.WrappedCallData = WrappedCallData; } + + /* A files cope function declaration with the 'extern' storage + ** class or without the 'inline' specifier ensures that the + ** function definition (if any) is a non-inline definition. + */ + if (SymTab == SymTab0 && + ((Flags & SC_STORAGEMASK) == SC_EXTERN || + (Flags & SC_INLINE) == 0)) { + Entry->Flags |= SC_NOINLINEDEF; + } } /* Add an alias of the global symbol to the local symbol table */ @@ -1486,14 +1526,22 @@ SymTable* GetLabelSymTab (void) -int SymIsLocal (SymEntry* Sym) -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym) +/* Return true if the symbol is declared in the highest lexical level */ { return (Sym->Owner == SymTab || Sym->Owner == TagTab); } +int SymIsGlobal (const SymEntry* Sym) +/* Return true if the symbol is declared in the file scope level */ +{ + return (Sym->Owner == SymTab0 || Sym->Owner == TagTab0); +} + + + void MakeZPSym (const char* Name) /* Mark the given symbol as zero page symbol */ { @@ -1553,12 +1601,14 @@ void EmitExternals (void) Entry = SymTab->SymHead; while (Entry) { unsigned Flags = Entry->Flags; - if (Flags & SC_EXTERN) { + if (Entry->Owner == SymTab0 && + (Flags & SC_STORAGEMASK) != SC_STATIC && + ((Flags & SC_TYPEMASK) == SC_FUNC || (Flags & SC_TYPEMASK) == SC_NONE)) { /* Only defined or referenced externs */ if (SymIsRef (Entry) && !SymIsDef (Entry)) { /* An import */ g_defimport (Entry->Name, Flags & SC_ZEROPAGE); - } else if (SymIsDef (Entry)) { + } else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) { /* An export */ g_defexport (Entry->Name, Flags & SC_ZEROPAGE); } @@ -1587,18 +1637,18 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST | SC_TYPEMASK)) == 0) { - if (Sym->Flags & SC_AUTO) { + if ((Sym->Flags & SC_TYPEMASK) == 0) { + if ((Sym->Flags & SC_STORAGEMASK) == SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); - } else if (Sym->Flags & SC_REGISTER) { + } else if ((Sym->Flags & SC_STORAGEMASK) == SC_REGISTER) { AddTextLine ("%s, \"%s\", \"00\", register, \"regbank\", %d", Head, Sym->Name, Sym->V.R.RegOffs); } else if (SymIsRef (Sym) && !SymIsDef (Sym)) { AddTextLine ("%s, \"%s\", \"00\", %s, \"%s\"", Head, Sym->Name, - (Sym->Flags & SC_EXTERN)? "extern" : "static", + (Sym->Flags & SC_STORAGEMASK) != SC_STATIC ? "extern" : "static", Sym->AsmName); } } diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 38edddcb0d..236bc090a8 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -142,7 +142,7 @@ void LeaveStructLevel (void); SymEntry* FindSym (const char* Name); -/* Find the symbol with the given name */ +/* Find with the given name the symbol visible in the current scope */ SymEntry* FindGlobalSym (const char* Name); /* Find the symbol with the given name in the global symbol table only */ @@ -151,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name); /* Find the symbol with the given name in the current symbol table only */ SymEntry* FindTagSym (const char* Name); -/* Find the symbol with the given name in the tag table */ +/* Find with the given name the tag symbol visible in the current scope */ SymEntry FindStructField (const Type* TypeArray, const char* Name); /* Find a struct/union field in the fields list. @@ -211,8 +211,11 @@ SymTable* GetFieldSymTab (void); SymTable* GetLabelSymTab (void); /* Return the label symbol table */ -int SymIsLocal (SymEntry* Sym); -/* Return true if the symbol is defined in the highest lexical level */ +int SymIsLocal (const SymEntry* Sym); +/* Return true if the symbol is declared in the highest lexical level */ + +int SymIsGlobal (const SymEntry* Sym); +/* Return true if the symbol is declared in the file scope level */ void MakeZPSym (const char* Name); /* Mark the given symbol as zero page symbol */ diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 2e52d6e7e0..141bcd2bdd 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -163,7 +163,7 @@ static void PVArgs (CPURegs* Regs) static void PVOpen (CPURegs* Regs) { - char Path[PVOPEN_PATH_SIZE]; + char Path[PV_PATH_SIZE]; int OFlag = O_INITIAL; int OMode = 0; unsigned RetVal, I = 0; @@ -184,7 +184,7 @@ static void PVOpen (CPURegs* Regs) break; } ++I; - if (I >= PVOPEN_PATH_SIZE) { + if (I >= PV_PATH_SIZE) { Error("PVOpen path too long at address $%04X",Name); } } @@ -253,6 +253,35 @@ static void PVClose (CPURegs* Regs) +static void PVSysRemove (CPURegs* Regs) +{ + char Path[PV_PATH_SIZE]; + unsigned RetVal, I = 0; + + unsigned Name = GetAX (Regs); + + Print (stderr, 2, "PVSysRemove ($%04X)\n", Name); + + do { + if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) { + break; + } + ++I; + if (I >= PV_PATH_SIZE) { + Error("PVSysRemove path too long at address $%04X", Name); + } + } + while (1); + + Print (stderr, 2, "PVSysRemove (\"%s\")\n", Path); + + RetVal = remove (Path); + + SetAX (Regs, RetVal); +} + + + static void PVRead (CPURegs* Regs) { unsigned char* Data; @@ -305,7 +334,17 @@ static void PVWrite (CPURegs* Regs) +static void PVOSMapErrno (CPURegs* Regs) +{ + unsigned err = GetAX(Regs); + SetAX (Regs, err != 0 ? -1 : 0); +} + + + static const PVFunc Hooks[] = { + PVSysRemove, + PVOSMapErrno, PVOpen, PVClose, PVRead, diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index 3badb50eac..f3281705ed 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -44,11 +44,11 @@ -#define PARAVIRT_BASE 0xFFF4 +#define PARAVIRT_BASE 0xFFF2 /* Lowest address used by a paravirtualization hook */ -#define PVOPEN_PATH_SIZE 1024 -/* Maximum path size supported by PVOpen */ +#define PV_PATH_SIZE 1024 +/* Maximum path size supported by PVOpen/PVSysRemove */ diff --git a/test/err/bug2304-var-use.c b/test/err/bug2304-var-use.c new file mode 100644 index 0000000000..8a88405e2d --- /dev/null +++ b/test/err/bug2304-var-use.c @@ -0,0 +1,15 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +void f1(void) +{ + extern int a; +} + +/* 'a' is still invisible in the file scope */ + +int main(void) +{ + return a * 0; /* Usage of 'a' should be an error */ +} + +int a = 42; diff --git a/test/misc/Makefile b/test/misc/Makefile index 811f7f4626..cfcae0530d 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -133,6 +133,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref +# this one requires failure with --std=c89, it fails with --std=cc65 due to +# stricter checks +$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg) + $(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should not compile until 3-byte struct by value tests are re-enabled $(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR) $(if $(QUIET),echo misc/struct-by-value.$1.$2.prg) diff --git a/test/misc/bug2304-implicit-func.c b/test/misc/bug2304-implicit-func.c new file mode 100644 index 0000000000..f6b7450fff --- /dev/null +++ b/test/misc/bug2304-implicit-func.c @@ -0,0 +1,21 @@ +/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */ + +/* This one should fail even in C89 */ + +void f1(void) +{ + extern unsigned int f(); +} + +/* 'f' is still invisible in the file scope */ + +int main(void) +{ + f(); /* Should be a conflict since the implicit function type is incompatible */ + return 0; +} + +unsigned int f() +{ + return 42; +} diff --git a/test/ref/Makefile b/test/ref/Makefile index 9ecb33c00f..5c189c6cb1 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -63,6 +63,7 @@ CUSTOMSOURCES = \ # exact error output is required ERRORSOURCES = \ custom-reference-error.c \ + inline-error.c \ bug1889-missing-identifier.c \ bug2312-preprocessor-error.c diff --git a/test/ref/bug1889-missing-identifier.cref b/test/ref/bug1889-missing-identifier.cref index 6317657d1b..70c485fab7 100644 --- a/test/ref/bug1889-missing-identifier.cref +++ b/test/ref/bug1889-missing-identifier.cref @@ -1,4 +1,4 @@ bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers -bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature -bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected +bug1889-missing-identifier.c:3: Warning: Implicit 'int' type specifier is an obsolete feature +bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration bug1889-missing-identifier.c:6: Error: Expression expected diff --git a/test/ref/inline-error.c b/test/ref/inline-error.c new file mode 100644 index 0000000000..2dad41b6de --- /dev/null +++ b/test/ref/inline-error.c @@ -0,0 +1,38 @@ +/* C99 inline in declarations */ + +inline typedef int; /* Error */ +static inline int; /* Error */ +inline static int a1; /* Error */ +int inline (*fp1)(void); /* Error */ +typedef inline int f1_t(void); /* Error */ +inline int f1a(void); /* OK here warning later */ +inline extern int f1b(void); /* OK here warning later */ +extern inline int f1b(void); /* Same as above */ +inline static int f1c(void); /* OK here warning later */ +static inline int f1c(void); /* Same as above */ + +void foo(inline int x); /* Error */ +int a = sizeof (inline int); /* TODO: better error message */ +int b = sizeof (inline int (int)); /* TODO: better error message */ + +inline int main(void) /* Error */ +{ + inline typedef int; /* Error */ + static inline int; /* Error */ + extern inline int a2; /* Error */ + int inline (*fp2)(void); /* Error */ + typedef inline int f2_t(void); /* Error */ + inline int f2a(void); /* OK here warning later */ + inline extern int f2b(void); /* OK here warning later */ + extern inline int f2b(void); /* Same as above */ + + f1a(); /* Still imported */ + f1b(); /* Still imported */ + f1c(); /* Not imported */ + f2a(); /* Still imported */ + f2b(); /* Still imported */ +} + +inline int main(void); /* Error */ + +/* Warning: non-external inline functions declared but undefined in TU */ diff --git a/test/ref/inline-error.cref b/test/ref/inline-error.cref new file mode 100644 index 0000000000..4ce1d70739 --- /dev/null +++ b/test/ref/inline-error.cref @@ -0,0 +1,21 @@ +inline-error.c:3: Error: 'inline' on empty declaration +inline-error.c:4: Error: 'inline' on empty declaration +inline-error.c:5: Error: 'inline' on non-function declaration +inline-error.c:6: Error: 'inline' on non-function declaration +inline-error.c:7: Error: 'inline' on non-function declaration +inline-error.c:14: Error: Unexpected function specifiers +inline-error.c:15: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:16: Error: Mixed declarations and code are not supported in cc65 +inline-error.c:19: Error: 'main' cannot be declared inline +inline-error.c:20: Error: 'inline' on empty declaration +inline-error.c:21: Error: 'inline' on empty declaration +inline-error.c:22: Error: 'inline' on non-function declaration +inline-error.c:23: Error: 'inline' on non-function declaration +inline-error.c:24: Error: 'inline' on non-function declaration +inline-error.c:34: Warning: Variable 'fp2' is defined but never used +inline-error.c:36: Error: 'main' cannot be declared inline +inline-error.c:39: Warning: Inline function 'f1a' used but never defined +inline-error.c:39: Warning: Inline function 'f1b' used but never defined +inline-error.c:39: Warning: Static function 'f1c' used but never defined +inline-error.c:39: Warning: Inline function 'f2a' used but never defined +inline-error.c:39: Warning: Inline function 'f2b' used but never defined diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c new file mode 100644 index 0000000000..0529b1651e --- /dev/null +++ b/test/ref/test_fgets.c @@ -0,0 +1,67 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fgets(buf, sizeof(buf), out) != NULL) { + printf("Error, could fgets with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fgets(NULL, 0, in) != NULL) { + printf("Error, could fgets with zero size\n"); + return 1; + } + + /* Test ungetc while we're at it */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + + while (fgets(buf, sizeof(buf), in) != NULL) + { + printf("%s",buf); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fputc.c b/test/ref/test_fputc.c new file mode 100644 index 0000000000..a19aeafaf7 --- /dev/null +++ b/test/ref/test_fputc.c @@ -0,0 +1,39 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +int c, err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fputc(c, in) != EOF) { + printf("Error: can fputc to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while ((c = fgetc(in)) != EOF) { + fputc(c, stdout); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fputs.c b/test/ref/test_fputs.c new file mode 100644 index 0000000000..ad05523171 --- /dev/null +++ b/test/ref/test_fputs.c @@ -0,0 +1,40 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +char buf[512], err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + strcpy(buf, "test"); + if (fputs(buf, in) != EOF) { + printf("Error: can fputs to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while (fgets(buf, 512, in) != NULL) { + fputs(buf, stdout); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_fread.c b/test/ref/test_fread.c new file mode 100644 index 0000000000..5d180d7237 --- /dev/null +++ b/test/ref/test_fread.c @@ -0,0 +1,70 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + static int r; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fread(buf, 1, sizeof(buf), out) != NULL) { + printf("Error, could fread with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + /* Test that ungetc doesn't break fread */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + r = fread(buf, 1, sizeof(buf), out); + + if (r == 0) { + printf("Error: could not start reading.\n"); + } + fwrite(buf, 1, r, stdout); + + /* Finish reading file. */ + while ((r = fread(buf, 1, sizeof(buf), out)) != 0) + { + fwrite(buf, 1, r, stdout); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} diff --git a/test/ref/test_gets.c b/test/ref/test_gets.c new file mode 100644 index 0000000000..003da55699 --- /dev/null +++ b/test/ref/test_gets.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! gets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include + +char buf[512]; + +#define INFILE "cf.in" + +#ifndef __CC65__ +/* Force declaration on host compiler, as gets() is deprecated for + * being dangerous as hell */ +char *gets (char *__s); +#endif + +#ifdef NO_OLD_FUNC_DECL +int main(int argc,char **argv) +#else +main(argc, argv) +int argc; +char *argv[]; +#endif +{ + /* Fake stdin with the reference file */ + if (freopen(INFILE, "rb", stdin) == NULL) { + return EXIT_FAILURE; + } + + while (gets(buf) != NULL) + { + printf("%s",buf); + } + + fclose(stdin); + return 0; +} diff --git a/test/val/bug2357.c b/test/val/bug2357.c new file mode 100644 index 0000000000..a0cff0d19c --- /dev/null +++ b/test/val/bug2357.c @@ -0,0 +1,38 @@ +/* bug #2357 - Compiler produces invalid code after d8a3938 +*/ + +unsigned long test; + +unsigned long longarray[7]; + +void jsr_threebytes(void) { + +} + +/* having replaced two sty $zp with two sta $abs, but forgetting + * to update the instruction size, coptlong.c could cause a build + * error "Error: Range error (131 not in [-128..127])" if the + * computed codesize was under 126, but the real codesize was above + * 127. + * This tests verifies that the bug is fixed. + */ +unsigned char __fastcall__ foo (unsigned char res) +{ + if (res == 0) { + longarray[1]=test; /* 24 bytes - but the compiler thought 22 */ + longarray[2]=test; /* 48 bytes - but 44 */ + longarray[3]=test; /* 72 bytes - 66 */ + longarray[4]=test; /* 96 bytes - 88 */ + longarray[6]=test; /* 120 bytes - 110 */ + jsr_threebytes(); /* 123 - 113 */ + jsr_threebytes(); /* 126 - 116 */ + jsr_threebytes(); /* 129 - 119 */ + } + return 0; +} + +int main (void) +{ + foo(42); + return 0; +} diff --git a/test/val/bug2395.c b/test/val/bug2395.c new file mode 100644 index 0000000000..07c5cd7c5c --- /dev/null +++ b/test/val/bug2395.c @@ -0,0 +1,44 @@ + +/* bug #2395: Bitwise operators with a boolean expression fail when optimized */ + +#include +#include +#include + +unsigned char a, b; +unsigned char c = 199; +unsigned char d = 100; + +int main(void) { + int fails = 0; + + a = c ^ (d != 0); + b = c ^ 1; + + printf("%u ^ (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("XOR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c | (d != 0); + b = c | 1; + + printf("%u | (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("OR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c & (d != 0); + b = c & 1; + + printf("%u & (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("AND error: a %d instead of %d\n", a, b); + fails++; + } + printf("%d errors\n", fails); + + return fails; +} diff --git a/test/val/inline-func.c b/test/val/inline-func.c new file mode 100644 index 0000000000..b9e127aaed --- /dev/null +++ b/test/val/inline-func.c @@ -0,0 +1,20 @@ +/* C99 inline */ + +#include + +inline static int f(int x, ...) +{ + return x * 2; +} + +extern inline int g(int x); + +int main(void) +{ + return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int g(int x) +{ + return x * 3; +} diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c index 143d158311..f0d9a83320 100644 --- a/test/val/lib_common_gmtime_localtime.c +++ b/test/val/lib_common_gmtime_localtime.c @@ -4,81 +4,107 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, -}; +typedef struct _test_data { + time_t t; + char *gmt; + char *local; +} test_data; -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates_gmt[] = { - "Thu Jan 1 00:00:00 1970\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL -}; +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, "Thu Jan 1 00:00:00 1970\n", "Thu Jan 1 01:00:00 1970\n"}, + {0x004e7970, "Sun Mar 1 12:34:56 1970\n", "Sun Mar 1 13:34:56 1970\n"}, + {0x01e1337f, "Thu Dec 31 23:59:59 1970\n", "Fri Jan 1 00:59:59 1971\n"}, + + /* First leap year */ + {0x03c26700, "Sat Jan 1 00:00:00 1972\n", "Sat Jan 1 01:00:00 1972\n"}, + {0x03c8fe7f, "Wed Jan 5 23:59:59 1972\n", "Thu Jan 6 00:59:59 1972\n"}, + {0x041180ff, "Tue Feb 29 23:59:59 1972\n", "Wed Mar 1 00:59:59 1972\n"}, + {0x04118100, "Wed Mar 1 00:00:00 1972\n", "Wed Mar 1 01:00:00 1972\n"}, + {0x05a4ebff, "Sun Dec 31 23:59:59 1972\n", "Mon Jan 1 00:59:59 1973\n"}, + + /* A non-leap year */ + {0x63b0cd00, "Sun Jan 1 00:00:00 2023\n", "Sun Jan 1 01:00:00 2023\n"}, + {0x63fe957f, "Tue Feb 28 23:59:59 2023\n", "Wed Mar 1 00:59:59 2023\n"}, + {0x63fe9580, "Wed Mar 1 00:00:00 2023\n", "Wed Mar 1 01:00:00 2023\n"}, + {0x656d4ec0, "Mon Dec 4 04:00:00 2023\n", "Mon Dec 4 05:00:00 2023\n"}, + {0x6592007f, "Sun Dec 31 23:59:59 2023\n", "Mon Jan 1 00:59:59 2024\n"}, + + /* Another leap year */ + {0x65920080, "Mon Jan 1 00:00:00 2024\n", "Mon Jan 1 01:00:00 2024\n"}, + {0x65e11a7f, "Thu Feb 29 23:59:59 2024\n", "Fri Mar 1 00:59:59 2024\n"}, + {0x65e11a80, "Fri Mar 1 00:00:00 2024\n", "Fri Mar 1 01:00:00 2024\n"}, + {0x6774857f, "Tue Dec 31 23:59:59 2024\n", "Wed Jan 1 00:59:59 2025\n"}, + + /* End of century */ + {0xf48656ff, "Thu Dec 31 23:59:59 2099\n", "Fri Jan 1 00:59:59 2100\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, "Fri Jan 1 00:00:00 2100\n", "Fri Jan 1 01:00:00 2100\n"}, + {0xf4d41f7f, "Sun Feb 28 23:59:59 2100\n", "Mon Mar 1 00:59:59 2100\n"}, + {0xf4d41f80, "Mon Mar 1 00:00:00 2100\n", "Mon Mar 1 01:00:00 2100\n"}, + {0xf4fceff0, "Wed Mar 31 23:00:00 2100\n", "Thu Apr 1 00:00:00 2100\n"}, + {0xf6678a7f, "Fri Dec 31 23:59:59 2100\n", "Sat Jan 1 00:59:59 2101\n"}, -const char *dates_gmt_plus_one[] = { - "Thu Jan 1 01:00:00 1970\n", - "Sun Jul 9 17:12:47 1995\n", - "Wed Jul 18 06:49:51 2029\n", - "Fri Jan 1 00:59:59 2100\n", - "Fri Jan 1 01:00:00 2100\n", - "Sat Mar 1 00:59:59 2104\n", - "Wed Mar 1 00:59:59 2028\n", - "Wed Mar 1 01:00:00 2028\n", - "Thu Jan 1 00:59:59 1970\n", - NULL + /* First post-2100 leap year */ + {0xfc0b2500, "Tue Jan 1 00:00:00 2104\n", "Tue Jan 1 01:00:00 2104\n"}, + {0xfc5a3eff, "Fri Feb 29 23:59:59 2104\n", "Sat Mar 1 00:59:59 2104\n"}, + {0xfc5a3f00, "Sat Mar 1 00:00:00 2104\n", "Sat Mar 1 01:00:00 2104\n"}, + {0xfcaa9c70, "Wed Apr 30 23:00:00 2104\n", "Thu May 1 00:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, "Thu Jan 1 00:00:00 2105\n", "Thu Jan 1 01:00:00 2105\n"}, + {0xffffffff, "Sun Feb 7 06:28:15 2106\n", "Thu Jan 1 00:59:59 1970\n"} }; int main (void) { int i; + struct tm *tm; + char *str; + + tm = gmtime(NULL); + if (tm != NULL) { + printf("gmtime should return NULL with a NULL parameter\n"); + fails++; + } - for (i = 0; dates_gmt[i] != NULL; i++) { - struct tm *tm; - char *str; + tm = localtime(NULL); + if (tm != NULL) { + printf("localtime should return NULL with a NULL parameter\n"); + fails++; + } - /* Check gmtime */ - tm = gmtime(×tamps[i]); + /* Verify conversion both ways */ + for (i = 0; ; i++) { + time_t t = data[i].t; + + tm = gmtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: gmtime: unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - - /* Check localtime with UTC timezone */ + _tz.timezone = 0; - tm = localtime(×tamps[i]); + tm = localtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: localtime (UTC+0): unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - /* Check localtime at UTC+1 */ _tz.timezone = 3600; - tm = localtime(×tamps[i]); + tm = localtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt_plus_one[i])) { + if (strcmp(data[i].local, str)) { + printf("0x%lx: localtime (UTC+1): unexpected result: expected %s, got %s\n", t, data[i].local, str); fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt_plus_one[i], str); } + + if (t == 0xFFFFFFFF) + break; } return fails; } diff --git a/test/val/lib_common_malloc.c b/test/val/lib_common_malloc.c new file mode 100644 index 0000000000..5c68dc39ab --- /dev/null +++ b/test/val/lib_common_malloc.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = malloc(0); + ASSERT_IsTrue (buf == NULL, "malloc (0) returned something"); + + for (i = 1; i < 10; i++) { + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "small returned nothing"); + } + + buf = malloc(4096); + ASSERT_IsTrue (buf != NULL, "malloc (4096) returned nothing"); + + buf = malloc(61000UL); + ASSERT_IsTrue (buf == NULL, "malloc (61000) returned something"); + + for (i = 65535UL; i > _heapmaxavail(); i--) { + buf = malloc(i); + ASSERT_IsTrue (buf == NULL, "malloc returned something but shouldn't have"); + } + + buf = malloc(i); + ASSERT_IsTrue (buf != NULL, "malloc returned nothing but should have"); + ASSERT_IsTrue(_heapmaxavail() == 0, "heapmaxavail should be 0"); +} +ENDTEST diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c index 832ce38341..6cabef2be4 100644 --- a/test/val/lib_common_mktime.c +++ b/test/val/lib_common_mktime.c @@ -4,58 +4,103 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, -}; +typedef struct _test_data { + time_t t; + struct tm tm; + char *str; +} test_data; + +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, {0, 0, 0, 1, 0, 70, 0, 4}, "Thu Jan 1 00:00:00 1970\n"}, + {0x004e7970, {56, 34, 12, 1, 2, 70, 59, 0}, "Sun Mar 1 12:34:56 1970\n"}, + {0x01e1337f, {59, 59, 23, 31, 11, 70, 364, 4}, "Thu Dec 31 23:59:59 1970\n"}, + + /* First leap year */ + {0x03c26700, {0, 0, 0, 1, 0, 72, 0, 6}, "Sat Jan 1 00:00:00 1972\n"}, + {0x03c8fe7f, {59, 59, 23, 5, 0, 72, 4, 3}, "Wed Jan 5 23:59:59 1972\n"}, + {0x041180ff, {59, 59, 23, 29, 1, 72, 59, 2}, "Tue Feb 29 23:59:59 1972\n"}, + {0x04118100, {0, 0, 0, 1, 2, 72, 60, 3}, "Wed Mar 1 00:00:00 1972\n"}, + {0x05a4ebff, {59, 59, 23, 31, 11, 72, 365, 0}, "Sun Dec 31 23:59:59 1972\n"}, + + /* A non-leap year */ + {0x63b0cd00, {0, 0, 0, 1, 0, 123, 0, 0}, "Sun Jan 1 00:00:00 2023\n"}, + {0x63fe957f, {59, 59, 23, 28, 1, 123, 58, 2}, "Tue Feb 28 23:59:59 2023\n"}, + {0x63fe9580, {0, 0, 0, 1, 2, 123, 59, 3}, "Wed Mar 1 00:00:00 2023\n"}, + {0x656d4ec0, {0, 0, 4, 4, 11, 123, 337, 1}, "Mon Dec 4 04:00:00 2023\n"}, + {0x6592007f, {59, 59, 23, 31, 11, 123, 364, 0}, "Sun Dec 31 23:59:59 2023\n"}, + + /* Another leap year */ + {0x65920080, {0, 0, 0, 1, 0, 124, 0, 1}, "Mon Jan 1 00:00:00 2024\n"}, + {0x65e11a7f, {59, 59, 23, 29, 1, 124, 59, 4}, "Thu Feb 29 23:59:59 2024\n"}, + {0x65e11a80, {0, 0, 0, 1, 2, 124, 60, 5}, "Fri Mar 1 00:00:00 2024\n"}, + {0x6774857f, {59, 59, 23, 31, 11, 124, 365, 2}, "Tue Dec 31 23:59:59 2024\n"}, -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates[] = { - "Thu Jan 1 00:00:00 1970\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL + /* End of century */ + {0xf48656ff, {59, 59, 23, 31, 11, 199, 364, 4}, "Thu Dec 31 23:59:59 2099\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, {0, 0, 0, 1, 0, 200, 0, 5}, "Fri Jan 1 00:00:00 2100\n"}, + {0xf4d41f7f, {59, 59, 23, 28, 1, 200, 58, 0}, "Sun Feb 28 23:59:59 2100\n"}, + {0xf4d41f80, {0, 0, 0, 1, 2, 200, 59, 1}, "Mon Mar 1 00:00:00 2100\n"}, + {0xf4fceff0, {0, 0, 23, 31, 2, 200, 89, 3}, "Wed Mar 31 23:00:00 2100\n"}, + {0xf6678a7f, {59, 59, 23, 31, 11, 200, 364, 5}, "Fri Dec 31 23:59:59 2100\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, {0, 0, 0, 1, 0, 204, 0, 2}, "Tue Jan 1 00:00:00 2104\n"}, + {0xfc5a3eff, {59, 59, 23, 29, 1, 204, 59, 5}, "Fri Feb 29 23:59:59 2104\n"}, + {0xfc5a3f00, {0, 0, 0, 1, 2, 204, 60, 6}, "Sat Mar 1 00:00:00 2104\n"}, + {0xfcaa9c70, {0, 0, 23, 30, 3, 204, 120, 3}, "Wed Apr 30 23:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, {0, 0, 0, 1, 0, 205, 0, 4}, "Thu Jan 1 00:00:00 2105\n"}, + {0xffffffff, {15, 28, 6, 7, 1, 206, 37, 0}, "Sun Feb 7 06:28:15 2106\n"} }; +static int compare_tm(time_t t, struct tm *tm, struct tm *ref) { + if (memcmp(tm, ref, sizeof(tm))) { + printf("0x%lx: unexpected tm from gmtime: " + "expected {%u, %u, %u, %u, %u, %u, %u, %u}, " + "got {%u, %u, %u, %u, %u, %u, %u, %u}\n", + t, + ref->tm_sec, ref->tm_min, ref->tm_hour, ref->tm_mday, ref->tm_mon, ref->tm_year, ref->tm_yday, ref->tm_wday, + tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_yday, tm->tm_wday); + return 1; + } + return 0; +} + int main (void) { - struct tm tm; - time_t t; int i; + if (mktime(NULL) != (time_t)-1) { + printf("mktime should return -1 with a NULL parameter\n"); + fails++; + } + /* Verify conversion both ways */ - for (t = 0x0FFFFFFF; ; t += 0x10000000) { + for (i = 0; ; i++) { + time_t t = data[i].t; + time_t r; struct tm *tm = gmtime(&t); - time_t r = mktime(tm); + r = mktime(tm); + if (t != r) { + printf("unexpected timestamp from mktime: expected 0x%lx, got 0x%lx\n", t, r); fails++; - printf("Unexpected result for t %lx: %lx\n", t, r); } - if (t == 0xFFFFFFFF) { - break; + if (compare_tm(t, tm, &data[i].tm)) { + fails++; } - } - - for (i = 0; dates[i] != NULL; i++) { - char *str = ctime(×tamps[i]); - if (strcmp(str, dates[i])) { + if (strcmp(data[i].str, ctime(&t))) { + printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); fails++; - printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates[i], str); } + + if (t == 0xFFFFFFFF) + break; } return fails; } diff --git a/test/val/lib_common_pmemalign.c b/test/val/lib_common_pmemalign.c new file mode 100644 index 0000000000..0e9e5f52f6 --- /dev/null +++ b/test/val/lib_common_pmemalign.c @@ -0,0 +1,31 @@ +#include +#include +#include "unittest.h" + +TEST +{ + void *buf; + int r; + + r = posix_memalign(&buf, 123, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with wrong alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with wrong alignment"); + + r = posix_memalign(&buf, 0, 1024); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 alignment"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 alignment"); + + r = posix_memalign(&buf, 256, 0); + ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 size"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 size"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == 0, "posix_memalign did not return 0 on correct call"); + ASSERT_IsTrue(buf != NULL, "posix_memalign left buf set to NULL on correct call"); + ASSERT_IsTrue(((unsigned int)buf & 0x00FF) == 0x00, "posix_memalign did not align memory"); + + r = posix_memalign(&buf, 256, 32768U); + ASSERT_IsTrue(r == ENOMEM, "posix_memalign did not return ENOMEM when no memory is available"); + ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL when no memory is available"); +} +ENDTEST diff --git a/test/val/lib_common_realloc.c b/test/val/lib_common_realloc.c new file mode 100644 index 0000000000..d1e4fa3ebd --- /dev/null +++ b/test/val/lib_common_realloc.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include "unittest.h" + +TEST +{ + char *buf, *buf2; + unsigned int i; + + buf = realloc(NULL, 0); + ASSERT_IsTrue (buf == NULL, "realloc (NULL, 0) returned something"); + + for (i = 1; i < 10; i++) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 != NULL, "small realloc returned nothing"); + if (i > 1) { + ASSERT_IsTrue (buf2 == buf, "buf shouldn't have moved"); + } + buf = buf2; + } + + buf = realloc(NULL, 15); + ASSERT_IsTrue (buf != NULL, "realloc (NULL, 15) returned nothing"); + + buf = realloc(buf, 0); + ASSERT_IsTrue (buf == NULL, "realloc (buf, 0) returned something"); + + buf = realloc(buf, 32); + memset(buf, 'a', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in buf"); + } + + /* Now realloc larger, while there's nothing else in the heap */ + buf = realloc(buf, 64); + memset(buf+32, 'b', 32); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 64; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Now realloc smaller, while there's nothing else in the heap */ + buf = realloc(buf, 40); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + /* Allocate something else, so next realloc has to change block */ + malloc(50); + + /* Now realloc larger, with something else in the heap */ + buf = realloc(buf, 128); + for (i = 0; i < 32; i++) { + ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf"); + } + for (i = 32; i < 40; i++) { + ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf"); + } + + for (i = 129; i < 8192; i++) { + buf = realloc(buf, i); + ASSERT_IsTrue(buf != NULL, "realloc failed"); + } + + malloc(4096); + + buf2 = realloc(buf, 58000UL); + ASSERT_IsTrue (buf2 == NULL, "realloc (buf, 58000) returned something"); + + for (i = 65535UL; i > 65527UL; i--) { + buf2 = realloc(buf, i); + ASSERT_IsTrue (buf2 == NULL, "realloc returned something but shouldn't have"); + } +} +ENDTEST diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c new file mode 100644 index 0000000000..7e3b4796ab --- /dev/null +++ b/test/val/lib_runtime_aslax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for aslax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != -32768); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c new file mode 100644 index 0000000000..3cdf2aab80 --- /dev/null +++ b/test/val/lib_runtime_asrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for asrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != -32768); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c new file mode 100644 index 0000000000..d2073ca3a6 --- /dev/null +++ b/test/val/lib_runtime_shlax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shlax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != 0); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != 0); + + return 0; +} diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c new file mode 100644 index 0000000000..d3bc0db733 --- /dev/null +++ b/test/val/lib_runtime_shrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (++ai != 0); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (++ac != 0); + + return 0; +} diff --git a/test/val/remove.c b/test/val/remove.c new file mode 100644 index 0000000000..eecf8be8f3 --- /dev/null +++ b/test/val/remove.c @@ -0,0 +1,55 @@ +#include +#include +#include + +int fails = 0; + + +static void create_out_file(const char *outfile_path) { + FILE *out; + + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + printf("Could not create %s\n", outfile_path); + fails++; + return; + } + fclose(out); +} + +int main (int argc, char **argv) +{ + int r; + static char outfile_path[FILENAME_MAX+1]; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + create_out_file(outfile_path); + r = remove(outfile_path); + if (r != 0) { + printf("could not remove() %s\n", outfile_path); + fails++; + } + + create_out_file(outfile_path); + r = unlink(outfile_path); + if (r != 0) { + printf("could not unlink() %s\n", outfile_path); + fails++; + } + + r = remove("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("remove()ing non-existent file succeeded\n"); + fails++; + } + + r = unlink("klsdfjqlsjdflkqjdsoizu"); + if (r == 0) { + printf("unlink()ing non-existent file succeeded\n"); + fails++; + } + + return fails; +}