From 17c55041291b0deb880cfd4f5322714fdb6a0a30 Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Fri, 3 Jul 2020 23:03:19 -0400 Subject: [PATCH 001/806] In Atari XEX output format, join memory areas if possible. This makes executables shorter if two memory areas are consecutive. --- doc/ld65.sgml | 3 ++- src/ld65/xex.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/ld65.sgml b/doc/ld65.sgml index d8e296996..cba06fef4 100644 --- a/doc/ld65.sgml +++ b/doc/ld65.sgml @@ -932,7 +932,8 @@ the standard format used by Atari DOS 2.0 and upward file managers in the Atari In the Atari segmented file format, the linker will write each . diff --git a/src/ld65/xex.c b/src/ld65/xex.c index c57fa0a8c..94f3920b5 100644 --- a/src/ld65/xex.c +++ b/src/ld65/xex.c @@ -241,9 +241,6 @@ static unsigned long XexWriteMem (XexDesc* D, MemoryArea* M) /* Store initial position to get total file size */ unsigned long StartPos = ftell (D->F); - /* Always write a segment header for each memory area */ - D->HeadPos = 0; - /* Get the start address and size of this memory area */ unsigned long Addr = M->Start; @@ -400,6 +397,7 @@ void XexWriteTarget (XexDesc* D, struct File* F) if (D->F == 0) { Error ("Cannot open `%s': %s", D->Filename, strerror (errno)); } + D->HeadPos = 0; /* Keep the user happy */ Print (stdout, 1, "Opened `%s'...\n", D->Filename); @@ -415,6 +413,8 @@ void XexWriteTarget (XexDesc* D, struct File* F) Write16 (D->F, 0x2E2); Write16 (D->F, 0x2E3); Write16 (D->F, GetExportVal (I->InitAd->Exp)); + /* Always write a new segment header after an INITAD segment */ + D->HeadPos = 0; } } From 286da30a269989be35cfd80a377da64d01ce2a95 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 4 Jul 2020 13:32:28 +0800 Subject: [PATCH 002/806] Quick fix for Issue #1071. --- src/cc65/coptstop.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index b14b23195..46ccf020b 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -145,6 +145,9 @@ struct StackOpData { /* Freedom of registers inside the sequence */ unsigned UsedRegs; /* Registers used */ + /* Whether the rhs is changed multiple times */ + int RhsMultiChg; + /* Register load information for lhs and rhs */ LoadInfo Lhs; LoadInfo Rhs; @@ -1972,6 +1975,7 @@ static void ResetStackOpData (StackOpData* Data) Data->ZPUsage = REG_NONE; Data->ZPChanged = REG_NONE; Data->UsedRegs = REG_NONE; + Data->RhsMultiChg = 0; ClearLoadInfo (&Data->Lhs); ClearLoadInfo (&Data->Rhs); @@ -2092,6 +2096,10 @@ static int PreCondOk (StackOpData* D) } } } + if (D->RhsMultiChg && (D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } Passed = 1; } while (0); @@ -2188,6 +2196,8 @@ unsigned OptStackOps (CodeSeg* S) int OldEntryCount; /* Old number of entries */ unsigned Used; /* What registers would be used */ unsigned PushedRegs; /* Track if the same regs are used after the push */ + int RhsALoadIndex; /* Track if rhs is changed more than once */ + int RhsXLoadIndex; /* Track if rhs is changed more than once */ enum { Initialize, @@ -2335,6 +2345,10 @@ unsigned OptStackOps (CodeSeg* S) } + /* Memorize the old rhs load indices before refreshing them */ + RhsALoadIndex = Data.Rhs.A.LoadIndex; + RhsXLoadIndex = Data.Rhs.X.LoadIndex; + /* Track register usage */ Used = TrackLoads (&Data.Rhs, &Data.Lhs, Data.Code, I); Data.ZPUsage |= (E->Use | E->Chg); @@ -2358,6 +2372,16 @@ unsigned OptStackOps (CodeSeg* S) } PushedRegs &= ~E->Chg; } + /* Check if rhs is changed again after the push */ + if ((RhsALoadIndex != Data.Lhs.A.LoadIndex && + RhsALoadIndex != Data.Rhs.A.LoadIndex) || + (RhsXLoadIndex != Data.Lhs.X.LoadIndex && + RhsXLoadIndex != Data.Rhs.X.LoadIndex)) { + /* This will disable those sub-opts that require removing + ** the rhs as they can't handle such cases correctly. + */ + Data.RhsMultiChg = 1; + } break; case FoundOp: From 539924249b96557f64e774db7e4ea479f299b801 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 4 Jul 2020 17:44:49 +0800 Subject: [PATCH 003/806] More complete fix for Issue #1071. --- src/cc65/coptstop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 46ccf020b..a4fc0dc65 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -946,7 +946,8 @@ static unsigned Opt_toseqax_tosneax (StackOpData* D, const char* BoolTransformer D->Lhs.A.Flags |= LI_REMOVE; } else if ((D->Rhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT && - (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT) { + (D->Rhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) == LI_DIRECT && + D->RhsMultiChg == 0) { CodeEntry* LoadX = D->Rhs.X.LoadEntry; CodeEntry* LoadA = D->Rhs.A.LoadEntry; From d1833cc441c79f018fa496e44672b2b5edb57c8d Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 8 Jul 2020 00:48:39 +0200 Subject: [PATCH 004/806] Fix handling of charcodes 254 and 255, fixes issue #988 --- libsrc/c128/cputc.s | 3 +-- libsrc/c64/cputc.s | 3 +-- libsrc/cbm510/cputc.s | 3 +-- libsrc/cbm610/cputc.s | 3 +-- libsrc/plus4/cputc.s | 3 +-- libsrc/vic20/cputc.s | 3 +-- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/libsrc/c128/cputc.s b/libsrc/c128/cputc.s index 667260843..5c0760e75 100644 --- a/libsrc/c128/cputc.s +++ b/libsrc/c128/cputc.s @@ -62,10 +62,9 @@ cputdirect: ; Handle character if high bit set L5: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L6 lda #$5E ; Load screen code for PI - bne cputdirect L6: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/c64/cputc.s b/libsrc/c64/cputc.s index d6b49607a..b893f2102 100644 --- a/libsrc/c64/cputc.s +++ b/libsrc/c64/cputc.s @@ -74,10 +74,9 @@ L5: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect diff --git a/libsrc/cbm510/cputc.s b/libsrc/cbm510/cputc.s index 73d45b422..7dd4cddb2 100644 --- a/libsrc/cbm510/cputc.s +++ b/libsrc/cbm510/cputc.s @@ -65,10 +65,9 @@ L3: sty CURS_X ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/cbm610/cputc.s b/libsrc/cbm610/cputc.s index 5888580ac..78e2a5581 100644 --- a/libsrc/cbm610/cputc.s +++ b/libsrc/cbm610/cputc.s @@ -73,10 +73,9 @@ L4: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect ; Branch always diff --git a/libsrc/plus4/cputc.s b/libsrc/plus4/cputc.s index 49b3a84dd..a72b4012a 100644 --- a/libsrc/plus4/cputc.s +++ b/libsrc/plus4/cputc.s @@ -74,10 +74,9 @@ L5: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect diff --git a/libsrc/vic20/cputc.s b/libsrc/vic20/cputc.s index 1db818546..9e818032e 100644 --- a/libsrc/vic20/cputc.s +++ b/libsrc/vic20/cputc.s @@ -95,10 +95,9 @@ L5: inc CURS_Y ; Handle character if high bit set L10: and #$7F - cmp #$7E ; PI? + cmp #$7F ; PI? bne L11 lda #$5E ; Load screen code for PI - bne cputdirect L11: ora #$40 bne cputdirect From 410e4502eee98f74d34477b3ce031bfe0f0386df Mon Sep 17 00:00:00 2001 From: Greg King Date: Wed, 8 Jul 2020 05:55:30 -0400 Subject: [PATCH 005/806] Added a 160x192x2 TGI (graphics) driver to the VIC-20 library. The driver requires a special linker configuration: "vic20-tgi.cfg". The VIC-20 computer needs at least 8K of expansion RAM! "tgidemo.c" needed to be adjusted because the VIC-20's vertical (y) range is greater than its horizontal (x) range -- the opposite of most other platforms. Also, the circle demo would jam on the VIC-20. --- cfg/vic20-tgi.cfg | 50 ++ doc/vic20.sgml | 29 +- include/tgi.h | 2 +- include/vic20.h | 43 +- libsrc/vic20/libref.s | 10 +- libsrc/vic20/tgi/vic20-hi.s | 1018 ++++++++++++++++++++++++++++++++ libsrc/vic20/tgi_stat_stddrv.s | 8 + libsrc/vic20/tgi_stddrv.s | 13 + libsrc/vic20/tgihdr.s | 67 +++ samples/Makefile | 14 + samples/tgidemo.c | 22 +- 11 files changed, 1253 insertions(+), 23 deletions(-) create mode 100644 cfg/vic20-tgi.cfg create mode 100644 libsrc/vic20/tgi/vic20-hi.s create mode 100644 libsrc/vic20/tgi_stat_stddrv.s create mode 100644 libsrc/vic20/tgi_stddrv.s create mode 100644 libsrc/vic20/tgihdr.s diff --git a/cfg/vic20-tgi.cfg b/cfg/vic20-tgi.cfg new file mode 100644 index 000000000..bc7845615 --- /dev/null +++ b/cfg/vic20-tgi.cfg @@ -0,0 +1,50 @@ +# Memory configuration which supports the "vic20-hi.tgi" driver. +# Memory configuration for a VIC-20 with, at least, 8K expansion RAM. +FEATURES { + STARTADDRESS: default = $1201; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __TGIHDR__: type = import; + __STACKSIZE__: type = weak, value = $0200; # 512-byte stack + __HIMEM__: type = weak, value = $4000; +} +MEMORY { + ZP: file = "", define = yes, start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $003E; + MAIN: file = %O, define = yes, start = $2000, size = __HIMEM__ - __MAIN_START__ - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; +# The start-up code needs EXEHDR, TGI1HDR, TGI2HDR, +# and STARTUP to be next to each other, in that order. + EXEHDR: load = HEADER, type = ro; + TGI1HDR: load = HEADER, type = ro; + TGI2HDR: load = MAIN, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + ONCE: load = MAIN, type = ro, optional = yes, define = yes; + INIT: load = MAIN, type = bss, optional = yes; + BSS: load = MAIN, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/doc/vic20.sgml b/doc/vic20.sgml index 4fcd0079c..0a6e85d4a 100644 --- a/doc/vic20.sgml +++ b/doc/vic20.sgml @@ -75,7 +75,6 @@ common to all CBM platforms. There are currently no special VIC20 functions. - CBM-specific functions

Some functions are available for all (or at least most) of the Commodore @@ -143,7 +142,33 @@ The names in the parentheses denote the symbols to be used for static linking of Graphics drivers

-No graphics drivers are currently available for the VIC20. + + + + This driver features a resolution of 160×192 with two colors. The + background can be chosen from a sixteen-color palette. The foreground can + be chosen from an eight-color palette. + + The driver will use memory from addresses $1000 to $1FFF as a graphics + buffer. Therefore, the VIC-20 must have, at least, 8K of expansion RAM. + + Programs that use this driver must be linked by the + cl65 -D DYN_DRV=0 -t vic20 -C vic20-tgi.cfg samples/mandelbrot.c + + + When the program starts, it will move itself up in RAM, to make room for the + buffer. When the program finishes, it will reset the BASIC interpreter. + That means that graphics pictures won't be preserved between the executions + of programs. Also, the graphics buffer shares RAM with the text screen. If + a picture must be saved, then a program must put it somewhere else (such as + a disk file) before returning to the text mode. + + Extended memory drivers

diff --git a/include/tgi.h b/include/tgi.h index 15a6437cc..135ef63f1 100644 --- a/include/tgi.h +++ b/include/tgi.h @@ -216,7 +216,7 @@ void __fastcall__ tgi_arc (int x, int y, unsigned char rx, unsigned char ry, /* Draw an ellipse arc with center at x/y and radii rx/ry using the current ** drawing color. The arc covers the angle between sa and ea (startangle and ** endangle), which must be in the range 0..360 (otherwise the function may -** bevave unextectedly). +** behave unexpectedly). */ void __fastcall__ tgi_pieslice (int x, int y, unsigned char rx, unsigned char ry, diff --git a/include/vic20.h b/include/vic20.h index 44512b3fd..d60c7e270 100644 --- a/include/vic20.h +++ b/include/vic20.h @@ -2,12 +2,12 @@ /* */ /* vic20.h */ /* */ -/* vic20 system specific definitions */ +/* VIC-20 system-specific definitions */ /* */ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -66,14 +66,39 @@ #define COLOR_GREEN 0x05 #define COLOR_BLUE 0x06 #define COLOR_YELLOW 0x07 +/* Only the background and multi-color characters can have these colors. */ #define COLOR_ORANGE 0x08 -#define COLOR_BROWN 0x09 -#define COLOR_LIGHTRED 0x0A -#define COLOR_GRAY1 0x0B -#define COLOR_GRAY2 0x0C +#define COLOR_LIGHTORANGE 0x09 +#define COLOR_PINK 0x0A +#define COLOR_LIGHTCYAN 0x0B +#define COLOR_LIGHTVIOLET 0x0C #define COLOR_LIGHTGREEN 0x0D #define COLOR_LIGHTBLUE 0x0E -#define COLOR_GRAY3 0x0F +#define COLOR_LIGHTYELLOW 0x0F + +/* TGI color defines */ +#define TGI_COLOR_BLACK COLOR_BLACK +#define TGI_COLOR_WHITE COLOR_WHITE +#define TGI_COLOR_RED COLOR_RED +#define TGI_COLOR_CYAN COLOR_CYAN +#define TGI_COLOR_VIOLET COLOR_VIOLET +#define TGI_COLOR_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +/* Only the background and multi-color graphics can have these colors. */ +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_LIGHTORANGE COLOR_LIGHTORANGE +#define TGI_COLOR_PINK COLOR_PINK +#define TGI_COLOR_LIGHTCYAN COLOR_LIGHTCYAN +#define TGI_COLOR_LIGHTVIOLET COLOR_LIGHTVIOLET +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_LIGHTYELLOW COLOR_LIGHTYELLOW + + + +/* tgi_ioctl() commands */ +#define TGI_IOCTL_VIC20_SET_PATTERN 0x01 /* Set 8-byte pattern for tgi_bar(). */ @@ -108,5 +133,9 @@ extern void vic20_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void vic20_rama_emd[]; extern void vic20_georam_emd[]; +extern void vic20_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ + + + /* End of vic20.h */ #endif diff --git a/libsrc/vic20/libref.s b/libsrc/vic20/libref.s index 468c540f1..333a66894 100644 --- a/libsrc/vic20/libref.s +++ b/libsrc/vic20/libref.s @@ -1,10 +1,14 @@ ; -; Oliver Schmidt, 2013-05-31 +; 2013-05-31, Oliver Schmidt +; 2018-03-11, Sven Michael Klose ; - .export joy_libref .export em_libref + .export joy_libref + .export tgi_libref + .import _exit -joy_libref := _exit em_libref := _exit +joy_libref := _exit +tgi_libref := _exit diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s new file mode 100644 index 000000000..8dfa46d6b --- /dev/null +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -0,0 +1,1018 @@ +; +; Graphics driver for a 160x192x2 mode on the VIC-20. +; +; Based on C64 TGI driver +; +; 2018-03-11, Sven Michael Klose +; 2020-07-06, Greg King +; + + .include "zeropage.inc" + .include "vic20.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .macpack generic + .macpack module + + +; ------------------------------------------------------------------------ +; Constants + +COLS = 20 +ROWS = 12 +XRES = COLS * 8 +YRES = ROWS * 16 + +TGI_IOCTL_VIC20_SET_PATTERN = $01 + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _vic20_hi_tgi + +; First part of the header is a structure that has a magic and defines the +; capabilities of the driver + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word XRES ; X resolution + .word YRES ; Y resolution + .byte 2 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0180 ; Aspect ratio 2.5:3 + .byte 0 ; TGI driver flags + +; Next comes the jump table. With the exception of IRQ, all entries must be +; valid and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + +; ------------------------------------------------------------------------ +; Data. + +; Variables mapped to the zero-page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 := ptr1 +Y1 := ptr2 +X2 := ptr3 +Y2 := ptr4 +TEXT := ptr3 + +POINT := regsave +SOURCE := tmp1 +DEST := tmp3 + +; Absolute variables used in the code + +.bss + +ERROR: .res 1 ; Error code +PALETTE: .res 2 ; The current palette + +CURCOL: .res 1 ; Current color. +BITMASK: .res 1 ; $00 = clear, $FF = set pixels + +; BAR variables + +XPOSR: .res 1 ; Used by BAR. +PATTERN: .res 2 ; Address of pattern. +USERPATTERN: .res 2 ; User-defined pattern set via CONTROL. +COUNTER: .res 2 +TMP: .res 1 +MASKS: .res 1 +MASKD: .res 1 +XCPOS: .res 1 +HEIGHT: .res 1 + +; Line variables + +CHUNK := X2 ; Used in the line routine +OLDCHUNK := X2+1 ; Ditto +TEMP := tmp4 +TEMP2 := sreg +DX: .res 2 +DY: .res 2 + +; Text output stuff + +TEXTMAGX: .res 1 +TEXTMAGY: .res 1 +TEXTDIR: .res 1 + +; Constants and tables + +.rodata + +DEFPALETTE: .byte $00, $01 ; White on black +PALETTESIZE = * - DEFPALETTE + +BITTAB: .byte $80, $40, $20, $10, $08, $04, $02, $01 +BITCHUNK: .byte $FF, $7F, $3F, $1F, $0F, $07, $03, $01 + +CHARROM := $8000 ; Character ROM base address +CBASE := $9400 ; Color memory base address +SBASE := $1000 ; Screen memory base address +VBASE := $1100 ; Video memory base address + +; These numbers are added to Kernal's default VIC settings. +; They make the Video Interface Controller show graphics instead of text. + +.proc VICREGS + .byte <+2 ; Left_edge + 2 + .byte <-2 ; Top_edge - 2 + .byte <-2 ; Columns - 2 + .byte <-(11 << 1) + 1 ; Rows - 11, chars. are 8 x 16 pixels + .byte 0 + .byte <+$0C ; Font_address = $1000 +.endproc + +XADDRS_L: + .repeat COLS, n + .byte <(VBASE + YRES * n) + .endrep + +XADDRS_H: + .repeat COLS, n + .byte >(VBASE + YRES * n) + .endrep + +MASKS_LEFT: + .byte %11111111 +MASKD_RIGHT: + .byte %01111111 + .byte %00111111 + .byte %00011111 + .byte %00001111 + .byte %00000111 + .byte %00000011 + .byte %00000001 +MASKD_LEFT: + .byte %00000000 +MASKS_RIGHT: + .byte %10000000 + .byte %11000000 + .byte %11100000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %11111110 + .byte %11111111 + +PATTERN_EMPTY: + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + +PATTERN_SOLID: + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO +; + +.proc INSTALL + +; Reset user-defined pattern. + + lda #$00 + sta PATTERN + sta PATTERN+1 + rts +.endproc + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL; but is probably empty most of the time. +; +; Must set an error code: NO +; + +.proc UNINSTALL + rts +.endproc + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once, so any code that is needed +; to initialize variables and so on must go here. Setting the palette +; is not needed because that is done by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active; so, there is no need to protect against that. +; +; Must set an error code: YES +; + +.proc INIT + +; Initialize variables + + ldx #$FF + stx BITMASK + +; Make screen columns. + + lda #SBASE + sta tmp2+1 + inx ; (ldx #$00) + stx ERROR ; Set to TGI_ERR_OK + +@NEXT_ROW: + ldy #$00 + txa + clc + adc #$10 + +@NEXT_COLUMN: + sta (tmp2),y + clc + adc #ROWS + iny + cpy #COLS + bne @NEXT_COLUMN + +; Step to next row on screen. + + lda tmp2 + clc + adc #COLS + sta tmp2 + bcc @L1 + inc tmp2+1 + +@L1: inx + cpx #ROWS + bne @NEXT_ROW + +; Set up VIC. + + ldx #.sizeof(VICREGS) - 1 +@L2: clc + lda $EDE4,x + adc VICREGS,x + sta VIC,x + dex + bpl @L2 + + jmp CLEAR +.endproc + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active; +; so, there is no need to protect against that. +; +; Must set an error code: NO +; + +.proc DONE + jmp $E518 ; Kernal console init. +.endproc + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in A, and clear it. + +.proc GETERROR + ldx #TGI_ERR_OK + lda ERROR + stx ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES +; + +.proc CONTROL + +; Set user-defined pattern. + + cmp #TGI_IOCTL_VIC20_SET_PATTERN + bne @INVALID_FUNC + + lda ptr1 + sta USERPATTERN + lda ptr1+1 + sta USERPATTERN+1 + + lda #TGI_ERR_OK + .byte $2C ;(bit $1234) + +; Return with error code for invalid function index. + +@INVALID_FUNC: + lda #TGI_ERR_INV_FUNC + sta ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CLEAR: Clears the screen. All pixels are set to the background color. +; +; Must set an error code: NO +; + +.proc CLEAR + lda #%00000000 + tay ; (ldy #$00) +@L1: sta VBASE + $0000,y + sta VBASE + $0100,y + sta VBASE + $0200,y + sta VBASE + $0300,y + sta VBASE + $0400,y + sta VBASE + $0500,y + sta VBASE + $0600,y + sta VBASE + $0700,y + sta VBASE + $0800,y + sta VBASE + $0900,y + sta VBASE + $0A00,y + sta VBASE + $0B00,y + sta VBASE + $0C00,y + sta VBASE + $0D00,y + sta VBASE + $0E00,y + iny + bne @L1 + rts +.endproc + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n). +; The page number already is checked, to be valid, by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) +; + +.proc SETVIEWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked, to be valid, by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) +; + +.proc SETDRAWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) +; + +.proc SETCOLOR + sta CURCOL + tax + beq @L1 + lda #$FF +@L1: sta BITMASK + rts +.endproc + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES +; + +.proc SETPALETTE + ldy #PALETTESIZE - 1 +@L1: lda (ptr1),y ; Copy the palette + and #$0F ; Make a valid color + sta PALETTE,y + dey + bpl @L1 + +; Initialize the color map with the new color settings. + + iny ; Set .Y to $00 + lda PALETTE+1 ; Foreground color +@L2: sta CBASE + $0000,y + sta CBASE + $0100,y + iny + bne @L2 + + lda PALETTE ; Background color + asl a ; Put it in high nybble + asl a + asl a + asl a + sta tmp2 + lda VIC_COLOR + and #$0F + ora tmp2 + sta VIC_COLOR + +; Done, reset the error code + + ;ldy #TGI_ERR_OK ; (Already zero) + sty ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in ".XA". Even drivers that cannot +; set the palette should return the default palette here; so, there's no +; way for this function to fail. +; +; Must set an error code: NO +; + +.proc GETPALETTE + lda #PALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in ".XA". All +; drivers should return something reasonable here, even drivers that don't +; support palettes; otherwise, the caller has no way to determine the colors +; of the (not changeable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) +; + +.proc GETDEFPALETTE + lda #DEFPALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area; so, there's no need for clipping inside this function. +; +; Must set an error code: NO +; + +.proc SETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + eor BITMASK + and BITTAB,X + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel; and return it in ".XA". The +; co-ordinates passed to this function never are outside the visible screen +; area; so, there's no need for clipping inside this function. + + +.proc GETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + and BITTAB,X + beq @L1 + iny + tya ; Get color value into .A +@L1: ldx #>$0000 ; Clear high byte + rts +.endproc + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scalings in x and y +; directions are passed in .X/.Y; the text direction is passed in .A. +; +; Must set an error code: NO +; + +.proc TEXTSTYLE + stx TEXTMAGX + sty TEXTMAGY + sta TEXTDIR + rts +.endproc + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO +; + +.proc OUTTEXT + rts +.endproc + +; ------------------------------------------------------------------------ +; Calculate address and X offset in char. line, to plot the pixel at X1/Y1. + +.proc CALC + lda X1+1 + bne @L1 + lda Y1+1 + bne @L1 + + lda X1 + lsr + lsr + lsr + tay + + lda XADDRS_L,y + clc + adc Y1 + sta POINT + lda XADDRS_H,y + adc #$00 + sta POINT+1 + + lda X1 + and #7 + tax + +@L1: rts +.endproc + +; ------------------------------------------------------------------------ +; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and +; X2/Y2 = ptr3/ptr4 using the current drawing color. +; +; X1,X2 etc. are set up above (x2=LINNUM in particular) +; Format is LINE x2,y2,x1,y1 +; +; Must set an error code: NO +; + +.proc LINE + +@CHECK: lda X2 ; Make sure x1 < x2 + sec + sbc X1 + tax + lda X2+1 + sbc X1+1 + bpl @CONT + lda Y2 ; If not, swap P1 and P2 + ldy Y1 + sta Y1 + sty Y2 + lda Y2+1 + ldy Y1+1 + sta Y1+1 + sty Y2+1 + lda X1 + ldy X2 + sty X1 + sta X2 + lda X2+1 + ldy X1+1 + sta X1+1 + sty X2+1 + bcc @CHECK + +@CONT: sta DX+1 + stx DX + + ldx #$C8 ; INY + lda Y2 ; Calculate dy + sec + sbc Y1 + tay + lda Y2+1 + sbc Y1+1 + bpl @DYPOS ; Is y2 >= y1? + lda Y1 ; Otherwise, dy = y1 - y2 + sec + sbc Y2 + tay + ldx #$88 ; DEY + +@DYPOS: sty DY ; 8-bit DY -- FIX ME? + stx YINCDEC + stx XINCDEC + + lda X1 + lsr + lsr + lsr + tay + lda XADDRS_L,y + sta POINT + lda XADDRS_H,y + sta POINT+1 + ldy Y1 + + lda X1 + and #7 + tax + lda BITCHUNK,X + sta OLDCHUNK + sta CHUNK + + ldx DY + cpx DX ; Who's bigger: dy or dx? + bcc STEPINX ; If dx, then... + lda DX+1 + bne STEPINX + +; +; Big steps in Y +; +; X is now counter, Y is y co-ordinate +; +; On entry, X=DY=number of loop iterations, and Y=Y1 +STEPINY: + lda #$00 + sta OLDCHUNK ; So plotting routine will work right + lda CHUNK + lsr ; Strip the bit + eor CHUNK + sta CHUNK + txa + bne @CONT ; If dy=0, it's just a point + inx +@CONT: lsr ; Init. counter to dy/2 +; +; Main loop +; +YLOOP: sta TEMP + + lda (POINT),y ; Plot + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y +YINCDEC: + iny ; Advance Y co-ordinate + lda TEMP ; Restore A + sec + sbc DX + bcc YFIXX +YCONT: dex ; X is counter + bne YLOOP +YCONT2: lda (POINT),y ; Plot endpoint + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y + rts + +YFIXX: ; x=x+1 + adc DY + lsr CHUNK + bne YCONT ; If we pass a column boundary, + ror CHUNK ; then reset CHUNK to $80 + sta TEMP2 + lda POINT + adc #YRES + sta POINT + bcc @CONT + inc POINT+1 +@CONT: lda TEMP2 + dex + bne YLOOP + beq YCONT2 + +; +; Big steps in X direction +; +; On entry, X=DY=number of loop iterations, and Y=Y1 + +.bss +COUNTHI: + .byte $00 ; Temporary counter, + ; used only once +.code +STEPINX: + ldx DX + lda DX+1 + sta COUNTHI + cmp #$80 + ror ; Need bit for initialization + sta Y1 ; High byte of counter + txa + bne @CONT ; Could be $100 + dec COUNTHI +@CONT: ror +; +; Main loop +; +XLOOP: lsr CHUNK + beq XFIXC ; If we pass a column boundary... +XCONT1: sbc DY + bcc XFIXY ; Time to step in Y? +XCONT2: dex + bne XLOOP + dec COUNTHI ; High bits set? + bpl XLOOP + + lsr CHUNK ; Advance to last point + jmp LINEPLOT ; Plot the last chunk +; +; CHUNK has passed a column; so, plot and increment pointer; +; and fix up CHUNK, OLDCHUNK. +; +XFIXC: sta TEMP + jsr LINEPLOT + lda #$FF + sta CHUNK + sta OLDCHUNK + lda POINT + clc + adc #YRES + sta POINT + lda TEMP + bcc XCONT1 + inc POINT+1 + jmp XCONT1 +; +; Check to make sure there isn't a high bit; plot chunk; +; and update Y co-ordinate. +; +XFIXY: dec Y1 ; Maybe high bit set + bpl XCONT2 + adc DX + sta TEMP + lda DX+1 + adc #$FF ; Hi byte + sta Y1 + + jsr LINEPLOT ; Plot chunk + lda CHUNK + sta OLDCHUNK + + lda TEMP +XINCDEC: + iny ; Y co-ord + jmp XCONT2 + +; +; Subroutine to plot chunks/points (to save a little +; room, gray hair, etc.) +; +LINEPLOT: ; Plot the line chunk + lda (POINT),Y + eor BITMASK + ora CHUNK + and OLDCHUNK + eor CHUNK + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; In: xpos, ypos, width, height +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so, on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO +; + +.proc BAR + +; Set user pattern if available. + + lda USERPATTERN + ora USERPATTERN+1 + beq @GET_PATTERN_BY_COLOR + + lda USERPATTERN + sta PATTERN + lda USERPATTERN+1 + sta PATTERN+1 + jmp @GOT_PATTERN + +; Determine pattern based on current colour. + +@GET_PATTERN_BY_COLOR: + lda #PATTERN_SOLID + ldy CURCOL + bne @L2 + lda #PATTERN_EMPTY +@L2: sta PATTERN + stx PATTERN+1 + +@GOT_PATTERN: + +; Get starting POINT on screen. + + jsr CALC + sty XCPOS + lda POINT ; One off for VFILL/VCOPY. + sec + sbc #1 + sta POINT + bcs @L3 + dec POINT+1 +@L3: + +; Get height for VFILL. + + lda Y2 + sec + sbc Y1 + sta HEIGHT + +; Get rightmost char column. + + lda X2 + and #7 + sta XPOSR + +; Get width in characters. + + lda X2 + lsr + lsr + lsr + sec + sbc XCPOS + beq @DRAW_SINGLE_COLUMN + sta COUNTER + +; Draw left end. + + lda X1 + and #7 + tax + lda MASKD_LEFT,x + sta MASKD + lda MASKS_LEFT,x + sta MASKS + jsr VFILL + jsr INCPOINTX + +; Draw middle. + + dec COUNTER + beq @DRAW_RIGHT_END +@L1: jsr VCOPY + jsr INCPOINTX + dec COUNTER + bne @L1 + +; Draw right end. + +@DRAW_RIGHT_END: + ldx XPOSR + lda MASKD_RIGHT,x + sta MASKD + lda MASKS_RIGHT,x + sta MASKS + jmp VFILL + +; Draw left end. + +@DRAW_SINGLE_COLUMN: + lda X1 + and #7 + tax + ldy XPOSR + lda MASKS_LEFT,x + and MASKS_RIGHT,y + sta MASKS + lda MASKD_LEFT,x + ora MASKD_RIGHT,y + sta MASKD + jmp VFILL +.endproc + +; In: HEIGHT, PATTERN +; MASKS: Source mask (ANDed with pattern). +; MASKD: Destination mask (ANDed with screen). +; POINT: Starting address. +; ------------------------------------------------------------------------ +; Fill column with pattern using masks. +; + +.proc VFILL + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@L1: lda (POINT),y + and MASKD + sta TMP +@MOD_PATTERN: + lda $FFFF,x + and MASKS + ora TMP + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +; In: HEIGHT, PATTERN, POINT +; ------------------------------------------------------------------------ +; Fill column with pattern. +; + +.proc VCOPY + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@MOD_PATTERN: +@L1: lda $FFFF,x + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +.proc INCPOINTX + lda POINT + clc + adc #YRES + sta POINT + bcc @L1 + inc POINT+1 +@L1: rts +.endproc diff --git a/libsrc/vic20/tgi_stat_stddrv.s b/libsrc/vic20/tgi_stat_stddrv.s new file mode 100644 index 000000000..6a94d66aa --- /dev/null +++ b/libsrc/vic20/tgi_stat_stddrv.s @@ -0,0 +1,8 @@ +; +; Address of the static standard TGI driver +; +; const void tgi_static_stddrv[]; +; + + .import _vic20_hi_tgi + .export _tgi_static_stddrv := _vic20_hi_tgi diff --git a/libsrc/vic20/tgi_stddrv.s b/libsrc/vic20/tgi_stddrv.s new file mode 100644 index 000000000..d56af37e7 --- /dev/null +++ b/libsrc/vic20/tgi_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard TGI driver +; +; 2018-03-11, Sven Michael Klose +; +; const char tgi_stddrv[]; +; + + .export _tgi_stddrv + +.rodata + +_tgi_stddrv: .asciiz "vic20-hi.tgi" diff --git a/libsrc/vic20/tgihdr.s b/libsrc/vic20/tgihdr.s new file mode 100644 index 000000000..30258162d --- /dev/null +++ b/libsrc/vic20/tgihdr.s @@ -0,0 +1,67 @@ +; +; This code sits immediately after the BASIC stub program. +; Therefore, it's executed by that stub. +; +; 2018-04-17, Greg King +; + + .export __TGIHDR__ : abs = 1 ; Mark as TGI housekeeper + + .import __MAIN_LAST__ + .importzp ptr1, ptr2, tmp1 + + +basic_reset := $C000 ; vector to BASIC's cold-start code + +; This code moves the program to $2000. That move allows $1000 - $1FFF +; to be used by the TGI driver to hold its graphics data. + +.segment "TGI1HDR" + + lda #<(tgi1end + prog_size) ; source + ldx #>(tgi1end + prog_size) + sta ptr1 + stx ptr1+1 + + lda #<(tgi2hdr + prog_size) ; destination + ldx #>(tgi2hdr + prog_size) + sta ptr2 + stx ptr2+1 + + ldx #<~prog_size + lda #>~prog_size ; use -(prog_size + 1) + sta tmp1 + + ldy #<$0000 + +; Copy loop + +@L1: inx ; bump counter's low byte + beq @L4 + +@L2: tya ; will .Y underflow? + bne @L3 + dec ptr1+1 ; yes, do next lower page + dec ptr2+1 +@L3: dey + + lda (ptr1),y + sta (ptr2),y + jmp @L1 + +@L4: inc tmp1 ; bump counter's high byte + bne @L2 + + jmp tgi2hdr ; go to moved program +tgi1end: + +.segment "TGI2HDR" + +tgi2hdr: + jsr tgi2end ; run actual program + jmp (basic_reset) +tgi2end: + +; The length of the TGI program (including the TGI2HDR segment) + +prog_size = __MAIN_LAST__ - tgi2hdr diff --git a/samples/Makefile b/samples/Makefile index adfe870e4..267dc5235 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -230,6 +230,20 @@ ovrldemo: overlaydemo.o OVERLAYLIST := $(foreach I,1 2 3,multdemo.$I ovrldemo.$I) +# -------------------------------------------------------------------------- +# TGI programs on the VIC-20 need a special ld65 configuration file. + +ifeq ($(SYS),vic20) +mandelbrot.o: override CFLAGS += -D DYN_DRV=0 +mandelbrot: mandelbrot.o + $(LD) $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib + +# tgidemo needs at least 16K of RAM expansion. +tgidemo.o: override CFLAGS += -D DYN_DRV=0 +tgidemo: tgidemo.o + $(LD) -D __HIMEM__=0x6000 $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib +endif + # -------------------------------------------------------------------------- # Rule to make a CBM disk with all samples. Needs the c1541 program that comes # with the VICE emulator. diff --git a/samples/tgidemo.c b/samples/tgidemo.c index 93e91899b..b431cfb98 100644 --- a/samples/tgidemo.c +++ b/samples/tgidemo.c @@ -70,9 +70,10 @@ static void DoCircles (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_ORANGE }; unsigned char I; - unsigned char Color = COLOR_FORE; - unsigned X = MaxX / 2; - unsigned Y = MaxY / 2; + unsigned char Color = COLOR_BACK; + const unsigned X = MaxX / 2; + const unsigned Y = MaxY / 2; + const unsigned Limit = (X < Y) ? Y : X; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); @@ -82,7 +83,7 @@ static void DoCircles (void) while (!kbhit ()) { Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE; tgi_setcolor (Color); - for (I = 10; I < 240; I += 10) { + for (I = 10; I <= Limit; I += 10) { tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio)); } } @@ -132,7 +133,7 @@ static void DoDiagram (void) tgi_setcolor (COLOR_FORE); tgi_clear (); - /* Determine zero and aplitude */ + /* Determine zero and amplitude */ YOrigin = MaxY / 2; XOrigin = 10; Amp = (MaxY - 19) / 2; @@ -168,16 +169,17 @@ static void DoLines (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLACK }; unsigned X; + const unsigned Min = (MaxX < MaxY) ? MaxX : MaxY; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); tgi_clear (); - for (X = 0; X <= MaxY; X += 10) { - tgi_line (0, 0, MaxY, X); - tgi_line (0, 0, X, MaxY); - tgi_line (MaxY, MaxY, 0, MaxY-X); - tgi_line (MaxY, MaxY, MaxY-X, 0); + for (X = 0; X <= Min; X += 10) { + tgi_line (0, 0, Min, X); + tgi_line (0, 0, X, Min); + tgi_line (Min, Min, 0, Min-X); + tgi_line (Min, Min, Min-X, 0); } cgetc (); From 021362fb757aa84d0d409bf9bdd4323f7b6e6682 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 2 Jul 2020 22:41:38 +0200 Subject: [PATCH 006/806] cl65: Remove temporary .o files --- src/cl65/main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/cl65/main.c b/src/cl65/main.c index 9d536db82..c07e93e81 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -112,6 +112,9 @@ static CmdDesc CO65 = { 0, 0, 0, 0, 0, 0, 0 }; static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 }; static CmdDesc GRC = { 0, 0, 0, 0, 0, 0, 0 }; +/* Pseudo-command to track files we want to delete */ +static CmdDesc RM = { 0, 0, 0, 0, 0, 0, 0 }; + /* Variables controlling the steps we're doing */ static int DoLink = 1; static int DoAssemble = 1; @@ -456,6 +459,19 @@ static void ExecProgram (CmdDesc* Cmd) +static void RemoveTempFiles (void) +{ + unsigned I; + for (I = 0; I < RM.FileCount; ++I) { + if (remove (RM.Files[I]) < 0) { + Warning ("Cannot remove temporary file '%s': %s", + RM.Files[I], strerror (errno)); + } + } +} + + + static void Link (void) /* Link the resulting executable */ { @@ -534,6 +550,8 @@ static void AssembleFile (const char* File, unsigned ArgCount) */ char* ObjName = MakeFilename (File, ".o"); CmdAddFile (&LD65, ObjName); + /* This is just a temporary file, schedule it for removal */ + CmdAddFile (&RM, ObjName); xfree (ObjName); } else { /* This is the final step. If an output name is given, set it */ @@ -1641,6 +1659,8 @@ int main (int argc, char* argv []) Link (); } + RemoveTempFiles (); + /* Return an apropriate exit code */ return EXIT_SUCCESS; } From 416adbce82c5dee998fc592561cd692fd0056174 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 3 Jul 2020 07:53:51 +0200 Subject: [PATCH 007/806] Add blank line --- src/cl65/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cl65/main.c b/src/cl65/main.c index c07e93e81..e7b9d5344 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -462,6 +462,7 @@ static void ExecProgram (CmdDesc* Cmd) static void RemoveTempFiles (void) { unsigned I; + for (I = 0; I < RM.FileCount; ++I) { if (remove (RM.Files[I]) < 0) { Warning ("Cannot remove temporary file '%s': %s", From 527df094caf40db07ac14f7591c8cf91f6d1a511 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 2 Jul 2020 22:24:57 +0200 Subject: [PATCH 008/806] Use xrealloc in cl65 Previously, xmalloc and xfree were used. --- src/cl65/main.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/cl65/main.c b/src/cl65/main.c index e7b9d5344..f75200c89 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -231,12 +231,8 @@ static char* CmdAllocArg (const char* Arg, unsigned Len) static void CmdExpand (CmdDesc* Cmd) /* Expand the argument vector */ { - unsigned NewMax = Cmd->ArgMax + 10; - char** NewArgs = xmalloc (NewMax * sizeof (char*)); - memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*)); - xfree (Cmd->Args); - Cmd->Args = NewArgs; - Cmd->ArgMax = NewMax; + Cmd->ArgMax += 10; + Cmd->Args = xrealloc (Cmd->Args, Cmd->ArgMax * sizeof (char*)); } @@ -324,12 +320,8 @@ static void CmdAddFile (CmdDesc* Cmd, const char* File) { /* Expand the file vector if needed */ if (Cmd->FileCount == Cmd->FileMax) { - unsigned NewMax = Cmd->FileMax + 10; - char** NewFiles = xmalloc (NewMax * sizeof (char*)); - memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*)); - xfree (Cmd->Files); - Cmd->Files = NewFiles; - Cmd->FileMax = NewMax; + Cmd->FileMax += 10; + Cmd->Files = xrealloc(Cmd->Files, Cmd->FileMax * sizeof(char*)); } /* If the file name is not NULL (which is legal and is used to terminate From c273c90bf22b087806f1b6d783b8666263f8766e Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 3 Jul 2020 07:56:05 +0200 Subject: [PATCH 009/806] Fix formatting --- src/cl65/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cl65/main.c b/src/cl65/main.c index f75200c89..ea5329335 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -321,7 +321,7 @@ static void CmdAddFile (CmdDesc* Cmd, const char* File) /* Expand the file vector if needed */ if (Cmd->FileCount == Cmd->FileMax) { Cmd->FileMax += 10; - Cmd->Files = xrealloc(Cmd->Files, Cmd->FileMax * sizeof(char*)); + Cmd->Files = xrealloc (Cmd->Files, Cmd->FileMax * sizeof(char*)); } /* If the file name is not NULL (which is legal and is used to terminate From 9e881a497e619fc97cbb48a3a163e0eea23fbd52 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 3 Jul 2020 07:57:53 +0200 Subject: [PATCH 010/806] Fix formatting --- src/cl65/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cl65/main.c b/src/cl65/main.c index ea5329335..dba4915f2 100644 --- a/src/cl65/main.c +++ b/src/cl65/main.c @@ -321,7 +321,7 @@ static void CmdAddFile (CmdDesc* Cmd, const char* File) /* Expand the file vector if needed */ if (Cmd->FileCount == Cmd->FileMax) { Cmd->FileMax += 10; - Cmd->Files = xrealloc (Cmd->Files, Cmd->FileMax * sizeof(char*)); + Cmd->Files = xrealloc (Cmd->Files, Cmd->FileMax * sizeof (char*)); } /* If the file name is not NULL (which is legal and is used to terminate From 9858e47dfd0d945b750bce7b19716a834f0d78e3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 21 Jun 2020 21:28:32 +0200 Subject: [PATCH 011/806] Pad bit-fields only to the next byte Fixes #1054. Previously, bit-fields followed by another field were aligned to two bytes. Bit-fields ending the struct were (and continue to be) aligned only to a single byte. ``` struct s { unsigned int x : 4; }; struct t { unsigned int x : 4; unsigned int y; }; ``` Before: `sizeof(struct s) == 1`, sizeof(struct t) == 4` After: `sizeof(struct s) == 1` sizeof(struct t) == 3` --- src/cc65/declare.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index d7940c93b..730406088 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -772,17 +772,19 @@ static SymEntry* ParseStructDecl (const char* Name) */ if (BitOffs > 0) { if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { + /* Bits needed to byte-align the next field. */ + unsigned PaddingBitWidth = -BitOffs % CHAR_BITS; /* We need an anonymous name */ AnonName (Ident, "bit-field"); /* Add an anonymous bit-field that aligns to the next - ** storage unit. + ** byte. */ - AddBitField (Ident, StructSize, BitOffs, INT_BITS - BitOffs); + AddBitField (Ident, StructSize, BitOffs, PaddingBitWidth); /* No bits left */ - StructSize += SIZEOF_INT; + StructSize += (BitOffs + PaddingBitWidth) / CHAR_BITS; BitOffs = 0; } } From 6dc2bf12265f106d8cd4916a73990873f62f5c36 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 22 Jun 2020 09:07:39 +0200 Subject: [PATCH 012/806] Rename PaddingBitWidth to PaddingBits --- src/cc65/declare.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 730406088..5e2e204cf 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -773,7 +773,7 @@ static SymEntry* ParseStructDecl (const char* Name) if (BitOffs > 0) { if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { /* Bits needed to byte-align the next field. */ - unsigned PaddingBitWidth = -BitOffs % CHAR_BITS; + unsigned PaddingBits = -BitOffs % CHAR_BITS; /* We need an anonymous name */ AnonName (Ident, "bit-field"); @@ -781,10 +781,10 @@ static SymEntry* ParseStructDecl (const char* Name) /* Add an anonymous bit-field that aligns to the next ** byte. */ - AddBitField (Ident, StructSize, BitOffs, PaddingBitWidth); + AddBitField (Ident, StructSize, BitOffs, PaddingBits); /* No bits left */ - StructSize += (BitOffs + PaddingBitWidth) / CHAR_BITS; + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; BitOffs = 0; } } From a70ac6be307e9de645b6fa7af895b226cb0c11de Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 27 Jun 2020 20:30:08 +0200 Subject: [PATCH 013/806] Add test of bit-field packing for #1054 and #1055 --- test/val/bitfield.c | 124 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 test/val/bitfield.c diff --git a/test/val/bitfield.c b/test/val/bitfield.c new file mode 100644 index 000000000..29e450359 --- /dev/null +++ b/test/val/bitfield.c @@ -0,0 +1,124 @@ +/* + !!DESCRIPTION!! Tests of bit-field packing + !!ORIGIN!! cc65 regression tests + !!LICENCE!! zlib + !!AUTHOR!! Jesse Rosenstock +*/ + +/* + see https://github.com/cc65/cc65/issues/1054 +*/ + +#include + +static unsigned char failures = 0; + +static struct four_bits { + unsigned int x : 4; +} fb = {1}; + +static void test_four_bits(void) +{ + if (sizeof(struct four_bits) != 1) { + fprintf(stderr, "Got sizeof(struct four_bits) = %zu, expected 1.\n", + sizeof(struct four_bits)); + failures++; + } + + if (fb.x != 1) { + fprintf(stderr, "Got fb.x = %u, expected 1.\n", fb.x); + failures++; + } + + fb.x = 3; + + if (fb.x != 3) { + fprintf(stderr, "Got fb.x = %u, expected 3.\n", fb.x); + failures++; + } +} + +static struct four_bits_with_int { + unsigned int x : 4; + unsigned int y; +} fbi = {1, 2}; + +static void test_four_bits_with_int(void) +{ + /* We would like this to be 3. https://github.com/cc65/cc65/issues/1054 */ + if (sizeof(struct four_bits_with_int) != 4) { + fprintf(stderr, + "Got sizeof(struct four_bits_with_int) = %zu, expected 4.\n", + sizeof(struct four_bits_with_int)); + failures++; + } + + if (fbi.x != 1) { + fprintf(stderr, "Got fbi.x = %u, expected 1.\n", fbi.x); + failures++; + } + + if (fbi.y != 2) { + fprintf(stderr, "Got fbi.y = %u, expected 2.\n", fbi.y); + failures++; + } + + fbi.x = 3; + fbi.y = 17; + + if (fbi.x != 3) { + fprintf(stderr, "Got fbi.x = %u, expected 3.\n", fbi.x); + failures++; + } + + if (fbi.y != 17) { + fprintf(stderr, "Got fbi.y = %u, expected 17.\n", fbi.y); + failures++; + } +} + +static struct overlap { + unsigned int x : 10; + unsigned int y : 10; +} o = {11, 22}; + +/* Tests that bit-fields can share allocation units. */ +static void test_overlap(void) +{ + if (sizeof(struct overlap) != 3) { + fprintf(stderr, "Got sizeof(struct overlap) = %zu, expected 3.\n", + sizeof(struct overlap)); + failures++; + } + + if (o.x != 11) { + fprintf(stderr, "Got o.x = %u, expected 11.\n", o.x); + failures++; + } + + if (o.y != 22) { + fprintf(stderr, "Got o.y = %u, expected 22.\n", o.y); + failures++; + } + + o.x = 33; + o.y = 44; + + if (o.x != 33) { + fprintf(stderr, "Got o.x = %u, expected 33.\n", o.x); + failures++; + } + + if (o.y != 44) { + fprintf(stderr, "Got o.y = %u, expected 44.\n", o.y); + failures++; + } +} + +int main(void) +{ + test_four_bits(); + test_four_bits_with_int(); + test_overlap(); + return failures; +} From 8a331ee7ecf7af1671f406aa87a29456417e1b58 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 27 Jun 2020 20:39:33 +0200 Subject: [PATCH 014/806] Print to stdout instead of stderr Print number of failures. This makes it consistent with the other val/ tests. --- test/val/bitfield.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index 29e450359..b203b9cce 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -20,20 +20,20 @@ static struct four_bits { static void test_four_bits(void) { if (sizeof(struct four_bits) != 1) { - fprintf(stderr, "Got sizeof(struct four_bits) = %zu, expected 1.\n", - sizeof(struct four_bits)); + printf("Got sizeof(struct four_bits) = %zu, expected 1.\n", + sizeof(struct four_bits)); failures++; } if (fb.x != 1) { - fprintf(stderr, "Got fb.x = %u, expected 1.\n", fb.x); + printf("Got fb.x = %u, expected 1.\n", fb.x); failures++; } fb.x = 3; if (fb.x != 3) { - fprintf(stderr, "Got fb.x = %u, expected 3.\n", fb.x); + printf("Got fb.x = %u, expected 3.\n", fb.x); failures++; } } @@ -47,19 +47,18 @@ static void test_four_bits_with_int(void) { /* We would like this to be 3. https://github.com/cc65/cc65/issues/1054 */ if (sizeof(struct four_bits_with_int) != 4) { - fprintf(stderr, - "Got sizeof(struct four_bits_with_int) = %zu, expected 4.\n", - sizeof(struct four_bits_with_int)); + printf("Got sizeof(struct four_bits_with_int) = %zu, expected 4.\n", + sizeof(struct four_bits_with_int)); failures++; } if (fbi.x != 1) { - fprintf(stderr, "Got fbi.x = %u, expected 1.\n", fbi.x); + printf("Got fbi.x = %u, expected 1.\n", fbi.x); failures++; } if (fbi.y != 2) { - fprintf(stderr, "Got fbi.y = %u, expected 2.\n", fbi.y); + printf("Got fbi.y = %u, expected 2.\n", fbi.y); failures++; } @@ -67,12 +66,12 @@ static void test_four_bits_with_int(void) fbi.y = 17; if (fbi.x != 3) { - fprintf(stderr, "Got fbi.x = %u, expected 3.\n", fbi.x); + printf("Got fbi.x = %u, expected 3.\n", fbi.x); failures++; } if (fbi.y != 17) { - fprintf(stderr, "Got fbi.y = %u, expected 17.\n", fbi.y); + printf("Got fbi.y = %u, expected 17.\n", fbi.y); failures++; } } @@ -86,18 +85,18 @@ static struct overlap { static void test_overlap(void) { if (sizeof(struct overlap) != 3) { - fprintf(stderr, "Got sizeof(struct overlap) = %zu, expected 3.\n", - sizeof(struct overlap)); + printf("Got sizeof(struct overlap) = %zu, expected 3.\n", + sizeof(struct overlap)); failures++; } if (o.x != 11) { - fprintf(stderr, "Got o.x = %u, expected 11.\n", o.x); + printf("Got o.x = %u, expected 11.\n", o.x); failures++; } if (o.y != 22) { - fprintf(stderr, "Got o.y = %u, expected 22.\n", o.y); + printf("Got o.y = %u, expected 22.\n", o.y); failures++; } @@ -105,12 +104,12 @@ static void test_overlap(void) o.y = 44; if (o.x != 33) { - fprintf(stderr, "Got o.x = %u, expected 33.\n", o.x); + printf("Got o.x = %u, expected 33.\n", o.x); failures++; } if (o.y != 44) { - fprintf(stderr, "Got o.y = %u, expected 44.\n", o.y); + printf("Got o.y = %u, expected 44.\n", o.y); failures++; } } @@ -120,5 +119,6 @@ int main(void) test_four_bits(); test_four_bits_with_int(); test_overlap(); + printf("failures: %u\n", failures); return failures; } From 2f456ce4e210a4437c8063a3a6579c18dc762afb Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 28 Jun 2020 21:17:42 +0200 Subject: [PATCH 015/806] Add comment explaining four_bits_with_int --- test/val/bitfield.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index b203b9cce..ce791b634 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -38,6 +38,11 @@ static void test_four_bits(void) } } +/* + Logic is somewhat diferent for bit-fields that end a struct vs + having additional fields. +*/ + static struct four_bits_with_int { unsigned int x : 4; unsigned int y; From 6f85ee9d95b29dbea84fbd641f26acba97a86c3d Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 28 Jun 2020 21:24:21 +0200 Subject: [PATCH 016/806] Add another test for bit-field unit overlap Test when there is another field after bit-fields with allocation unit overlap. --- test/val/bitfield.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index ce791b634..4298e7d46 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -119,11 +119,62 @@ static void test_overlap(void) } } +static struct overlap_with_int { + unsigned int x : 10; + unsigned int y : 10; + unsigned int z; +} oi = {111, 222, 333}; + +static void test_overlap_with_int(void) +{ + /* We would like this to be 5. */ + if (sizeof(struct overlap_with_int) != 6) { + printf("Got sizeof(struct overlap_with_int) = %zu, expected 6.\n", + sizeof(struct overlap_with_int)); + failures++; + } + + if (oi.x != 111) { + printf("Got oi.x = %u, expected 111.\n", oi.x); + failures++; + } + + if (oi.y != 222) { + printf("Got oi.y = %u, expected 222.\n", oi.y); + failures++; + } + + if (oi.z != 333) { + printf("Got oi.z = %u, expected 333.\n", oi.z); + failures++; + } + + oi.x = 444; + oi.y = 555; + oi.z = 666; + + if (oi.x != 444) { + printf("Got oi.x = %u, expected 444.\n", oi.x); + failures++; + } + + if (oi.y != 555) { + printf("Got oi.y = %u, expected 555.\n", oi.y); + failures++; + } + + if (oi.z != 666) { + printf("Got oi.z = %u, expected 666.\n", oi.z); + failures++; + } +} + int main(void) { test_four_bits(); test_four_bits_with_int(); test_overlap(); + test_overlap_with_int(); printf("failures: %u\n", failures); return failures; } From 532e6b2554452135dc67ffc5078b5c7ea656991c Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 2 Jul 2020 09:01:30 +0200 Subject: [PATCH 017/806] Add copyright notice --- test/val/bitfield.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index 4298e7d46..21b45789b 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -3,6 +3,7 @@ !!ORIGIN!! cc65 regression tests !!LICENCE!! zlib !!AUTHOR!! Jesse Rosenstock + !!COPYRIGHT!! Copyright 2020 Google LLC */ /* From 8449c9eaa03563d64894f462ff604fc9efd498a7 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 2 Jul 2020 09:08:57 +0200 Subject: [PATCH 018/806] Add zlib license text --- test/val/bitfield.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index 21b45789b..c249b0f59 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -1,13 +1,25 @@ /* - !!DESCRIPTION!! Tests of bit-field packing - !!ORIGIN!! cc65 regression tests - !!LICENCE!! zlib - !!AUTHOR!! Jesse Rosenstock - !!COPYRIGHT!! Copyright 2020 Google LLC + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. */ /* - see https://github.com/cc65/cc65/issues/1054 + Tests of bit-field packing; see https://github.com/cc65/cc65/issues/1054 */ #include From 359da1ae76fbce6cf017aa48f690c52cfaa26f14 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 8 Jul 2020 17:11:41 +0200 Subject: [PATCH 019/806] Update bit-field tests after #1058 merge --- test/val/bitfield.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/val/bitfield.c b/test/val/bitfield.c index c249b0f59..81d5b2aa1 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -63,9 +63,9 @@ static struct four_bits_with_int { static void test_four_bits_with_int(void) { - /* We would like this to be 3. https://github.com/cc65/cc65/issues/1054 */ - if (sizeof(struct four_bits_with_int) != 4) { - printf("Got sizeof(struct four_bits_with_int) = %zu, expected 4.\n", + /* The first 4-bit bit-field just takes one byte, so the size is 3. */ + if (sizeof(struct four_bits_with_int) != 3) { + printf("Got sizeof(struct four_bits_with_int) = %zu, expected 3.\n", sizeof(struct four_bits_with_int)); failures++; } @@ -140,9 +140,9 @@ static struct overlap_with_int { static void test_overlap_with_int(void) { - /* We would like this to be 5. */ - if (sizeof(struct overlap_with_int) != 6) { - printf("Got sizeof(struct overlap_with_int) = %zu, expected 6.\n", + /* First two fields in 3 bytes, then another 2 bytes. */ + if (sizeof(struct overlap_with_int) != 5) { + printf("Got sizeof(struct overlap_with_int) = %zu, expected 5.\n", sizeof(struct overlap_with_int)); failures++; } From 9e5b8d99a301066f3c300a48ce836a313693531f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 8 Jul 2020 21:28:31 +0200 Subject: [PATCH 020/806] Fix MSVC build broken by #1058 MSVC complains about unary negation of unsigned, but it's intended. Suppress the warning. https://github.com/cc65/cc65/pull/1058#discussion_r451757967 "Tested" with godbolt.org. --- src/cc65/declare.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 5e2e204cf..140558ad3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -772,7 +772,11 @@ static SymEntry* ParseStructDecl (const char* Name) */ if (BitOffs > 0) { if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { - /* Bits needed to byte-align the next field. */ + /* Bits needed to byte-align the next field. MSVC complains + ** about unary negation of unsigned, but it's intended. + ** Disable the warning for the next line only. + */ + #pragma warning(disable: 4146) unsigned PaddingBits = -BitOffs % CHAR_BITS; /* We need an anonymous name */ From 2c16453a9fab93007d77213d914759120ea36802 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 8 Jul 2020 22:46:40 +0200 Subject: [PATCH 021/806] Guard MSVC pragma with ifdef _MSC_VER Fix broken travis-ci with gcc -Werror [-Werror=unknown-pragmas]. --- src/cc65/declare.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 140558ad3..ab9b45e15 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -776,7 +776,9 @@ static SymEntry* ParseStructDecl (const char* Name) ** about unary negation of unsigned, but it's intended. ** Disable the warning for the next line only. */ + #ifdef _MSC_VER #pragma warning(disable: 4146) + #endif unsigned PaddingBits = -BitOffs % CHAR_BITS; /* We need an anonymous name */ From 82c8bd6e2b9e1c53cfb60462d56ce049a01d0554 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 8 Jul 2020 23:16:15 +0200 Subject: [PATCH 022/806] Replace unary negation with subtraction Remove MSVC pragma. --- src/cc65/declare.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ab9b45e15..58baaf769 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -772,14 +772,11 @@ static SymEntry* ParseStructDecl (const char* Name) */ if (BitOffs > 0) { if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { - /* Bits needed to byte-align the next field. MSVC complains - ** about unary negation of unsigned, but it's intended. - ** Disable the warning for the next line only. + /* Bits needed to byte-align the next field. + ** MSVC complains about unary negation of unsigned, + ** so it has been rewritten as subtraction. */ - #ifdef _MSC_VER - #pragma warning(disable: 4146) - #endif - unsigned PaddingBits = -BitOffs % CHAR_BITS; + unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; /* We need an anonymous name */ AnonName (Ident, "bit-field"); From e98fe04cc282f6d11e0a1b9c208e41917b3d1c2d Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 023/806] Almost fixed Issue #169. The only denominator not working right now is -2147483648. --- src/cc65/codegen.c | 102 ++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 35af5281e..29d14a475 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1479,48 +1479,9 @@ void g_scale (unsigned flags, long val) /* Scale down */ val = -val; - if ((p2 = PowerOf2 (val)) > 0 && p2 <= 4) { - /* Factor is 2, 4, 8 and 16 use special function */ - switch (flags & CF_TYPEMASK) { - - case CF_CHAR: - if (flags & CF_FORCECHAR) { - if (flags & CF_UNSIGNED) { - while (p2--) { - AddCodeLine ("lsr a"); - } - break; - } else if (p2 <= 2) { - AddCodeLine ("cmp #$80"); - AddCodeLine ("ror a"); - break; - } - } - /* FALLTHROUGH */ - - case CF_INT: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr lsrax%d", p2); - } else { - AddCodeLine ("jsr asrax%d", p2); - } - break; - - case CF_LONG: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr lsreax%d", p2); - } else { - AddCodeLine ("jsr asreax%d", p2); - } - break; - - default: - typeerror (flags); - - } - - } else if (val != 1) { + /* g_div will use asr if feasible */ + if (val != 1) { /* Use a division instead */ g_div (flags | CF_CONST, val); @@ -2668,11 +2629,52 @@ void g_div (unsigned flags, unsigned long val) "tosdivax", "tosudivax", "tosdiveax", "tosudiveax" }; + unsigned DoShiftLabel, EndLabel; + /* Do strength reduction if the value is constant and a power of two */ int p2; if ((flags & CF_CONST) && (p2 = PowerOf2 (val)) >= 0) { /* Generate a shift instead */ - g_asr (flags, p2); + if (flags & CF_UNSIGNED) { + g_asr (flags, p2); + } else if (p2 > 0) { + /* GitHub #169 - if abs(expr) < abs(val), the result is always 0 */ + DoShiftLabel = GetLocalLabel (); + EndLabel = GetLocalLabel (); + switch (flags & CF_TYPEMASK) { + case CF_CHAR: + if (flags & CF_FORCECHAR) { + AddCodeLine ("cmp #$00"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + } + /* FALLTHROUGH */ + + case CF_INT: + AddCodeLine ("cpx #$00"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + + case CF_LONG: + AddCodeLine ("ldy sreg+1"); + AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); + break; + + default: + typeerror (flags); + break; + } + g_save (flags); + g_le (flags, (unsigned long)-(signed long)val); + AddCodeLine ("lsr a"); + g_restore (flags); + AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); + g_getimmed (flags | CF_ABSOLUTE, 0, 0); + g_jump (EndLabel); + g_defcodelabel (DoShiftLabel); + g_asr (flags, p2); + g_defcodelabel (EndLabel); + } } else { /* Generate a division */ if (flags & CF_CONST) { @@ -2956,6 +2958,22 @@ void g_asr (unsigned flags, unsigned long val) switch (flags & CF_TYPEMASK) { case CF_CHAR: + if (flags & CF_FORCECHAR) { + if ((flags & CF_UNSIGNED) != 0 && val <= 4) { + while (val--) { + AddCodeLine ("lsr a"); + } + return; + } else if (val <= 2) { + while (val--) { + AddCodeLine ("cmp #$80"); + AddCodeLine ("ror a"); + } + return; + } + } + /* FALLTHROUGH */ + case CF_INT: val &= 0x0F; if (val >= 8) { From 30835e3d9d929554f443521cd622ce8ac4d1cae8 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 024/806] More optimized codegen for the correct cases of the Issue #169 fix. --- src/cc65/codegen.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 29d14a475..2e25cb6ad 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1480,12 +1480,9 @@ void g_scale (unsigned flags, long val) /* Scale down */ val = -val; - /* g_div will use asr if feasible */ + /* Use a division instead */ if (val != 1) { - - /* Use a division instead */ g_div (flags | CF_CONST, val); - } } } @@ -2631,6 +2628,9 @@ void g_div (unsigned flags, unsigned long val) unsigned DoShiftLabel, EndLabel; + /* -Val truncated to the correct size */ + unsigned long NegatedVal; + /* Do strength reduction if the value is constant and a power of two */ int p2; if ((flags & CF_CONST) && (p2 = PowerOf2 (val)) >= 0) { @@ -2641,9 +2641,11 @@ void g_div (unsigned flags, unsigned long val) /* GitHub #169 - if abs(expr) < abs(val), the result is always 0 */ DoShiftLabel = GetLocalLabel (); EndLabel = GetLocalLabel (); + NegatedVal = (unsigned long)-(signed long)val; switch (flags & CF_TYPEMASK) { case CF_CHAR: if (flags & CF_FORCECHAR) { + NegatedVal &= 0xFF; AddCodeLine ("cmp #$00"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; @@ -2651,11 +2653,13 @@ void g_div (unsigned flags, unsigned long val) /* FALLTHROUGH */ case CF_INT: + NegatedVal &= 0xFFFF; AddCodeLine ("cpx #$00"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; case CF_LONG: + NegatedVal &= 0xFFFFFFFF; AddCodeLine ("ldy sreg+1"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; @@ -2665,7 +2669,7 @@ void g_div (unsigned flags, unsigned long val) break; } g_save (flags); - g_le (flags, (unsigned long)-(signed long)val); + g_le (flags | CF_UNSIGNED, NegatedVal); AddCodeLine ("lsr a"); g_restore (flags); AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); From 09bcff08627dc247413ee875519e69455a222320 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 025/806] Added support for changing divisions by negative power-of-2 denominators into bit shifts, and fixed #169 including the case of -2147483648 which is negative but appears positive. --- src/cc65/codegen.c | 85 +++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 2e25cb6ad..600bd7bc0 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2628,24 +2628,34 @@ void g_div (unsigned flags, unsigned long val) unsigned DoShiftLabel, EndLabel; - /* -Val truncated to the correct size */ + /* Deal with negative values as well as different sizes */ + int Negation; unsigned long NegatedVal; + unsigned MaskedVal; /* Do strength reduction if the value is constant and a power of two */ int p2; - if ((flags & CF_CONST) && (p2 = PowerOf2 (val)) >= 0) { - /* Generate a shift instead */ - if (flags & CF_UNSIGNED) { - g_asr (flags, p2); - } else if (p2 > 0) { - /* GitHub #169 - if abs(expr) < abs(val), the result is always 0 */ - DoShiftLabel = GetLocalLabel (); - EndLabel = GetLocalLabel (); - NegatedVal = (unsigned long)-(signed long)val; - switch (flags & CF_TYPEMASK) { + if (flags & CF_CONST) { + Negation = (flags & CF_UNSIGNED) == 0 && (signed long)val < 0; + NegatedVal = (unsigned long)-(signed long)val; + p2 = PowerOf2 (Negation ? NegatedVal : val); + if (p2 >= 0) { + /* Generate a shift instead */ + if (flags & CF_UNSIGNED) { + g_asr (flags, p2); + return; + } + + /* Generate a conditional shift instead */ + if (p2 > 0) { + /* GitHub #169 - if abs(expr) < abs(val), the result is always 0 */ + DoShiftLabel = GetLocalLabel (); + EndLabel = GetLocalLabel (); + MaskedVal = Negation ? val : NegatedVal; + switch (flags & CF_TYPEMASK) { case CF_CHAR: if (flags & CF_FORCECHAR) { - NegatedVal &= 0xFF; + MaskedVal &= 0xFF; AddCodeLine ("cmp #$00"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; @@ -2653,13 +2663,13 @@ void g_div (unsigned flags, unsigned long val) /* FALLTHROUGH */ case CF_INT: - NegatedVal &= 0xFFFF; + MaskedVal &= 0xFFFF; AddCodeLine ("cpx #$00"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; case CF_LONG: - NegatedVal &= 0xFFFFFFFF; + MaskedVal &= 0xFFFFFFFF; AddCodeLine ("ldy sreg+1"); AddCodeLine ("bpl %s", LocalLabelName (DoShiftLabel)); break; @@ -2667,27 +2677,38 @@ void g_div (unsigned flags, unsigned long val) default: typeerror (flags); break; + } + g_save (flags); + g_le (flags | CF_UNSIGNED, MaskedVal); + AddCodeLine ("lsr a"); + g_restore (flags); + AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); + g_getimmed (flags | CF_ABSOLUTE, 0, 0); + g_jump (EndLabel); + g_defcodelabel (DoShiftLabel); + g_asr (flags, p2); + g_defcodelabel (EndLabel); } - g_save (flags); - g_le (flags | CF_UNSIGNED, NegatedVal); - AddCodeLine ("lsr a"); - g_restore (flags); - AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); - g_getimmed (flags | CF_ABSOLUTE, 0, 0); - g_jump (EndLabel); - g_defcodelabel (DoShiftLabel); - g_asr (flags, p2); - g_defcodelabel (EndLabel); + + /* Negate the result if val is negative */ + if (Negation) { + g_neg (flags); + } + + /* Done */ + return; } - } else { - /* Generate a division */ - if (flags & CF_CONST) { - /* lhs is not on stack */ - flags &= ~CF_FORCECHAR; /* Handle chars as ints */ - g_push (flags & ~CF_CONST, 0); - } - oper (flags, val, ops); + + /* If we go here, we didn't emit code. Push the lhs on stack and fall + ** into the normal, non-optimized stuff. + */ + flags &= ~CF_FORCECHAR; /* Handle chars as ints */ + g_push (flags & ~CF_CONST, 0); } + + /* Generate a division */ + oper (flags, val, ops); + } From 85e73e91f8eec70c7d598c6b60eabe3fe603edb1 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 3 Jul 2020 16:21:01 +0800 Subject: [PATCH 026/806] Only enable signed div replacements with shift according to the code size factor settings. Also with better comments. --- src/cc65/codegen.c | 58 ++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 600bd7bc0..920d8fe90 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2626,32 +2626,32 @@ void g_div (unsigned flags, unsigned long val) "tosdivax", "tosudivax", "tosdiveax", "tosudiveax" }; - unsigned DoShiftLabel, EndLabel; - - /* Deal with negative values as well as different sizes */ - int Negation; - unsigned long NegatedVal; - unsigned MaskedVal; - /* Do strength reduction if the value is constant and a power of two */ - int p2; if (flags & CF_CONST) { - Negation = (flags & CF_UNSIGNED) == 0 && (signed long)val < 0; - NegatedVal = (unsigned long)-(signed long)val; - p2 = PowerOf2 (Negation ? NegatedVal : val); - if (p2 >= 0) { - /* Generate a shift instead */ - if (flags & CF_UNSIGNED) { - g_asr (flags, p2); - return; - } + /* Deal with negative values as well as different sizes */ + int Negation = (flags & CF_UNSIGNED) == 0 && (long)val < 0; + unsigned long NegatedVal = (unsigned long)-(long)val; + int p2 = PowerOf2 (Negation ? NegatedVal : val); + + /* Generate a shift instead */ + if ((flags & CF_UNSIGNED) != 0 && p2 > 0) { + g_asr (flags, p2); + return; + } + + /* Check if we can afford using shift instead of multiplication at the + ** cost of code size */ + if (p2 == 0 || (p2 > 0 && IS_Get (&CodeSizeFactor) >= (Negation ? 200 : 170))) { /* Generate a conditional shift instead */ if (p2 > 0) { - /* GitHub #169 - if abs(expr) < abs(val), the result is always 0 */ - DoShiftLabel = GetLocalLabel (); - EndLabel = GetLocalLabel (); - MaskedVal = Negation ? val : NegatedVal; + unsigned int DoShiftLabel = GetLocalLabel (); + unsigned int EndLabel = GetLocalLabel (); + unsigned long MaskedVal = Negation ? val : NegatedVal; + + /* GitHub #169 - if abs(expr) < abs(val), the result is always 0. + ** First, check whether expr >= 0 and skip to the shift if true. + */ switch (flags & CF_TYPEMASK) { case CF_CHAR: if (flags & CF_FORCECHAR) { @@ -2678,19 +2678,33 @@ void g_div (unsigned flags, unsigned long val) typeerror (flags); break; } + /* Second, check whether expr <= -asb(val) and skip to the + ** shift if true. The original content of expr has to be saved + ** before the checking comparison and restored after that, as + ** the content in Primary register will be destroyed. + ** The result of the comparison is a boolean. We can store + ** it in the Carry flag with a LSR and branch on it later. + */ g_save (flags); g_le (flags | CF_UNSIGNED, MaskedVal); AddCodeLine ("lsr a"); g_restore (flags); AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); + + /* The result is 0. We can just load 0 and skip the shifting. */ g_getimmed (flags | CF_ABSOLUTE, 0, 0); g_jump (EndLabel); + + /* Do the shift. The sign of the result may need be corrected + ** later. + */ g_defcodelabel (DoShiftLabel); g_asr (flags, p2); g_defcodelabel (EndLabel); } - /* Negate the result if val is negative */ + /* Negate the result as long as val < 0, even if val == -1 and no + ** shift was generated. */ if (Negation) { g_neg (flags); } From 93f0df58e5323be4e4043c5d9e8b6d93ffb7cf9e Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 9 Jul 2020 15:57:51 +0200 Subject: [PATCH 027/806] test related to issue #1071 --- test/val/bug1071.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 test/val/bug1071.c diff --git a/test/val/bug1071.c b/test/val/bug1071.c new file mode 100644 index 000000000..02b069de0 --- /dev/null +++ b/test/val/bug1071.c @@ -0,0 +1,87 @@ + +/* test related to issue #1071 */ + +#include +#include +#include + +struct ImageStruct +{ + uint8_t _imageData; + #if !defined(NO_COLOR) + uint8_t _color; + #endif +}; + +typedef struct ImageStruct Image; + +struct CharacterStruct +{ + // character coordinates + uint8_t _x; + uint8_t _y; + + // _status decides whether the character is active + uint8_t _status; + + Image* _imagePtr; +}; + +typedef struct CharacterStruct Character; + +uint16_t ghostLevel; +uint8_t level; +uint16_t loop; + +#define GHOSTS_NUMBER 10 +#define BOMBS_NUMBER 3 + +#define MAX_GHOST_LEVEL_SCALE 3 +#define MAX_GHOST_LEVEL (1400/MAX_GHOST_LEVEL_SCALE) + +#define MAX_GHOST_LOOP_SCALE 3 +#define MAX_GHOST_LOOP (1800/MAX_GHOST_LOOP_SCALE) + +#define INITIAL_GHOST_SLOWDOWN 16000 + +#define ACTION_GHOST_MIN_SLOWDOWN_SCALE 1 +#define GHOST_MIN_SLOWDOWN_SCALE ACTION_GHOST_MIN_SLOWDOWN_SCALE +#define GHOST_MIN_SLOWDOWN (3000/GHOST_MIN_SLOWDOWN_SCALE) + +Character ghosts[GHOSTS_NUMBER]; +Character bombs[BOMBS_NUMBER]; + +uint16_t test1(void) +{ + if((loop Date: Thu, 9 Jul 2020 16:05:57 +0200 Subject: [PATCH 028/806] tests for issue #169 --- test/val/div-char-char.c | 86 ++++++++++++++++++++++++ test/val/div-common.h | 52 +++++++++++++++ test/val/div-int-int.c | 114 +++++++++++++++++++++++++++++++ test/val/div-long-long.c | 141 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 test/val/div-char-char.c create mode 100644 test/val/div-common.h create mode 100644 test/val/div-int-int.c create mode 100644 test/val/div-long-long.c diff --git a/test/val/div-char-char.c b/test/val/div-char-char.c new file mode 100644 index 000000000..aa86bcd19 --- /dev/null +++ b/test/val/div-char-char.c @@ -0,0 +1,86 @@ +#include +#include +#include "div-common.h" + +int res = 0; + +/* we check A_8 and B_8 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_8(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 32, 64, 0) +TEST(12, -32, 64, 0) +TEST(13, 32, -64, 0) +TEST(14, -32, -64, 0) +TEST(15, 64, 32, 2) +TEST(16, -64, 32, -2) +TEST(17, 64, -32, -2) +TEST(18, -64, -32, 2) + +/* +128 can't be tested for 8-bit signed char */ +TEST(101, 127, -128, 0) +TEST(102, -127, -128, 0) +TEST(103, -128, -128, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + + return res; +} diff --git a/test/val/div-common.h b/test/val/div-common.h new file mode 100644 index 000000000..b67de54dc --- /dev/null +++ b/test/val/div-common.h @@ -0,0 +1,52 @@ +#ifndef DIV_COMMON_H +#define DIV_COMMON_H + +/* check if the result is correct */ +#define TEST_A_T(type, _n,_a,_b,_r) \ + int test##_n##a(void) { \ + typedef type int_t; \ + int_t a = ((int_t)_a), b = ((int_t)_b); \ + if (((a/((int_t)_b)) == ((int_t)_r)) && ((a/b) == ((int_t)_r))) { \ + return 0; \ + } else { \ + printf("%d\tincorrect: a/%ld = %ld, a/b = %ld\n\texpected: %ld/%ld = %ld\n", \ + (_n), (long)((int_t)_b), (long)(a/((int_t)_b)), (long)(a/b), (long)((int_t)_a), (long)((int_t)_b), (long)((int_t)_r)); \ + return 1; \ + } \ + } + +/* check if the results are equal */ +#define TEST_B_T(type, _n,_a,_b,_r) \ + int test##_n##b(void) { \ + typedef type int_t; \ + int_t a = ((int_t)_a), b = ((int_t)_b); \ + if (((a/((int_t)_b)) == (a/b))) { \ + return 0; \ + } else { \ + printf("%d\tnot equal: %ld != %ld, a = %ld, b = %ld\n\texpected: %ld/%ld = %ld\n", \ + (_n), (long)(a/((int_t)_b)), (long)(a/b), (long)(a), (long)(b), (long)((int_t)_a), (long)((int_t)_b), (long)((int_t)_r)); \ + return 1; \ + } \ + } + +#define TEST_A_8(_n,_a,_b,_r) TEST_A_T(int8_t, _n,_a,_b,_r) +#define TEST_B_8(_n,_a,_b,_r) TEST_B_T(int8_t, _n,_a,_b,_r) +#define TEST_A_16(_n,_a,_b,_r) TEST_A_T(int16_t, _n,_a,_b,_r) +#define TEST_B_16(_n,_a,_b,_r) TEST_B_T(int16_t, _n,_a,_b,_r) +#define TEST_A_32(_n,_a,_b,_r) TEST_A_T(int32_t, _n,_a,_b,_r) +#define TEST_B_32(_n,_a,_b,_r) TEST_B_T(int32_t, _n,_a,_b,_r) + +/* A and B */ +#define TEST_AB_8(_n,_a,_b,_r) \ + TEST_A_8(_n,_a,_b,_r) \ + TEST_B_8(_n,_a,_b,_r) + +#define TEST_AB_16(_n,_a,_b,_r) \ + TEST_A_16(_n,_a,_b,_r) \ + TEST_B_16(_n,_a,_b,_r) + +#define TEST_AB_32(_n,_a,_b,_r) \ + TEST_A_32(_n,_a,_b,_r) \ + TEST_B_32(_n,_a,_b,_r) + +#endif diff --git a/test/val/div-int-int.c b/test/val/div-int-int.c new file mode 100644 index 000000000..efcb12a48 --- /dev/null +++ b/test/val/div-int-int.c @@ -0,0 +1,114 @@ +#include +#include +#include "div-common.h" + +int res = 0; + +/* we check A_16 and B_16 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_16(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 2048, 512, 4) +TEST(12, -2048, 512, -4) +TEST(13, 2048, -512, -4) +TEST(14, -2048, -512, 4) +TEST(15, 512, 2048, 0) +TEST(16, -512, 2048, 0) +TEST(17, 512, -2048, 0) +TEST(18, -512, -2048, 0) + +/* values that are around min/max of the type(s) */ +TEST(101, 127, 128, 0) +TEST(102, -127, 128, 0) +TEST(103, 127, -128, 0) +TEST(104, -127, -128, 0) +TEST(105, 128, 128, 1) +TEST(106, 128, -128, -1) +TEST(107, -128, 128, -1) +TEST(108, -128, -128, 1) + +/* +32768 can't be tested for 16-bit signed short */ +TEST(201, 32767L, -32768L, 0) +TEST(202, 32767L, -32768L, 0) +TEST(203, -32768L, -32768L, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + DO_TEST_A(104); + DO_TEST_A(105); + DO_TEST_A(106); + DO_TEST_A(107); + DO_TEST_A(108); + + DO_TEST_A(201); + DO_TEST_A(202); + DO_TEST_A(203); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + DO_TEST_B(104); + DO_TEST_B(105); + DO_TEST_B(106); + DO_TEST_B(107); + DO_TEST_B(108); + + DO_TEST_B(201); + DO_TEST_B(202); + DO_TEST_B(203); + + return res; +} diff --git a/test/val/div-long-long.c b/test/val/div-long-long.c new file mode 100644 index 000000000..d2553e3c3 --- /dev/null +++ b/test/val/div-long-long.c @@ -0,0 +1,141 @@ +#include +#include +#include "div-common.h" + +int res = 0; + +/* we check A_32 and B_32 signed */ +#define TEST(_n,_a,_b,_r) TEST_AB_32(_n,_a,_b,_r) + +#define DO_TEST_A(_n) res += test##_n##a() +#define DO_TEST_B(_n) res += test##_n##b() + +/* arbitrary values */ +TEST(1, 1, 8, 0) +TEST(2, -1, 8, 0) +TEST(3, 1, -8, 0) +TEST(4, -1, -8, 0) +TEST(5, 8, 1, 8) +TEST(6, -8, 1, -8) +TEST(7, 8, -1, -8) +TEST(8, -8, -1, 8) + +TEST(11, 2048, 512, 4) +TEST(12, -2048, 512, -4) +TEST(13, 2048, -512, -4) +TEST(14, -2048, -512, 4) +TEST(15, 512, 2048, 0) +TEST(16, -512, 2048, 0) +TEST(17, 512, -2048, 0) +TEST(18, -512, -2048, 0) + +/* values that are around min/max of the type(s) */ +TEST(101, 127, 128, 0) +TEST(102, -127, 128, 0) +TEST(103, 127, -128, 0) +TEST(104, -127, -128, 0) +TEST(105, 128, 128, 1) +TEST(106, 128, -128, -1) +TEST(107, -128, 128, -1) +TEST(108, -128, -128, 1) + +TEST(201, 32767L, 32768L, 0) +TEST(202, -32767L, 32768L, 0) +TEST(203, 32767L, -32768L, 0) +TEST(204, 32767L, -32768L, 0) +TEST(205, 32768L, 32768L, 1) +TEST(206, 32768L, -32768L, -1) +TEST(207, -32768L, 32768L, -1) +TEST(208, -32768L, -32768L, 1) + +/* +2147483648 can't be tested for 32-bit signed long */ +TEST(401, 2147483647UL, -2147483648UL, 0) +TEST(402, -2147483647UL, -2147483648UL, 0) +TEST(403, -2147483648UL, -2147483648UL, 1) + +int main(void) +{ + /* check if the result is correct */ + DO_TEST_A(1); + DO_TEST_A(2); + DO_TEST_A(3); + DO_TEST_A(4); + DO_TEST_A(5); + DO_TEST_A(6); + DO_TEST_A(7); + DO_TEST_A(8); + + DO_TEST_A(11); + DO_TEST_A(12); + DO_TEST_A(13); + DO_TEST_A(14); + DO_TEST_A(15); + DO_TEST_A(16); + DO_TEST_A(17); + DO_TEST_A(18); + + DO_TEST_A(101); + DO_TEST_A(102); + DO_TEST_A(103); + DO_TEST_A(104); + DO_TEST_A(105); + DO_TEST_A(106); + DO_TEST_A(107); + DO_TEST_A(108); + + DO_TEST_A(201); + DO_TEST_A(202); + DO_TEST_A(203); + DO_TEST_A(204); + DO_TEST_A(205); + DO_TEST_A(206); + DO_TEST_A(207); + DO_TEST_A(208); + + DO_TEST_A(401); + DO_TEST_A(402); + DO_TEST_A(403); + + /* check if the results are equal */ + DO_TEST_B(1); + DO_TEST_B(2); + DO_TEST_B(3); + DO_TEST_B(4); + DO_TEST_B(5); + DO_TEST_B(6); + DO_TEST_B(7); + DO_TEST_B(8); + + DO_TEST_B(11); + DO_TEST_B(12); + DO_TEST_B(13); + DO_TEST_B(14); + DO_TEST_B(15); + DO_TEST_B(16); + DO_TEST_B(17); + DO_TEST_B(18); + + DO_TEST_B(101); + DO_TEST_B(102); + DO_TEST_B(103); + DO_TEST_B(104); + DO_TEST_B(105); + DO_TEST_B(106); + DO_TEST_B(107); + DO_TEST_B(108); + + DO_TEST_B(201); + DO_TEST_B(202); + DO_TEST_B(203); + DO_TEST_B(204); + DO_TEST_B(205); + DO_TEST_B(206); + DO_TEST_B(207); + DO_TEST_B(208); + + DO_TEST_B(401); + DO_TEST_B(402); + DO_TEST_B(403); + + return res; +} From 5925a7f8eec335b5c566c93c345ab044e1fc8b23 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 9 Jul 2020 16:16:46 +0200 Subject: [PATCH 029/806] test for issue #1077 --- test/todo/bug1077.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/todo/bug1077.c diff --git a/test/todo/bug1077.c b/test/todo/bug1077.c new file mode 100644 index 000000000..2fb3ef168 --- /dev/null +++ b/test/todo/bug1077.c @@ -0,0 +1,31 @@ +/* bug #1077 - Wrong code: a non-array constant address with a non-constant subscript */ + +#include +#include + +int test1(void) +{ + int a = 0; + (&a)[a] = 42; + return a; +} +int test2(void) +{ + int a = 0; + int b = 0; + (&a)[b] = 42; + return a; +} + +int main(void) +{ + int res, ret = EXIT_SUCCESS; + res = test1(); + printf("%d\n", res); + if (res != 42) ret = EXIT_FAILURE; + res = test2(); + printf("%d\n", res); + if (res != 42) ret = EXIT_FAILURE; + + return ret; +} From ff18218b0a42cc07e42869622fb395d18301ca33 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 9 Jul 2020 16:17:16 +0200 Subject: [PATCH 030/806] test for issue #170 --- test/todo/bug170.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/todo/bug170.c diff --git a/test/todo/bug170.c b/test/todo/bug170.c new file mode 100644 index 000000000..ac54d54d8 --- /dev/null +++ b/test/todo/bug170.c @@ -0,0 +1,40 @@ +/* bug #170 - Wrong implicit conversion of integers */ + +#include +#include +#include + +int main(void) +{ + uint8_t c = 2; + uint32_t u = 2; + int16_t a = -2; + int32_t l = -2; + + /* Generated code should use tosmulax but uses tosumulax */ + int16_t r = c * a; + /* Generated code should use tosmuleax but uses tosumuleax */ + int32_t lr = u * l; + + int32_t n = -95; + uint16_t d = 3; + int16_t r1 = n / d; // produces 21813 instead of -31 + + int16_t r2 = n / (int32_t) d; // workaround + + printf("r: %d (-4)\n", r); +#ifdef REFERENCE + printf("lr: %d (-4)\n", lr); +#else + printf("lr: %ld (-4)\n", lr); +#endif + printf("r1: %d (-31)\n", r1); + printf("r2: %d (-31)\n", r2); + + if (r != -4) { return EXIT_FAILURE; } + if (lr != -4) { return EXIT_FAILURE; } + if (r1 != -31) { return EXIT_FAILURE; } + if (r2 != -31) { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} From 2428285694099da27a4a4cff1ed9c446ca2d4f9e Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 9 Jul 2020 16:17:31 +0200 Subject: [PATCH 031/806] test for issue #327 --- test/todo/bug327.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/todo/bug327.c diff --git a/test/todo/bug327.c b/test/todo/bug327.c new file mode 100644 index 000000000..f6a79962b --- /dev/null +++ b/test/todo/bug327.c @@ -0,0 +1,34 @@ +/* bug #327 - Promoting u8 to s16 gives wrong result */ + +#include +#include + +static const uint8_t arr[2] = { + 0, + 255 +}; + +static int16_t get16() { + return -arr[1]; +} + +static int16_t get16_2() { + return -(int16_t) arr[1]; +} + +char res = 0; + +int main() { + + printf("Value %d, should be -255\n", get16()); + printf("Value %d, should be -255\n", get16_2()); + + if (get16() != -255) { + res++; + } + if (get16_2() != -255) { + res++; + } + + return res; +} From 579b50f0c5f068d0194fd6f2a19f7054b7c6945a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 9 Jul 2020 16:18:08 +0200 Subject: [PATCH 032/806] test for issue #927 --- test/todo/bug927.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/todo/bug927.c diff --git a/test/todo/bug927.c b/test/todo/bug927.c new file mode 100644 index 000000000..e0f916e66 --- /dev/null +++ b/test/todo/bug927.c @@ -0,0 +1,47 @@ + +/* bug #927: format specifiers related to leading zeros do not work as expected */ + +/* expected output: +0023 +0023 +-0023 +-023 +*/ + +#include +#include +#include + +char b1[10]; +char b2[10]; +char b3[10]; +char b4[10]; + +int main(void) { + printf("%.4d\n", 23); + printf("%04d\n", 23); + printf("%.4d\n", -23); + printf("%04d\n\n", -23); + + sprintf(b1, "%.4d", 23); + sprintf(b2, "%04d", 23); + sprintf(b3, "%.4d", -23); + sprintf(b4, "%04d", -23); + + printf("%s\n", b1); + printf("%s\n", b2); + printf("%s\n", b3); + printf("%s\n\n", b4); + + printf("%d\n", strcmp(b1, "0023")); + printf("%d\n", strcmp(b2, "0023")); + printf("%d\n", strcmp(b3, "-0023")); + printf("%d\n", strcmp(b4, "-023")); + + if(strcmp(b1, "0023") != 0) return EXIT_FAILURE; + if(strcmp(b2, "0023") != 0) return EXIT_FAILURE; + if(strcmp(b3, "-0023") != 0) return EXIT_FAILURE; + if(strcmp(b4, "-023") != 0) return EXIT_FAILURE; + + return EXIT_SUCCESS; +} From f5afc75cbdb5cba180467185f1d8a45de6e3a005 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 9 Jul 2020 21:18:42 +0200 Subject: [PATCH 033/806] ar65/LibClose: Include filename in error messages ``` ar65: Error: Problem deleting temporary library file '../lib/apple2enh.lib.temp': No such file or directory ``` is the error I'm getting with `make -j 19` when trying to debug #1080. --- src/ar65/library.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ar65/library.c b/src/ar65/library.c index 4d18168d8..08fdeb563 100644 --- a/src/ar65/library.c +++ b/src/ar65/library.c @@ -399,9 +399,11 @@ void LibClose (void) Error ("Problem closing '%s': %s", LibName, strerror (errno)); } if (NewLib && fclose (NewLib) != 0) { - Error ("Problem closing temporary library file: %s", strerror (errno)); + Error ("Problem closing temporary library file '%s': %s", + NewLibName, strerror (errno)); } if (NewLibName && remove (NewLibName) != 0) { - Error ("Problem deleting temporary library file: %s", strerror (errno)); + Error ("Problem deleting temporary library file '%s': %s", + NewLibName, strerror (errno)); } } From 878e4a57c8197485d02e1be0e90a7144a37e4203 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 2 Jul 2020 08:57:11 +0200 Subject: [PATCH 034/806] Make Makefiles more -j friendly Add .$1.$2 to outputs missing them. --- test/Makefile | 2 -- test/asm/Makefile | 3 +++ test/misc/Makefile | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/Makefile b/test/Makefile index c85883517..fc1ac246c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -16,8 +16,6 @@ WORKDIR = ../testwrk all: dotests -.NOTPARALLEL: - dotests: mostlyclean continue continue: diff --git a/test/asm/Makefile b/test/asm/Makefile index 93210aaee..62f2f8dd1 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -41,6 +41,9 @@ CPUDETECT_BINS = $(foreach cpu,$(CPUDETECT_CPUS),$(WORKDIR)/$(cpu)-cpudetect.bin all: $(OPCODE_BINS) $(CPUDETECT_BINS) +# cpudetect.o is written by multiple rules +.NOTPARALLEL: + $(WORKDIR): $(call MKDIR,$(WORKDIR)) diff --git a/test/misc/Makefile b/test/misc/Makefile index 15999ba3c..1d6b2b9d2 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -69,13 +69,13 @@ $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(WORKDIR)/limits.$1.$2.prg: limits.c $(DIFF) $(if $(QUIET),echo misc/limits.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/limits.$1.out - $(DIFF) $(WORKDIR)/limits.$1.out limits.ref + $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/limits.$1.$2.out + $(DIFF) $(WORKDIR)/limits.$1.$2.out limits.ref $(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) $(if $(QUIET),echo misc/goto.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.out - $(DIFF) $(WORKDIR)/goto.$1.out goto.ref + $(CL65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out + $(DIFF) $(WORKDIR)/goto.$1.$2.out goto.ref # the rest are tests that fail currently for one reason or another $(WORKDIR)/fields.$1.$2.prg: fields.c | $(WORKDIR) From 3999f2ad75e053dbddff08b1a98204b9ec6e4ce3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 10 Jul 2020 11:48:47 +0200 Subject: [PATCH 035/806] Move .NOTPARALLEL closer to rule that needs it --- test/asm/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/asm/Makefile b/test/asm/Makefile index 62f2f8dd1..a0825574c 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -41,9 +41,6 @@ CPUDETECT_BINS = $(foreach cpu,$(CPUDETECT_CPUS),$(WORKDIR)/$(cpu)-cpudetect.bin all: $(OPCODE_BINS) $(CPUDETECT_BINS) -# cpudetect.o is written by multiple rules -.NOTPARALLEL: - $(WORKDIR): $(call MKDIR,$(WORKDIR)) @@ -61,6 +58,9 @@ endef # OPCODE_template $(foreach cpu,$(OPCODE_CPUS),$(eval $(call OPCODE_template,$(cpu)))) +# cpudetect.o is written by multiple rules +.NOTPARALLEL: + define CPUDETECT_template $(WORKDIR)/$1-cpudetect.bin: cpudetect.s $(DIFF) From df975704719a92ba58294c8b2c12d14634495078 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 10 Jul 2020 11:49:19 +0200 Subject: [PATCH 036/806] Set .NOTPARALLEL in test/misc and test/val The cl65 intermediate files stomp each other in these directories. --- test/misc/Makefile | 5 +++++ test/val/Makefile | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/test/misc/Makefile b/test/misc/Makefile index 1d6b2b9d2..cda0c789a 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -50,6 +50,11 @@ TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02. all: $(TESTS) +# The same input file is processed with different cl65 args, +# but cl65 uses the input file name to make the temp file name, +# and they stomp each other. +.NOTPARALLEL: + $(WORKDIR): $(call MKDIR,$(WORKDIR)) diff --git a/test/val/Makefile b/test/val/Makefile index df1d314e4..417c0b6c8 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -41,6 +41,11 @@ TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02. all: $(TESTS) +# The same input file is processed with different cl65 args, +# but cl65 uses the input file name to make the temp file name, +# and they stomp each other. +.NOTPARALLEL: + $(WORKDIR): $(call MKDIR,$(WORKDIR)) From 695b1b01d8deaaf2c4a9122681e95b266a2688f1 Mon Sep 17 00:00:00 2001 From: compyx Date: Fri, 10 Jul 2020 19:35:23 +0200 Subject: [PATCH 037/806] C64 soft80 conio: save 6 bytes in `firstinit` As a bonus, save 6 cycles. --- libsrc/c64/soft80_conio.s | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libsrc/c64/soft80_conio.s b/libsrc/c64/soft80_conio.s index 48039d288..355ae51a0 100644 --- a/libsrc/c64/soft80_conio.s +++ b/libsrc/c64/soft80_conio.s @@ -67,22 +67,20 @@ firstinit: inc soft80_first_init - lda #soft80_charset - sta ptr1 + sty ptr1 stx ptr1+1 - lda #soft80_lo_charset - sta ptr2 + sty ptr2 stx ptr2+1 - lda #soft80_hi_charset - sta ptr3 + sty ptr3 stx ptr3+1 ldx #4 -@l2: - ldy #0 @l1: lda (ptr1),y sta (ptr2),y @@ -97,17 +95,17 @@ firstinit: inc ptr2+1 inc ptr3+1 dex - bne @l2 + bne @l1 ; copy the kplot tables to ram under I/O ;ldx #0 ; is 0 -@l3: +@l2: lda soft80_tables_data_start,x sta soft80_bitmapxlo,x lda soft80_tables_data_start + (soft80_tables_data_end - soft80_tables_data_start - $0100),x sta soft80_bitmapxlo + (soft80_tables_data_end - soft80_tables_data_start - $0100),x inx - bne @l3 + bne @l2 pla sta $01 From fb7996b0ce66be7675cb856d53780e73b5f3d5fd Mon Sep 17 00:00:00 2001 From: compyx Date: Fri, 10 Jul 2020 19:56:34 +0200 Subject: [PATCH 038/806] Revert "C64 soft80 conio: save 6 bytes in `firstinit`" This reverts commit 943e68be6a3b48529540e8f86061f171634f80f9. --- libsrc/c64/soft80_conio.s | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libsrc/c64/soft80_conio.s b/libsrc/c64/soft80_conio.s index 355ae51a0..48039d288 100644 --- a/libsrc/c64/soft80_conio.s +++ b/libsrc/c64/soft80_conio.s @@ -67,20 +67,22 @@ firstinit: inc soft80_first_init - ; save 6 bytes due to soft80_charset, soft80_lo_charset and - ; soft80_hi_charset being page-aligned. - ldy #0 + lda #soft80_charset - sty ptr1 + sta ptr1 stx ptr1+1 + lda #soft80_lo_charset - sty ptr2 + sta ptr2 stx ptr2+1 + lda #soft80_hi_charset - sty ptr3 + sta ptr3 stx ptr3+1 ldx #4 +@l2: + ldy #0 @l1: lda (ptr1),y sta (ptr2),y @@ -95,17 +97,17 @@ firstinit: inc ptr2+1 inc ptr3+1 dex - bne @l1 + bne @l2 ; copy the kplot tables to ram under I/O ;ldx #0 ; is 0 -@l2: +@l3: lda soft80_tables_data_start,x sta soft80_bitmapxlo,x lda soft80_tables_data_start + (soft80_tables_data_end - soft80_tables_data_start - $0100),x sta soft80_bitmapxlo + (soft80_tables_data_end - soft80_tables_data_start - $0100),x inx - bne @l2 + bne @l3 pla sta $01 From e45e57d7ce95ffce2b39f7bb5bffc974782c93a1 Mon Sep 17 00:00:00 2001 From: compyx Date: Fri, 10 Jul 2020 20:08:44 +0200 Subject: [PATCH 039/806] C64 soft80 conio: save 4 bytes in `firstinit` Also save 6 cycles as a very small bonus. --- libsrc/c64/soft80_conio.s | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libsrc/c64/soft80_conio.s b/libsrc/c64/soft80_conio.s index 48039d288..4291c2078 100644 --- a/libsrc/c64/soft80_conio.s +++ b/libsrc/c64/soft80_conio.s @@ -67,22 +67,21 @@ firstinit: inc soft80_first_init + ; save 4 bytes due to soft80_lo_charset and soft80_hi_charset being + ; page-aligned. + ldy #0 lda #soft80_charset sta ptr1 stx ptr1+1 - lda #soft80_lo_charset - sta ptr2 + sty ptr2 stx ptr2+1 - lda #soft80_hi_charset - sta ptr3 + sty ptr3 stx ptr3+1 ldx #4 -@l2: - ldy #0 @l1: lda (ptr1),y sta (ptr2),y @@ -97,17 +96,17 @@ firstinit: inc ptr2+1 inc ptr3+1 dex - bne @l2 + bne @l1 ; copy the kplot tables to ram under I/O ;ldx #0 ; is 0 -@l3: +@l2: lda soft80_tables_data_start,x sta soft80_bitmapxlo,x lda soft80_tables_data_start + (soft80_tables_data_end - soft80_tables_data_start - $0100),x sta soft80_bitmapxlo + (soft80_tables_data_end - soft80_tables_data_start - $0100),x inx - bne @l3 + bne @l2 pla sta $01 From 2c4dd5decf27ad2c0e10df920e63d9f9fc03cb4a Mon Sep 17 00:00:00 2001 From: compyx Date: Sat, 11 Jul 2020 09:39:21 +0200 Subject: [PATCH 040/806] Shorten comment as requested --- libsrc/c64/soft80_conio.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsrc/c64/soft80_conio.s b/libsrc/c64/soft80_conio.s index 4291c2078..7036f384d 100644 --- a/libsrc/c64/soft80_conio.s +++ b/libsrc/c64/soft80_conio.s @@ -67,8 +67,7 @@ firstinit: inc soft80_first_init - ; save 4 bytes due to soft80_lo_charset and soft80_hi_charset being - ; page-aligned. + ; soft80_lo_charset and soft80_hi_charset are page-aligned ldy #0 lda #soft80_charset From 381a32d9aa44fc49c9d34e812212405bde24f4af Mon Sep 17 00:00:00 2001 From: compyx Date: Sat, 11 Jul 2020 13:18:14 +0200 Subject: [PATCH 041/806] C64 soft80-conio cgetc: save 14 cycles in `invertcursor` By 'inverting' the loop, we can save 16 cycles by removing the `cpy #8`, saving 16 cycles. But we need an extra `ldy #7` at the start of the loop, so the total cycles saved is 14. Code size doesn't increase due to the addition of the `ldy #7` negating the removal of the `cpy #xx`. --- libsrc/c64/soft80_cgetc.s | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libsrc/c64/soft80_cgetc.s b/libsrc/c64/soft80_cgetc.s index 05e9e5b47..aa12cd8ca 100644 --- a/libsrc/c64/soft80_cgetc.s +++ b/libsrc/c64/soft80_cgetc.s @@ -46,17 +46,16 @@ invertcursor: lda #$34 sta $01 - ldy #$00 jsr setcolor ldx soft80_internal_cursorxlsb + ldy #7 @lp1: lda (SCREEN_PTR),y eor nibble,x sta (SCREEN_PTR),y - iny - cpy #8 - bne @lp1 + dey + bpl @lp1 pla sta $01 ; enable I/O @@ -67,7 +66,7 @@ invertcursor: ; shown using the current textcolor without disturbing the "color voodoo" ; in soft80_cputc setcolor: - ;ldy #0 ; is 0 + ldy #0 bcs @set ; restore old value lda tmp1 From 7d652d42dc7616cf4024aff301e6fc257ee4aa0a Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 042/806] Added a warning on promoting a decimal constant without a 'u'/'U' suffix to unsigned long. --- src/cc65/scanner.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 3e829aabb..91abd332d 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -530,10 +530,12 @@ static void NumericConst (void) if (!IsFloat) { unsigned Types; - int HaveSuffix; + unsigned WarnTypes = 0; - /* Check for a suffix and determine the possible types */ - HaveSuffix = 1; + /* Check for a suffix and determine the possible types. It is always + ** possible to convert the data to unsigned long even if the IT_ULONG + ** flag were not set, but we are not doing that. + */ if (toupper (CurC) == 'U') { /* Unsigned type */ NextChar (); @@ -548,17 +550,18 @@ static void NumericConst (void) NextChar (); if (toupper (CurC) != 'U') { Types = IT_LONG | IT_ULONG; + WarnTypes = IT_ULONG; } else { NextChar (); Types = IT_ULONG; } } else { - HaveSuffix = 0; if (Prefix == 10) { /* Decimal constants are of any type but uint */ Types = IT_INT | IT_LONG | IT_ULONG; + WarnTypes = IT_LONG | IT_ULONG; } else { - /* Octal or hex constants are of any type */ + /* Binary, octal or hex constants can be of any type */ Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; } } @@ -568,11 +571,14 @@ static void NumericConst (void) /* Out of range for int */ Types &= ~IT_INT; /* If the value is in the range 0x8000..0xFFFF, unsigned int is not - ** allowed, and we don't have a type specifying suffix, emit a - ** warning, because the constant is of type long. + ** allowed, and we don't have a long type specifying suffix, emit a + ** warning, because the constant is of type long while the user + ** might expect an unsigned int. */ - if (IVal <= 0xFFFF && (Types & IT_UINT) == 0 && !HaveSuffix) { - Warning ("Constant is long"); + if (IVal <= 0xFFFF && + (Types & IT_UINT) == 0 && + (WarnTypes & IT_LONG) != 0) { + Warning ("Integer constant is long"); } } if (IVal > 0xFFFF) { @@ -582,6 +588,15 @@ static void NumericConst (void) if (IVal > 0x7FFFFFFF) { /* Out of range for long int */ Types &= ~IT_LONG; + /* If the value is in the range 0x80000000..0xFFFFFFFF, decimal, + ** and we have no unsigned type specifying suffix, emit a warning, + ** because the constant is of type unsigned long while the user + ** might expect a signed integer constant, especially if there is + ** a preceding unary op or when it is used in constant calculation. + */ + if (WarnTypes & IT_ULONG) { + Warning ("Integer constant is unsigned long"); + } } /* Now set the type string to the smallest type in types */ From 727040d1ac77bff60628b87256c7bb8054fd4c02 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 12 Jul 2020 12:18:55 +0800 Subject: [PATCH 043/806] Comment fix. --- src/cc65/scanner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 91abd332d..5040cdf08 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -561,7 +561,7 @@ static void NumericConst (void) Types = IT_INT | IT_LONG | IT_ULONG; WarnTypes = IT_LONG | IT_ULONG; } else { - /* Binary, octal or hex constants can be of any type */ + /* Binary, octal and hex constants can be of any type */ Types = IT_INT | IT_UINT | IT_LONG | IT_ULONG; } } From 04cc463452de739262b91ef1fa4757ad249d6762 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 12 Jul 2020 22:19:38 +0200 Subject: [PATCH 044/806] Implemented some CONIO peek functions. Please refer to https://github.com/cc65/cc65/pull/532 for background info. I wrote in https://sourceforge.net/p/cc65/mailman/message/35873183/ === cputs() wraps to the next line if the strings is too long to fit in the current line. I don't know if it's worth the effort to allow cpeeks() to continue reading from the next line. I'd like to discuss this aspect with the actual implementers. === This is still as unclear today as it was when I wrote the above. Therefore this change just doesn't add cpeeks() at all. Since https://github.com/cc65/cc65/commit/f8c6c58373426551eed096dcfab4eab137fc0430 the Apple II CONIO implementation doesn't "need" revers() anymore - meaning that (nearly) every possible value can be placed in VRAM with a straight cputc() (without the need for a previous revers(1)). The implementation of cpeekc() leverages that cputc() ability by always returning the value that can be fed into cputc() without a previous revers(1). Accordingly, cpeekrevers() always returns 0. So after the sequence revers(1); cputc(x); a cpeekc() will return a value different from x! However, I don't see this behavior braking the cpeekc() contract. I see the cpeekc() contract being defined by the sequence textcolor(cpeekcolor()); revers(cpeekrevers()); cputc(cpeekc()); placing the very same value in VRAM that there was before. And that contract is fulfilled. --- include/apple2.h | 2 ++ include/conio.h | 11 +++++++---- libsrc/apple2/cpeekc.s | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 libsrc/apple2/cpeekc.s diff --git a/include/apple2.h b/include/apple2.h index dbed3364d..57d7086ce 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -207,6 +207,8 @@ void rebootafterexit (void); #define _textcolor(color) COLOR_WHITE #define _bgcolor(color) COLOR_BLACK #define _bordercolor(color) COLOR_BLACK +#define _cpeekcolor() COLOR_WHITE +#define _cpeekrevers() 0 diff --git a/include/conio.h b/include/conio.h index 72421af86..bac20e3c5 100644 --- a/include/conio.h +++ b/include/conio.h @@ -201,16 +201,19 @@ void __fastcall__ cputhex16 (unsigned val); */ #ifdef _textcolor -# define textcolor(x) _textcolor(x) +# define textcolor(color) _textcolor(color) #endif #ifdef _bgcolor -# define bgcolor(x) _bgcolor(x) +# define bgcolor(color) _bgcolor(color) #endif #ifdef _bordercolor -# define bordercolor(x) _bordercolor(x) +# define bordercolor(color) _bordercolor(color) #endif #ifdef _cpeekcolor -# define cpeekcolor(x) _cpeekcolor(x) +# define cpeekcolor() _cpeekcolor() +#endif +#ifdef _cpeekrevers +# define cpeekrevers() _cpeekrevers() #endif diff --git a/libsrc/apple2/cpeekc.s b/libsrc/apple2/cpeekc.s new file mode 100644 index 000000000..a547f7867 --- /dev/null +++ b/libsrc/apple2/cpeekc.s @@ -0,0 +1,28 @@ +; +; 2020-07-12, Oliver Schmidt +; +; char cpeekc (void); +; + + .export _cpeekc + + .include "apple2.inc" + +_cpeekc: + ldy CH + .ifdef __APPLE2ENH__ + bit RD80VID ; In 80 column mode? + bpl peek ; No, just go ahead + tya + lsr ; Div by 2 + tay + bcs peek ; Odd cols are in main memory + bit HISCR ; Assume SET80COL + .endif +peek: lda (BASL),Y ; Get character + .ifdef __APPLE2ENH__ + bit LOWSCR ; Doesn't hurt in 40 column mode + .endif + eor #$80 ; Invert high bit + ldx #$00 + rts From bcb8b4990726e9add92ae54d70204e49977f50e5 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 12 Jul 2020 23:11:43 +0200 Subject: [PATCH 045/806] Removed executable bit. --- libsrc/c128/emd/c128-efnram.s | 0 libsrc/c128/emd/c128-ifnram.s | 0 libsrc/c64/acc_c65_speed.s | 0 libsrc/c64/acc_chameleon_speed.s | 0 libsrc/c64/acc_detect_c65.s | 0 libsrc/c64/acc_detect_chameleon.s | 0 libsrc/c64/acc_detect_turbomaster.s | 0 libsrc/c64/acc_turbomaster_speed.s | 0 libsrc/cbm510/pencalib.c | 0 libsrc/vic20/emd/vic20-georam.s | 0 samples/supervisionhello.c | 0 test/ref/cf.in | 0 test/ref/yacc.in | 0 test/val/add3a.c | 0 test/val/casttochar.c | 0 test/val/cc65141002.c | 0 test/val/cc65141011.c | 0 test/val/cc65141022.c | 0 testcode/lib/accelerator/chameleon-test.c | 0 testcode/lib/accelerator/turbomaster-test.c | 0 20 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 libsrc/c128/emd/c128-efnram.s mode change 100755 => 100644 libsrc/c128/emd/c128-ifnram.s mode change 100755 => 100644 libsrc/c64/acc_c65_speed.s mode change 100755 => 100644 libsrc/c64/acc_chameleon_speed.s mode change 100755 => 100644 libsrc/c64/acc_detect_c65.s mode change 100755 => 100644 libsrc/c64/acc_detect_chameleon.s mode change 100755 => 100644 libsrc/c64/acc_detect_turbomaster.s mode change 100755 => 100644 libsrc/c64/acc_turbomaster_speed.s mode change 100755 => 100644 libsrc/cbm510/pencalib.c mode change 100755 => 100644 libsrc/vic20/emd/vic20-georam.s mode change 100755 => 100644 samples/supervisionhello.c mode change 100755 => 100644 test/ref/cf.in mode change 100755 => 100644 test/ref/yacc.in mode change 100755 => 100644 test/val/add3a.c mode change 100755 => 100644 test/val/casttochar.c mode change 100755 => 100644 test/val/cc65141002.c mode change 100755 => 100644 test/val/cc65141011.c mode change 100755 => 100644 test/val/cc65141022.c mode change 100755 => 100644 testcode/lib/accelerator/chameleon-test.c mode change 100755 => 100644 testcode/lib/accelerator/turbomaster-test.c diff --git a/libsrc/c128/emd/c128-efnram.s b/libsrc/c128/emd/c128-efnram.s old mode 100755 new mode 100644 diff --git a/libsrc/c128/emd/c128-ifnram.s b/libsrc/c128/emd/c128-ifnram.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_c65_speed.s b/libsrc/c64/acc_c65_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_chameleon_speed.s b/libsrc/c64/acc_chameleon_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_c65.s b/libsrc/c64/acc_detect_c65.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_chameleon.s b/libsrc/c64/acc_detect_chameleon.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_detect_turbomaster.s b/libsrc/c64/acc_detect_turbomaster.s old mode 100755 new mode 100644 diff --git a/libsrc/c64/acc_turbomaster_speed.s b/libsrc/c64/acc_turbomaster_speed.s old mode 100755 new mode 100644 diff --git a/libsrc/cbm510/pencalib.c b/libsrc/cbm510/pencalib.c old mode 100755 new mode 100644 diff --git a/libsrc/vic20/emd/vic20-georam.s b/libsrc/vic20/emd/vic20-georam.s old mode 100755 new mode 100644 diff --git a/samples/supervisionhello.c b/samples/supervisionhello.c old mode 100755 new mode 100644 diff --git a/test/ref/cf.in b/test/ref/cf.in old mode 100755 new mode 100644 diff --git a/test/ref/yacc.in b/test/ref/yacc.in old mode 100755 new mode 100644 diff --git a/test/val/add3a.c b/test/val/add3a.c old mode 100755 new mode 100644 diff --git a/test/val/casttochar.c b/test/val/casttochar.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141002.c b/test/val/cc65141002.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141011.c b/test/val/cc65141011.c old mode 100755 new mode 100644 diff --git a/test/val/cc65141022.c b/test/val/cc65141022.c old mode 100755 new mode 100644 diff --git a/testcode/lib/accelerator/chameleon-test.c b/testcode/lib/accelerator/chameleon-test.c old mode 100755 new mode 100644 diff --git a/testcode/lib/accelerator/turbomaster-test.c b/testcode/lib/accelerator/turbomaster-test.c old mode 100755 new mode 100644 From a43cac580ed70559b509227a11556deb6db29b5a Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Wed, 8 Jul 2020 21:37:39 +0300 Subject: [PATCH 046/806] Tiny optimization --- libsrc/vic20/tgi/vic20-hi.s | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s index 8dfa46d6b..506de92f7 100644 --- a/libsrc/vic20/tgi/vic20-hi.s +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -261,16 +261,15 @@ PATTERN_SOLID: sta tmp2+1 inx ; (ldx #$00) stx ERROR ; Set to TGI_ERR_OK + clc @NEXT_ROW: ldy #$00 txa - clc adc #$10 @NEXT_COLUMN: sta (tmp2),y - clc adc #ROWS iny cpy #COLS @@ -282,10 +281,8 @@ PATTERN_SOLID: clc adc #COLS sta tmp2 - bcc @L1 - inc tmp2+1 -@L1: inx + inx cpx #ROWS bne @NEXT_ROW From 693e3a710970627baf4ff30bdc6da19b448ab3ad Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:12:04 +0200 Subject: [PATCH 047/806] added testcase for issue #1048 --- test/misc/bug1048.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test/misc/bug1048.c diff --git a/test/misc/bug1048.c b/test/misc/bug1048.c new file mode 100644 index 000000000..d022474d6 --- /dev/null +++ b/test/misc/bug1048.c @@ -0,0 +1,15 @@ +/* bug #1048: The following code has two errors: a redeclared enum type and an + undeclared enum type: */ + +#include + +// this should NOT compile - but with cc65 it does +enum e { x }; +enum e { y }; + +int f() { return sizeof(enum undeclared); } + +int main(void) +{ + return EXIT_SUCCESS; +} From f5d99106e6610f9cb6bd699aa587c7321ef47074 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:12:30 +0200 Subject: [PATCH 048/806] added testcase for issue #1075 --- test/misc/bug1075.c | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/misc/bug1075.c diff --git a/test/misc/bug1075.c b/test/misc/bug1075.c new file mode 100644 index 000000000..be4cd75f4 --- /dev/null +++ b/test/misc/bug1075.c @@ -0,0 +1,10 @@ +/* bug #1075 Internal compiler error */ + +long rhs; + +int main(void) +{ + /* the whole lhs is errorneously treated as an absolute address (integer + constant) neglecting its dereference */ + return *(char *)0xD77C + rhs; +} From f8873c2508203f4b86954d49e25fffc0fcea51c1 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:12:47 +0200 Subject: [PATCH 049/806] added testcase for issue #250 --- test/misc/bug250.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/misc/bug250.c diff --git a/test/misc/bug250.c b/test/misc/bug250.c new file mode 100644 index 000000000..60f3c633d --- /dev/null +++ b/test/misc/bug250.c @@ -0,0 +1,13 @@ +/* bug #250 - Array size compile-time optimization stops halfway */ + +#include + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +unsigned char c[2*4]; +unsigned char b[2*LZO_MAX(8,sizeof(int))]; // this will not compile + +int main(void) +{ + /* FIXME: add some runtime check */ + return EXIT_SUCCESS; +} From bec140143ba6fc043b1901024ce03bf931af517c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:13:06 +0200 Subject: [PATCH 050/806] added testcase for issue #760 --- test/misc/bug264.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/misc/bug264.c diff --git a/test/misc/bug264.c b/test/misc/bug264.c new file mode 100644 index 000000000..a114939a4 --- /dev/null +++ b/test/misc/bug264.c @@ -0,0 +1,62 @@ +/* bug #264 - cc65 fails to warn about a function returning struct */ + +#include +#include +#include + +typedef uint16_t u16; +typedef uint8_t u8; + +typedef struct { + u16 quot; + u16 rem; +} udiv_t; + +udiv_t div3(u16 in) { + + udiv_t u; + u16 q = 0; + + while (in >= 300) { + in -= 300; + q += 100; + } + + while (in >= 30) { + in -= 30; + q += 10; + } + + while (in >= 3) { + in -= 3; + ++q; + } + + u.quot = q; + u.rem = in; + + return u; +} + +int res = 0; + +int main(void) { + + u16 i; + div_t d; + udiv_t u; + + for (i = 1024; i; i--) { + d = div(i, 3); + u = div3(i); + + if (d.quot != u.quot || d.rem != u.rem) { + printf("Mismatch at %u/3, div %u %u, div3 %u %u\n", i, + d.quot, d.rem, u.quot, u.rem); + res++; + } + } + + return res; +} + From 36ff37214993310a832ae75953347b100ef647fe Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:13:22 +0200 Subject: [PATCH 051/806] added testcase for issue #760 --- test/misc/bug760.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/misc/bug760.c diff --git a/test/misc/bug760.c b/test/misc/bug760.c new file mode 100644 index 000000000..dc8573b6d --- /dev/null +++ b/test/misc/bug760.c @@ -0,0 +1,12 @@ +/* bug#760 - Error when using macros as pragma arguments */ + +#include + +#define BANK "PRG0" + +#pragma rodata-name(push, BANK) + +int main(void) +{ + return EXIT_SUCCESS; +} From 5597b83d041301554726d329548644b145a80cd8 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:13:38 +0200 Subject: [PATCH 052/806] added testcase for issue #975 --- test/misc/bug975.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/misc/bug975.c diff --git a/test/misc/bug975.c b/test/misc/bug975.c new file mode 100644 index 000000000..458524a5a --- /dev/null +++ b/test/misc/bug975.c @@ -0,0 +1,18 @@ +/* bug #975 - Forward array reference fails to compile */ + +#include + +// this works +static const unsigned char array2[3]; +int test2(void) { + return array2[0]; +} +static const unsigned char array2[] = { 0, 1, 2 }; + +// this should work, but does not compile +static const unsigned char array[]; +int main() { + if (test2() != 0) return EXIT_FAILURE; + return array[0]; +} +static const unsigned char array[] = { 0, 1, 2 }; From 390f9720149f13ebfbb5947a8fa2e5d700bcee62 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 15:16:26 +0200 Subject: [PATCH 053/806] updated Makefile with exception rules for the added tests --- test/misc/Makefile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/misc/Makefile b/test/misc/Makefile index cda0c789a..898ea1b2b 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -63,6 +63,36 @@ $(DIFF): ../bdiff.c | $(WORKDIR) define PRG_template +# should compile, but gives an error +$(WORKDIR)/bug975.$1.$2.prg: bug975.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug975.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug250.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug760.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# this should fail to compile, because cc65 does not support returning structs +$(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug264.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# this should fail to compile, because there are errors in the code +$(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1048.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# internal compiler error +$(WORKDIR)/bug1075.$1.$2.prg: bug1075.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1075.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) From e758110f6135bd10cf4266fa5c4ec5517d306f7f Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 17:00:17 +0200 Subject: [PATCH 054/806] added testcode to check petscii char mapping, related to issue #988 --- testcode/lib/cbm/Makefile | 39 ++++++++++++++++++ testcode/lib/cbm/petscii.c | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 testcode/lib/cbm/Makefile create mode 100644 testcode/lib/cbm/petscii.c diff --git a/testcode/lib/cbm/Makefile b/testcode/lib/cbm/Makefile new file mode 100644 index 000000000..5217f0cc6 --- /dev/null +++ b/testcode/lib/cbm/Makefile @@ -0,0 +1,39 @@ +# Run 'make SYS='; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + +all: petscii.prg + +petscii.prg: petscii.c + $(CL) -t $(SYS) -O -o petscii.prg petscii.c + +clean: + $(DEL) petscii.prg diff --git a/testcode/lib/cbm/petscii.c b/testcode/lib/cbm/petscii.c new file mode 100644 index 000000000..60a12b18b --- /dev/null +++ b/testcode/lib/cbm/petscii.c @@ -0,0 +1,82 @@ + +/* this program prints all available "petscii" characters to screen, once + using putchar (which wraps to kernal i/o) and once using conio (which + will do direct videoram access). after that the produced screencodes + are compared (they should match) (related to issue #988 */ + +#include +#include +#include +#include + +#if defined(__C64__) +#define VRAMPEEK(x) (*(char*)(0x0400 + (x))) +#define CRAMPOKE(x, y) *(char*)(0xd800 + (x)) = (y) +#else +#error "this target is not supported yet" +#endif + +unsigned char x, y, c; +unsigned char c1, c2; +unsigned char *p1, *p2; + +int err = 0; + +int main(void) +{ + clrscr(); + bgcolor(COLOR_BLACK); + bordercolor(COLOR_BLACK); + + /* output all characters using putchar() */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + /* skip the codes that are unprintable control codes */ + if (!((c < 32) || ((c > 127) && (c < 160)))) { + gotoxy(x, y); putchar(c); + } + c++; + } + } + + /* output all characters using conio */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + /* skip the codes that are unprintable control codes */ + if (!((c < 32) || ((c > 127) && (c < 160)))) { + gotoxy(x + 20, y); cputc(c); + } + c++; + } + } + + /* compare the two outputs */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + c1 = VRAMPEEK((y * 40) + x); + c2 = VRAMPEEK((y * 40) + x + 0x14); + if (c1 == c2) { + c = COLOR_GREEN; + } else { + c = COLOR_RED; + err = 1; + } + CRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x + 0x14, c); + } + } + + /* show the result */ + textcolor(COLOR_WHITE); + gotoxy(0, 17); + if (err) { + bordercolor(COLOR_RED); + cputs("errors detected"); + } else { + bordercolor(COLOR_GREEN); + cputs("all fine"); + } + return 0; +} From d940c9aa8567f8fc208f770d5a9c95285fefe85b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 17:04:22 +0200 Subject: [PATCH 055/806] added a bit more precise description --- test/readme.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/readme.txt b/test/readme.txt index 2d4413f45..57ebfdd23 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -9,13 +9,16 @@ compiler. /err - contains tests that MUST NOT compile -/todo - these tests fail due to open compiler issues +/todo - these tests fail due to open compiler issues. + when an issue was fixed, the test should get moved to /var /asm - contains the assembler regression tests /dasm - contains the disassembler regression tests /misc - a few tests that need special care of some sort + tests that (incorrectly) fail to compile and other tests that fail and + do NOT return an exit code are collected here. to run the tests use "make" in this (top) directory, the makefile should exit From 882194c22125d435fcdf46520e3da080d6ad40b5 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 21:25:13 +0200 Subject: [PATCH 056/806] move a bunch of tests from testcode/lib to test/val (and a failing one to test/todo) --- {testcode/lib => test/todo}/sprintf-test.c | 0 {testcode/lib => test/val}/atoi-test.c | 0 {testcode/lib => test/val}/shift-test.c | 0 {testcode/lib => test/val}/signal-test.c | 0 {testcode/lib => test/val}/snprintf-test.c | 0 {testcode/lib => test/val}/strncmp-test.c | 0 {testcode/lib => test/val}/strnicmp-test.c | 0 {testcode/lib => test/val}/strpbrk-test.c | 0 {testcode/lib => test/val}/strtol-test.c | 0 {testcode/lib => test/val}/strtoul-test.c | 0 {testcode/lib => test/val}/time-test.c | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename {testcode/lib => test/todo}/sprintf-test.c (100%) rename {testcode/lib => test/val}/atoi-test.c (100%) rename {testcode/lib => test/val}/shift-test.c (100%) rename {testcode/lib => test/val}/signal-test.c (100%) rename {testcode/lib => test/val}/snprintf-test.c (100%) rename {testcode/lib => test/val}/strncmp-test.c (100%) rename {testcode/lib => test/val}/strnicmp-test.c (100%) rename {testcode/lib => test/val}/strpbrk-test.c (100%) rename {testcode/lib => test/val}/strtol-test.c (100%) rename {testcode/lib => test/val}/strtoul-test.c (100%) rename {testcode/lib => test/val}/time-test.c (100%) diff --git a/testcode/lib/sprintf-test.c b/test/todo/sprintf-test.c similarity index 100% rename from testcode/lib/sprintf-test.c rename to test/todo/sprintf-test.c diff --git a/testcode/lib/atoi-test.c b/test/val/atoi-test.c similarity index 100% rename from testcode/lib/atoi-test.c rename to test/val/atoi-test.c diff --git a/testcode/lib/shift-test.c b/test/val/shift-test.c similarity index 100% rename from testcode/lib/shift-test.c rename to test/val/shift-test.c diff --git a/testcode/lib/signal-test.c b/test/val/signal-test.c similarity index 100% rename from testcode/lib/signal-test.c rename to test/val/signal-test.c diff --git a/testcode/lib/snprintf-test.c b/test/val/snprintf-test.c similarity index 100% rename from testcode/lib/snprintf-test.c rename to test/val/snprintf-test.c diff --git a/testcode/lib/strncmp-test.c b/test/val/strncmp-test.c similarity index 100% rename from testcode/lib/strncmp-test.c rename to test/val/strncmp-test.c diff --git a/testcode/lib/strnicmp-test.c b/test/val/strnicmp-test.c similarity index 100% rename from testcode/lib/strnicmp-test.c rename to test/val/strnicmp-test.c diff --git a/testcode/lib/strpbrk-test.c b/test/val/strpbrk-test.c similarity index 100% rename from testcode/lib/strpbrk-test.c rename to test/val/strpbrk-test.c diff --git a/testcode/lib/strtol-test.c b/test/val/strtol-test.c similarity index 100% rename from testcode/lib/strtol-test.c rename to test/val/strtol-test.c diff --git a/testcode/lib/strtoul-test.c b/test/val/strtoul-test.c similarity index 100% rename from testcode/lib/strtoul-test.c rename to test/val/strtoul-test.c diff --git a/testcode/lib/time-test.c b/test/val/time-test.c similarity index 100% rename from testcode/lib/time-test.c rename to test/val/time-test.c From 5ad365c5df9bf743f5672516358d2bf41cdf196c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 21:26:07 +0200 Subject: [PATCH 057/806] some tweaks to the moved tests to make them more suitable for automatic testing --- test/todo/sprintf-test.c | 10 ++--- test/val/signal-test.c | 13 ++++-- test/val/snprintf-test.c | 5 ++- test/val/strncmp-test.c | 14 ++++--- test/val/strnicmp-test.c | 85 ++++++++++++++++++++++------------------ test/val/strpbrk-test.c | 12 +++++- test/val/time-test.c | 33 +++++++++++----- 7 files changed, 108 insertions(+), 64 deletions(-) diff --git a/test/todo/sprintf-test.c b/test/todo/sprintf-test.c index 55354be20..bd5de44b4 100644 --- a/test/todo/sprintf-test.c +++ b/test/todo/sprintf-test.c @@ -1,13 +1,13 @@ #include #include #include -#if defined(__CC65__) +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) #include #endif /* Flag to #ifdef the tests out that crash the old implementation */ -/*#define NOCRASH 1 */ +#define NOCRASH 1 @@ -532,9 +532,9 @@ int main (void) /* Alternative form with zero value */ #ifndef NOCRASH OneTest (__LINE__, "0", 1, "%#o", 0U); +#endif OneTest (__LINE__, "0", 1, "%#x", 0U); OneTest (__LINE__, "0", 1, "%#X", 0U); -#endif /* Alternative form with zero value and precision 1 */ OneTest (__LINE__, "0", 1, "%#.1o", 0U); @@ -570,8 +570,8 @@ int main (void) } /* Wait for a key so we can read the result */ -#if defined(__CC65__) +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc (); #endif - return 0; + return Failures; } diff --git a/test/val/signal-test.c b/test/val/signal-test.c index 4e34a281d..31dd8ed5c 100644 --- a/test/val/signal-test.c +++ b/test/val/signal-test.c @@ -1,12 +1,16 @@ #include +#include #include #include #include +int signalcounter = 0; + void __fastcall__ sighandler (int sig) { printf ("Got signal #%d\n", sig); + signalcounter++; } @@ -15,15 +19,18 @@ int main (void) { if (signal (SIGSEGV, sighandler) == SIG_ERR) { printf ("signal failure %d: %s\n", errno, strerror (errno)); - return 1; + return EXIT_FAILURE; } printf ("About to raise SIGSEGV...\n"); raise (SIGSEGV); printf ("Back from signal handler\n"); printf ("About to raise SIGILL...\n"); raise (SIGILL); - printf ("Back from signal handler\n"); - return 0; + printf ("Back from signal handler, signalcounter = %d\n", signalcounter); + if (signalcounter != 1) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/test/val/snprintf-test.c b/test/val/snprintf-test.c index d3af47d78..e3a60d99b 100644 --- a/test/val/snprintf-test.c +++ b/test/val/snprintf-test.c @@ -137,8 +137,9 @@ unsigned char main(void) } else { printf("There were no"); } - printf(" failures.\nTap a key. "); + printf(" failures.\nTap a key.\n"); +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc(); - +#endif return failures; } diff --git a/test/val/strncmp-test.c b/test/val/strncmp-test.c index b15565036..7dbbb4b8c 100644 --- a/test/val/strncmp-test.c +++ b/test/val/strncmp-test.c @@ -10,15 +10,19 @@ static const char S2[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0', 'B' }; - - +int fails = 0; int main (void) { char I; + int ret; for (I = 0; I < 20; ++I) { - printf ("%02d: %d\n", I, strncmp (S1, S2, I)); + ret = strncmp (S1, S2, I); + printf ("%02d: %d\n", I, ret); + if ((ret != 0) && (I < 7)) { + fails++; + } } - return 0; + printf("fails: %d\n", fails); + return fails; } - diff --git a/test/val/strnicmp-test.c b/test/val/strnicmp-test.c index b2d942a97..6376a39bb 100644 --- a/test/val/strnicmp-test.c +++ b/test/val/strnicmp-test.c @@ -3,70 +3,79 @@ #include #include +int fails = 0; + static int do_test(const char *s1, const char *s2, size_t n) { printf("strnicmp(\"%s\", \"%s\", %d): ", s1, s2, (int)n); return strncasecmp(s1, s2, n); } +static void printresult(int ret) +{ + if (ret) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + +static void printresultgt(int ret) +{ + if (ret >= 0) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + +static void printresultlt(int ret) +{ + if (ret <= 0) { + printf("fail (%d)\n", ret); + fails++; + } else { + printf("OK (%d)\n", ret); + } +} + int main(void) { int ret; ret = do_test("Wurzl", "wURZL", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzl", "wURZL", 6); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzl", "wURZL", 10); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("Wurzla", "wURZLB", 10); - if (ret >= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultgt(ret); ret = do_test("Wurzla", "wURZLb", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresult(ret); ret = do_test("BLI", "bla", 5); - if (ret <= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultlt(ret); ret = do_test("", "bla", 5); - if (ret >= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultgt(ret); ret = do_test("BLI", "", 5); - if (ret <= 0) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); + printresultlt(ret); ret = do_test("", "", 5); - if (ret) - printf("fail (%d)\n", ret); - else - printf("OK (%d)\n", ret); - + printresult(ret); + + printf("fails: %d\n", fails); + +#if defined(__CC65__) && !defined(__SIM6502__) && !defined(__SIM65C02__) cgetc(); - return 0; +#endif + return fails; } diff --git a/test/val/strpbrk-test.c b/test/val/strpbrk-test.c index 25c2c2fbe..6a688732d 100644 --- a/test/val/strpbrk-test.c +++ b/test/val/strpbrk-test.c @@ -3,24 +3,32 @@ static const char fox[] = "The quick brown fox jumped over the lazy dogs."; -void main (void) +int fails = 0; + +int main (void) { printf ("Testing strpbrk():\n"); if (strpbrk (fox, "qwerty") != &fox[2]) { printf ("\nThe first 'e' wasn't found.\n"); + fails++; } if (strpbrk (fox, "QWERTY") != &fox[0]) { printf ("The 'T' wasn't found.\n"); + fails++; } if (strpbrk (fox, "asdfg") != &fox[16]) { printf ("The 'f' wasn't found.\n"); + fails++; } if (strpbrk (fox, "nxv,zmb") != &fox[10]) { printf ("The 'b' wasn't found.\n"); + fails++; } if (strpbrk (fox, "!@#$%^&*()-+=[];:',/?<>.") != &fox[45]) { printf ("The '.' wasn't found.\n"); + fails++; } - printf ("\nFinished.\n"); + printf ("\nFinished. fails = %d\n", fails); + return fails; } diff --git a/test/val/time-test.c b/test/val/time-test.c index 99d16be01..304238fa0 100644 --- a/test/val/time-test.c +++ b/test/val/time-test.c @@ -1,7 +1,11 @@ #include +#include #include +#define EXPECTSTR "3DD173D1 - Tue Nov 12 21:34:09 2002\n" +char result[0x100]; +int fails = 0; int main (void) { @@ -23,16 +27,27 @@ int main (void) t = mktime (&tm); printf ("Test passes if the following lines are\n" "all identical:\n"); - printf ("3DD173D1 - Tue Nov 12 21:34:09 2002\n"); - printf ("%08lX - %s", t, asctime (&tm)); - printf ("%08lX - %s", t, asctime (gmtime (&t))); + printf (EXPECTSTR); + + sprintf (result, "%08lX - %s", t, asctime (&tm)); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + sprintf (result, "%08lX - %s", t, asctime (gmtime (&t))); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + strftime (buf, sizeof (buf), "%c", &tm); - printf ("%08lX - %s\n", t, buf); + sprintf (result, "%08lX - %s\n", t, buf); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + strftime (buf, sizeof (buf), "%a %b %d %H:%M:%S %Y", &tm); - printf ("%08lX - %s\n", t, buf); + sprintf (result, "%08lX - %s\n", t, buf); + printf (result); + if (strcmp(result, EXPECTSTR) != 0) { fails++; } + + printf("fails: %d\n", fails); - return 0; + return fails; } - - - From a4f1e37f0c46f1f56c0772d1da85a2c7c6f19b1b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 13 Jul 2020 21:26:31 +0200 Subject: [PATCH 058/806] increase the maximum amount of cycles, else the shift test will fail --- test/val/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/val/Makefile b/test/val/Makefile index 417c0b6c8..be63cd8da 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -24,7 +24,7 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -SIM65FLAGS = -x 200000000 +SIM65FLAGS = -x 5000000000 CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) From 1f2fdbd9b1c103bc57fe057255bdc780b9f51043 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 14 Jul 2020 14:22:29 +0200 Subject: [PATCH 059/806] implemented conio peek functions for PCE target --- libsrc/pce/cpeekc.s | 24 +++++++++++++++ libsrc/pce/cpeekcolor.s | 28 ++++++++++++++++++ libsrc/pce/cpeekrevers.s | 26 +++++++++++++++++ libsrc/pce/cpeeks.s | 63 ++++++++++++++++++++++++++++++++++++++++ libsrc/pce/cputc.s | 4 +-- 5 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 libsrc/pce/cpeekc.s create mode 100644 libsrc/pce/cpeekcolor.s create mode 100644 libsrc/pce/cpeekrevers.s create mode 100644 libsrc/pce/cpeeks.s diff --git a/libsrc/pce/cpeekc.s b/libsrc/pce/cpeekc.s new file mode 100644 index 000000000..e144f94ac --- /dev/null +++ b/libsrc/pce/cpeekc.s @@ -0,0 +1,24 @@ +; +; 2020-07-14, Groepaz +; +; char cpeekc (void); +; +; get character from current position, do NOT advance cursor + + .export _cpeekc + + .include "pce.inc" + .include "extzp.inc" + +_cpeekc: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_LO ; character + and #<~$80 ; remove reverse bit + ldx #0 + rts diff --git a/libsrc/pce/cpeekcolor.s b/libsrc/pce/cpeekcolor.s new file mode 100644 index 000000000..8b96d29d4 --- /dev/null +++ b/libsrc/pce/cpeekcolor.s @@ -0,0 +1,28 @@ +; +; 2020-07-14, Groepaz +; +; unsigned char cpeekcolor (void); +; +; get color from current position, do NOT advance cursor + + .export _cpeekcolor + + .include "pce.inc" + .include "extzp.inc" + +_cpeekcolor: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_HI + and #<~$02 + lsr a + lsr a + lsr a + lsr a + ldx #0 + rts diff --git a/libsrc/pce/cpeekrevers.s b/libsrc/pce/cpeekrevers.s new file mode 100644 index 000000000..3f208fd10 --- /dev/null +++ b/libsrc/pce/cpeekrevers.s @@ -0,0 +1,26 @@ +; +; 2020-07-14, Groepaz +; +; unsigned char cpeekrevers (void); +; +; get inverse flag from current position, do NOT advance cursor + + .export _cpeekrevers + + .include "pce.inc" + .include "extzp.inc" + +_cpeekrevers: + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + lda VDC_DATA_LO ; character (bit 7 is revers bit) + rol a + rol a + and #1 + ldx #0 + rts diff --git a/libsrc/pce/cpeeks.s b/libsrc/pce/cpeeks.s new file mode 100644 index 000000000..fe5e28687 --- /dev/null +++ b/libsrc/pce/cpeeks.s @@ -0,0 +1,63 @@ +; +; 2020-07-14, Groepaz +; +; void cpeeks (char* s, unsigned length); +; +; get string from current position, do NOT advance cursor + + .export _cpeeks + + .import popax + .importzp ptr1, ptr2, tmp1, tmp2 + + .macpack generic + + .include "pce.inc" + .include "extzp.inc" + +_cpeeks: + eor #<$FFFF ; counting a word upward is faster + sta ptr2 ; so, we use -(length + 1) + txa + eor #>$FFFF + sta ptr2+1 + + st0 #VDC_MARR ; Memory-Address Read + ldy SCREEN_PTR + ldx SCREEN_PTR+1 + sty VDC_DATA_LO + stx VDC_DATA_HI + + st0 #VDC_VRR ; VRAM Read Register + + jsr popax + sta tmp1 ; (will be a .Y index) + stx ptr1+1 + + ldx #<$0000 + stx ptr1 + beq L2 ; branch always + +L3: ldy tmp2 + lda VDC_DATA_LO ; get character + bit VDC_DATA_HI ; we need to "read" the highbyte to advance the address + iny + sty tmp2 + and #<~$80 ; remove reverse bit + + ldy tmp1 + sta (ptr1),y + iny + bne L1 + inc ptr1+1 +L1: sty tmp1 + +L2: inc ptr2 ; count length + bne L3 + inc ptr2+1 + bne L3 + + txa ; terminate the string + ldy tmp1 + sta (ptr1),y + rts diff --git a/libsrc/pce/cputc.s b/libsrc/pce/cputc.s index 04d901423..626d3ef8e 100644 --- a/libsrc/pce/cputc.s +++ b/libsrc/pce/cputc.s @@ -68,10 +68,10 @@ putchar: sty VDC_DATA_LO stx VDC_DATA_HI - st0 #VDC_VWR + st0 #VDC_VWR ; VRAM Write Register sta VDC_DATA_LO ; character - lda CHARCOLOR ; pallette number + lda CHARCOLOR ; palette number asl a asl a asl a From f99a44d8e1ab44c6a20a4794b1b2adcafe154655 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 14 Jul 2020 14:24:19 +0200 Subject: [PATCH 060/806] added rudimentary testing for the peek functions --- testcode/lib/pce/conio.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/testcode/lib/pce/conio.c b/testcode/lib/pce/conio.c index 858f01918..55f828f26 100644 --- a/testcode/lib/pce/conio.c +++ b/testcode/lib/pce/conio.c @@ -7,6 +7,10 @@ static int datavar = 10; +static char hex[16] = { "0123456789abcdef" }; +static char charbuf[0x20]; +static char colbuf[0x20]; + void main(void) { int stackvar = 42; @@ -21,11 +25,27 @@ void main(void) screensize(&xsize, &ysize); cputs("hello world"); + gotoxy(0,0); + cpeeks(charbuf, 11); + gotoxy(12,0); + cputs(charbuf); + cputsxy(0, 2, "colors:" ); for (i = 0; i < 16; ++i) { textcolor(i); - cputc('X'); + cputc(hex[i]); } + for (i = 0; i < 16; ++i) { + gotoxy(7 + i, 2); + charbuf[i] = cpeekc(); + colbuf[i] = cpeekcolor(); + } + gotoxy(25, 2); + for (i = 0; i < 16; ++i) { + textcolor(colbuf[i]); + cputc(charbuf[i]); + } + textcolor(1); gotoxy(0,4); @@ -115,6 +135,17 @@ void main(void) cputs(" revers"); revers(0); + for (i = 0; i < 9; ++i) { + gotoxy(xsize - 10 + i, 3); + charbuf[i] = cpeekc(); + colbuf[i] = cpeekrevers(); + } + gotoxy(xsize - 10, 4); + for (i = 0; i < 9; ++i) { + revers(colbuf[i]); + cputc(charbuf[i]); + } + if ((n & 0x1f) == 0x00) { nn = p[15]; ((char*)memmove(p + 1, p, 15))[-1] = nn; From 3558c0796dc01f3659b797d1639107ebcad40e69 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 14 Jul 2020 15:00:43 +0200 Subject: [PATCH 061/806] added a second test that checks all available characters (including inverse) --- testcode/lib/cbm/petscii.c | 80 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/testcode/lib/cbm/petscii.c b/testcode/lib/cbm/petscii.c index 60a12b18b..7d5ba9528 100644 --- a/testcode/lib/cbm/petscii.c +++ b/testcode/lib/cbm/petscii.c @@ -11,6 +11,8 @@ #if defined(__C64__) #define VRAMPEEK(x) (*(char*)(0x0400 + (x))) +#define VRAMPOKE(x, y) *(char*)(0x0400 + (x)) = (y) +#define CRAMPEEK(x) ((*(char*)(0xd800 + (x))) & 15) #define CRAMPOKE(x, y) *(char*)(0xd800 + (x)) = (y) #else #error "this target is not supported yet" @@ -78,5 +80,83 @@ int main(void) bordercolor(COLOR_GREEN); cputs("all fine"); } + cputs(" - press a key "); + cursor(1); + cgetc(); + cursor(0); + + clrscr(); + bordercolor(COLOR_BLACK); + + /* output all characters directly to the scree */ + c = 0; + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + VRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x, c & 15); + c++; + } + } + + /* read the characters with conio peek functions and output with conio */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + gotoxy(x, y); + c1 = cpeekc(); + c2 = cpeekrevers(); + c = cpeekcolor(); + + gotoxy(x + 0x14, y); + revers(c2); + textcolor(c); + cputc(c1); + } + } + + revers(0); + textcolor(COLOR_WHITE); + gotoxy(0, 17); + cputs("press a key to compare "); + cursor(1); + cgetc(); + cursor(0); + + /* compare the two outputs */ + for (y = 0; y < 16; y++) { + for (x = 0; x < 16; x++) { + c = COLOR_GREEN; + c1 = VRAMPEEK((y * 40) + x); + c2 = VRAMPEEK((y * 40) + x + 0x14); + if (c1 != c2) { + c = COLOR_RED; + err = 1; + } + c1 = CRAMPEEK((y * 40) + x); + c2 = CRAMPEEK((y * 40) + x + 0x14); + if (c1 != c2) { + c = COLOR_RED; + err = 1; + } + CRAMPOKE((y * 40) + x, c); + CRAMPOKE((y * 40) + x + 0x14, c); + } + } + + /* show the result */ + revers(0); + textcolor(COLOR_WHITE); + gotoxy(0, 17); + if (err) { + bordercolor(COLOR_RED); + cputs("errors detected"); + } else { + bordercolor(COLOR_GREEN); + cputs("all fine"); + } + cputs(" - press a key "); + cursor(1); + cgetc(); + cursor(0); + return 0; } From 9023e975df8b89384207366521e16979026cfffc Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 14 Jul 2020 16:06:21 -0400 Subject: [PATCH 062/806] Stopped the C128 mouse drivers from blocking certain keys such as '1', '2', and 'Q'. This extra fix is needed because the C128 keyboard scanner works a little differently than the C64 scanner works. Fixes #696. Fixes #853. --- libsrc/c128/mou/c128-1351.s | 4 ++-- libsrc/c128/mou/c128-inkwell.s | 6 +++--- libsrc/c128/mou/c128-joy.s | 4 ++-- libsrc/c128/mou/c128-pot.s | 4 ++-- libsrc/c128/mou/callback.inc | 11 ++++++----- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/libsrc/c128/mou/c128-1351.s b/libsrc/c128/mou/c128-1351.s index d06e942c4..79ccbe0de 100644 --- a/libsrc/c128/mou/c128-1351.s +++ b/libsrc/c128/mou/c128-1351.s @@ -4,7 +4,7 @@ ; ; 2009-09-26, Ullrich von Bassewitz ; 2014-04-26, Christian Groessler -; 2014-04-30, Greg King +; 2020-07-14, Greg King ; .include "zeropage.inc" @@ -377,7 +377,7 @@ IOCTL: lda # Date: Wed, 15 Jul 2020 00:17:11 +0200 Subject: [PATCH 063/806] added missing gotox/gotoy functions --- libsrc/pce/gotox.s | 14 ++++++++++++++ libsrc/pce/gotoy.s | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 libsrc/pce/gotox.s create mode 100644 libsrc/pce/gotoy.s diff --git a/libsrc/pce/gotox.s b/libsrc/pce/gotox.s new file mode 100644 index 000000000..fe8a23734 --- /dev/null +++ b/libsrc/pce/gotox.s @@ -0,0 +1,14 @@ +; +; void __fastcall__ gotox (unsigned char x); +; + + .export _gotox + + .import plot + + .include "pce.inc" + .include "extzp.inc" + +_gotox: + sta CURS_X ; Set X + jmp plot ; Set the cursor position diff --git a/libsrc/pce/gotoy.s b/libsrc/pce/gotoy.s new file mode 100644 index 000000000..9585b035b --- /dev/null +++ b/libsrc/pce/gotoy.s @@ -0,0 +1,14 @@ +; +; void __fastcall__ gotoy (unsigned char y); +; + + .export _gotoy + + .import plot + + .include "pce.inc" + .include "extzp.inc" + +_gotoy: + sta CURS_Y ; Set Y + jmp plot ; Set the cursor position From e2ec517aae26d48a983010464ad3e094649e98d8 Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Mon, 13 Jul 2020 12:09:01 +0300 Subject: [PATCH 064/806] Save another 3 bytes --- libsrc/vic20/tgi/vic20-hi.s | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s index 506de92f7..9d345947b 100644 --- a/libsrc/vic20/tgi/vic20-hi.s +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -270,16 +270,14 @@ PATTERN_SOLID: @NEXT_COLUMN: sta (tmp2),y - adc #ROWS iny - cpy #COLS - bne @NEXT_COLUMN + adc #ROWS + bcc @NEXT_COLUMN ; Step to next row on screen. lda tmp2 - clc - adc #COLS + adc #COLS-1 ; Carry is set sta tmp2 inx From de5678af5de94121bd75fec18ebb354e67fed4eb Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Mon, 13 Jul 2020 16:59:47 +0300 Subject: [PATCH 065/806] Added optimizations by dmsc --- libsrc/vic20/tgi/vic20-hi.s | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s index 9d345947b..dd540ce88 100644 --- a/libsrc/vic20/tgi/vic20-hi.s +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -136,6 +136,7 @@ BITCHUNK: .byte $FF, $7F, $3F, $1F, $0F, $07, $03, $01 CHARROM := $8000 ; Character ROM base address CBASE := $9400 ; Color memory base address SBASE := $1000 ; Screen memory base address +.assert (SBASE sta tmp2+1 - inx ; (ldx #$00) - stx ERROR ; Set to TGI_ERR_OK + iny ; (ldy #$00) + sty tmp2 + sty ERROR ; Set to TGI_ERR_OK clc + ldx #$10 @NEXT_ROW: - ldy #$00 txa - adc #$10 @NEXT_COLUMN: sta (tmp2),y @@ -276,12 +275,8 @@ PATTERN_SOLID: ; Step to next row on screen. - lda tmp2 - adc #COLS-1 ; Carry is set - sta tmp2 - inx - cpx #ROWS + cpx #ROWS+$10 bne @NEXT_ROW ; Set up VIC. From b3703de983306dbc4a0666cbe69aba246e4b7fc8 Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Tue, 14 Jul 2020 12:19:46 +0300 Subject: [PATCH 066/806] Code style fixes --- libsrc/vic20/tgi/vic20-hi.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s index dd540ce88..416eb58b0 100644 --- a/libsrc/vic20/tgi/vic20-hi.s +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -253,13 +253,13 @@ PATTERN_SOLID: ldy #$FF sty BITMASK + iny ; (ldy #$00) ; Make screen columns. + sty tmp2 lda #>SBASE sta tmp2+1 - iny ; (ldy #$00) - sty tmp2 sty ERROR ; Set to TGI_ERR_OK clc ldx #$10 From a02bec11e9eacf5a765e5ad2ad51793c15cf6475 Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Tue, 14 Jul 2020 21:15:57 +0300 Subject: [PATCH 067/806] Another code style fix --- libsrc/vic20/tgi/vic20-hi.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s index 416eb58b0..826a09c14 100644 --- a/libsrc/vic20/tgi/vic20-hi.s +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -254,13 +254,13 @@ PATTERN_SOLID: ldy #$FF sty BITMASK iny ; (ldy #$00) + sty ERROR ; Set to TGI_ERR_OK ; Make screen columns. sty tmp2 lda #>SBASE sta tmp2+1 - sty ERROR ; Set to TGI_ERR_OK clc ldx #$10 From ba0ef5938d267dc47ca6d30af18b596190b4f932 Mon Sep 17 00:00:00 2001 From: Greg King Date: Wed, 15 Jul 2020 04:55:38 -0400 Subject: [PATCH 068/806] Moved the font into a separate module in the library. The font can be replaced, at link-time, by a custom file. --- libsrc/pce/conio.s | 8 +++----- libsrc/pce/{vga.inc => vga.s} | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) rename libsrc/pce/{vga.inc => vga.s} (99%) diff --git a/libsrc/pce/conio.s b/libsrc/pce/conio.s index 8fefe01f6..ac306f193 100644 --- a/libsrc/pce/conio.s +++ b/libsrc/pce/conio.s @@ -3,6 +3,7 @@ .import vdc_init .import psg_init .import colors + .import _pce_font .importzp ptr1, tmp1 .include "pce.inc" @@ -53,8 +54,8 @@ load_font: ; rts ; (fall through) ; Point to the font data. -copy: lda #font +copy: lda #<_pce_font + ldx #>_pce_font sta ptr1 stx ptr1+1 @@ -84,6 +85,3 @@ fillloop: bne charloop ; next character rts - -.rodata -font: .include "vga.inc" diff --git a/libsrc/pce/vga.inc b/libsrc/pce/vga.s similarity index 99% rename from libsrc/pce/vga.inc rename to libsrc/pce/vga.s index 72c173146..630fbe8db 100644 --- a/libsrc/pce/vga.inc +++ b/libsrc/pce/vga.s @@ -1,10 +1,15 @@ ;---------------------------------------------------------------------------- ; VGA font for the PC-Engine conio implementation + .export _pce_font + ; The character tiles use only two colors from each pallette. Color zero ; comes from pallette zero; color one is different in each pallette. The ; color of a character is set by choosing one of the 16 pallettes. +.rodata + +_pce_font: .byte $00, $00, $00, $00, $00, $00, $00, $00 .byte %00000000 From 72fff0cfbc9a04f6df5de43eb065c8bc984d793f Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Wed, 15 Jul 2020 22:23:29 +0200 Subject: [PATCH 069/806] atari.h: fix definition of KEY_UP noticed by Stefan Wessels --- include/atari.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/atari.h b/include/atari.h index 582e23be6..1a00a4c67 100644 --- a/include/atari.h +++ b/include/atari.h @@ -209,7 +209,7 @@ #define KEY_CLEAR (KEY_LESSTHAN | KEY_SHIFT) #define KEY_INSERT (KEY_GREATERTHAN | KEY_SHIFT) -#define KEY_UP (KEY_UNDERLINE | KEY_CTRL) +#define KEY_UP (KEY_DASH | KEY_CTRL) #define KEY_DOWN (KEY_EQUALS | KEY_CTRL) #define KEY_LEFT (KEY_PLUS | KEY_CTRL) #define KEY_RIGHT (KEY_ASTERISK | KEY_CTRL) From 4296cbaf82d47bfe1c9d9b100dcdf534601f018b Mon Sep 17 00:00:00 2001 From: Greg King Date: Wed, 15 Jul 2020 17:11:38 -0400 Subject: [PATCH 070/806] Added a 320x200x256 TGI driver to the Commander X16 library. Made the mandelbrot sample program handle the X16's 256 colors. --- doc/cx16.sgml | 37 +++- include/cx16.h | 24 +- libsrc/cx16/tgi/cx320p1.s | 401 ++++++++++++++++++++++++++++++++++ libsrc/cx16/tgi_stat_stddrv.s | 7 +- libsrc/cx16/tgi_stddrv.s | 6 +- samples/mandelbrot.c | 12 +- 6 files changed, 465 insertions(+), 22 deletions(-) create mode 100644 libsrc/cx16/tgi/cx320p1.s diff --git a/doc/cx16.sgml b/doc/cx16.sgml index 20094d609..a73bb3c1e 100644 --- a/doc/cx16.sgml +++ b/doc/cx16.sgml @@ -16,9 +16,12 @@ compiler. Overview

-The Commander X16 is a modern small computer with firmware that is based on -the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips -(WDC65C22 VIA) that are like the ones in the VIC-20. +The Commander X16 is a modern small computer with firmware that is based partly +on the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips +(WDC65C22 VIA) that are like the ones in the VIC-20. It supports file storage +on Secure Digital cards. It allows two joysticks and a mouse. It has three +sound devices. It's VGA screen has twice the range of the C64 (similar to the +C128's 80-column screen), with 256 colors. This file contains an overview of the CX16 run-time system as it comes with the cc65 C compiler. It describes the memory layout, CX16-specific header files, @@ -108,7 +111,7 @@ cl65 -o file.prg -t cx16 -C cx16-asm.cfg source.s To generate code that loads to $A000: -cl65 -o file.prg -Wl -S,$A000 -t cX16 -C cX16-asm.cfg source.s +cl65 -o file.prg -Wl -S,$A000 -t cx16 -C cx16-asm.cfg source.s It also is possible to add a small BASIC header to the program, that uses SYS @@ -208,12 +211,22 @@ structures, accessing the struct fields will access the chip registers. Loadable drivers

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

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

Extended memory drivers

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

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

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

diff --git a/include/cx16.h b/include/cx16.h index 253fa2482..f129b26f4 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -111,6 +111,25 @@ #define COLOR_LIGHTBLUE 0x0E #define COLOR_GRAY3 0x0F +/* TGI color defines */ +#define TGI_COLOR_BLACK COLOR_BLACK +#define TGI_COLOR_WHITE COLOR_WHITE +#define TGI_COLOR_RED COLOR_RED +#define TGI_COLOR_CYAN COLOR_CYAN +#define TGI_COLOR_VIOLET COLOR_VIOLET +#define TGI_COLOR_PURPLE COLOR_PURPLE +#define TGI_COLOR_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_BROWN COLOR_BROWN +#define TGI_COLOR_LIGHTRED COLOR_LIGHTRED +#define TGI_COLOR_GRAY1 COLOR_GRAY1 +#define TGI_COLOR_GRAY2 COLOR_GRAY2 +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_GRAY3 COLOR_GRAY3 + /* NES controller masks for joy_read() */ #define JOY_BTN_1_MASK 0x80 @@ -280,8 +299,9 @@ struct __emul { /* The addresses of the static drivers */ -extern void cx16_std_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void cx16_std_mou[]; /* Referred to by mouse_static_stddrv[] */ +extern void cx16_std_joy[]; /* Referenced by joy_static_stddrv[] */ +extern void cx16_std_mou[]; /* Referenced by mouse_static_stddrv[] */ +extern void cx320p1_tgi[]; /* Referenced by tgi_static_stddrv[] */ diff --git a/libsrc/cx16/tgi/cx320p1.s b/libsrc/cx16/tgi/cx320p1.s new file mode 100644 index 000000000..1ad5c2cbd --- /dev/null +++ b/libsrc/cx16/tgi/cx320p1.s @@ -0,0 +1,401 @@ +; +; Graphics driver for the 320 pixels across, 200 pixels down, 256 colors mode +; on the Commander X16 +; +; 2020-07-02, Greg King +; + + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .include "cbm_kernal.inc" + .include "cx16.inc" + + .macpack generic + .macpack module + + +; Macro that copies a word into a pseudo-register + +.mac setReg reg, src + lda src + ldx src+1 + sta gREG::reg + stx gREG::reg+1 +.endmac + + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + +; First part of the header is a structure that has a signature, +; and defines the capabilities of the driver. + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word 320 ; X resolution + .word 200 ; Y resolution + .byte <$0100 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0100 ; Aspect ratio (based on VGA display) + .byte 0 ; TGI driver flags + +; Next, comes the jump table. Currently, all entries must be valid, +; and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + + +; ------------------------------------------------------------------------ +; Constant + +GRAPH320 = $80 + +; ------------------------------------------------------------------------ +; Data + +; Variables mapped to the zero-page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 := ptr1 +Y1 := ptr2 +X2 := ptr3 +Y2 := ptr4 + +; Absolute variables used in the code + +.bss + +; The colors are indicies into a TGI palette. The TGI palette is indicies into +; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels. +; The first 16 RGB elements mimic the Commodore 64's colors. + +defpalette: .res $0100 +palette: .res $0100 + +bcolor := palette + 0 ; Background color +color: .res 1 ; Stroke and fill index +mode: .res 1 ; Old text mode + +.data + +error: .byte TGI_ERR_OK ; Error code + + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO + +INSTALL: +; Create the default palette. + + ldx #$00 +: txa + sta defpalette,x + inx + bnz :- + + ; Fall through. + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL, but is probably empty most of the time. +; +; Must set an error code: NO + +UNINSTALL: + rts + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once; so, any code that is needed +; to initiate variables and so on must go here. Setting the palette is not +; needed because that is called by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active, so there is no need to protect against that. +; +; Must set an error code: YES + +INIT: stz error ; #TGI_ERR_OK + +; Save the current text mode. + + lda SCREEN_MODE + sta mode + +; Switch into (320 x 200 x 256) graphics mode. + + lda #GRAPH320 + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active, +; so there is no need to protect against that. +; +; Must set an error code: NO + +DONE: +; Work around a prerelease 37 Kernal bug. +; VERA (graphics) layer 0 isn't disabled by SCREEN_SET_MODE. + + stz VERA::CTRL + lda VERA::DISP::VIDEO + and #<~VERA::DISP::ENABLE::LAYER0 + sta VERA::DISP::VIDEO + + lda mode + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in .A, and clear it. + +GETERROR: + lda error + stz error + rts + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES + +CONTROL: + lda #TGI_ERR_INV_FUNC + sta error + rts + +; ------------------------------------------------------------------------ +; CLEAR: Clear the screen. +; +; Must set an error code: NO + +CLEAR := GRAPH_CLEAR + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETVIEWPAGE: + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETDRAWPAGE: + rts + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES + +SETPALETTE: + stz error ; #TGI_ERR_OK + ldy #$00 +: lda (ptr1),y + sta palette,y + iny + bnz :- + + lda color ; Get stroke and fill index + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) + +SETCOLOR: + tax + sta color + lda palette,x ; Set stroke and fill color + tax + ldy bcolor ; Get background color + jmp GRAPH_SET_COLORS + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in .XA. Even drivers that cannot +; set the palette should return the default palette here, so there's no +; way for this function to fail. +; +; Must set an error code: NO + +GETPALETTE: + lda #palette + rts + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in .XA. All +; drivers should return something reasonable here, even drivers that don't +; support palettes, otherwise the caller has no way to determine the colors +; of the (not changable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) + +GETDEFPALETTE: + lda #defpalette + rts + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area, so there is no need for clipping inside this function. +; +; Must set an error code: NO + +SETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + ldx color + lda palette,x + jmp FB_SET_PIXEL + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel, and return it in .XA. The +; co-ordinates passed to this function never are outside the visible screen +; area, so there is no need for clipping inside this function. + +GETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + jsr FB_GET_PIXEL + ldx #>$0000 + rts + +; ------------------------------------------------------------------------ +; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and +; X2/Y2 = ptr3/ptr4, using the current drawing color. +; +; Must set an error code: NO + +LINE: jsr Point + setReg r2, X2 + setReg r3, Y2 + jmp GRAPH_DRAW_LINE + +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4, using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO + +BAR: +; Set the starting corner. + + jsr Point + +; Set the width. + + lda X2 + sub X1 + sta gREG::r2 + lda X2+1 + sbc X1+1 + sta gREG::r2+1 + +; Set the height. + + lda Y2 + sub Y1 + sta gREG::r3 + lda Y2+1 + sbc Y1+1 + sta gREG::r3+1 + +; Set the corner radius. + + stz gREG::r4 + stz gREG::r4+1 + + sec ; Fill the rectangle + jmp GRAPH_DRAW_RECT + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y +; directions are passed in .X and .Y, the text direction is passed in .A. +; +; Must set an error code: NO + +TEXTSTYLE: + rts + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO + +OUTTEXT: + jsr Point + + ldy #$00 +@next: lda (ptr3),y + bze @end + phy + jsr GRAPH_PUT_CHAR + ply + iny + bnz @next +@end: rts + +; ------------------------------------------------------------------------ +; Point: Set the arguments for the first point of a Kernal graphics function. + +Point: setReg r0, X1 + setReg r1, Y1 + rts diff --git a/libsrc/cx16/tgi_stat_stddrv.s b/libsrc/cx16/tgi_stat_stddrv.s index e501f4c2f..e3cec1c8c 100644 --- a/libsrc/cx16/tgi_stat_stddrv.s +++ b/libsrc/cx16/tgi_stat_stddrv.s @@ -1,10 +1,11 @@ ; ; Address of the static standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const void tgi_static_stddrv[]; ; - .import _cx16_320x8b_tgi - .export _tgi_static_stddrv := _cx16_320x8b_tgi + .import _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + + .export _tgi_static_stddrv := _cx320p1_tgi diff --git a/libsrc/cx16/tgi_stddrv.s b/libsrc/cx16/tgi_stddrv.s index 0e46a6cb3..f05ab7131 100644 --- a/libsrc/cx16/tgi_stddrv.s +++ b/libsrc/cx16/tgi_stddrv.s @@ -1,12 +1,14 @@ ; ; Name of the standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const char tgi_stddrv[]; ; .export _tgi_stddrv +; A FAT32 8+3 file-name (for SD cards) + .rodata -_tgi_stddrv: .asciiz "cx16-320x8b.tgi" +_tgi_stddrv: .asciiz "cx320p1.tgi" diff --git a/samples/mandelbrot.c b/samples/mandelbrot.c index d7291c5b5..595bcfbbb 100644 --- a/samples/mandelbrot.c +++ b/samples/mandelbrot.c @@ -51,6 +51,7 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, register signed short r, r1, i; register signed short xs, ys, xx, yy; register signed short x, y; + register unsigned char maxcol = MAXCOL; /* Calc stepwidth */ xs = ((x2 - x1) / (SCREEN_X)); @@ -76,10 +77,15 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, if (count == maxiterations) { tgi_setcolor (0); } else { - if (MAXCOL == 2) { + switch (maxcol) { + case 2: tgi_setcolor (1); - } else { - tgi_setcolor (count % MAXCOL); + break; + case 0: /* 256 colors */ + tgi_setcolor (count); + break; + default: + tgi_setcolor (count % maxcol); } } /* Set pixel */ From 0e55d33cc309b88e04b739638a0ed68bafe98a80 Mon Sep 17 00:00:00 2001 From: Spiro Trikaliotis Date: Thu, 16 Jul 2020 17:56:58 +0200 Subject: [PATCH 071/806] Fix info page building for newer linuxdoc In ca65.sgml, the following pattern was used in tables in order to create an empty row: ||~@ That is, the first two columns are empty, the last one has an   Unfortunately, with newer linuxdoc, this fails, as the empty columns create two @item directly after each other, which is not allowed. Changing this to ~|~|~@ fixes it by adding an " " into each column. Furthermore, the last line had a "newrow" (@) separator, which created an artifact. Removed that one. --- doc/ca65.sgml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 2c4593d21..8d97bddfd 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -622,7 +622,7 @@ problem in most cases. | Built-in string functions| 0@ -||~@ +~|~|~@ | Built-in pseudo-variables| 1@ | Built-in pseudo-functions| 1@ +| Unary positive| 1@ @@ -635,7 +635,7 @@ problem in most cases. .HIBYTE| Unary high-byte operator| 1@ ^ .BANKBYTE| Unary bank-byte operator| 1@ -||~@ +~|~|~@ *| Multiplication| 2@ /| Division| 2@ .MOD| Modulo operator| 2@ @@ -647,28 +647,28 @@ problem in most cases. .SHL| Shift-left operator| 2@ >> .SHR| Shift-right operator| 2@ -||~@ +~|~|~@ +| Binary addition| 3@ -| Binary subtraction| 3@ | .BITOR| Bitwise or| 3@ -||~@ +~|~|~@ = | Compare operator (equal)| 4@ <>| Compare operator (not equal)| 4@ <| Compare operator (less)| 4@ >| Compare operator (greater)| 4@ <=| Compare operator (less or equal)| 4@ >=| Compare operator (greater or equal)| 4@ -||~@ +~|~|~@ && .AND| Boolean and| 5@ .XOR| Boolean xor| 5@ -||~@ +~|~|~@ || .OR| Boolean or| 6@ -||~@ +~|~|~@ ! -.NOT| Boolean not| 7@ +.NOT| Boolean not| 7 Available operators, sorted by precedence From ba48dfe65ddab173a00f85bcba272fc227f59824 Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 16 Jul 2020 14:06:23 -0400 Subject: [PATCH 072/806] Fixed a typo in the CX16 document. --- doc/cx16.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cx16.sgml b/doc/cx16.sgml index a73bb3c1e..490d07849 100644 --- a/doc/cx16.sgml +++ b/doc/cx16.sgml @@ -20,7 +20,7 @@ The Commander X16 is a modern small computer with firmware that is based partly on the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips (WDC65C22 VIA) that are like the ones in the VIC-20. It supports file storage on Secure Digital cards. It allows two joysticks and a mouse. It has three -sound devices. It's VGA screen has twice the range of the C64 (similar to the +sound devices. Its VGA screen has twice the range of the C64 (similar to the C128's 80-column screen), with 256 colors. This file contains an overview of the CX16 run-time system as it comes with the From 21084895230d137db87f8a4febac8a9359e8e548 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 15 Jul 2020 20:22:28 +0800 Subject: [PATCH 073/806] Fix for Issue #1075 and #1077. --- src/cc65/assignment.c | 6 +- src/cc65/codegen.c | 5 + src/cc65/codegen.h | 7 +- src/cc65/declare.c | 9 +- src/cc65/expr.c | 231 +++++++++++++++++++------------------ src/cc65/exprdesc.c | 196 ++++++++++++++++++++++++++----- src/cc65/exprdesc.h | 263 ++++++++++++++++++++++++++++++++---------- src/cc65/loadexpr.c | 80 ++++++++----- src/cc65/locals.c | 2 +- src/cc65/shiftexpr.c | 10 +- src/cc65/stdfunc.c | 30 ++--- src/cc65/typeconv.c | 19 ++- 12 files changed, 598 insertions(+), 260 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index f0607ac58..8e42a338f 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -98,7 +98,7 @@ void Assignment (ExprDesc* Expr) if (UseReg) { PushAddr (Expr); } else { - ED_MakeRVal (Expr); + ED_MarkExprAsRVal (Expr); LoadExpr (CF_NONE, Expr); g_push (CF_PTR | CF_UNSIGNED, 0); } @@ -127,7 +127,7 @@ void Assignment (ExprDesc* Expr) } else { /* We will use memcpy. Push the address of the rhs */ - ED_MakeRVal (&Expr2); + ED_MarkExprAsRVal (&Expr2); LoadExpr (CF_NONE, &Expr2); /* Push the address (or whatever is in ax in case of errors) */ @@ -264,5 +264,5 @@ void Assignment (ExprDesc* Expr) } /* Value is still in primary and not an lvalue */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 920d8fe90..7af329b91 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -100,6 +100,11 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) /* Create the correct label name */ switch (Flags & CF_ADDRMASK) { + case CF_IMM: + /* Immediate constant values */ + xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned)((Offs) & 0xFFFF)); + break; + case CF_STATIC: /* Static memory cell */ if (Offs) { diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 6f61b33a6..af539df8b 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -81,11 +81,12 @@ #define CF_TEST 0x0080 /* Test value */ #define CF_FIXARGC 0x0100 /* Function has fixed arg count */ #define CF_FORCECHAR 0x0200 /* Handle chars as chars, not ints */ -#define CF_REG 0x0800 /* Value is in primary register */ /* Type of static address */ -#define CF_ADDRMASK 0xF000 /* Type of address */ -#define CF_STATIC 0x0000 /* Static local */ +#define CF_ADDRMASK 0xFC00 /* Type of address */ +#define CF_IMM 0x0000 /* Value is pure rvalue and has no address */ +#define CF_REG 0x0400 /* Value is in primary register */ +#define CF_STATIC 0x0800 /* Static local */ #define CF_EXTERNAL 0x1000 /* Static external */ #define CF_ABSOLUTE 0x2000 /* Numeric absolute address */ #define CF_LOCAL 0x4000 /* Auto variable */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 58baaf769..5827eb6dc 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1773,9 +1773,14 @@ static void DefineData (ExprDesc* Expr) { switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: + /* Immediate numeric value with no storage */ + g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + break; + case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_defdata (TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + /* Absolute numeric address */ + g_defdata (CF_ABSOLUTE | TypeOf(Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 84f11189e..38a52e744 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -83,6 +83,7 @@ static unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: return CF_IMM; case E_LOC_ABS: return CF_ABSOLUTE; case E_LOC_GLOBAL: return CF_EXTERNAL; case E_LOC_STATIC: return CF_STATIC; @@ -183,7 +184,7 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) /* Generate type adjustment code if needed */ ltype = TypeOf (lhst); - if (ED_IsLocAbs (lhs)) { + if (ED_IsLocNone (lhs)) { ltype |= CF_CONST; } if (NoPush) { @@ -191,7 +192,7 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) ltype |= CF_REG; } rtype = TypeOf (rhst); - if (ED_IsLocAbs (rhs)) { + if (ED_IsLocNone (rhs)) { rtype |= CF_CONST; } flags = g_typeadjust (ltype, rtype); @@ -506,7 +507,7 @@ static void FunctionCall (ExprDesc* Expr) ** the pointer into the primary and mark it as an expression. */ LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); /* Remember the code position */ GetCodePos (&Mark); @@ -646,7 +647,7 @@ static void FunctionCall (ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); } @@ -663,7 +664,7 @@ static void Primary (ExprDesc* E) /* Character and integer constants. */ if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { E->IVal = CurTok.IVal; - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->Type = CurTok.Type; NextToken (); return; @@ -672,7 +673,7 @@ static void Primary (ExprDesc* E) /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { E->FVal = CurTok.FVal; - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->Type = CurTok.Type; NextToken (); return; @@ -716,7 +717,7 @@ static void Primary (ExprDesc* E) NextToken (); Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND); /* output its label */ - E->Flags = E_RTYPE_RVAL | E_LOC_STATIC; + E->Flags = E_RTYPE_RVAL | E_LOC_STATIC | E_ADDRESS_OF; E->Name = Entry->V.L.Label; E->Type = PointerTo (type_void); NextToken (); @@ -756,7 +757,7 @@ static void Primary (ExprDesc* E) /* Check for legal symbol types */ if ((Sym->Flags & SC_CONST) == SC_CONST) { /* Enum or some other numeric constant */ - E->Flags = E_LOC_ABS | E_RTYPE_RVAL; + E->Flags = E_LOC_NONE | E_RTYPE_RVAL; E->IVal = Sym->V.ConstVal; } else if ((Sym->Flags & SC_FUNC) == SC_FUNC) { /* Function */ @@ -797,12 +798,12 @@ static void Primary (ExprDesc* E) /* We've made all variables lvalues above. However, this is ** not always correct: An array is actually the address of its - ** first element, which is a rvalue, and a function is a + ** first element, which is an rvalue, and a function is an ** rvalue, too, because we cannot store anything in a function. ** So fix the flags depending on the type. */ if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) { - ED_MakeRVal (E); + ED_AddrExpr (E); } } else { @@ -845,7 +846,7 @@ static void Primary (ExprDesc* E) /* String literal */ E->LVal = UseLiteral (CurTok.SVal); E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); - E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL; + E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; E->IVal = 0; E->Name = GetLiteralLabel (CurTok.SVal); NextToken (); @@ -854,7 +855,7 @@ static void Primary (ExprDesc* E) case TOK_ASM: /* ASM statement */ AsmStatement (); - E->Flags = E_LOC_EXPR | E_RTYPE_RVAL; + E->Flags = E_RTYPE_RVAL; E->Type = type_void; break; @@ -912,12 +913,11 @@ static void ArrayRef (ExprDesc* Expr) /* We can apply a special treatment for arrays that have a const base ** address. This is true for most arrays and will produce a lot better - ** code. Check if this is a const base address. + ** code. Check if this is a "quasi-const base" address. */ - ConstBaseAddr = ED_IsRVal (Expr) && - (ED_IsLocConst (Expr) || ED_IsLocStack (Expr)); + ConstBaseAddr = ED_IsRVal (Expr) && ED_IsLocQuasiConst (Expr); - /* If we have a constant base, we delay the address fetch */ + /* If we have a quasi-const base address, we delay the address fetch */ GetCodePos (&Mark1); if (!ConstBaseAddr) { /* Get a pointer to the array into the primary */ @@ -984,7 +984,7 @@ static void ArrayRef (ExprDesc* Expr) /* If the subscript is a bit-field, load it and make it an rvalue */ if (ED_IsBitField (&Subscript)) { LoadExpr (CF_NONE, &Subscript); - ED_MakeRValExpr (&Subscript); + ED_FinalizeRValLoad (&Subscript); } /* Check if the subscript is constant absolute value */ @@ -1015,25 +1015,21 @@ static void ArrayRef (ExprDesc* Expr) ** already in Expr. If the base address was a constant, we can even ** remove the code that loaded the address into the primary. */ - if (IsTypeArray (Expr->Type)) { - - /* Adjust the offset */ - Expr->IVal += Subscript.IVal; - - } else { - + if (!IsTypeArray (Expr->Type)) { + /* It's a pointer, so we do have to load it into the primary ** first (if it's not already there). */ - if (ConstBaseAddr || ED_IsLVal (Expr)) { + if (!ConstBaseAddr && ED_IsLVal (Expr)) { LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } - - /* Use the offset */ - Expr->IVal = Subscript.IVal; } + /* Adjust the offset */ + Expr->IVal += Subscript.IVal; + + } else { /* Scale the rhs value according to the element type */ @@ -1106,7 +1102,7 @@ static void ArrayRef (ExprDesc* Expr) ** subscript was not scaled, that is, if this was a byte array ** or pointer. */ - if ((ED_IsLocConst (&Subscript) || ED_IsLocStack (&Subscript)) && + if (ED_IsLocQuasiConst (&Subscript) && CheckedSizeOf (ElementType) == SIZEOF_CHAR) { unsigned Flags; @@ -1131,12 +1127,13 @@ static void ArrayRef (ExprDesc* Expr) } } else { - if (ED_IsLocAbs (Expr)) { + if (ED_IsLocNone (Expr) || + (ED_IsLocAbs (Expr) && ED_IsAddrExpr (Expr))) { /* Constant numeric address. Just add it */ g_inc (CF_INT, Expr->IVal); } else if (ED_IsLocStack (Expr)) { /* Base address is a local variable address */ - if (IsTypeArray (Expr->Type)) { + if (ED_IsAddrExpr (Expr)) { g_addaddr_local (CF_INT, Expr->IVal); } else { g_addlocal (CF_PTR, Expr->IVal); @@ -1144,7 +1141,7 @@ static void ArrayRef (ExprDesc* Expr) } else { /* Base address is a static variable address */ unsigned Flags = CF_INT | GlobalModeFlags (Expr); - if (ED_IsRVal (Expr)) { + if (ED_IsAddrExpr (Expr)) { /* Add the address of the location */ g_addaddr_static (Flags, Expr->Name, Expr->IVal); } else { @@ -1153,27 +1150,27 @@ static void ArrayRef (ExprDesc* Expr) } } } - - } - /* The result is an expression in the primary */ - ED_MakeRValExpr (Expr); + /* The pointer is an rvalue in the primary */ + ED_FinalizeRValLoad (Expr); } - /* Result is of element type */ + /* The result is usually an lvalue expression of element type referenced in + ** the primary, unless it's an array which is a rare case. We can just + ** assume the usual case first, and change it later if necessary. + */ + ED_IndExpr (Expr); Expr->Type = ElementType; - /* An array element is actually a variable. So the rules for variables - ** with respect to the reference type apply: If it's an array, it is - ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue, - ** but an array cannot contain functions). + /* An array element is actually a variable. So the rules for variables with + ** respect to the reference type apply: If it's an array, it is virtually + ** an rvalue address, otherwise it's an lvalue reference. (A function would + ** also be an rvalue address, but an array cannot contain functions). */ if (IsTypeArray (Expr->Type)) { - ED_MakeRVal (Expr); - } else { - ED_MakeLVal (Expr); + ED_AddrExpr (Expr); } /* Consume the closing bracket */ @@ -1210,16 +1207,26 @@ static void StructRef (ExprDesc* Expr) return; } - /* If we have a struct pointer that is an lvalue and not already in the - ** primary, load it now. - */ - if (ED_IsLVal (Expr) && IsTypePtr (Expr->Type)) { + if (IsTypePtr (Expr->Type)) { + /* If we have a struct pointer that is an lvalue and not already in the + ** primary, load its content now. + */ + if (!ED_IsConst (Expr)) { + /* Load into the primary */ + LoadExpr (CF_NONE, Expr); - /* Load into the primary */ + /* Clear the offset */ + Expr->IVal = 0; + } + + /* Dereference the expression */ + ED_IndExpr (Expr); + + } else if (!ED_IsLocQuasiConst (Expr) && !ED_IsLocPrimaryOrExpr (Expr)) { + /* Load the base address into the primary (and use it as a reference + ** later) if it's not quasi-const or in the primary already. + */ LoadExpr (CF_NONE, Expr); - - /* Make it an lvalue expression */ - ED_MakeLValExpr (Expr); } /* The type is the type of the field plus any qualifiers from the struct */ @@ -1235,8 +1242,8 @@ static void StructRef (ExprDesc* Expr) FinalType->C |= Q; } - /* A struct is usually an lvalue. If not, it is a struct in the primary - ** register. + /* A struct is usually an lvalue. If not, it is a struct referenced in the + ** primary register, which is likely to be returned from a function. */ if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) { @@ -1289,13 +1296,12 @@ static void StructRef (ExprDesc* Expr) /* An struct member is actually a variable. So the rules for variables ** with respect to the reference type apply: If it's an array, it is - ** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue, - ** but a struct field cannot be a function). + ** virtually an rvalue address, otherwise it's an lvalue reference. (A + ** function would also be an rvalue address, but a struct field cannot + ** contain functions). */ if (IsTypeArray (Expr->Type)) { - ED_MakeRVal (Expr); - } else { - ED_MakeLVal (Expr); + ED_AddrExpr (Expr); } /* Make the expression a bit field if necessary */ @@ -1391,7 +1397,7 @@ void Store (ExprDesc* Expr, const Type* StoreType) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_putstatic (Flags, Expr->IVal, 0); break; @@ -1421,10 +1427,14 @@ void Store (ExprDesc* Expr, const Type* StoreType) break; case E_LOC_EXPR: - /* An expression in the primary register */ + /* An expression referenced in the primary register */ g_putind (Flags, Expr->IVal); break; + case E_LOC_NONE: + /* We may get here as a result of previous compiler errors */ + break; + default: Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); } @@ -1466,7 +1476,7 @@ static void PreInc (ExprDesc* Expr) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_addeqstatic (Flags, Expr->IVal, 0, Val); break; @@ -1497,7 +1507,7 @@ static void PreInc (ExprDesc* Expr) break; case E_LOC_EXPR: - /* An expression in the primary register */ + /* An expression referenced in the primary register */ g_addeqind (Flags, Expr->IVal, Val); break; @@ -1506,7 +1516,7 @@ static void PreInc (ExprDesc* Expr) } /* Result is an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1542,7 +1552,7 @@ static void PreDec (ExprDesc* Expr) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_subeqstatic (Flags, Expr->IVal, 0, Val); break; @@ -1582,7 +1592,7 @@ static void PreDec (ExprDesc* Expr) } /* Result is an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1638,7 +1648,7 @@ static void PostInc (ExprDesc* Expr) } /* The result is always an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1694,7 +1704,7 @@ static void PostDec (ExprDesc* Expr) } /* The result is always an expression, no reference */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -1741,8 +1751,8 @@ static void UnaryOp (ExprDesc* Expr) default: Internal ("Unexpected token: %d", Tok); } - /* The result is a rvalue in the primary */ - ED_MakeRValExpr (Expr); + /* The result is an rvalue in the primary */ + ED_FinalizeRValLoad (Expr); } } @@ -1776,7 +1786,7 @@ void hie10 (ExprDesc* Expr) Expr->IVal = !Expr->IVal; } else { g_bneg (TypeOf (Expr->Type)); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* bneg will set cc */ } break; @@ -1784,13 +1794,14 @@ void hie10 (ExprDesc* Expr) case TOK_STAR: NextToken (); ExprWithCheck (hie10, Expr); - if (ED_IsLVal (Expr) || !(ED_IsLocConst (Expr) || ED_IsLocStack (Expr))) { - /* Not a const, load it into the primary and make it a + if (ED_IsLVal (Expr) || !ED_IsLocQuasiConst (Expr)) { + /* Not a const, load the pointer into the primary and make it a ** calculated value. */ LoadExpr (CF_NONE, Expr); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } + /* If the expression is already a pointer to function, the ** additional dereferencing operator must be ignored. A function ** itself is represented as "pointer to function", so any number @@ -1799,7 +1810,7 @@ void hie10 (ExprDesc* Expr) */ if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) { /* Expression not storable */ - ED_MakeRVal (Expr); + ED_MarkExprAsRVal (Expr); } else { if (IsClassPtr (Expr->Type)) { Expr->Type = Indirect (Expr->Type); @@ -1810,8 +1821,8 @@ void hie10 (ExprDesc* Expr) ** address -- it already is the location of the first element. */ if (!IsTypeArray (Expr->Type)) { - /* The * operator yields an lvalue */ - ED_MakeLVal (Expr); + /* The * operator yields an lvalue reference */ + ED_IndExpr (Expr); } } break; @@ -1831,8 +1842,8 @@ void hie10 (ExprDesc* Expr) Expr->Flags &= ~E_BITFIELD; } Expr->Type = PointerTo (Expr->Type); - /* The & operator yields an rvalue */ - ED_MakeRVal (Expr); + /* The & operator yields an rvalue address */ + ED_AddrExpr (Expr); } break; @@ -2067,8 +2078,8 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Generate code */ Gen->Func (type, Expr->IVal); - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } else { @@ -2100,8 +2111,8 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Generate code */ Gen->Func (type, Expr2.IVal); - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } } } @@ -2441,7 +2452,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ GenFunc (flags, Expr2.IVal); /* The result is an rvalue in the primary */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } /* Result type is always int */ @@ -2534,7 +2545,7 @@ static void parseadd (ExprDesc* Expr) rhst = Expr2.Type; /* Setup flags */ - if (ED_IsLocAbs (Expr)) { + if (ED_IsLocNone (Expr)) { /* A numerical constant */ flags |= CF_CONST; } else { @@ -2549,7 +2560,7 @@ static void parseadd (ExprDesc* Expr) /* Operate on pointers, result type is a pointer */ flags |= CF_PTR; /* Generate the code for the add */ - if (ED_GetLoc (Expr) == E_LOC_ABS) { + if (ED_GetLoc (Expr) == E_LOC_NONE) { /* Numeric constant */ g_inc (flags, Expr->IVal); } else { @@ -2569,7 +2580,7 @@ static void parseadd (ExprDesc* Expr) ** not a numeric constant, and the scale factor is not one ** (no scaling), we must take the long way over the stack. */ - if (ED_IsLocAbs (Expr)) { + if (ED_IsLocNone (Expr)) { /* Numeric constant, scale lhs */ Expr->IVal *= ScaleFactor; /* Generate the code for the add */ @@ -2588,7 +2599,7 @@ static void parseadd (ExprDesc* Expr) /* Integer addition */ flags |= typeadjust (Expr, &Expr2, 1); /* Generate the code for the add */ - if (ED_IsLocAbs (Expr)) { + if (ED_IsLocNone (Expr)) { /* Numeric constant */ g_inc (flags, Expr->IVal); } else { @@ -2601,8 +2612,8 @@ static void parseadd (ExprDesc* Expr) flags = CF_INT; } - /* Result is a rvalue in primary register */ - ED_MakeRValExpr (Expr); + /* Result is an rvalue in primary register */ + ED_FinalizeRValLoad (Expr); } } else { @@ -2692,8 +2703,8 @@ static void parseadd (ExprDesc* Expr) } - /* Result is a rvalue in primary register */ - ED_MakeRValExpr (Expr); + /* Result is an rvalue in primary register */ + ED_FinalizeRValLoad (Expr); } /* Condition codes not set */ @@ -2825,8 +2836,8 @@ static void parsesub (ExprDesc* Expr) g_scale (flags, -rscale); } - /* Result is a rvalue in the primary register */ - ED_MakeRValExpr (Expr); + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); ED_MarkAsUntested (Expr); } @@ -2860,8 +2871,8 @@ static void parsesub (ExprDesc* Expr) ** the lhs is const, we have to remove this mark, since this is no ** longer true, lhs is on stack instead. */ - if (ED_IsLocAbs (Expr)) { - ED_MakeRValExpr (Expr); + if (ED_IsLocNone (Expr)) { + ED_FinalizeRValLoad (Expr); } /* Adjust operand types */ flags = typeadjust (Expr, &Expr2, 0); @@ -2879,8 +2890,8 @@ static void parsesub (ExprDesc* Expr) g_scale (flags, -rscale); } - /* Result is a rvalue in the primary register */ - ED_MakeRValExpr (Expr); + /* Result is an rvalue in the primary register */ + ED_FinalizeRValLoad (Expr); ED_MarkAsUntested (Expr); } } @@ -3070,7 +3081,7 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) g_defcodelabel (FalseLab); /* The result is an rvalue in primary */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* Condition codes are set */ } } @@ -3133,7 +3144,7 @@ static void hieOr (ExprDesc *Expr) } /* The result is an rvalue in primary */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* Condition codes are set */ } @@ -3189,7 +3200,7 @@ static void hieQuest (ExprDesc* Expr) if (!IsTypeVoid (Expr2.Type)) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr2); - ED_MakeRValExpr (&Expr2); + ED_FinalizeRValLoad (&Expr2); Expr2.Type = PtrConversion (Expr2.Type); } @@ -3212,7 +3223,7 @@ static void hieQuest (ExprDesc* Expr) if (!IsTypeVoid (Expr3.Type)) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr3); - ED_MakeRValExpr (&Expr3); + ED_FinalizeRValLoad (&Expr3); Expr3.Type = PtrConversion (Expr3.Type); } @@ -3278,7 +3289,7 @@ static void hieQuest (ExprDesc* Expr) g_defcodelabel (TrueLab); /* Setup the target expression */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = ResultType; } } @@ -3294,7 +3305,7 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) int MustScale; /* op= can only be used with lvalues */ - if (!ED_IsLVal (Expr)) { + if (ED_IsRVal (Expr)) { Error ("Invalid lvalue in assignment"); return; } @@ -3396,7 +3407,7 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0); } Store (Expr, 0); - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } @@ -3410,7 +3421,7 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) int MustScale; - /* We're currently only able to handle some adressing modes */ + /* We're currently only able to handle some addressing modes */ if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { /* Use generic routine */ opeq (Gen, Expr, Op); @@ -3487,7 +3498,7 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ if (Gen->Tok == TOK_PLUS_ASSIGN) { g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); } else { @@ -3536,8 +3547,8 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); } - /* Expression is a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* Expression is an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); } diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 46377ac6b..5ff848fb7 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -80,6 +80,39 @@ void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) +#if !defined(HAVE_INLINE) +int ED_IsLocQuasiConst (const ExprDesc* Expr) +/* Return true if the expression is a constant location of some sort or on the +** stack. +*/ +{ + return ED_IsLocConst (Expr) || ED_IsLocStack (Expr); +} +#endif + + + +#if !defined(HAVE_INLINE) +int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ +{ + return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); +} +#endif + + + +#if !defined(HAVE_INLINE) +int ED_IsIndExpr (const ExprDesc* Expr) +/* Check if the expression is a reference to its value */ +{ + return (Expr->Flags & E_ADDRESS_OF) == 0 && + !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr); +} +#endif + + + void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) /* Set the code range for this expression */ { @@ -115,8 +148,9 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) /* Generate a label depending on the location */ switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ SB_Printf (&Buf, "$%04X", (int)(Offs & 0xFFFF)); break; @@ -168,11 +202,11 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) -/* Make Expr an absolute const with the given value and type. */ +/* Replace Expr with an absolute const with the given value and type */ { Expr->Sym = 0; Expr->Type = Type; - Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); Expr->Name = 0; Expr->IVal = Value; Expr->FVal = FP_D_Make (0.0); @@ -182,11 +216,11 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) -/* Make Expr a constant integer expression with the given value */ +/* Replace Expr with a constant integer expression with the given value */ { Expr->Sym = 0; Expr->Type = type_int; - Expr->Flags = E_LOC_ABS | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); Expr->Name = 0; Expr->IVal = Value; Expr->FVal = FP_D_Make (0.0); @@ -195,14 +229,13 @@ ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) -ExprDesc* ED_MakeRValExpr (ExprDesc* Expr) -/* Convert Expr into a rvalue which is in the primary register without an -** offset. -*/ +ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) +/* Finalize the result of LoadExpr to be an rvalue in the primary register */ { Expr->Sym = 0; - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); - Expr->Flags |= (E_LOC_EXPR | E_RTYPE_RVAL); + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF); + Expr->Flags &= ~(E_NEED_TEST | E_CC_SET); + Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); Expr->Name = 0; Expr->IVal = 0; /* No offset */ Expr->FVal = FP_D_Make (0.0); @@ -211,18 +244,106 @@ ExprDesc* ED_MakeRValExpr (ExprDesc* Expr) -ExprDesc* ED_MakeLValExpr (ExprDesc* Expr) -/* Convert Expr into a lvalue which is in the primary register without an -** offset. +ExprDesc* ED_AddrExpr (ExprDesc* Expr) +/* Take address of Expr. The result is always an rvalue */ +{ + switch (Expr->Flags & E_MASK_LOC) { + case E_LOC_NONE: + Error ("Cannot get the address of a numeric constant"); + break; + + case E_LOC_EXPR: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_PRIMARY | E_RTYPE_RVAL; + break; + + default: + if ((Expr->Flags & E_ADDRESS_OF) == 0) { + Expr->Flags &= ~E_MASK_RTYPE; + Expr->Flags |= E_ADDRESS_OF | E_RTYPE_RVAL; + } else { + /* Due to the way we handle arrays, this may happen if we take + ** the address of a pointer to an array element. + */ + if (!IsTypePtr (Expr->Type)) { + Error ("Cannot get the address of an address"); + } + Expr->Flags &= ~E_MASK_RTYPE; + Expr->Flags |= E_RTYPE_RVAL; + } + break; + } + return Expr; +} + + + +ExprDesc* ED_IndExpr (ExprDesc* Expr) +/* Dereference Expr */ +{ + switch (Expr->Flags & E_MASK_LOC) { + case E_LOC_NONE: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_ABS | E_RTYPE_LVAL; + break; + + case E_LOC_PRIMARY: + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; + break; + + default: + if ((Expr->Flags & E_ADDRESS_OF) != 0) { + Expr->Flags &= ~(E_MASK_RTYPE | E_ADDRESS_OF); + Expr->Flags |= E_RTYPE_LVAL; + } else { + /* Due to the limitation of LoadExpr, this may happen after we + ** have loaded the value from a referenced address, in which + ** case the content in the primary no longer refers to the + ** original address. We simply mark this as E_LOC_EXPR so that + ** some info about the original location can be retained. + ** If it's really meant to dereference a "pointer value", it + ** should be done in two steps where the pointervalue should + ** be the manually loaded first before a call into this, and + ** the offset should be manually cleared somewhere outside. + */ + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE); + Expr->Flags |= E_LOC_EXPR | E_RTYPE_LVAL; + } + break; + } + return Expr; +} + + + +#if !defined(HAVE_INLINE) +int ED_IsAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a numeric value or address. */ +{ + return (Expr->Flags & (E_MASK_LOC)) == (E_LOC_NONE) || + (Expr->Flags & (E_MASK_LOC|E_ADDRESS_OF)) == (E_LOC_ABS|E_ADDRESS_OF); +} +#endif + + + +#if !defined(HAVE_INLINE) +int ED_IsConstAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a constant absolute value. This can be +** a numeric constant, cast to any type. */ { - Expr->Sym = 0; - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_NEED_TEST | E_CC_SET); - Expr->Flags |= (E_LOC_EXPR | E_RTYPE_LVAL); - Expr->Name = 0; - Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); - return Expr; + return ED_IsRVal (Expr) && ED_IsAbs (Expr); +} +#endif + + + +int ED_IsConstAbsInt (const ExprDesc* Expr) +/* Return true if the expression is a constant (numeric) integer. */ +{ + return ED_IsConstAbs (Expr) && IsClassInt (Expr->Type); } @@ -233,16 +354,27 @@ int ED_IsConst (const ExprDesc* Expr) ** similar. */ { - return ED_IsRVal (Expr) && (Expr->Flags & E_LOC_CONST) != 0; + return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE || ED_IsConstAddr (Expr); } -int ED_IsConstAbsInt (const ExprDesc* Expr) -/* Return true if the expression is a constant (numeric) integer. */ +int ED_IsConstAddr (const ExprDesc* Expr) +/* Return true if the expression denotes a constant address of some sort. This +** can be the address of a global variable (maybe with offset) or similar. +*/ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL) && - IsClassInt (Expr->Type); + return ED_IsAddrExpr (Expr) && ED_IsLocConst (Expr); +} + + + +int ED_IsQuasiConstAddr (const ExprDesc* Expr) +/* Return true if the expression denotes a quasi-constant address of some sort. +** This can be a constant address or a stack variable address. +*/ +{ + return ED_IsAddrExpr (Expr) && ED_IsLocQuasiConst (Expr); } @@ -251,7 +383,7 @@ int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == - (E_LOC_ABS|E_RTYPE_RVAL) && + (E_LOC_NONE|E_RTYPE_RVAL) && Expr->IVal == 0 && IsClassInt (Expr->Type); } @@ -293,6 +425,11 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags = E->Flags; Sep = '('; fprintf (F, "Flags: 0x%04X ", Flags); + if ((Flags & E_MASK_LOC) == E_LOC_NONE) { + fprintf (F, "%cE_LOC_NONE", Sep); + Flags &= ~E_LOC_NONE; + Sep = ','; + } if (Flags & E_LOC_ABS) { fprintf (F, "%cE_LOC_ABS", Sep); Flags &= ~E_LOC_ABS; @@ -353,6 +490,11 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_CC_SET; Sep = ','; } + if (Flags & E_ADDRESS_OF) { + fprintf (F, "%cE_ADDRESS_OF", Sep); + Flags &= ~E_ADDRESS_OF; + Sep = ','; + } if (Flags) { fprintf (F, "%c,0x%04X", Sep, Flags); Sep = ','; diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index e86534902..a61c5d814 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -59,22 +59,57 @@ /* Defines for the flags field of the expression descriptor */ enum { - /* Location: Where is the value we're talking about? */ + /* Location: Where is the value we're talking about? + ** + ** Remarks: + ** - E_LOC_ refers to any other than E_LOC_NONE and E_LOC_PRIMARY. + ** - E_LOC_EXPR can be regarded as a generalized E_LOC_. + ** - E_LOC_NONE can be regarded as E_LOC_PRIMARY + E_ADDRESS_OF unless + ** remarked otherwise (see below). + ** - An E_LOC_NONE value is not considered to be an "address". + ** - ref-load doesn't change the location, while rval-load puts into the + ** primary register a "temporary" that is the straight integer rvalue or + ** a "delegate" to the real rvalue somewhere else. + ** - ref-load doesn't change the rval/lval category of the expression, + ** while rval-load converts it to an rvalue if it wasn't. + ** - In practice, ref-load is unimplemented, and can be simulated with + ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, + ** whilst val-load is done with LoadExpr + FinalizeRValLoad. + ** + ** E_LOC_NONE -- ref-load -> + E_LOADED (int rvalue) + ** E_LOC_PRIMARY -- ref-load -> + E_LOADED (unchanged) + ** E_LOC_ -- ref-load -> + E_LOADED (reference lvalue) + ** + E_ADDRESS_OF -- ref-load -> + E_LOADED (address rvalue) + ** E_LOC_NONE -- val-load -> E_LOC_PRIMARY (int rvalue) + ** E_LOC_PRIMARY -- val-load -> E_LOC_PRIMARY (unchanged) + ** E_LOC_ -- val-load -> E_LOC_PRIMARY (rvalue/delegate) + ** + E_ADDRESS_OF -- val-load -> E_LOC_PRIMARY (address rvalue) + ** E_LOC_NONE -- take address -> (error) + ** E_LOC_PRIMARY -- take address -> + E_ADDRESS_OF (or error) + ** E_LOC_EXPR -- take address -> E_LOC_PRIMARY (address) + ** E_LOC_ -- take address -> + E_ADDRESS_OF (address) + ** + E_ADDRESS_OF -- take address -> (error) + ** E_LOC_NONE -- dereference -> E_LOC_ABS (lvalue reference) + ** E_LOC_PRIMARY -- dereference -> E_LOC_EXPR (lvalue reference) + ** E_LOC_ -- dereference -> E_LOC_EXPR (pointed-to-value, must load) + ** + E_ADDRESS_OF -- dereference -> (lvalue reference) + */ E_MASK_LOC = 0x00FF, - E_LOC_ABS = 0x0001, /* Absolute: numeric address or const */ + E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ + E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ E_LOC_STATIC = 0x0004, /* Static variable */ E_LOC_REGISTER = 0x0008, /* Register variable */ E_LOC_STACK = 0x0010, /* Value on the stack */ - E_LOC_PRIMARY = 0x0020, /* The primary register */ - E_LOC_EXPR = 0x0040, /* An expression in the primary register */ + E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ + E_LOC_EXPR = 0x0040, /* A location that the primary register points to */ E_LOC_LITERAL = 0x0080, /* Literal in the literal pool */ /* Constant location of some sort (only if rval) */ - E_LOC_CONST = E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | + E_LOC_CONST = E_LOC_NONE | E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | E_LOC_REGISTER | E_LOC_LITERAL, - /* Reference? */ + /* lvalue/rvalue in C language's sense */ E_MASK_RTYPE = 0x0100, E_RTYPE_RVAL = 0x0000, E_RTYPE_LVAL = 0x0100, @@ -88,6 +123,10 @@ enum { E_HAVE_MARKS = 0x1000, /* Code marks are valid */ + E_LOADED = 0x4000, /* Expression is loaded in primary */ + + E_ADDRESS_OF = 0x8000, /* Expression is the address of the lvalue */ + }; /* Forward */ @@ -135,8 +174,18 @@ INLINE int ED_GetLoc (const ExprDesc* Expr) #endif #if defined(HAVE_INLINE) -INLINE int ED_IsLocAbs (const ExprDesc* Expr) +INLINE int ED_IsLocNone (const ExprDesc* Expr) /* Return true if the expression is an absolute value */ +{ + return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE; +} +#else +# define ED_IsLocNone(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_NONE) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsLocAbs (const ExprDesc* Expr) +/* Return true if the expression is referenced with an absolute address */ { return (Expr->Flags & E_MASK_LOC) == E_LOC_ABS; } @@ -151,7 +200,7 @@ INLINE int ED_IsLocRegister (const ExprDesc* Expr) return (Expr->Flags & E_MASK_LOC) == E_LOC_REGISTER; } #else -# define ED_IsLocRegister(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_REGISTER) +# define ED_IsLocRegister(Expr) (((Expr)->Flags & E_MASK_LOC) == E_LOC_REGISTER) #endif #if defined(HAVE_INLINE) @@ -198,50 +247,25 @@ INLINE int ED_IsLocLiteral (const ExprDesc* Expr) INLINE int ED_IsLocConst (const ExprDesc* Expr) /* Return true if the expression is a constant location of some sort */ { - return (Expr->Flags & E_LOC_CONST) != 0; + return ((Expr)->Flags & E_MASK_LOC & ~E_LOC_CONST) == 0; } #else -# define ED_IsLocConst(Expr) (((Expr)->Flags & E_LOC_CONST) != 0) +# define ED_IsLocConst(Expr) (((Expr)->Flags & E_MASK_LOC & ~E_LOC_CONST) == 0) #endif #if defined(HAVE_INLINE) -INLINE int ED_IsLVal (const ExprDesc* Expr) -/* Return true if the expression is a reference */ +INLINE int ED_IsLocQuasiConst (const ExprDesc* Expr) +/* Return true if the expression is a constant location of some sort or on the +** stack. +*/ { - return (Expr->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL; + return ED_IsLocConst (Expr) || ED_IsLocStack (Expr); } #else -# define ED_IsLVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL) -#endif - -#if defined(HAVE_INLINE) -INLINE int ED_IsRVal (const ExprDesc* Expr) -/* Return true if the expression is a rvalue */ -{ - return (Expr->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL; -} -#else -# define ED_IsRVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_MakeLVal (ExprDesc* Expr) -/* Make the expression a lvalue. */ -{ - Expr->Flags |= E_RTYPE_LVAL; -} -#else -# define ED_MakeLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_MakeRVal (ExprDesc* Expr) -/* Make the expression a rvalue. */ -{ - Expr->Flags &= ~E_RTYPE_LVAL; -} -#else -# define ED_MakeRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) +int ED_IsLocQuasiConst (const ExprDesc* Expr); +/* Return true if the expression denotes a constant address of some sort. This +** can be the address of a global variable (maybe with offset) or similar. +*/ #endif #if defined(HAVE_INLINE) @@ -308,6 +332,52 @@ INLINE void ED_MarkAsUntested (ExprDesc* Expr) # define ED_MarkAsUntested(Expr) do { (Expr)->Flags &= ~E_CC_SET; } while (0) #endif +#if defined(HAVE_INLINE) +INLINE int ED_IsLoaded (const ExprDesc* Expr) +/* Check if the expression is loaded. +** NOTE: This is currently unused and not working due to code complexity. +*/ +{ + return (Expr->Flags & E_LOADED) != 0; +} +#else +# define ED_IsLoaded(Expr) (((Expr)->Flags & E_LOADED) != 0) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ +{ + return ED_IsLocPrimary (Expr) || ED_IsLocExpr (Expr); +} +#else +int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr); +/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsAddrExpr (const ExprDesc* Expr) +/* Check if the expression is taken address of instead of its value. +*/ +{ + return (Expr->Flags & E_ADDRESS_OF) != 0; +} +#else +# define ED_IsAddrExpr(Expr) (((Expr)->Flags & E_ADDRESS_OF) != 0) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_IsIndExpr (const ExprDesc* Expr) +/* Check if the expression is a reference to its value */ +{ + return (Expr->Flags & E_ADDRESS_OF) == 0 && + !ED_IsLocNone (Expr) && !ED_IsLocPrimary (Expr); +} +#else +int ED_IsIndExpr (const ExprDesc* Expr); +/* Check if the expression is a reference to its value */ +#endif + void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End); /* Set the code range for this expression */ @@ -326,26 +396,79 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs); */ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type); -/* Make Expr an absolute const with the given value and type. */ +/* Replace Expr with an absolute const with the given value and type */ ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value); -/* Make Expr a constant integer expression with the given value */ +/* Replace Expr with an constant integer with the given value */ -ExprDesc* ED_MakeRValExpr (ExprDesc* Expr); -/* Convert Expr into a rvalue which is in the primary register without an -** offset. -*/ +ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr); +/* Finalize the result of LoadExpr to be an rvalue in the primary register */ -ExprDesc* ED_MakeLValExpr (ExprDesc* Expr); -/* Convert Expr into a lvalue which is in the primary register without an -** offset. -*/ +#if defined(HAVE_INLINE) +INLINE int ED_IsLVal (const ExprDesc* Expr) +/* Return true if the expression is a reference */ +{ + return ((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL; +} +#else +# define ED_IsLVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_LVAL) +#endif -int ED_IsConst (const ExprDesc* Expr); -/* Return true if the expression denotes a constant of some sort. This can be a -** numeric constant, the address of a global variable (maybe with offset) or -** similar. +#if defined(HAVE_INLINE) +INLINE int ED_IsRVal (const ExprDesc* Expr) +/* Return true if the expression is an rvalue */ +{ + return ((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL; +} +#else +# define ED_IsRVal(Expr) (((Expr)->Flags & E_MASK_RTYPE) == E_RTYPE_RVAL) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_MarkExprAsLVal (ExprDesc* Expr) +/* Mark the expression as an lvalue. +** HINT: Consider using ED_IndExpr instead of this, unless you know what +** consequence there will be, as there are both a big part in the code +** assuming rvalue = const and a big part assuming rvalue = address. */ +{ + Expr->Flags |= E_RTYPE_LVAL; +} +#else +# define ED_MarkExprAsLVal(Expr) do { (Expr)->Flags |= E_RTYPE_LVAL; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE void ED_MarkExprAsRVal (ExprDesc* Expr) +/* Mark the expression as an rvalue. +** HINT: Consider using ED_AddrExpr instead of this, unless you know what +** consequence there will be, as there are both a big part in the code +** assuming rvalue = const and a big part assuming rvalue = address. +*/ +{ + Expr->Flags &= ~E_RTYPE_LVAL; +} +#else +# define ED_MarkExprAsRVal(Expr) do { (Expr)->Flags &= ~E_RTYPE_LVAL; } while (0) +#endif + +ExprDesc* ED_AddrExpr (ExprDesc* Expr); +/* Take address of Expr */ + +ExprDesc* ED_IndExpr (ExprDesc* Expr); +/* Dereference Expr */ + +#if defined(HAVE_INLINE) +INLINE int ED_IsAbs (const ExprDesc* Expr) +/* Return true if the expression denotes a numeric value or address. */ +{ + return (Expr->Flags & (E_MASK_LOC)) == (E_LOC_NONE) || + (Expr->Flags & (E_MASK_LOC|E_ADDRESS_OF)) == (E_LOC_ABS|E_ADDRESS_OF); +} +#else +int ED_IsAbs (const ExprDesc* Expr); +/* Return true if the expression denotes a numeric value or address. */ +#endif #if defined(HAVE_INLINE) INLINE int ED_IsConstAbs (const ExprDesc* Expr) @@ -353,16 +476,34 @@ INLINE int ED_IsConstAbs (const ExprDesc* Expr) ** a numeric constant, cast to any type. */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL); + return ED_IsRVal (Expr) && ED_IsAbs (Expr); } #else -# define ED_IsConstAbs(E) \ - (((E)->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == (E_LOC_ABS|E_RTYPE_RVAL)) +int ED_IsConstAbs (const ExprDesc* Expr); +/* Return true if the expression denotes a constant absolute value. This can be +** a numeric constant, cast to any type. +*/ #endif int ED_IsConstAbsInt (const ExprDesc* Expr); /* Return true if the expression is a constant (numeric) integer. */ +int ED_IsConst (const ExprDesc* Expr); +/* Return true if the expression denotes a constant of some sort. This can be a +** numeric constant, the address of a global variable (maybe with offset) or +** similar. +*/ + +int ED_IsConstAddr (const ExprDesc* Expr); +/* Return true if the expression denotes a constant address of some sort. This +** can be the address of a global variable (maybe with offset) or similar. +*/ + +int ED_IsQuasiConstAddr (const ExprDesc* Expr); +/* Return true if the expression denotes a quasi-constant address of some sort. +** This can be a constant address or a stack variable address. +*/ + int ED_IsNullPtr (const ExprDesc* Expr); /* Return true if the given expression is a NULL pointer constant */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index fa37c6bbd..7eef174e2 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -48,14 +48,14 @@ -static void LoadConstant (unsigned Flags, ExprDesc* Expr) -/* Load the primary register with some constant value. */ +static void LoadAddress (unsigned Flags, ExprDesc* Expr) +/* Load the primary register with some address value. */ { switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Number constant */ - g_getimmed (Flags | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + /* Numberic address */ + g_getimmed (Flags | CF_IMM | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: @@ -83,25 +83,38 @@ static void LoadConstant (unsigned Flags, ExprDesc* Expr) g_leasp (Expr->IVal); break; + case E_LOC_EXPR: + if (Expr->IVal != 0) { + /* We have an expression in the primary plus a constant + ** offset. Adjust the value in the primary accordingly. + */ + g_inc (Flags | CF_CONST, Expr->IVal); + } + break; + default: - Internal ("Unknown constant type: %04X", Expr->Flags); + Internal ("Unknown address type: %04X", Expr->Flags); } } void LoadExpr (unsigned Flags, struct ExprDesc* Expr) -/* Load an expression into the primary register if it is not already there. */ +/* Load an expression into the primary register if it is not already there. +** Note: This function can't modify the content in Expr since there are many +** instances of the "GetCodePos + LoadExpr (maybe indirectly) + RemoveCode" +** code pattern here and there which assumes that Expr should be unchanged, +** unfortunately. +*/ { - if (ED_IsLVal (Expr)) { + if (!ED_IsAddrExpr (Expr)) { - /* Dereferenced lvalue. If this is a bit field its type is unsigned. - ** But if the field is completely contained in the lower byte, we will - ** throw away the high byte anyway and may therefore load just the - ** low byte. + /* Lvalue. If this is a bit field its type is unsigned. But if the + ** field is completely contained in the lower byte, we will throw away + ** the high byte anyway and may therefore load just the low byte. */ if (ED_IsBitField (Expr)) { - Flags |= (Expr->BitOffs + Expr->BitWidth <= CHAR_BITS)? CF_CHAR : CF_INT; + Flags |= (Expr->BitOffs + Expr->BitWidth <= CHAR_BITS) ? CF_CHAR : CF_INT; Flags |= CF_UNSIGNED; } else { Flags |= TypeOf (Expr->Type); @@ -110,10 +123,16 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) Flags |= CF_TEST; } + /* Load the content of Expr */ switch (ED_GetLoc (Expr)) { + case E_LOC_NONE: + /* Immediate number constant */ + g_getimmed (Flags | CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + break; + case E_LOC_ABS: - /* Absolute: numeric address or const */ + /* Absolute numeric addressed variable */ g_getstatic (Flags | CF_ABSOLUTE, Expr->IVal, 0); break; @@ -139,7 +158,15 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) break; case E_LOC_PRIMARY: - /* The primary register - just test if necessary */ + /* The primary register */ + if (Expr->IVal != 0) { + /* We have an expression in the primary plus a constant + ** offset. Adjust the value in the primary accordingly. + */ + g_inc (Flags | CF_CONST, Expr->IVal); + + /* We might want to clear the offset, but we can't */ + } if (Flags & CF_TEST) { g_test (Flags); } @@ -148,6 +175,13 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) case E_LOC_EXPR: /* Reference to address in primary with offset in Expr */ g_getind (Flags, Expr->IVal); + + /* Since the content in primary is now overwritten with the + ** dereference value, we might want to change the expression + ** loc to E_LOC_PRIMARY as well. That way we could be able to + ** call this function as many times as we want. Unfortunately, + ** we can't. + */ break; default: @@ -173,24 +207,14 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ED_TestDone (Expr); } else { - /* An rvalue */ - if (ED_IsLocExpr (Expr)) { - if (Expr->IVal != 0) { - /* We have an expression in the primary plus a constant - ** offset. Adjust the value in the primary accordingly. - */ - Flags |= TypeOf (Expr->Type); - g_inc (Flags | CF_CONST, Expr->IVal); - } - } else { - /* Constant of some sort, load it into the primary */ - LoadConstant (Flags, Expr); - } + /* An address */ + Flags |= CF_INT | CF_UNSIGNED; + /* Constant of some sort, load it into the primary */ + LoadAddress (Flags, Expr); /* Are we testing this value? */ if (ED_NeedsTest (Expr)) { /* Yes, force a test */ - Flags |= TypeOf (Expr->Type); g_test (Flags); ED_TestDone (Expr); } diff --git a/src/cc65/locals.c b/src/cc65/locals.c index d0aab7f9c..9f8ee86ef 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -260,7 +260,7 @@ static void ParseAutoDecl (Declaration* Decl) Flags |= CF_CONST; } else { LoadExpr (CF_NONE, &Expr); - ED_MakeRVal (&Expr); + ED_MarkExprAsRVal (&Expr); } /* Push the value */ diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index c61514f43..c7aea5255 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -173,8 +173,8 @@ void ShiftExpr (struct ExprDesc* Expr) goto Next; } - /* If we're shifting an integer or unsigned to the right, the - ** lhs has a const address, and the shift count is larger than 8, + /* If we're shifting an integer or unsigned to the right, the lhs + ** has a quasi-const address, and the shift count is larger than 8, ** we can load just the high byte as a char with the correct ** signedness, and reduce the shift count by 8. If the remaining ** shift count is zero, we're done. @@ -182,7 +182,7 @@ void ShiftExpr (struct ExprDesc* Expr) if (Tok == TOK_SHR && IsTypeInt (Expr->Type) && ED_IsLVal (Expr) && - (ED_IsLocConst (Expr) || ED_IsLocStack (Expr)) && + ED_IsLocQuasiConst (Expr) && Expr2.IVal >= 8) { Type* OldType; @@ -227,8 +227,8 @@ void ShiftExpr (struct ExprDesc* Expr) } MakeRVal: - /* We have a rvalue in the primary now */ - ED_MakeRValExpr (Expr); + /* We have an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); Next: /* Set the type of the result */ diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 2d4317ef8..6d61f2750 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -343,7 +343,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && - !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); + !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -421,7 +421,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && - !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); + !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ Offs = ED_GetStackOffs (&Arg2.Expr, 0); @@ -520,7 +520,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("lda ptr1"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* Bail out, no need for further processing */ @@ -529,7 +529,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -743,7 +743,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("lda ptr1"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* Bail out, no need for further processing */ @@ -752,7 +752,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -955,7 +955,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); /* We expect the closing brace */ @@ -1069,7 +1069,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && - !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); + !(ED_IsLocNone (&Arg1.Expr) && Arg1.Expr.IVal < 256); /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg2.Expr, 0); @@ -1116,7 +1116,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** address calculation could overflow in the linker. */ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && - !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); + !(ED_IsLocNone (&Arg2.Expr) && Arg2.Expr.IVal < 256); /* Calculate the real stack offset */ int Offs = ED_GetStackOffs (&Arg1.Expr, 0); @@ -1153,7 +1153,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) } /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = GetFuncReturn (Expr->Type); ExitPoint: @@ -1250,7 +1250,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1279,7 +1279,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("ldx #$00"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1304,7 +1304,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1333,7 +1333,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("tya"); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; /* Bail out, no need for further processing */ @@ -1348,7 +1348,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) AddCodeLine ("jsr _%s", Func_strlen); /* The function result is an rvalue in the primary register */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); Expr->Type = type_size_t; ExitPoint: diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 21ad33f12..e8d581f54 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -70,7 +70,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** conversion void -> void. */ if (IsTypeVoid (NewType)) { - ED_MakeRVal (Expr); /* Never an lvalue */ + ED_MarkExprAsRVal (Expr); /* Never an lvalue */ goto ExitPoint; } @@ -105,10 +105,10 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); /* Value is now in primary and an rvalue */ - ED_MakeRValExpr (Expr); + ED_FinalizeRValLoad (Expr); } - } else if (ED_IsLocAbs (Expr)) { + } else if (ED_IsConstAbs (Expr)) { /* A cast of a constant numeric value to another type. Be sure ** to handle sign extension correctly. @@ -136,6 +136,15 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) } } + /* Do the integer constant <-> absolute address conversion if necessary */ + if (IsClassPtr (NewType)) { + Expr->Flags &= ~E_LOC_NONE; + Expr->Flags |= E_LOC_ABS | E_ADDRESS_OF; + } else if (IsClassInt (NewType)) { + Expr->Flags &= ~(E_LOC_ABS | E_ADDRESS_OF); + Expr->Flags |= E_LOC_NONE; + } + } else { /* The value is not a constant. If the sizes of the types are @@ -150,8 +159,8 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Emit typecast code. */ g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); - /* Value is now a rvalue in the primary */ - ED_MakeRValExpr (Expr); + /* Value is now an rvalue in the primary */ + ED_FinalizeRValLoad (Expr); } } From d23b5773312cc9dc2d3b38620ea66aa272786153 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Jul 2020 01:07:53 +0800 Subject: [PATCH 074/806] More compiler flags on address types to match the location types of expressions. --- src/cc65/codegen.c | 4 ++-- src/cc65/codegen.h | 39 +++++++++++++++++++++------------------ src/cc65/expr.c | 20 ++++++++++---------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 7af329b91..20bfdf434 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1349,7 +1349,7 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) rtype = CF_LONG; } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) { /* We must promote the lhs to long */ - if (lhs & CF_REG) { + if (lhs & CF_PRIMARY) { g_reglong (lhs); } else { g_toslong (lhs); @@ -2338,7 +2338,7 @@ void g_call (unsigned Flags, const char* Label, unsigned ArgSize) void g_callind (unsigned Flags, unsigned ArgSize, int Offs) /* Call subroutine indirect */ { - if ((Flags & CF_LOCAL) == 0) { + if ((Flags & CF_STACK) == 0) { /* Address is in a/x */ if ((Flags & CF_FIXARGC) == 0) { /* Pass arg count */ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index af539df8b..c63fc5398 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -62,35 +62,38 @@ #define CF_NONE 0x0000 /* No special flags */ /* Values for the actual type */ -#define CF_CHAR 0x0003 /* Operation on characters */ -#define CF_INT 0x0001 /* Operation on ints */ +#define CF_CHAR 0x0007 /* Operation on characters */ +#define CF_INT 0x0003 /* Operation on ints */ +#define CF_SHORT CF_INT /* Alias */ #define CF_PTR CF_INT /* Alias for readability */ -#define CF_LONG 0x0000 /* Operation on longs */ -#define CF_FLOAT 0x0004 /* Operation on a float */ +#define CF_LONG 0x0001 /* Operation on longs */ +#define CF_FLOAT 0x0010 /* Operation on a float */ /* Signedness */ #define CF_UNSIGNED 0x0008 /* Value is unsigned */ /* Masks for retrieving type information */ -#define CF_TYPEMASK 0x0007 /* Type information */ -#define CF_STYPEMASK 0x000F /* Includes signedness */ +#define CF_TYPEMASK 0x0017 /* Type information */ +#define CF_STYPEMASK 0x001F /* Includes signedness */ -#define CF_NOKEEP 0x0010 /* Value may get destroyed when storing */ -#define CF_CONST 0x0020 /* Constant value available */ -#define CF_CONSTADDR 0x0040 /* Constant address value available */ +#define CF_CONST 0x0040 /* Constant value available */ #define CF_TEST 0x0080 /* Test value */ #define CF_FIXARGC 0x0100 /* Function has fixed arg count */ #define CF_FORCECHAR 0x0200 /* Handle chars as chars, not ints */ +#define CF_NOKEEP 0x0400 /* Value may get destroyed when storing */ -/* Type of static address */ -#define CF_ADDRMASK 0xFC00 /* Type of address */ -#define CF_IMM 0x0000 /* Value is pure rvalue and has no address */ -#define CF_REG 0x0400 /* Value is in primary register */ -#define CF_STATIC 0x0800 /* Static local */ -#define CF_EXTERNAL 0x1000 /* Static external */ -#define CF_ABSOLUTE 0x2000 /* Numeric absolute address */ -#define CF_LOCAL 0x4000 /* Auto variable */ -#define CF_REGVAR 0x8000 /* Register variable */ +/* Type of address */ +#define CF_ADDRMASK 0xF000 /* Bit mask of address type */ +#define CF_IMM 0x0000 /* Value is pure rvalue and has no storage */ +#define CF_ABSOLUTE 0x1000 /* Numeric absolute address */ +#define CF_EXTERNAL 0x2000 /* External */ +#define CF_REGVAR 0x4000 /* Register variable */ +#define CF_LITERAL 0x7000 /* Literal */ +#define CF_PRIMARY 0x8000 /* Value is in primary register */ +#define CF_EXPR 0x9000 /* Value is addressed by primary register */ +#define CF_STATIC 0xA000 /* Local static */ +#define CF_CODE 0xB000 /* C code label location */ +#define CF_STACK 0xC000 /* Function-local auto on stack */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 38a52e744..d3b5255d8 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -88,10 +88,10 @@ static unsigned GlobalModeFlags (const ExprDesc* Expr) case E_LOC_GLOBAL: return CF_EXTERNAL; case E_LOC_STATIC: return CF_STATIC; case E_LOC_REGISTER: return CF_REGVAR; - case E_LOC_STACK: return CF_NONE; - case E_LOC_PRIMARY: return CF_NONE; - case E_LOC_EXPR: return CF_NONE; - case E_LOC_LITERAL: return CF_STATIC; /* Same as static */ + case E_LOC_STACK: return CF_STACK; + case E_LOC_PRIMARY: return CF_PRIMARY; + case E_LOC_EXPR: return CF_EXPR; + case E_LOC_LITERAL: return CF_LITERAL; default: Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); /* NOTREACHED */ @@ -189,7 +189,7 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) } if (NoPush) { /* Value is in primary register*/ - ltype |= CF_REG; + ltype |= CF_PRIMARY; } rtype = TypeOf (rhst); if (ED_IsLocNone (rhs)) { @@ -587,7 +587,7 @@ static void FunctionCall (ExprDesc* Expr) ** Since fastcall functions may never be variadic, we can use the ** index register for this purpose. */ - g_callind (CF_LOCAL, ParamSize, PtrOffs); + g_callind (CF_STACK, ParamSize, PtrOffs); } /* If we have a pointer on stack, remove it */ @@ -2068,7 +2068,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ if ((Gen->Flags & GEN_NOPUSH) == 0) { g_push (ltype, 0); } else { - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } /* Determine the type of the operation result. */ @@ -2100,7 +2100,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ } if ((Gen->Flags & GEN_NOPUSH) != 0) { RemoveCode (&Mark2); - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } } @@ -2276,7 +2276,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_CONST; if ((Gen->Flags & GEN_NOPUSH) != 0) { RemoveCode (&Mark2); - ltype |= CF_REG; /* Value is in register */ + ltype |= CF_PRIMARY; /* Value is in register */ } } @@ -2550,7 +2550,7 @@ static void parseadd (ExprDesc* Expr) flags |= CF_CONST; } else { /* Constant address label */ - flags |= GlobalModeFlags (Expr) | CF_CONSTADDR; + flags |= GlobalModeFlags (Expr); } /* Check for pointer arithmetic */ From 9198b3be00168d148000a501b4270c39b8fdf598 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 17 Jul 2020 15:10:27 +0800 Subject: [PATCH 075/806] Fixed '&function' and '&array'. --- src/cc65/expr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d3b5255d8..79650109f 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1841,9 +1841,12 @@ void hie10 (ExprDesc* Expr) /* Do it anyway, just to avoid further warnings */ Expr->Flags &= ~E_BITFIELD; } + /* It's allowed in C to take the address of an array this way */ + if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { + /* The & operator yields an rvalue address */ + ED_AddrExpr (Expr); + } Expr->Type = PointerTo (Expr->Type); - /* The & operator yields an rvalue address */ - ED_AddrExpr (Expr); } break; From 333fa9732642787d6ae47f9128b189f77acb1814 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 18 Jul 2020 10:34:57 +0800 Subject: [PATCH 076/806] Whitespaces/newlines fixes. --- src/cc65/declare.c | 2 +- src/cc65/expr.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 5827eb6dc..2a21e5a76 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1780,7 +1780,7 @@ static void DefineData (ExprDesc* Expr) case E_LOC_ABS: /* Absolute numeric address */ - g_defdata (CF_ABSOLUTE | TypeOf(Expr->Type) | CF_CONST, Expr->IVal, 0); + g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); break; case E_LOC_GLOBAL: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 79650109f..f6aa5aad6 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1029,7 +1029,6 @@ static void ArrayRef (ExprDesc* Expr) /* Adjust the offset */ Expr->IVal += Subscript.IVal; - } else { /* Scale the rhs value according to the element type */ From 66ecc0e52cc61e5969efc22c0045a739c7f6f9f3 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 18 Jul 2020 23:30:09 +0800 Subject: [PATCH 077/806] New utility to get the proper replacement type for passing structs/unions by value. New utility to get basic type names such as 'struct', 'union' and so on. --- src/cc65/datatype.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/datatype.h | 8 ++++++++ 2 files changed, 55 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2d54316cd..5fcfa7115 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -78,6 +78,33 @@ Type type_double[] = { TYPE(T_DOUBLE), TYPE(T_END) }; +const char* GetBasicTypeName (const Type* T) +/* Return a const name string of the basic type. +** Return "type" for unknown basic types. +*/ +{ + switch (GetType (T)) { + case T_TYPE_CHAR: return "char"; + case T_TYPE_SHORT: return "short"; + case T_TYPE_INT: return "integer"; + case T_TYPE_LONG: return "long"; + case T_TYPE_LONGLONG: return "long long"; + case T_TYPE_ENUM: return "enum"; + case T_TYPE_FLOAT: return "poinfloatter"; + case T_TYPE_DOUBLE: return "double"; + case T_TYPE_VOID: return "void"; + case T_TYPE_STRUCT: return "struct"; + case T_TYPE_UNION: return "union"; + case T_TYPE_ARRAY: return "array"; + case T_TYPE_PTR: return "pointer"; + case T_TYPE_FUNC: return "function"; + case T_TYPE_NONE: /* FALLTHROUGH */ + default: return "type"; + } +} + + + unsigned TypeLen (const Type* T) /* Return the length of the type string */ { @@ -198,6 +225,26 @@ Type* GetImplicitFuncType (void) +const Type* GetReplacementType (const Type* SType) +/* Get a replacement type for passing a struct/union in the primary register */ +{ + const Type* NewType; + /* If the size is less than or equal to that of a long, we will copy the + ** struct using the primary register, otherwise we will use memcpy. + */ + switch (SizeOf (SType)) { + case 1: NewType = type_uchar; break; + case 2: NewType = type_uint; break; + case 3: /* FALLTHROUGH */ + case 4: NewType = type_ulong; break; + default: NewType = SType; break; + } + + return NewType; +} + + + Type* PointerTo (const Type* T) /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 92b3d0122..62ea8d06d 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -209,6 +209,11 @@ struct SymEntry; +const char* GetBasicTypeName (const Type* T); +/* Return a const name string of the basic type. +** Return "type" for unknown basic types. +*/ + unsigned TypeLen (const Type* T); /* Return the length of the type string */ @@ -238,6 +243,9 @@ Type* GetCharArrayType (unsigned Len); Type* GetImplicitFuncType (void); /* Return a type string for an inplicitly declared function */ +const Type* GetReplacementType (const Type* SType); +/* Get a replacement type for passing a struct/union in the primary register */ + Type* PointerTo (const Type* T); /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. From 9f67b45ea0abd003a42b241d86bc763ef51b60bc Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 18 Jul 2020 23:30:09 +0800 Subject: [PATCH 078/806] Fixed returning by value structs/unions <= 4 bytes in size from functions. Larger ones are forbidden for now. --- src/cc65/assignment.c | 144 +++++++++++++++++++----------------------- src/cc65/expr.c | 57 +++++++++++------ src/cc65/loadexpr.c | 2 +- src/cc65/stmt.c | 16 ++++- 4 files changed, 118 insertions(+), 101 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 8e42a338f..2a278f4a5 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -54,6 +54,69 @@ +static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) +/* Copy the struct represented by RExpr to the one represented by LExpr */ +{ + /* If the size is that of a basic type (char, int, long), we will copy + ** the struct using the primary register, otherwise we use memcpy. In + ** the former case, push the address only if really needed. + */ + const Type* ltype = LExpr->Type; + const Type* stype = GetReplacementType (ltype); + int UseReg = (stype != LExpr->Type); + + if (UseReg) { + PushAddr (LExpr); + } else { + ED_MarkExprAsRVal (LExpr); + LoadExpr (CF_NONE, LExpr); + g_push (CF_PTR | CF_UNSIGNED, 0); + } + + /* Get the expression on the right of the '=' into the primary */ + hie1 (RExpr); + + /* Check for equality of the structs */ + if (TypeCmp (ltype, RExpr->Type) < TC_STRICT_COMPATIBLE) { + Error ("Incompatible types"); + } + + /* Do we copy using the primary? */ + if (UseReg) { + + /* Check if the right hand side is an lvalue */ + if (ED_IsLVal (RExpr)) { + /* Just load the value into the primary as the replacement type. */ + LoadExpr (TypeOf (stype) | CF_FORCECHAR, RExpr); + } + + /* Store it into the new location */ + Store (LExpr, stype); + + } else { + + /* Check if the right hand side is an lvalue */ + if (ED_IsLVal (RExpr)) { + /* We will use memcpy. Push the address of the rhs */ + ED_MarkExprAsRVal (RExpr); + LoadExpr (CF_NONE, RExpr); + } + + /* Push the address (or whatever is in ax in case of errors) */ + g_push (CF_PTR | CF_UNSIGNED, 0); + + /* Load the size of the struct into the primary */ + g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, CheckedSizeOf (ltype), 0); + + /* Call the memcpy function */ + g_call (CF_FIXARGC, Func_memcpy, 4); + } + + return 0; +} + + + void Assignment (ExprDesc* Expr) /* Parse an assignment */ { @@ -79,85 +142,8 @@ void Assignment (ExprDesc* Expr) ** family, allow it here. */ if (IsClassStruct (ltype)) { - - /* Get the size of the left hand side. */ - unsigned Size = SizeOf (ltype); - - /* If the size is that of a basic type (char, int, long), we will copy - ** the struct using the primary register, otherwise we use memcpy. In - ** the former case, push the address only if really needed. - */ - int UseReg = 1; - Type* stype; - switch (Size) { - case SIZEOF_CHAR: stype = type_uchar; break; - case SIZEOF_INT: stype = type_uint; break; - case SIZEOF_LONG: stype = type_ulong; break; - default: stype = ltype; UseReg = 0; break; - } - if (UseReg) { - PushAddr (Expr); - } else { - ED_MarkExprAsRVal (Expr); - LoadExpr (CF_NONE, Expr); - g_push (CF_PTR | CF_UNSIGNED, 0); - } - - /* Get the expression on the right of the '=' into the primary */ - hie1 (&Expr2); - - /* Check for equality of the structs */ - if (TypeCmp (ltype, Expr2.Type) < TC_STRICT_COMPATIBLE) { - Error ("Incompatible types"); - } - - /* Check if the right hand side is an lvalue */ - if (ED_IsLVal (&Expr2)) { - /* We have an lvalue. Do we copy using the primary? */ - if (UseReg) { - /* Just use the replacement type */ - Expr2.Type = stype; - - /* Load the value into the primary */ - LoadExpr (CF_FORCECHAR, &Expr2); - - /* Store it into the new location */ - Store (Expr, stype); - - } else { - - /* We will use memcpy. Push the address of the rhs */ - ED_MarkExprAsRVal (&Expr2); - LoadExpr (CF_NONE, &Expr2); - - /* Push the address (or whatever is in ax in case of errors) */ - g_push (CF_PTR | CF_UNSIGNED, 0); - - /* Load the size of the struct into the primary */ - g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, CheckedSizeOf (ltype), 0); - - /* Call the memcpy function */ - g_call (CF_FIXARGC, Func_memcpy, 4); - } - - } else { - - /* We have an rvalue. This can only happen if a function returns - ** a struct, since there is no other way to generate an expression - ** that has a struct as an rvalue result. We allow only 1, 2, and 4 - ** byte sized structs, and do direct assignment. - */ - if (UseReg) { - /* Do the store */ - Store (Expr, stype); - } else { - /* Print a diagnostic */ - Error ("Structs of this size are not supported"); - /* Adjust the stack so we won't run in an internal error later */ - pop (CF_PTR); - } - - } + /* Copy the struct by value */ + CopyStruct (Expr, &Expr2); } else if (ED_IsBitField (Expr)) { diff --git a/src/cc65/expr.c b/src/cc65/expr.c index f6aa5aad6..70b28a408 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -470,6 +470,7 @@ static void FunctionCall (ExprDesc* Expr) int PtrOffs = 0; /* Offset of function pointer on stack */ int IsFastcall = 0; /* True if it's a fast-call function */ int PtrOnStack = 0; /* True if a pointer copy is on stack */ + Type* ReturnType; /* Skip the left paren */ NextToken (); @@ -648,7 +649,19 @@ static void FunctionCall (ExprDesc* Expr) /* The function result is an rvalue in the primary register */ ED_FinalizeRValLoad (Expr); - Expr->Type = GetFuncReturn (Expr->Type); + ReturnType = GetFuncReturn (Expr->Type); + + /* Handle struct specially */ + if (IsTypeStruct (ReturnType)) { + /* If there is no replacement type, then it is just the address */ + if (ReturnType == GetReplacementType (ReturnType)) { + /* Dereference it */ + ED_IndExpr (Expr); + ED_MarkExprAsRVal (Expr); + } + } + + Expr->Type = ReturnType; } @@ -1206,12 +1219,20 @@ static void StructRef (ExprDesc* Expr) return; } - if (IsTypePtr (Expr->Type)) { - /* If we have a struct pointer that is an lvalue and not already in the - ** primary, load its content now. - */ - if (!ED_IsConst (Expr)) { - /* Load into the primary */ + /* A struct is usually an lvalue. If not, it is a struct passed in the + ** primary register, which is usually a result returned from a function. + ** However, it is possible that this rvalue is a result of certain + ** operations on an lvalue, and there are no reasons to disallow that. + ** So we just rely on the check on function returns to catch the errors + ** and dereference the rvalue address of the struct here. + */ + if (IsTypePtr (Expr->Type) || + (ED_IsRVal (Expr) && + ED_IsLocPrimary (Expr) && + Expr->Type == GetReplacementType (Expr->Type))) { + + if (!ED_IsConst (Expr) && !ED_IsLocPrimary (Expr)) { + /* If we have a non-const struct pointer, load its content now */ LoadExpr (CF_NONE, Expr); /* Clear the offset */ @@ -1241,27 +1262,25 @@ static void StructRef (ExprDesc* Expr) FinalType->C |= Q; } - /* A struct is usually an lvalue. If not, it is a struct referenced in the - ** primary register, which is likely to be returned from a function. - */ - if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) { + if (ED_IsRVal (Expr) && ED_IsLocPrimary (Expr) && !IsTypePtr (Expr->Type)) { unsigned Flags = 0; unsigned BitOffs; /* Get the size of the type */ - unsigned Size = SizeOf (Expr->Type); + unsigned StructSize = SizeOf (Expr->Type); + unsigned FieldSize = SizeOf (Field->Type); /* Safety check */ - CHECK (Field->V.Offs + Size <= SIZEOF_LONG); + CHECK (Field->V.Offs + FieldSize <= StructSize); /* The type of the operation depends on the type of the struct */ - switch (Size) { - case 1: Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; break; - case 2: Flags = CF_INT | CF_UNSIGNED | CF_CONST; break; + switch (StructSize) { + case 1: Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; break; + case 2: Flags = CF_INT | CF_UNSIGNED | CF_CONST; break; case 3: /* FALLTHROUGH */ - case 4: Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break; - default: Internal ("Invalid struct size: %u", Size); break; + case 4: Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break; + default: Internal ("Invalid struct size: %u", StructSize); break; } /* Generate a shift to get the field in the proper position in the @@ -1274,7 +1293,7 @@ static void StructRef (ExprDesc* Expr) /* Mask the value. This is unnecessary if the shift executed above ** moved only zeroes into the value. */ - if (BitOffs + Field->V.B.BitWidth != Size * CHAR_BITS) { + if (BitOffs + Field->V.B.BitWidth != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, (0x0001U << Field->V.B.BitWidth) - 1U); } diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 7eef174e2..bc0ee1dd0 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -116,7 +116,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) if (ED_IsBitField (Expr)) { Flags |= (Expr->BitOffs + Expr->BitWidth <= CHAR_BITS) ? CF_CHAR : CF_INT; Flags |= CF_UNSIGNED; - } else { + } else if ((Flags & CF_TYPEMASK) == 0) { Flags |= TypeOf (Expr->Type); } if (ED_NeedsTest (Expr)) { diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 657bc9963..a1384b0ed 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -309,7 +309,8 @@ static void WhileStatement (void) static void ReturnStatement (void) /* Handle the 'return' statement */ { - ExprDesc Expr; + ExprDesc Expr; + const Type* ReturnType; NextToken (); if (CurTok.Tok != TOK_SEMI) { @@ -328,7 +329,18 @@ static void ReturnStatement (void) TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); + if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { + /* Handle struct/union specially */ + ReturnType = GetReplacementType (Expr.Type); + if (ReturnType == Expr.Type) { + Error ("Returning %s of this size by value is not supported", GetBasicTypeName (Expr.Type)); + } + LoadExpr (TypeOf (ReturnType), &Expr); + + } else { + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + } } } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) { From b45d373fd6e9839d86301f539a7c458340eb6769 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 18 Jul 2020 23:30:09 +0800 Subject: [PATCH 079/806] Fixed passing by value structs/unions <= 4 bytes in size to functions. Larger ones are forbidden for now. --- src/cc65/expr.c | 16 +++++++++++----- src/cc65/function.c | 26 +++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 70b28a408..44137a773 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -395,12 +395,18 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } + /* Handle struct/union specially */ + if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { + /* Use the replacement type */ + Flags |= TypeOf (GetReplacementType (Expr.Type)); + } else { + /* Use the type of the argument for the push */ + Flags |= TypeOf (Expr.Type); + } + /* Load the value into the primary if it is not already there */ LoadExpr (Flags, &Expr); - /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); - /* If this is a fastcall function, don't push the last argument */ if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { unsigned ArgSize = sizeofarg (Flags); @@ -651,8 +657,8 @@ static void FunctionCall (ExprDesc* Expr) ED_FinalizeRValLoad (Expr); ReturnType = GetFuncReturn (Expr->Type); - /* Handle struct specially */ - if (IsTypeStruct (ReturnType)) { + /* Handle struct/union specially */ + if (IsTypeStruct (ReturnType) || IsTypeUnion (ReturnType)) { /* If there is no replacement type, then it is just the address */ if (ReturnType == GetReplacementType (ReturnType)) { /* Dereference it */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 8da084ac1..df667e8fa 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -380,6 +380,7 @@ void NewFunc (SymEntry* Func) { int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; + const Type* RType; /* Real type used for struct parameters */ /* Get the function descriptor from the function entry */ FuncDesc* D = Func->V.F.Func; @@ -475,7 +476,12 @@ void NewFunc (SymEntry* Func) /* Pointer to function */ Flags = CF_PTR; } else { - Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; + /* Handle struct/union specially */ + if (IsTypeStruct (D->LastParam->Type) || IsTypeUnion (D->LastParam->Type)) { + Flags = TypeOf (GetReplacementType (D->LastParam->Type)) | CF_FORCECHAR; + } else { + Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; + } } g_push (Flags, 0); } @@ -498,11 +504,25 @@ void NewFunc (SymEntry* Func) Param = D->SymTab->SymHead; while (Param && (Param->Flags & SC_PARAM) != 0) { + /* Check if we need copy for struct/union type */ + RType = Param->Type; + if (IsTypeStruct (RType) || IsTypeUnion (RType)) { + RType = GetReplacementType (RType); + + /* If there is no replacement type, then it is just the address. + ** We don't currently support this case. + */ + if (RType == Param->Type) { + Error ("Passing %s of this size by value is not supported", GetBasicTypeName (Param->Type)); + } + } + + /* Check for a register variable */ if (SymIsRegVar (Param)) { /* Allocate space */ - int Reg = F_AllocRegVar (CurrentFunc, Param->Type); + int Reg = F_AllocRegVar (CurrentFunc, RType); /* Could we allocate a register? */ if (Reg < 0) { @@ -513,7 +533,7 @@ void NewFunc (SymEntry* Func) Param->V.R.RegOffs = Reg; /* Generate swap code */ - g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (Param->Type)); + g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); } } From 0c3e1b491fce1c5ae6ad74ac2e37151c8e9efc03 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 18 Jul 2020 23:30:09 +0800 Subject: [PATCH 080/806] Disabled -Wstruct-param by default. --- src/cc65/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index 858a80826..c3ebebf77 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -67,7 +67,7 @@ IntStack WarningsAreErrors = INTSTACK(0); /* Treat warnings as errors */ IntStack WarnConstComparison= INTSTACK(1); /* - constant comparison results */ IntStack WarnNoEffect = INTSTACK(1); /* - statements without an effect */ IntStack WarnRemapZero = INTSTACK(1); /* - remapping character code zero */ -IntStack WarnStructParam = INTSTACK(1); /* - structs passed by val */ +IntStack WarnStructParam = INTSTACK(0); /* - structs passed by val */ IntStack WarnUnknownPragma = INTSTACK(1); /* - unknown #pragmas */ IntStack WarnUnusedLabel = INTSTACK(1); /* - unused labels */ IntStack WarnUnusedParam = INTSTACK(1); /* - unused parameters */ From 768e03a47415f9c5aef0263175612a43e45b788e Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 13:47:48 +0800 Subject: [PATCH 081/806] Small fixes and tidy-up based on PR review. Renamed GetReplacementType() to GetStructReplacementType(). Clarified in comments that most *Struct* facilities work for unions as well. Made it clear in some error messages with regards to structs/unions. --- src/cc65/assignment.c | 17 +++++------ src/cc65/datatype.c | 2 +- src/cc65/datatype.h | 4 +-- src/cc65/expr.c | 66 +++++++++++++++++++++++++------------------ src/cc65/function.c | 4 +-- src/cc65/stmt.c | 2 +- src/cc65/symtab.c | 10 +++---- src/cc65/symtab.h | 2 +- 8 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 2a278f4a5..ed374ef8a 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -55,15 +55,15 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) -/* Copy the struct represented by RExpr to the one represented by LExpr */ +/* Copy the struct/union represented by RExpr to the one represented by LExpr */ { /* If the size is that of a basic type (char, int, long), we will copy ** the struct using the primary register, otherwise we use memcpy. In ** the former case, push the address only if really needed. */ const Type* ltype = LExpr->Type; - const Type* stype = GetReplacementType (ltype); - int UseReg = (stype != LExpr->Type); + const Type* stype = GetStructReplacementType (ltype); + int UseReg = (stype != ltype); if (UseReg) { PushAddr (LExpr); @@ -105,7 +105,7 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Push the address (or whatever is in ax in case of errors) */ g_push (CF_PTR | CF_UNSIGNED, 0); - /* Load the size of the struct into the primary */ + /* Load the size of the struct or union into the primary */ g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, CheckedSizeOf (ltype), 0); /* Call the memcpy function */ @@ -137,12 +137,13 @@ void Assignment (ExprDesc* Expr) /* Skip the '=' token */ NextToken (); - /* cc65 does not have full support for handling structs by value. Since - ** assigning structs is one of the more useful operations from this - ** family, allow it here. + /* cc65 does not have full support for handling structs or unions. Since + ** assigning structs is one of the more useful operations from this family, + ** allow it here. + ** Note: IsClassStruct() is also true for union types. */ if (IsClassStruct (ltype)) { - /* Copy the struct by value */ + /* Copy the struct or union by value */ CopyStruct (Expr, &Expr2); } else if (ED_IsBitField (Expr)) { diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 5fcfa7115..2a989e9f8 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -225,7 +225,7 @@ Type* GetImplicitFuncType (void) -const Type* GetReplacementType (const Type* SType) +const Type* GetStructReplacementType (const Type* SType) /* Get a replacement type for passing a struct/union in the primary register */ { const Type* NewType; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 62ea8d06d..3464e8a16 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -243,7 +243,7 @@ Type* GetCharArrayType (unsigned Len); Type* GetImplicitFuncType (void); /* Return a type string for an inplicitly declared function */ -const Type* GetReplacementType (const Type* SType); +const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union in the primary register */ Type* PointerTo (const Type* T); @@ -483,7 +483,7 @@ INLINE int IsClassPtr (const Type* T) #if defined(HAVE_INLINE) INLINE int IsClassStruct (const Type* T) -/* Return true if this is a struct type */ +/* Return true if this is a struct or union type */ { return (GetClass (T) == T_CLASS_STRUCT); } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 44137a773..828e0fd75 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -398,7 +398,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) /* Handle struct/union specially */ if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { /* Use the replacement type */ - Flags |= TypeOf (GetReplacementType (Expr.Type)); + Flags |= TypeOf (GetStructReplacementType (Expr.Type)); } else { /* Use the type of the argument for the push */ Flags |= TypeOf (Expr.Type); @@ -660,7 +660,7 @@ static void FunctionCall (ExprDesc* Expr) /* Handle struct/union specially */ if (IsTypeStruct (ReturnType) || IsTypeUnion (ReturnType)) { /* If there is no replacement type, then it is just the address */ - if (ReturnType == GetReplacementType (ReturnType)) { + if (ReturnType == GetStructReplacementType (ReturnType)) { /* Dereference it */ ED_IndExpr (Expr); ED_MarkExprAsRVal (Expr); @@ -1198,7 +1198,7 @@ static void ArrayRef (ExprDesc* Expr) static void StructRef (ExprDesc* Expr) -/* Process struct field after . or ->. */ +/* Process struct/union field after . or ->. */ { ident Ident; SymEntry* Field; @@ -1214,38 +1214,41 @@ static void StructRef (ExprDesc* Expr) return; } - /* Get the symbol table entry and check for a struct field */ + /* Get the symbol table entry and check for a struct/union field */ strcpy (Ident, CurTok.Ident); NextToken (); Field = FindStructField (Expr->Type, Ident); if (Field == 0) { - Error ("Struct/union has no field named '%s'", Ident); + Error ("No field named '%s' found in %s", GetBasicTypeName (Expr->Type), Ident); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; } - /* A struct is usually an lvalue. If not, it is a struct passed in the - ** primary register, which is usually a result returned from a function. - ** However, it is possible that this rvalue is a result of certain - ** operations on an lvalue, and there are no reasons to disallow that. - ** So we just rely on the check on function returns to catch the errors - ** and dereference the rvalue address of the struct here. + /* A struct/union is usually an lvalue. If not, it is a struct/union passed + ** in the primary register, which is usually the result returned from a + ** function. However, it is possible that this rvalue is the result of + ** certain kind of operations on an lvalue such as assignment, and there + ** are no reasons to disallow such use cases. So we just rely on the check + ** upon function returns to catch the unsupported cases and dereference the + ** rvalue address of the struct/union here all the time. */ if (IsTypePtr (Expr->Type) || (ED_IsRVal (Expr) && ED_IsLocPrimary (Expr) && - Expr->Type == GetReplacementType (Expr->Type))) { + Expr->Type == GetStructReplacementType (Expr->Type))) { if (!ED_IsConst (Expr) && !ED_IsLocPrimary (Expr)) { - /* If we have a non-const struct pointer, load its content now */ + /* If we have a non-const struct/union pointer that is not in the + ** primary yet, load its content now. + */ LoadExpr (CF_NONE, Expr); /* Clear the offset */ Expr->IVal = 0; } - /* Dereference the expression */ + /* Dereference the address expression */ ED_IndExpr (Expr); } else if (!ED_IsLocQuasiConst (Expr) && !ED_IsLocPrimaryOrExpr (Expr)) { @@ -1255,7 +1258,7 @@ static void StructRef (ExprDesc* Expr) LoadExpr (CF_NONE, Expr); } - /* The type is the type of the field plus any qualifiers from the struct */ + /* The type is the field type plus any qualifiers from the struct/union */ if (IsClassStruct (Expr->Type)) { Q = GetQualifier (Expr->Type); } else { @@ -1280,13 +1283,22 @@ static void StructRef (ExprDesc* Expr) /* Safety check */ CHECK (Field->V.Offs + FieldSize <= StructSize); - /* The type of the operation depends on the type of the struct */ + /* The type of the operation depends on the type of the struct/union */ switch (StructSize) { - case 1: Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; break; - case 2: Flags = CF_INT | CF_UNSIGNED | CF_CONST; break; - case 3: /* FALLTHROUGH */ - case 4: Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break; - default: Internal ("Invalid struct size: %u", StructSize); break; + case 1: + Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; + break; + case 2: + Flags = CF_INT | CF_UNSIGNED | CF_CONST; + break; + case 3: + /* FALLTHROUGH */ + case 4: + Flags = CF_LONG | CF_UNSIGNED | CF_CONST; + break; + default: + Internal ("Invalid %s size: %u", GetBasicTypeName (Expr->Type), StructSize); + break; } /* Generate a shift to get the field in the proper position in the @@ -1312,16 +1324,16 @@ static void StructRef (ExprDesc* Expr) } else { - /* Set the struct field offset */ + /* Set the struct/union field offset */ Expr->IVal += Field->V.Offs; /* Use the new type */ Expr->Type = FinalType; - /* An struct member is actually a variable. So the rules for variables - ** with respect to the reference type apply: If it's an array, it is + /* The usual rules for variables with respect to the reference types + ** apply to struct/union fields as well: If a field is an array, it is ** virtually an rvalue address, otherwise it's an lvalue reference. (A - ** function would also be an rvalue address, but a struct field cannot + ** function would also be an rvalue address, but a struct/union cannot ** contain functions). */ if (IsTypeArray (Expr->Type)) { @@ -1376,7 +1388,7 @@ static void hie11 (ExprDesc *Expr) case TOK_DOT: if (!IsClassStruct (Expr->Type)) { - Error ("Struct expected"); + Error ("Struct or union expected"); } StructRef (Expr); break; @@ -1387,7 +1399,7 @@ static void hie11 (ExprDesc *Expr) Expr->Type = ArrayToPtr (Expr->Type); } if (!IsClassPtr (Expr->Type) || !IsClassStruct (Indirect (Expr->Type))) { - Error ("Struct pointer expected"); + Error ("Struct pointer or union pointer expected"); } StructRef (Expr); break; diff --git a/src/cc65/function.c b/src/cc65/function.c index df667e8fa..4cd5565c4 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -478,7 +478,7 @@ void NewFunc (SymEntry* Func) } else { /* Handle struct/union specially */ if (IsTypeStruct (D->LastParam->Type) || IsTypeUnion (D->LastParam->Type)) { - Flags = TypeOf (GetReplacementType (D->LastParam->Type)) | CF_FORCECHAR; + Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; } @@ -507,7 +507,7 @@ void NewFunc (SymEntry* Func) /* Check if we need copy for struct/union type */ RType = Param->Type; if (IsTypeStruct (RType) || IsTypeUnion (RType)) { - RType = GetReplacementType (RType); + RType = GetStructReplacementType (RType); /* If there is no replacement type, then it is just the address. ** We don't currently support this case. diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index a1384b0ed..6c839c420 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -331,7 +331,7 @@ static void ReturnStatement (void) /* Load the value into the primary */ if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { /* Handle struct/union specially */ - ReturnType = GetReplacementType (Expr.Type); + ReturnType = GetStructReplacementType (Expr.Type); if (ReturnType == Expr.Type) { Error ("Returning %s of this size by value is not supported", GetBasicTypeName (Expr.Type)); } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index f19a251bd..f363d9134 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -488,24 +488,24 @@ SymEntry* FindTagSym (const char* Name) SymEntry* FindStructField (const Type* T, const char* Name) -/* Find a struct field in the fields list */ +/* Find a struct/union field in the fields list */ { SymEntry* Field = 0; - /* The given type may actually be a pointer to struct */ + /* The given type may actually be a pointer to struct/union */ if (IsTypePtr (T)) { ++T; } - /* Non-structs do not have any struct fields... */ + /* Only structs/unions have struct/union fields... */ if (IsClassStruct (T)) { /* Get a pointer to the struct/union type */ const SymEntry* Struct = GetSymEntry (T); CHECK (Struct != 0); - /* Now search in the struct symbol table. Beware: The table may not - ** exist. + /* Now search in the struct/union symbol table. Beware: The table may + ** not exist. */ if (Struct->V.S.SymTab) { Field = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 62e731042..94e5f2d9b 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -134,7 +134,7 @@ SymEntry* FindTagSym (const char* Name); /* Find the symbol with the given name in the tag table */ SymEntry* FindStructField (const Type* TypeArray, const char* Name); -/* Find a struct field in the fields list */ +/* Find a struct/union field in the fields list */ unsigned short FindSPAdjustment (const char* Name); /* Search for an entry in the table of SP adjustments */ From bbcb39978cf3f6ed14487a3c0195b0de25429bd2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 18 Jul 2020 23:12:07 +0200 Subject: [PATCH 082/806] Add test of signed bitfields for #1095 --- test/todo/bug1095.c | 101 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/todo/bug1095.c diff --git a/test/todo/bug1095.c b/test/todo/bug1095.c new file mode 100644 index 000000000..a00a10585 --- /dev/null +++ b/test/todo/bug1095.c @@ -0,0 +1,101 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of signed bit-fields; see https://github.com/cc65/cc65/issues/1095 +*/ + +#include + +static unsigned char failures = 0; + +static struct signed_ints { + signed int a : 3; + signed int b : 3; + signed int c : 3; +} si = {-4, -1, 3}; + +static void test_signed_bitfield(void) +{ + if (si.a >= 0) { + printf("Got si.a = %d, expected negative.\n", si.a); + failures++; + } + if (si.a != -4) { + printf("Got si.a = %d, expected -4.\n", si.a); + failures++; + } + + if (si.b >= 0) { + printf("Got si.b = %d, expected negative.\n", si.b); + failures++; + } + if (si.b != -1) { + printf("Got si.b = %d, expected -1.\n", si.b); + failures++; + } + + if (si.b <= 0) { + printf("Got si.c = %d, expected positive.\n", si.c); + failures++; + } + if (si.c != 3) { + printf("Got si.c = %d, expected 3.\n", si.c); + failures++; + } + + si.a = -3; + si.b = 1; + si.c = -2; + + if (si.a >= 0) { + printf("Got si.a = %d, expected negative.\n", si.a); + failures++; + } + if (si.a != -3) { + printf("Got si.a = %d, expected -3.\n", si.a); + failures++; + } + + if (si.b <= 0) { + printf("Got si.b = %d, expected positive.\n", si.b); + failures++; + } + if (si.b != 1) { + printf("Got si.b = %d, expected 1.\n", si.b); + failures++; + } + + if (si.b >= 0) { + printf("Got si.c = %d, expected negative.\n", si.c); + failures++; + } + if (si.c != -2) { + printf("Got si.c = %d, expected -2.\n", si.c); + failures++; + } +} + +int main(void) +{ + test_signed_bitfield(); + printf("failures: %u\n", failures); + return failures; +} From 29c50ab25f24deb168cb0293c8c3259dad4c4b8f Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 17:56:15 +0800 Subject: [PATCH 083/806] Corrected the error message about struct/union members not found. --- src/cc65/expr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 828e0fd75..f28aa3965 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1219,7 +1219,7 @@ static void StructRef (ExprDesc* Expr) NextToken (); Field = FindStructField (Expr->Type, Ident); if (Field == 0) { - Error ("No field named '%s' found in %s", GetBasicTypeName (Expr->Type), Ident); + Error ("No field named '%s' found in %s", Ident, GetBasicTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; From ee208aecd6285848a8470d42ce2a7213fcf63e90 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 19 Jul 2020 13:16:12 +0200 Subject: [PATCH 084/806] Removed unnecessary tool. It's not the domain of cc65 to provide tools already available elsewhere. --- util/cbm/cbmcvt.c | 55 ----------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 util/cbm/cbmcvt.c diff --git a/util/cbm/cbmcvt.c b/util/cbm/cbmcvt.c deleted file mode 100644 index 11c0325e4..000000000 --- a/util/cbm/cbmcvt.c +++ /dev/null @@ -1,55 +0,0 @@ -/* cbmcvt.c -- PetSCII <--> ISO-8859-1 Conversion Filter Tool */ -/* 2010-09-06, Greg King */ - -#include -#include - -/* Translation table ISO-8859-1 -> PetSCII */ -static const unsigned char CTPET[256] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x14,0x09,0x0D,0x11,0x93,0x0A,0x0E,0x0F, - 0x10,0x0B,0x12,0x13,0x08,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, - 0x40,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, - 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0x5B,0xBF,0x5D,0x5E,0xA4, - 0xAD,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, - 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0xB3,0xDD,0xAB,0xB1,0xDF, - 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, - 0x90,0x91,0x92,0x0C,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, - 0xA0,0xA1,0xA2,0xA3,0x5F,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0x7D,0xAC,0x60,0xAE,0xAF, - 0xB0,0x7E,0xB2,0x7B,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0x5C, - 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, - 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0xDC,0x7C,0xDE,0x7F, - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, - 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF -}; - -static unsigned char CTISO[256]; - - -int main (int argc, char *argv[]) { - int C; - size_t I = 0u; - - if (isatty(fileno(stdin))) { - fputs("cbmcvt v2.1 -- Conversion Filter (stdin --> stdout)\n" - " -p converts ISO-8859-1 to PetSCII\n" - " else, converts in other direction.\n", stderr); - return 0; - } - if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'p') { - while ((C = fgetc (stdin)) != EOF) { - fputc (CTPET[C], stdout); - } - } else { - /* Create translation table PetSCII -> ISO-8859-1 */ - for (; I < sizeof CTPET; ++I) { - CTISO[CTPET[I]] = I; - } - - while ((C = fgetc (stdin)) != EOF) { - fputc (CTISO[C], stdout); - } - } - return 0; -} From fd0a6955da15c2dfd2beeab1f25fc2ed8aa319dc Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 19 Jul 2020 14:30:22 -0400 Subject: [PATCH 085/806] Changed "IsTypeStruct() || IsTypeUnion()" expressions into shorter "IsClassStruct()" expressions. Type-classes are groups of types that can be handled in the same way (similar syntax). --- src/cc65/expr.c | 4 ++-- src/cc65/function.c | 4 ++-- src/cc65/stmt.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index f28aa3965..ad3710e80 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -396,7 +396,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } /* Handle struct/union specially */ - if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { + if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ Flags |= TypeOf (GetStructReplacementType (Expr.Type)); } else { @@ -658,7 +658,7 @@ static void FunctionCall (ExprDesc* Expr) ReturnType = GetFuncReturn (Expr->Type); /* Handle struct/union specially */ - if (IsTypeStruct (ReturnType) || IsTypeUnion (ReturnType)) { + if (IsClassStruct (ReturnType)) { /* If there is no replacement type, then it is just the address */ if (ReturnType == GetStructReplacementType (ReturnType)) { /* Dereference it */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 4cd5565c4..ff4c23656 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -477,7 +477,7 @@ void NewFunc (SymEntry* Func) Flags = CF_PTR; } else { /* Handle struct/union specially */ - if (IsTypeStruct (D->LastParam->Type) || IsTypeUnion (D->LastParam->Type)) { + if (IsClassStruct (D->LastParam->Type)) { Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; @@ -506,7 +506,7 @@ void NewFunc (SymEntry* Func) /* Check if we need copy for struct/union type */ RType = Param->Type; - if (IsTypeStruct (RType) || IsTypeUnion (RType)) { + if (IsClassStruct (RType)) { RType = GetStructReplacementType (RType); /* If there is no replacement type, then it is just the address. diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 6c839c420..b02ae1cd6 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -329,7 +329,7 @@ static void ReturnStatement (void) TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); /* Load the value into the primary */ - if (IsTypeStruct (Expr.Type) || IsTypeUnion (Expr.Type)) { + if (IsClassStruct (Expr.Type)) { /* Handle struct/union specially */ ReturnType = GetStructReplacementType (Expr.Type); if (ReturnType == Expr.Type) { From 3c52ad1d9ef15170589f4d348072fa6bd3a9caa9 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 21:23:08 +0800 Subject: [PATCH 086/806] New utility ED_DisBitField() to make an expression no longer a bit-field. --- src/cc65/exprdesc.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index a61c5d814..1541c2e4e 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -278,6 +278,16 @@ INLINE int ED_IsBitField (const ExprDesc* Expr) # define ED_IsBitField(Expr) (((Expr)->Flags & E_BITFIELD) != 0) #endif +#if defined(HAVE_INLINE) +INLINE void ED_DisBitField (ExprDesc* Expr) +/* Make the expression no longer a bit field */ +{ + Expr->Flags &= ~E_BITFIELD; +} +#else +# define ED_DisBitField(Expr) ((Expr)->Flags &= ~E_BITFIELD) +#endif + void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); /* Make this expression a bit field expression */ From 62a6e3748737ab864759744b0a15d536c98a8fb2 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 21:23:08 +0800 Subject: [PATCH 087/806] Made the code handling '&expression' slightly tidier. --- src/cc65/expr.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index ad3710e80..b05083323 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1867,23 +1867,23 @@ void hie10 (ExprDesc* Expr) NextToken (); ExprWithCheck (hie10, Expr); /* The & operator may be applied to any lvalue, and it may be - ** applied to functions, even if they're no lvalues. + ** applied to functions and arrays, even if they're not lvalues. */ - if (ED_IsRVal (Expr) && !IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { - Error ("Illegal address"); - } else { + if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { + if (ED_IsRVal (Expr)) { + Error ("Illegal address"); + break; + } + if (ED_IsBitField (Expr)) { Error ("Cannot take address of bit-field"); /* Do it anyway, just to avoid further warnings */ - Expr->Flags &= ~E_BITFIELD; + ED_DisBitField (Expr); } - /* It's allowed in C to take the address of an array this way */ - if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { - /* The & operator yields an rvalue address */ - ED_AddrExpr (Expr); - } - Expr->Type = PointerTo (Expr->Type); + /* The & operator yields an rvalue address */ + ED_AddrExpr (Expr); } + Expr->Type = PointerTo (Expr->Type); break; case TOK_SIZEOF: From b67b8ddd38c5f974ef686c29fdf2798ac7f77faf Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 21:23:20 +0800 Subject: [PATCH 088/806] Disabled applying 'sizeof' to bit-fields. --- src/cc65/expr.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index b05083323..0b668f402 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1898,14 +1898,19 @@ void hie10 (ExprDesc* Expr) CodeMark Mark; GetCodePos (&Mark); hie10 (Expr); - /* If the expression is a literal string, release it, so it - ** won't be output as data if not used elsewhere. - */ - if (ED_IsLocLiteral (Expr)) { - ReleaseLiteral (Expr->LVal); + if (ED_IsBitField (Expr)) { + Error ("Cannot apply 'sizeof' to bit-field"); + Size = 0; + } else { + /* If the expression is a literal string, release it, so it + ** won't be output as data if not used elsewhere. + */ + if (ED_IsLocLiteral (Expr)) { + ReleaseLiteral (Expr->LVal); + } + /* Calculate the size */ + Size = CheckedSizeOf (Expr->Type); } - /* Calculate the size */ - Size = CheckedSizeOf (Expr->Type); /* Remove any generated code */ RemoveCode (&Mark); } From 22457833459ed453579a318aef9d5b3f68224fe4 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 19 Jul 2020 21:26:55 +0800 Subject: [PATCH 089/806] Fixed ability to do actual type conversion from bit-fields to integers. Note this doesn't try to fix the signedness issues. --- src/cc65/typeconv.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e8d581f54..3bc7d47cb 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -59,8 +59,8 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Emit code to convert the given expression to a new type. */ { Type* OldType; - unsigned OldSize; - unsigned NewSize; + unsigned OldBits; + unsigned NewBits; /* Remember the old type */ @@ -83,8 +83,12 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Get the sizes of the types. Since we've excluded void types, checking ** for known sizes makes sense here. */ - OldSize = CheckedSizeOf (OldType); - NewSize = CheckedSizeOf (NewType); + if (ED_IsBitField (Expr)) { + OldBits = Expr->BitWidth; + } else { + OldBits = CheckedSizeOf (OldType) * CHAR_BITS; + } + NewBits = CheckedSizeOf (NewType) * CHAR_BITS; /* lvalue? */ if (ED_IsLVal (Expr)) { @@ -97,7 +101,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** If both sizes are equal, do also leave the value alone. ** If the new size is larger, we must convert the value. */ - if (NewSize > OldSize) { + if (NewBits > OldBits) { /* Load the value into the primary */ LoadExpr (CF_NONE, Expr); @@ -114,10 +118,6 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** to handle sign extension correctly. */ - /* Get the current and new size of the value */ - unsigned OldBits = OldSize * 8; - unsigned NewBits = NewSize * 8; - /* Check if the new datatype will have a smaller range. If it ** has a larger range, things are OK, since the value is ** internally already represented by a long. @@ -151,7 +151,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** not equal, add conversion code. Be sure to convert chars ** correctly. */ - if (OldSize != NewSize) { + if (OldBits != NewBits) { /* Load the value into the primary */ LoadExpr (CF_NONE, Expr); @@ -167,6 +167,11 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ExitPoint: /* The expression has always the new type */ ReplaceType (Expr, NewType); + + /* Bit-fields are converted to integers */ + if (ED_IsBitField (Expr)) { + ED_DisBitField (Expr); + } } From 71c2d27705fafeacfcf04fac554925328cd01fbb Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 20 Jul 2020 07:36:14 +0800 Subject: [PATCH 090/806] Removed an ED_IsBitField() test according to PR review. --- src/cc65/typeconv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 3bc7d47cb..e915d7392 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -169,9 +169,7 @@ ExitPoint: ReplaceType (Expr, NewType); /* Bit-fields are converted to integers */ - if (ED_IsBitField (Expr)) { - ED_DisBitField (Expr); - } + ED_DisBitField (Expr); } From 78342fa82c3d7e6c9b586cfacbf229d1642860ed Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 20 Jul 2020 20:40:41 +0800 Subject: [PATCH 091/806] Fix for "auto" variables made "static" with the "-Cl" options. --- src/cc65/locals.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 9f8ee86ef..0007879d8 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -340,7 +340,7 @@ static void ParseAutoDecl (Declaration* Decl) LoadExpr (CF_NONE, &Expr); /* Store the value into the variable */ - g_putstatic (TypeOf (Sym->Type), DataLabel, 0); + g_putstatic (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); } /* Mark the variable as referenced */ From 5a9d76ad5211c944d5ef004c5742139f8cdc8dcd Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 20 Jul 2020 15:50:11 +0200 Subject: [PATCH 092/806] added test for issue #1108 --- test/val/bug1108.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/val/bug1108.c diff --git a/test/val/bug1108.c b/test/val/bug1108.c new file mode 100644 index 000000000..17ef022c3 --- /dev/null +++ b/test/val/bug1108.c @@ -0,0 +1,40 @@ + +/* bug #1108 - Problem with static locals? */ + +#include + +#pragma static-locals (on) + +unsigned char x = 0; + +unsigned char PrintVar1(void) +{ + unsigned char cx = x + 1; + printf("cx:%d x:%d\n", cx, x); + return cx == 0; +} + +unsigned char PrintVar2(void) +{ + unsigned char cx = x + 1; + unsigned char cy; + cy = x + 1; + printf("cx:%d cy:%d x:%d\n", cx, cy, x); + return cx != cy; +} + +#pragma static-locals (off) + +unsigned char n; +unsigned char ret = 0; + +int main(void) +{ + for (n = 0; n < 10; n++) { + ++x; + ret |= PrintVar1(); + ret |= PrintVar2(); + } + return ret; +} + From 517df130ccdba93d99d27ab7fb42676cc84ce18e Mon Sep 17 00:00:00 2001 From: Greg King Date: Mon, 20 Jul 2020 17:16:11 -0400 Subject: [PATCH 093/806] Made a regression test increment a variable after, instead of before, using it. That change allows the initial value of zero to be tested. --- test/val/bug1108.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/val/bug1108.c b/test/val/bug1108.c index 17ef022c3..1cd23c8e5 100644 --- a/test/val/bug1108.c +++ b/test/val/bug1108.c @@ -7,34 +7,31 @@ unsigned char x = 0; -unsigned char PrintVar1(void) +static unsigned char PrintVar1(void) { unsigned char cx = x + 1; + printf("cx:%d x:%d\n", cx, x); return cx == 0; } -unsigned char PrintVar2(void) +static unsigned char PrintVar2(void) { unsigned char cx = x + 1; unsigned char cy; + cy = x + 1; printf("cx:%d cy:%d x:%d\n", cx, cy, x); return cx != cy; } -#pragma static-locals (off) - -unsigned char n; -unsigned char ret = 0; +static unsigned char ret = 0; int main(void) { - for (n = 0; n < 10; n++) { - ++x; + do { ret |= PrintVar1(); ret |= PrintVar2(); - } + } while (++x < 10); return ret; } - From b60b303c5d24f718b0c14022b47ca1a838112ceb Mon Sep 17 00:00:00 2001 From: Greg King Date: Mon, 20 Jul 2020 21:40:44 -0400 Subject: [PATCH 094/806] Added a missing asterisk to a "hardware" struct definition. --- include/cx16.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cx16.h b/include/cx16.h index f129b26f4..87a6d37ca 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -290,7 +290,7 @@ struct __emul { unsigned char keymap; /* Keyboard layout number */ const char detect[2]; /* "16" if running on x16emu */ }; -#define EMULATOR (*(volatile struct __emul)0x9FB0) +#define EMULATOR (*(volatile struct __emul *)0x9FB0) /* An array window into the half Mebibyte or two Mebibytes of banked RAM */ #define BANK_RAM ((unsigned char[0x2000])0xA000) From 638e2546684f466495a253b37325ace82cb79ced Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 14:48:49 +0200 Subject: [PATCH 095/806] rework pptest2 into a runable test and move to test/misc (since it does not compile with cc65) --- test/misc/Makefile | 5 +++ test/misc/pptest2.c | 73 +++++++++++++++++++++++++++++++++++++ testcode/compiler/pptest2.c | 19 ---------- 3 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 test/misc/pptest2.c delete mode 100644 testcode/compiler/pptest2.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 898ea1b2b..96f61dba1 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -78,6 +78,11 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(if $(QUIET),echo misc/bug760.$1.$2.prg) -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) + $(if $(QUIET),echo misc/pptest2.$1.$2.prg) + -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this should fail to compile, because cc65 does not support returning structs $(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) $(if $(QUIET),echo misc/bug264.$1.$2.prg) diff --git a/test/misc/pptest2.c b/test/misc/pptest2.c new file mode 100644 index 000000000..7857c67b2 --- /dev/null +++ b/test/misc/pptest2.c @@ -0,0 +1,73 @@ + +/* preprocessor test #2 */ + +#include +#include +#include + +int y = 7; +int z[] = {1, 2, 3}; +int res; + +int f(int i) { + printf("f: %d\n", i); + return i + 1; +} +int t(int i) { + printf("t: %d\n", i); + return i + 1; +} +int m(int i, int j) { + printf("m: %d %d\n", i, j); + return i + j + 1; +} + +#define x 3 +#define f(a) f(x * (a)) +#undef x +#define x 2 +#define g f +#define z z[0] +#define h g(~ +#define m(a) a(w) +#define w 0,1 +#define t(a) a +#define p() int +#define q(x) x +#define r(x,y) x ## y +#define str(x) # x + +int main(void) +{ + res = f(y+1) + f(f(z)) % t(t(g) (0) + t)(1); + printf("res: %d expected: 19\n", res); + if (res != 19) { + return EXIT_FAILURE; + } + + res = g(x+(3,4)-w) | h 5) & m(f)^m(m); + printf("res: %d expected: 3\n", res); + if (res != 3) { + return EXIT_FAILURE; + } + + p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; + printf("i[]: %d %d %d %d\n", i[0], i[1], i[2], i[3]); + printf("expected: %d %d %d %d\n", 1, 23, 4, 5); + if ((i[0] != 1) || (i[1] != 23) || (i[2] != 4) || (i[3] != 5)) { + return EXIT_FAILURE; + } + + char c[2][6] = { str(hello), str() }; + printf ("c[0]: %s expected: %s\n", c[0], "hello"); + printf ("c[1]: %s expected: %s\n", c[1], ""); + if (strcmp(c[0], "hello") != 0) { + return EXIT_FAILURE; + } + if (strcmp(c[1], "") != 0) { + return EXIT_FAILURE; + } + + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/testcode/compiler/pptest2.c b/testcode/compiler/pptest2.c deleted file mode 100644 index e127d53fb..000000000 --- a/testcode/compiler/pptest2.c +++ /dev/null @@ -1,19 +0,0 @@ -#define x 3 -#define f(a) f(x * (a)) -#undef x -#define x 2 -#define g f -#define z z[0] -#define h g(~ -#define m(a) a(w) -#define w 0,1 -#define t(a) a -#define p() int -#define q(x) x -#define r(x,y) x ## y -#define str(x) # x - -f(y+1) + f(f(z)) % t(t(g) (0) + t)(1); -g(x+(3,4)-w) | h 5) & m(f)^m(m); -p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; -char c[2][6] = { str(hello), str() }; From 010ed6d7290c9939495f8feb0d6ad3fba9510a05 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 14:58:15 +0200 Subject: [PATCH 096/806] reworked pptest1/4/5 into executable tests and moved to test/val --- test/val/pptest1.c | 27 +++++++++++++++++++++++++++ test/val/pptest4.c | 24 ++++++++++++++++++++++++ test/val/pptest5.c | 24 ++++++++++++++++++++++++ testcode/compiler/pptest1.c | 6 ------ testcode/compiler/pptest4.c | 3 --- testcode/compiler/pptest5.c | 3 --- 6 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 test/val/pptest1.c create mode 100644 test/val/pptest4.c create mode 100644 test/val/pptest5.c delete mode 100644 testcode/compiler/pptest1.c delete mode 100644 testcode/compiler/pptest4.c delete mode 100644 testcode/compiler/pptest5.c diff --git a/test/val/pptest1.c b/test/val/pptest1.c new file mode 100644 index 000000000..52b381b0b --- /dev/null +++ b/test/val/pptest1.c @@ -0,0 +1,27 @@ + +/* preprocessor test #1 */ + +#define hash_hash # ## # +#define mkstr(a) # a +#define in_between(a) mkstr(a) +#define join(c, d) in_between(c hash_hash d) + +#define x "first" +#define y "second" + +char p[] = join(x, y); // Comment + +#include +#include +#include + +int main(void) +{ + printf("expected: %s\n", "\"first\" ## \"second\""); + printf("p: %s\n", p); + if (!strcmp(p, "\"first\" ## \"second\"")) { + return EXIT_SUCCESS; + } + printf("all fine\n"); + return EXIT_FAILURE; +} diff --git a/test/val/pptest4.c b/test/val/pptest4.c new file mode 100644 index 000000000..827be7200 --- /dev/null +++ b/test/val/pptest4.c @@ -0,0 +1,24 @@ + +/* preprocessor test #4 */ + +#define t(x,y,z) x ## y ## z +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; + +int e[] = { 123, 45, 67, 89, 10, 11, 12, }; + +#include +#include +#include + +unsigned char i; + +int main(void) +{ + for (i = 0; i < 7; ++i) { + printf("j: %d expect: %d\n", j[i], e[i]); + if (j[i] != e[i]) return EXIT_FAILURE; + } + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/test/val/pptest5.c b/test/val/pptest5.c new file mode 100644 index 000000000..82f642c8e --- /dev/null +++ b/test/val/pptest5.c @@ -0,0 +1,24 @@ + +/* preprocessor test #5 */ + +#define t(x,y,z) x ## y ## z +int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), + t(10,,), t(,11,), t(,,12), t(,,) }; + +int e[] = { 123, 45, 67, 89, 10, 11, 12, }; + +#include +#include +#include + +unsigned char i; + +int main(void) +{ + for (i = 0; i < 7; ++i) { + printf("j: %d expect: %d\n", j[i], e[i]); + if (j[i] != e[i]) return EXIT_FAILURE; + } + printf("all fine\n"); + return EXIT_SUCCESS; +} diff --git a/testcode/compiler/pptest1.c b/testcode/compiler/pptest1.c deleted file mode 100644 index e42135688..000000000 --- a/testcode/compiler/pptest1.c +++ /dev/null @@ -1,6 +0,0 @@ -#define hash_hash # ## # -#define mkstr(a) # a -#define in_between(a) mkstr(a) -#define join(c, d) in_between(c hash_hash d) - -char p[] = join(x, y); // Comment diff --git a/testcode/compiler/pptest4.c b/testcode/compiler/pptest4.c deleted file mode 100644 index b8540b5c5..000000000 --- a/testcode/compiler/pptest4.c +++ /dev/null @@ -1,3 +0,0 @@ -#define t(x,y,z) x ## y ## z -int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), - t(10,,), t(,11,), t(,,12), t(,,) }; diff --git a/testcode/compiler/pptest5.c b/testcode/compiler/pptest5.c deleted file mode 100644 index 1f0bd4328..000000000 --- a/testcode/compiler/pptest5.c +++ /dev/null @@ -1,3 +0,0 @@ -#define t(x,y,z) x ## y ## z -int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), - t(10,,), t(,11,), t(,,12), t(,,) }; From e4fc7a0feca596d18098c08598a97f388617e89f Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 15:30:35 +0200 Subject: [PATCH 097/806] reworked pptest3 into an exectutable test and moved to test/val --- test/val/pptest3.c | 44 +++++++++++++++++++++++++++++++++++++ testcode/compiler/pptest3.c | 16 -------------- 2 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 test/val/pptest3.c delete mode 100644 testcode/compiler/pptest3.c diff --git a/test/val/pptest3.c b/test/val/pptest3.c new file mode 100644 index 000000000..b48677703 --- /dev/null +++ b/test/val/pptest3.c @@ -0,0 +1,44 @@ + +/* preprocessor test #3 */ + +#include +#include +#include + +char *x1 = "123"; +char *x2 = "456"; +FILE *s; +char *str = "789"; + +#define str(s) # s +#define xstr(s) str(s) +#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ + x ## s, x ## t) +#define INCFILE(n) vers ## n // Comment +#define glue(a,b) a ## b +#define xglue(a,b) glue(a,b) +#define HIGHLOW "hello" +#define LOW LOW ", world" + +int main(void) { + s = stdout; + + debug (1, 2); + + fputs (str (strncmp("abc\0d", "abc", '\4') // Comment + == 0) str (: @\n), s); + + str = glue (HIGH, LOW); + printf("str: %s\n", str); + if (strcmp(str, "hello") != 0) { + return EXIT_FAILURE; + } + + str = xglue (HIGH, LOW); + printf("str: %s\n", str); + if (strcmp(str, "hello, world") != 0) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/testcode/compiler/pptest3.c b/testcode/compiler/pptest3.c deleted file mode 100644 index 62aa7f705..000000000 --- a/testcode/compiler/pptest3.c +++ /dev/null @@ -1,16 +0,0 @@ -#define str(s) # s -#define xstr(s) str(s) -#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \ - x ## s, x ## t) -#define INCFILE(n) vers ## n // Comment -#define glue(a,b) a ## b -#define xglue(a,b) glue(a,b) -#define HIGHLOW "hello" -#define LOW LOW ", world" - -debug (1, 2); -fputs (str (strncmp("abc\0d", "abc", '\4') // Comment - == 0) str (: @\n), s); -glue (HIGH, LOW); -xglue (HIGH, LOW); - From 0250c87ac6326d0a9114457d9d9af5b259044baa Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 20 Jul 2020 22:34:57 +0800 Subject: [PATCH 098/806] Fixed SC_* type masks by making them all bitwise-exclusive. --- src/cc65/compile.c | 2 +- src/cc65/declare.c | 2 +- src/cc65/expr.c | 2 +- src/cc65/symentry.h | 49 ++++++++++++++++++++++++--------------------- src/cc65/symtab.c | 6 +++--- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index ef66d38d2..4806017eb 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -112,7 +112,7 @@ static void Parse (void) ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); /* Don't accept illegal storage classes */ - if ((Spec.StorageClass & SC_TYPE) == 0) { + if ((Spec.StorageClass & SC_TYPEMASK) == 0) { if ((Spec.StorageClass & SC_AUTO) != 0 || (Spec.StorageClass & SC_REGISTER) != 0) { Error ("Illegal storage class"); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2a21e5a76..3b1cf63e7 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -490,7 +490,7 @@ static void ParseEnumDecl (void) } /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUM, EnumVal++); + AddConstSym (Ident, type_int, SC_ENUM|SC_CONST, EnumVal++); /* Check for end of definition */ if (CurTok.Tok != TOK_COMMA) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 0b668f402..d31b153a5 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -758,7 +758,7 @@ static void Primary (ExprDesc* E) /* Check for illegal symbol types */ CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL); - if (Sym->Flags & SC_TYPE) { + if (Sym->Flags & SC_ESUTYPEMASK) { /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 62bf0b0e7..27c6ccc1e 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -69,39 +69,42 @@ struct CodeEntry; /* Storage classes and flags */ -#define SC_AUTO 0x0001U /* Auto variable */ -#define SC_REGISTER 0x0002U /* Register variable */ -#define SC_STATIC 0x0004U /* Static */ -#define SC_EXTERN 0x0008U /* Extern linkage */ +#define SC_NONE 0x0000U /* Nothing */ +#define SC_STRUCT 0x0001U /* Struct */ +#define SC_UNION 0x0002U /* Union */ +#define SC_TYPEDEF 0x0004U /* A typedef */ +#define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ +#define SC_ENUM 0x0008U /* An enum */ +#define SC_BITFIELD 0x0010U /* A bit-field inside a struct or union */ +#define SC_TYPEMASK 0x001FU /* Mask for above types */ -#define SC_ENUM 0x0030U /* An enum */ #define SC_CONST 0x0020U /* A numeric constant with a type */ -#define SC_LABEL 0x0040U /* A goto label */ +#define SC_LABEL 0x0040U /* A goto code label */ #define SC_PARAM 0x0080U /* A function parameter */ #define SC_FUNC 0x0100U /* A function */ - #define SC_DEFTYPE 0x0200U /* Parameter has default type (=int, old style) */ -#define SC_STORAGE 0x0400U /* Symbol with associated storage */ -#define SC_DEFAULT 0x0800U /* Flag: default storage class was used */ +#define SC_STRUCTFIELD 0x0400U /* Struct or union field */ + +#define SC_DECL 0x0800U /* Symbol is declared in global scope */ #define SC_DEF 0x1000U /* Symbol is defined */ #define SC_REF 0x2000U /* Symbol is referenced */ -#define SC_TYPE 0x4000U /* This is a type, struct, typedef, etc. */ -#define SC_STRUCT 0x4001U /* Struct */ -#define SC_UNION 0x4002U /* Union */ -#define SC_STRUCTFIELD 0x4003U /* Struct or union field */ -#define SC_BITFIELD 0x4004U /* A bit-field inside a struct or union */ -#define SC_TYPEDEF 0x4005U /* A typedef */ -#define SC_TYPEMASK 0x400FU /* Mask for above types */ +#define SC_ZEROPAGE 0x4000U /* Symbol marked as zeropage */ -#define SC_ZEROPAGE 0x8000U /* Symbol marked as zeropage */ +#define SC_STORAGE 0x8000U /* Symbol with associated storage */ -#define SC_HAVEATTR 0x10000U /* Symbol has attributes */ +#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_GOTO 0x20000U -#define SC_SPADJUSTMENT 0x40000U -#define SC_GOTO_IND 0x80000U /* Indirect goto */ +#define SC_HAVEATTR 0x100000U /* Symbol has attributes */ + +#define SC_GOTO 0x200000U +#define SC_SPADJUSTMENT 0x400000U +#define SC_GOTO_IND 0x800000U /* Indirect goto */ @@ -245,10 +248,10 @@ INLINE int SymIsRegVar (const SymEntry* Sym) /* Return true if the given entry is a register variable */ /* ### HACK! Fix the ugly type flags! */ { - return ((Sym->Flags & (SC_REGISTER|SC_TYPE)) == SC_REGISTER); + return ((Sym->Flags & (SC_REGISTER|SC_TYPEMASK)) == SC_REGISTER); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_TYPE)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_ESUTYPEMASK)) == SC_REGISTER) #endif int SymIsOutputFunc (const SymEntry* Sym); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index f363d9134..1408f9f4c 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -821,7 +821,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Set the symbol attributes */ Entry->Type = TypeDup (T); - if ((Flags & SC_AUTO) == SC_AUTO) { + if ((Flags & SC_AUTO) == SC_AUTO || (Flags & SC_TYPEDEF) == SC_TYPEDEF) { Entry->V.Offs = Offs; } else if ((Flags & SC_REGISTER) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; @@ -872,7 +872,7 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } /* We have a symbol with this name already */ - if (Entry->Flags & SC_TYPE) { + if (Entry->Flags & SC_TYPEMASK) { Error ("Multiple definition for '%s'", Name); return Entry; } @@ -1109,7 +1109,7 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST|SC_TYPE)) == 0) { + if ((Sym->Flags & (SC_CONST|SC_TYPEMASK)) == 0) { if (Sym->Flags & SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); From 65081aebed14588cf048bc5fe6fcff474c6e38e8 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 20 Jul 2020 22:34:57 +0800 Subject: [PATCH 099/806] Made able to recognize global declarations of static arrays. Fixed Issue #975. --- src/cc65/compile.c | 25 ++++++++++++++++--------- src/cc65/expr.c | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 4806017eb..f7e4bd35d 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -151,14 +151,17 @@ static void Parse (void) ** This means that "extern int i;" will not get storage allocated. */ if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && - (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF && - ((Spec.Flags & DS_DEF_STORAGE) != 0 || - (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || - ((Decl.StorageClass & SC_EXTERN) != 0 && - CurTok.Tok == TOK_ASSIGN))) { - - /* We will allocate storage */ - Decl.StorageClass |= SC_STORAGE; + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + if ((Spec.Flags & DS_DEF_STORAGE) != 0 || + (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || + ((Decl.StorageClass & SC_EXTERN) != 0 && + CurTok.Tok == TOK_ASSIGN)) { + /* We will allocate storage */ + Decl.StorageClass |= SC_STORAGE; + } else { + /* It's a declaration */ + Decl.StorageClass |= SC_DECL; + } } /* If this is a function declarator that is not followed by a comma @@ -243,7 +246,11 @@ static void Parse (void) if (!IsTypeArray (Decl.Type)) { Error ("Variable '%s' has unknown size", Decl.Ident); } - Entry->Flags &= ~(SC_STORAGE | SC_DEF); + /* Do this only if the same array has not been defined */ + if (!SymIsDef (Entry)) { + Entry->Flags &= ~(SC_STORAGE | SC_DEF); + Entry->Flags |= SC_DECL; + } } else { /* A global (including static) uninitialized variable is ** only a tentative definition. For example, this is valid: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d31b153a5..a35ad59a9 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -802,7 +802,7 @@ static void Primary (ExprDesc* E) E->Name = Sym->V.R.RegOffs; } else if ((Sym->Flags & SC_STATIC) == SC_STATIC) { /* Static variable */ - if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { + if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) { E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL; E->Name = (uintptr_t) Sym->Name; } else { From 18bd76bb9043c7549bbd2db35c6218f9f228f3f2 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jul 2020 13:09:41 +0800 Subject: [PATCH 100/806] Minor fixes and improvements. --- src/cc65/pragma.c | 2 +- src/cc65/symentry.c | 24 +++++++++++++++++++----- src/cc65/symentry.h | 20 +++++++++----------- src/cc65/symtab.c | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index b05ef6122..93c83906f 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -523,7 +523,7 @@ static void WrappedCallPragma (StrBuf* B) Entry = FindSym(Name); /* Check if the name is valid */ - if (Entry && Entry->Flags & SC_FUNC) { + if (Entry && (Entry->Flags & SC_FUNC) == SC_FUNC) { PushWrappedCall(Entry, (unsigned char) Val); Entry->Flags |= SC_REF; diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 18cc026ea..fbcc84c98 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -104,16 +104,21 @@ void FreeSymEntry (SymEntry* E) void DumpSymEntry (FILE* F, const SymEntry* E) /* Dump the given symbol table entry to the file in readable form */ { - static const struct { + typedef const struct { const char* Name; unsigned Val; - } Flags [] = { - /* Beware: Order is important! */ + } SCFlagTable; + + static SCFlagTable ESUTypes[] = { { "SC_TYPEDEF", SC_TYPEDEF }, - { "SC_BITFIELD", SC_BITFIELD }, - { "SC_STRUCTFIELD", SC_STRUCTFIELD }, { "SC_UNION", SC_UNION }, { "SC_STRUCT", SC_STRUCT }, + }; + + static SCFlagTable Flags[] = { + /* Beware: Order is important! */ + { "SC_BITFIELD", SC_BITFIELD }, + { "SC_STRUCTFIELD", SC_STRUCTFIELD }, { "SC_AUTO", SC_AUTO }, { "SC_REGISTER", SC_REGISTER }, { "SC_STATIC", SC_STATIC }, @@ -124,6 +129,7 @@ void DumpSymEntry (FILE* F, const SymEntry* E) { "SC_PARAM", SC_PARAM }, { "SC_FUNC", SC_FUNC }, { "SC_STORAGE", SC_STORAGE }, + { "SC_DECL", SC_DECL }, { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, { "SC_ZEROPAGE", SC_ZEROPAGE }, @@ -143,6 +149,14 @@ void DumpSymEntry (FILE* F, const SymEntry* E) /* Print the flags */ SymFlags = E->Flags; fprintf (F, " Flags:"); + 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); + } + } + } for (I = 0; I < sizeof (Flags) / sizeof (Flags[0]) && SymFlags != 0; ++I) { if ((SymFlags & Flags[I].Val) == Flags[I].Val) { SymFlags &= ~Flags[I].Val; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 27c6ccc1e..820ed5025 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -74,24 +74,22 @@ struct CodeEntry; #define SC_UNION 0x0002U /* Union */ #define SC_TYPEDEF 0x0004U /* A typedef */ #define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ -#define SC_ENUM 0x0008U /* An enum */ +#define SC_ENUM 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_CONST 0x0020U /* A numeric constant with a type */ +#define SC_FUNC 0x0020U /* A function */ #define SC_LABEL 0x0040U /* A goto code label */ -#define SC_PARAM 0x0080U /* A function parameter */ -#define SC_FUNC 0x0100U /* A function */ +#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_DECL 0x0800U /* Symbol is declared in global scope */ +#define SC_ZEROPAGE 0x0800U /* Symbol marked as zeropage */ #define SC_DEF 0x1000U /* Symbol is defined */ #define SC_REF 0x2000U /* Symbol is referenced */ - -#define SC_ZEROPAGE 0x4000U /* Symbol marked as zeropage */ - +#define SC_DECL 0x4000U /* Symbol is declared in global scope */ #define SC_STORAGE 0x8000U /* Symbol with associated storage */ #define SC_AUTO 0x010000U /* Auto variable */ @@ -217,10 +215,10 @@ INLINE int SymIsBitField (const SymEntry* Sym) INLINE int SymIsTypeDef (const SymEntry* Sym) /* Return true if the given entry is a typedef entry */ { - return ((Sym->Flags & SC_TYPEDEF) == SC_TYPEDEF); + return ((Sym->Flags & SC_TYPEMASK) == SC_TYPEDEF); } #else -# define SymIsTypeDef(Sym) (((Sym)->Flags & SC_TYPEDEF) == SC_TYPEDEF) +# define SymIsTypeDef(Sym) (((Sym)->Flags & SC_TYPEMASK) == SC_TYPEDEF) #endif #if defined(HAVE_INLINE) @@ -251,7 +249,7 @@ INLINE int SymIsRegVar (const SymEntry* Sym) return ((Sym->Flags & (SC_REGISTER|SC_TYPEMASK)) == SC_REGISTER); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_ESUTYPEMASK)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_TYPEMASK)) == SC_REGISTER) #endif int SymIsOutputFunc (const SymEntry* Sym); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 1408f9f4c..6395f9a4a 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -821,7 +821,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Set the symbol attributes */ Entry->Type = TypeDup (T); - if ((Flags & SC_AUTO) == SC_AUTO || (Flags & SC_TYPEDEF) == SC_TYPEDEF) { + if ((Flags & SC_AUTO) == SC_AUTO || (Flags & SC_TYPEMASK) == SC_TYPEDEF) { Entry->V.Offs = Offs; } else if ((Flags & SC_REGISTER) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; From c66d0881b9bc34f3d2213eedcedc236b56da1f03 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jul 2020 15:16:47 +0800 Subject: [PATCH 101/806] Made the enum/enumerator types clearer and improved DumpSymEntry() output. --- src/cc65/declare.c | 2 +- src/cc65/symentry.c | 37 +++++++++++++++++++++++++------------ src/cc65/symentry.h | 5 +++-- src/cc65/symtab.c | 6 +++--- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 3b1cf63e7..cd5142952 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -490,7 +490,7 @@ static void ParseEnumDecl (void) } /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUM|SC_CONST, EnumVal++); + AddConstSym (Ident, type_int, SC_ENUMERATOR|SC_CONST, EnumVal++); /* Check for end of definition */ if (CurTok.Tok != TOK_COMMA) diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index fbcc84c98..c8d4f9e2a 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -113,26 +113,29 @@ void DumpSymEntry (FILE* F, const SymEntry* E) { "SC_TYPEDEF", SC_TYPEDEF }, { "SC_UNION", SC_UNION }, { "SC_STRUCT", SC_STRUCT }, + { "SC_ENUM", SC_ENUM }, }; - static SCFlagTable Flags[] = { - /* Beware: Order is important! */ + static SCFlagTable Types[] = { { "SC_BITFIELD", SC_BITFIELD }, { "SC_STRUCTFIELD", SC_STRUCTFIELD }, - { "SC_AUTO", SC_AUTO }, - { "SC_REGISTER", SC_REGISTER }, - { "SC_STATIC", SC_STATIC }, - { "SC_EXTERN", SC_EXTERN }, - { "SC_ENUM", SC_ENUM }, + { "SC_ENUMERATOR", SC_ENUMERATOR }, { "SC_CONST", SC_CONST }, { "SC_LABEL", SC_LABEL }, { "SC_PARAM", SC_PARAM }, { "SC_FUNC", SC_FUNC }, + }; + + static SCFlagTable Storages[] = { + { "SC_AUTO", SC_AUTO }, + { "SC_REGISTER", SC_REGISTER }, + { "SC_STATIC", SC_STATIC }, + { "SC_EXTERN", SC_EXTERN }, { "SC_STORAGE", SC_STORAGE }, + { "SC_ZEROPAGE", SC_ZEROPAGE }, { "SC_DECL", SC_DECL }, { "SC_DEF", SC_DEF }, { "SC_REF", SC_REF }, - { "SC_ZEROPAGE", SC_ZEROPAGE }, }; unsigned I; @@ -149,18 +152,28 @@ 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); + break; } } } - for (I = 0; I < sizeof (Flags) / sizeof (Flags[0]) && SymFlags != 0; ++I) { - if ((SymFlags & Flags[I].Val) == Flags[I].Val) { - SymFlags &= ~Flags[I].Val; - fprintf (F, " %s", Flags[I].Name); + /* Other type flags */ + for (I = 0; I < sizeof (Types) / sizeof (Types[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Types[I].Val) == Types[I].Val) { + SymFlags &= ~Types[I].Val; + fprintf (F, " %s", Types[I].Name); + } + } + /* Storage flags */ + for (I = 0; I < sizeof (Storages) / sizeof (Storages[0]) && SymFlags != 0; ++I) { + if ((SymFlags & Storages[I].Val) == Storages[I].Val) { + SymFlags &= ~Storages[I].Val; + fprintf (F, " %s", Storages[I].Name); } } if (SymFlags != 0) { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 820ed5025..c112ff013 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -72,9 +72,10 @@ struct CodeEntry; #define SC_NONE 0x0000U /* Nothing */ #define SC_STRUCT 0x0001U /* Struct */ #define SC_UNION 0x0002U /* Union */ -#define SC_TYPEDEF 0x0004U /* A typedef */ +#define SC_ENUM 0x0003U /* Enum */ +#define SC_TYPEDEF 0x0004U /* Typedef */ #define SC_ESUTYPEMASK 0x0007U /* Mask for above types */ -#define SC_ENUM 0x0008U /* An enumerator */ +#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 */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 6395f9a4a..fdbbff7dc 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -633,7 +633,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val /* Add an constant symbol to the symbol table and return it */ { /* Enums must be inserted in the global symbol table */ - SymTable* Tab = ((Flags & SC_ENUM) == SC_ENUM)? SymTab0 : SymTab; + SymTable* Tab = ((Flags & SC_ENUMERATOR) == SC_ENUMERATOR) ? SymTab0 : SymTab; /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); @@ -867,8 +867,8 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* If the existing symbol is an enumerated constant, ** then avoid a compiler crash. See GitHub issue #728. */ - if (Entry->Flags & SC_ENUM) { - Fatal ("Can't redeclare enum constant '%s' as global variable", Name); + if (Entry->Flags & SC_ENUMERATOR) { + Fatal ("Can't redeclare enumerator constant '%s' as global variable", Name); } /* We have a symbol with this name already */ From 07e18774f76e09d8cc149366c6b420e002348de0 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jul 2020 22:41:31 +0800 Subject: [PATCH 102/806] Added spaces around '|' with regex replacement. --- src/cc65/declare.c | 2 +- src/cc65/symentry.h | 4 ++-- src/cc65/symtab.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index cd5142952..323e9af14 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -490,7 +490,7 @@ static void ParseEnumDecl (void) } /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUMERATOR|SC_CONST, EnumVal++); + AddConstSym (Ident, type_int, SC_ENUMERATOR | SC_CONST, EnumVal++); /* Check for end of definition */ if (CurTok.Tok != TOK_COMMA) diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index c112ff013..13829e958 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -247,10 +247,10 @@ 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_REGISTER | SC_TYPEMASK)) == SC_REGISTER); } #else -# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER|SC_TYPEMASK)) == SC_REGISTER) +# define SymIsRegVar(Sym) (((Sym)->Flags & (SC_REGISTER | SC_TYPEMASK)) == SC_REGISTER) #endif int SymIsOutputFunc (const SymEntry* Sym); diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index fdbbff7dc..26a09cd87 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -717,7 +717,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) for (i = 0; i < CollCount (Entry->V.L.DefsOrRefs); i++) { DOR = CollAt (Entry->V.L.DefsOrRefs, i); - if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & (SC_GOTO|SC_GOTO_IND))) { + if ((DOR->Flags & SC_DEF) && (Flags & SC_REF) && (Flags & (SC_GOTO | SC_GOTO_IND))) { /* We're processing a goto and here is its destination label. ** This means the difference between SP values is already known, ** so we simply emit the SP adjustment code. @@ -739,7 +739,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) } - if ((DOR->Flags & SC_REF) && (DOR->Flags & (SC_GOTO|SC_GOTO_IND)) && (Flags & SC_DEF)) { + if ((DOR->Flags & SC_REF) && (DOR->Flags & (SC_GOTO | SC_GOTO_IND)) && (Flags & SC_DEF)) { /* We're processing a label, let's update all gotos encountered ** so far */ @@ -1109,7 +1109,7 @@ void EmitDebugInfo (void) } Sym = SymTab->SymHead; while (Sym) { - if ((Sym->Flags & (SC_CONST|SC_TYPEMASK)) == 0) { + if ((Sym->Flags & (SC_CONST | SC_TYPEMASK)) == 0) { if (Sym->Flags & SC_AUTO) { AddTextLine ("%s, \"%s\", \"00\", auto, %d", Head, Sym->Name, Sym->V.Offs); From b2d799824197bb899459e34fbfdf085f20880cf1 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 19:15:41 +0200 Subject: [PATCH 103/806] update makefile to use $(NOT) as discussed with Oliver --- test/misc/Makefile | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index 96f61dba1..1405e05bf 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -66,37 +66,42 @@ define PRG_template # should compile, but gives an error $(WORKDIR)/bug975.$1.$2.prg: bug975.c | $(WORKDIR) $(if $(QUIET),echo misc/bug975.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug250.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug760.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/pptest2.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # this should fail to compile, because cc65 does not support returning structs $(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiles but should give an error." $(if $(QUIET),echo misc/bug264.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # this should fail to compile, because there are errors in the code $(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiles but should give an error." $(if $(QUIET),echo misc/bug1048.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # internal compiler error $(WORKDIR)/bug1075.$1.$2.prg: bug1075.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1075.$1.$2.prg) - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) @@ -106,13 +111,13 @@ $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) # these need reference data that can't be generated by a host-compiled program, # in a useful way -$(WORKDIR)/limits.$1.$2.prg: limits.c $(DIFF) +$(WORKDIR)/limits.$1.$2.prg: limits.c $(DIFF) | $(WORKDIR) $(if $(QUIET),echo misc/limits.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/limits.$1.$2.out $(DIFF) $(WORKDIR)/limits.$1.$2.out limits.ref -$(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) +$(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) | $(WORKDIR) $(if $(QUIET),echo misc/goto.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out $(DIFF) $(WORKDIR)/goto.$1.$2.out goto.ref @@ -121,11 +126,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) $(WORKDIR)/fields.$1.$2.prg: fields.c | $(WORKDIR) @echo "FIXME: " $$@ "currently will fail." $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) + $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) + $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently will fail." - -$(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# -$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) + @echo "FIXME: " $$@ "currently does not compile." + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template From a0c80a8c7248e76744056e3495519629c988fe28 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 19:24:39 +0200 Subject: [PATCH 104/806] move (now working) tests to test/val --- test/misc/Makefile | 12 +----------- test/{misc => val}/bug975.c | 0 test/{misc => val}/fields.c | 0 3 files changed, 1 insertion(+), 11 deletions(-) rename test/{misc => val}/bug975.c (100%) rename test/{misc => val}/fields.c (100%) diff --git a/test/misc/Makefile b/test/misc/Makefile index 1405e05bf..42fbe2c55 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -63,11 +63,6 @@ $(DIFF): ../bdiff.c | $(WORKDIR) define PRG_template -# should compile, but gives an error -$(WORKDIR)/bug975.$1.$2.prg: bug975.c | $(WORKDIR) - $(if $(QUIET),echo misc/bug975.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but gives an error $(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." @@ -98,7 +93,7 @@ $(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1048.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# internal compiler error +# compiles, but we cant easily check if it works $(WORKDIR)/bug1075.$1.$2.prg: bug1075.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1075.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) @@ -123,11 +118,6 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) | $(WORKDIR) $(DIFF) $(WORKDIR)/goto.$1.$2.out goto.ref # the rest are tests that fail currently for one reason or another -$(WORKDIR)/fields.$1.$2.prg: fields.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently will fail." - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) - $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) diff --git a/test/misc/bug975.c b/test/val/bug975.c similarity index 100% rename from test/misc/bug975.c rename to test/val/bug975.c diff --git a/test/misc/fields.c b/test/val/fields.c similarity index 100% rename from test/misc/fields.c rename to test/val/fields.c From 42c162c15e8d30eea651118c059a43de3b721cb9 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 19:58:36 +0200 Subject: [PATCH 105/806] rework test for issue #1075 to return an exit code --- test/misc/Makefile | 1 + test/misc/bug1075.c | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index 42fbe2c55..879ca7945 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -97,6 +97,7 @@ $(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) $(WORKDIR)/bug1075.$1.$2.prg: bug1075.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1075.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) diff --git a/test/misc/bug1075.c b/test/misc/bug1075.c index be4cd75f4..6ff5ec8e7 100644 --- a/test/misc/bug1075.c +++ b/test/misc/bug1075.c @@ -1,10 +1,31 @@ /* bug #1075 Internal compiler error */ +#include +#include +#include + long rhs; -int main(void) +int test(void) { /* the whole lhs is errorneously treated as an absolute address (integer constant) neglecting its dereference */ return *(char *)0xD77C + rhs; -} +} + +int res; + +int main(void) +{ + memset(*(char *)0xD76C, 11, 0x80); + rhs = 0x10; + *(char *)(0xD77C + rhs) = 13; + *(char *)0xD77C = 23; + *(char *)0xD78C = 42; + res = test(); + printf("res: %d\n", res); + if (res != (23 + 0x10)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} From 145084c41c1b1ea2b559ac4ec62654756ceb2404 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 20:02:51 +0200 Subject: [PATCH 106/806] move test for issue #1075 to test/var --- test/misc/Makefile | 6 ------ test/{misc => val}/bug1075.c | 0 2 files changed, 6 deletions(-) rename test/{misc => val}/bug1075.c (100%) diff --git a/test/misc/Makefile b/test/misc/Makefile index 879ca7945..7e19d6360 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -93,12 +93,6 @@ $(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1048.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# compiles, but we cant easily check if it works -$(WORKDIR)/bug1075.$1.$2.prg: bug1075.c | $(WORKDIR) - $(if $(QUIET),echo misc/bug1075.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) - # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) diff --git a/test/misc/bug1075.c b/test/val/bug1075.c similarity index 100% rename from test/misc/bug1075.c rename to test/val/bug1075.c From 9e43c0a56977c69885b31f3c9710103fafb7a042 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 21:04:41 +0200 Subject: [PATCH 107/806] added a test related to pr#1102 - we can now return structs by value when they are max. 4 bytes --- test/val/pr1102.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/val/pr1102.c diff --git a/test/val/pr1102.c b/test/val/pr1102.c new file mode 100644 index 000000000..2a78e997b --- /dev/null +++ b/test/val/pr1102.c @@ -0,0 +1,77 @@ + +/* pr#1102 - Passing structs/unions <= 4 bytes to/from functions */ + +#include +#include + +typedef struct test1_s { + char a; + int b; + char c; +} test1_t; + +test1_t test1(int a, int b, int c) +{ + test1_t res; + res.a = a; + res.b = b; + res.c = c; + return res; +} + +int test2(test1_t s) +{ + printf("a: %d b: %d c: %d\n", s.a, s.b, s.c); + if ((s.a != 13) || (s.b != 4711) || (s.c != 99)) { + return 0; + } + return 1; +} + +typedef struct test2_s { + char a; + char b; + char c; + char d; +} test2_t; + +test2_t test3(test1_t s) +{ + test2_t ret; + printf("a: %d b: %d c: %d\n", s.a, s.b, s.c); + ret.a = s.c; + ret.b = s.b & 0xff; + ret.c = s.b >> 8; + ret.d = s.a; + return ret; +} + +int main(void) { + test1_t t1; + test2_t t2; + + t1 = test1(12, 1842, 23); + printf("a: %d b: %d c: %d\n", t1.a, t1.b, t1.c); + if ((t1.a != 12) || (t1.b != 1842) || (t1.c != 23)) { + return EXIT_FAILURE; + } + + t1.a = 13; + t1.b = 4711; + t1.c = 99; + if (test2(t1) != 1) { + return EXIT_FAILURE; + } + + t1.a = 66; + t1.b = 0x7788; + t1.c = 22; + t2 = test3(t1); + printf("a: %d b: %d c: %d d: %d\n", t2.a, t2.b, t2.c, t2.d); + if ((t2.a != 22) || (t2.b != 0x88) || (t2.c != 0x77) || (t2.d != 66)) { + return EXIT_FAILURE; + } + + printf("all fine\n"); + return EXIT_SUCCESS; +} From 2bbea6779a0034e7353907776d89ef71dc1bf695 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 23:04:03 +0200 Subject: [PATCH 108/806] properly configure sitest so it could work, if we had the respective features :) --- test/misc/sitest.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/test/misc/sitest.c b/test/misc/sitest.c index 3332c064e..d13acf92c 100644 --- a/test/misc/sitest.c +++ b/test/misc/sitest.c @@ -19,8 +19,9 @@ NOTE: This is not a thorough validation test of the facilities. */ +#define STANDARD_CC65 +#define NO_WCHAR #define NO_INTERNAL_WCHAR -/*#define STANDALONE*/ #include #include /* for CHAR_BIT */ @@ -37,7 +38,7 @@ #ifdef NO_WCHAR -#warn "this test checks C99 features, but NO_WCHAR is defined so the test will most definetly fails." +#warn "this test checks C99 features, but NO_WCHAR is defined so the test will most definitely fail." #endif @@ -51,16 +52,6 @@ #include /* test idempotency */ -#ifdef STANDALONE - -FILE *outfile=NULL; -#define opentest(x) outfile=stdout; -#define closetest(x) - -#else - -#endif - #if __STDC_VERSION__ >= 199901 #ifndef __Q8_QT #define __Q8_QT long long @@ -1588,4 +1579,4 @@ main() { return status; } -#endif \ No newline at end of file +#endif From 7e1f4760e75d679aee7b4e325ca72b34dde92646 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 23:31:58 +0200 Subject: [PATCH 109/806] remove common.h from test/misc, its no more used --- test/misc/common.h | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 test/misc/common.h diff --git a/test/misc/common.h b/test/misc/common.h deleted file mode 100644 index dada61a14..000000000 --- a/test/misc/common.h +++ /dev/null @@ -1,22 +0,0 @@ - -#include -#include - -#define NO_OLD_FUNC_DECL -#define NO_TYPELESS_INT -#define NO_TYPELESS_INT_PTR -#define MAIN_RETURNS_INT -#define NO_IMPLICIT_FUNC_PROTOTYPES -#define NO_FLOATS -#define NO_WCHAR -#define NO_EMPTY_FUNC_ARGS -#define NO_SLOPPY_STRUCT_INIT -#define NO_FUNCS_TAKE_STRUCTS -#define NO_FUNCS_RETURN_STRUCTS -#define CAST_STRUCT_PTR -#define NO_TYPELESS_STRUCT_PTR -#define NO_IMPLICIT_FUNCPTR_CONV -#define SIZEOF_INT_16BIT -#define SIZEOF_LONG_32BIT -#define UNSIGNED_CHARS -#define UNSIGNED_BITFIELDS From 041f981960a7bbe79654fc5617a9ca208c4ffde8 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Tue, 21 Jul 2020 17:38:18 -0400 Subject: [PATCH 110/806] rand() use XOR to break up unwanted pair correlation (#1107) * rand() use XOR to break up unwanted pair correlation This form of rand() cannot return the same value twice in a row. Two additonal EOR instructions produce a more even distribution of successive pairs. see comments on #951 * rand.s document purpose of XOR * suggested srand() optimization: zero fill unnecessary * test to validate implementation of rand() * srand() improving behaviour and adding startup test * srand() with a tail call to rand() for better initial shuffle * srand() can fall through to rand() instead of tail call --- libsrc/common/rand.s | 32 ++++++------- test/val/rand.c | 110 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 test/val/rand.c diff --git a/libsrc/common/rand.s b/libsrc/common/rand.s index a272af80a..798d6343a 100644 --- a/libsrc/common/rand.s +++ b/libsrc/common/rand.s @@ -15,14 +15,14 @@ ; Multiplier must be 1 (mod 4) ; Added value must be 1 (mod 2) ; This guarantees max. period (2**32) -; The lowest bits have poor entropy and -; exhibit easily detectable patterns, so -; only the upper bits 16-22 and 24-31 of the -; 4-byte state are returned. +; The quality of entropy in the bits of the seed are poorest in the lowest +; bits, and best in the highest bits. ; -; The best 8 bits, 24-31 are returned in the -; low byte A to provide the best entropy in the -; most commonly used part of the return value. +; The high 8 bits are used for the low byte A to provide the best entropy in +; the most commonly used part of the return value. +; +; Finally XOR with the lower 2 bytes is used on the output, which breaks up +; some minor deficient sequential patterns. (#951) ; ; Uses the following LCG values for ax + c (mod m) ; a = $01010101 @@ -42,10 +42,15 @@ ; The seed. When srand() is not called, the C standard says that that rand() ; should behave as if srand() was called with an argument of 1 before. -rand: .dword 1 +rand: .dword $B5B5B4B4 .code +_srand: sta rand+0 ; Store the seed + stx rand+1 + sta rand+2 ; argument << 16 is convenient fill for MSW + stx rand+3 + ; fall through to rand() to sufficiently "shuffle" first rand() result _rand: clc lda rand+0 adc #$B3 @@ -54,18 +59,11 @@ _rand: clc sta rand+1 adc rand+2 sta rand+2 + eor rand+0 and #$7f ; Suppress sign bit (make it positive) tax lda rand+2 adc rand+3 sta rand+3 + eor rand+1 rts ; return bit (16-22,24-31) in (X,A) - -_srand: sta rand+0 ; Store the seed - stx rand+1 - lda #0 - sta rand+2 ; Set MSW to zero - sta rand+3 - rts - - diff --git a/test/val/rand.c b/test/val/rand.c new file mode 100644 index 000000000..f2f604449 --- /dev/null +++ b/test/val/rand.c @@ -0,0 +1,110 @@ +/* This test verifies that the assembly implementation of rand() matches its + * theoretical high level equivalent. + * + * This does about 3000 tests from various starting srand() seeds. + * A more thorough test might visit the entire sequence with 2^32 tests, but + * that takes hours to simulate, and this should be a sufficient sampling. + * + * This will also fail if rand() is ever altered, which might be a warning to + * tread carefully. Some past discussion of RNG here: + * https://github.com/cc65/cc65/pull/951 + */ + +#include +#include +#include + +/* for faster execution */ +#pragma static-locals (on) + +/* values tested per seed */ +#define SUBTESTS 50 + +/* increments used between tested seeds */ +/* 653 is prime and divides 32768 by ~50 */ +#define TESTINC 653 + +static uint32_t seed; + +int ref_rand() +{ + uint16_t output; + /* seed follows the LCG sequence * 0x01010101 + 0xB3B3B3B3 */ + seed = seed * 0x01010101UL + 0xB3B3B3B3UL; + /* output uses the top two bytes (reversed) XOR with bottom two bytes */ + { + uint16_t s0 = (seed >> 0) & 0xFF; + uint16_t s1 = (seed >> 8) & 0xFF; + uint16_t s2 = (seed >> 16) & 0xFF; + uint16_t s3 = (seed >> 24) & 0xFF; + uint16_t o0 = s3 ^ s1; + uint16_t o1 = s2 ^ s0; + output = o0 | (o1 << 8); + } + return (int)(output & 0x7FFF); +} + +void ref_srand(int ax) +{ + uint32_t s = (unsigned int)ax; + seed = s | (s << 16); /* low 16 bits is convenient filler for high 16 bits */ + ref_rand(); /* one pre-call "shuffles" the first rand() result so it isn't too predictable */ +} + +int main(void) +{ + unsigned int i,j; + int a,b; + + /* test that startup state is equivalent to srand(1) */ + { + //srand(1); // implied + ref_srand(1); + for (j=0; j Date: Tue, 21 Jul 2020 23:44:36 +0200 Subject: [PATCH 111/806] Minor style change. --- libsrc/common/rand.s | 1 + 1 file changed, 1 insertion(+) diff --git a/libsrc/common/rand.s b/libsrc/common/rand.s index 798d6343a..ec1df9860 100644 --- a/libsrc/common/rand.s +++ b/libsrc/common/rand.s @@ -51,6 +51,7 @@ _srand: sta rand+0 ; Store the seed sta rand+2 ; argument << 16 is convenient fill for MSW stx rand+3 ; fall through to rand() to sufficiently "shuffle" first rand() result + _rand: clc lda rand+0 adc #$B3 From df900e30b85b1b8f27e1a4d997555d07333591e7 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 23:50:23 +0200 Subject: [PATCH 112/806] removed references to macros from common.h --- test/ref/cc65110210.c | 17 ----------------- test/ref/wf1.c | 22 ---------------------- 2 files changed, 39 deletions(-) diff --git a/test/ref/cc65110210.c b/test/ref/cc65110210.c index ca5b39203..319b2c529 100644 --- a/test/ref/cc65110210.c +++ b/test/ref/cc65110210.c @@ -20,27 +20,10 @@ cl65: Subprocess 'ld65' aborted by signal 11 */ -/* #define STANDALONE */ - #include #include -#ifdef STANDALONE - -#define NO_IMPLICIT_FUNCPTR_CONV - -#define OPENTEST() -#define CLOSETEST() - -#else - -#endif - -#ifdef NO_IMPLICIT_FUNCPTR_CONV -void (*p1func)(void); -#else void (*p1func)(); -#endif void func(void) { diff --git a/test/ref/wf1.c b/test/ref/wf1.c index e1ed7a417..574e7205f 100644 --- a/test/ref/wf1.c +++ b/test/ref/wf1.c @@ -25,35 +25,21 @@ int next; /* index of next free entry in words */ /*struct node *lookup();*/ -#if defined(NO_NEW_PROTOTYPES_FOR_OLD_FUNC_DECL) && !defined(NO_OLD_FUNC_DECL) - -#else - int err(char *s); int getword(char *buf); void tprint(struct node *tree); struct node *lookup(char *word, struct node **p); -#endif - int isletter(char c); /* err - print error message s and die */ -#ifndef NO_OLD_FUNC_DECL err(s) char *s; { -#else -int err(char *s) { -#endif printf("? %s\n", s); exit(EXIT_FAILURE); } /* getword - get next input word into buf, return 0 on EOF */ -#ifndef NO_OLD_FUNC_DECL int getword(buf) char *buf; -#else -int getword(char *buf) -#endif { char *s; int c; @@ -77,12 +63,8 @@ int isletter(char c) } /* lookup - lookup word in tree; install if necessary */ -#ifndef NO_OLD_FUNC_DECL struct node *lookup(word, p) char *word; struct node **p; -#else -struct node *lookup(char *word, struct node **p) -#endif { int cond; /* char *malloc(); */ @@ -108,11 +90,7 @@ struct node *lookup(char *word, struct node **p) } /* tprint - print tree */ -#ifndef NO_OLD_FUNC_DECL void tprint(tree) struct node *tree; { -#else -void tprint(struct node *tree) { -#endif if (tree) { tprint(tree->left); printf("%d:%s\n", tree->count, tree->word); From 4a9c5ff63b7752b6c4210e6f0748558581aa3845 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 21 Jul 2020 23:59:05 +0200 Subject: [PATCH 113/806] use uint16_t instead of magic ifdefs, leaving support for bit type in there incase we support it some day --- test/val/add2.c | 29 +++++++---------------------- test/val/compare4.c | 20 +++++--------------- test/val/rotate3.c | 19 +++++-------------- test/val/rotate4.c | 19 +++++-------------- test/val/rotate5.c | 19 +++++-------------- test/val/rotate6.c | 19 +++++-------------- test/val/rotate7.c | 19 +++---------------- test/val/sub1.c | 18 ++++++------------ 8 files changed, 41 insertions(+), 121 deletions(-) diff --git a/test/val/add2.c b/test/val/add2.c index 90f0f4175..6fc23e5da 100644 --- a/test/val/add2.c +++ b/test/val/add2.c @@ -6,33 +6,18 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; unsigned char dummy=0; -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -unsigned short aint2 = 0; -unsigned short aint3 = 0; - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -unsigned int aint2 = 0; -unsigned int aint3 = 0; - -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -unsigned int aint2 = 0; -unsigned int aint3 = 0; - -#endif +uint16_t aint0 = 0; +uint16_t aint1 = 0; +uint16_t aint2 = 0; +uint16_t aint3 = 0; unsigned char achar0 = 0; unsigned char achar1 = 0; diff --git a/test/val/compare4.c b/test/val/compare4.c index 47948c3a3..8e3baad5d 100644 --- a/test/val/compare4.c +++ b/test/val/compare4.c @@ -6,11 +6,14 @@ #include #include +#include /* compare4.c */ +/*#define SUPPORT_BIT_TYPES */ + /*#define COMPARE_OUT_OF_RANGE 1*/ unsigned char success = 0; @@ -20,22 +23,9 @@ unsigned char dummy = 0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -short int0 = 0; -short int1 = 0; -#else -int int0 = 0; -int int1 = 0; - -#endif - -#else -int int0 = 0; -int int1 = 0; - -#endif +int16_t int0 = 0; +int16_t int1 = 0; signed char char0 = 0; signed char char1 = 0; diff --git a/test/val/rotate3.c b/test/val/rotate3.c index 21b2dc370..8ac2581e9 100644 --- a/test/val/rotate3.c +++ b/test/val/rotate3.c @@ -6,6 +6,9 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; - -#endif unsigned char achar0 = 0; unsigned char achar1 = 0; unsigned char achar2 = 0; diff --git a/test/val/rotate4.c b/test/val/rotate4.c index 09b1ebf4c..4b50b0d1c 100644 --- a/test/val/rotate4.c +++ b/test/val/rotate4.c @@ -6,6 +6,9 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#endif - -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; - -#endif unsigned char uchar0 = 0; unsigned char uchar1 = 0; unsigned char uchar2 = 0; diff --git a/test/val/rotate5.c b/test/val/rotate5.c index 501e2e567..9de897321 100644 --- a/test/val/rotate5.c +++ b/test/val/rotate5.c @@ -6,6 +6,9 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; -#else -signed int aint0 = 0; -signed int aint1 = 0; +int16_t aint0 = 0; +int16_t aint1 = 0; -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif signed char achar0 = 0; signed char achar1 = 0; signed char achar2 = 0; diff --git a/test/val/rotate6.c b/test/val/rotate6.c index 9109e124e..93a770d99 100644 --- a/test/val/rotate6.c +++ b/test/val/rotate6.c @@ -6,6 +6,9 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -14,22 +17,10 @@ unsigned char dummy=0; #ifdef SUPPORT_BIT_TYPES bit bit0 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; -#else -signed int aint0 = 0; -signed int aint1 = 0; +int16_t aint0 = 0; +int16_t aint1 = 0; -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif signed char achar0 = 0; signed char achar1 = 0; signed char achar2 = 0; diff --git a/test/val/rotate7.c b/test/val/rotate7.c index 2b30b86dd..4a044c16a 100644 --- a/test/val/rotate7.c +++ b/test/val/rotate7.c @@ -6,27 +6,14 @@ #include #include +#include unsigned char success=0; unsigned char failures=0; unsigned char dummy=0; -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -signed short aint0 = 0; -signed short aint1 = 0; - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif - -#else -signed int aint0 = 0; -signed int aint1 = 0; - -#endif +int16_t aint0 = 0; +int16_t aint1 = 0; /* signed char achar0 = 0; diff --git a/test/val/sub1.c b/test/val/sub1.c index f1ae9394f..5dbba97df 100644 --- a/test/val/sub1.c +++ b/test/val/sub1.c @@ -6,6 +6,10 @@ #include #include +#include + +/* #define SUPPORT_BIT_TYPES */ +/* #define SUPPORT_BIT_ARITHMETIC */ unsigned char success=0; unsigned char failures=0; @@ -28,19 +32,9 @@ bit bit11 = 0; #endif -#ifdef SIZEOF_INT_16BIT -#if defined(__LINUX__) || defined(LINUX) -unsigned short aint0 = 0; -unsigned short aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -#endif +uint16_t aint0 = 0; +uint16_t aint1 = 0; -#else -unsigned int aint0 = 0; -unsigned int aint1 = 0; -#endif unsigned char achar0 = 0; unsigned char achar1 = 0; unsigned char achar2 = 0; From eb094ecf6a07f4aa8063c5ed96f9eb4875456807 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 00:21:23 +0200 Subject: [PATCH 114/806] remove ifdef magic --- test/val/ptrfunc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/val/ptrfunc.c b/test/val/ptrfunc.c index 4aa23b77b..78e53e254 100644 --- a/test/val/ptrfunc.c +++ b/test/val/ptrfunc.c @@ -6,7 +6,7 @@ #include -#define NO_IMPLICIT_FUNCPTR_CONV +/* #define SUPPORT_BIT_TYPES */ unsigned char success=0; unsigned char failures=0; @@ -33,15 +33,9 @@ volatile unsigned char uchar1 = 0; volatile unsigned char uchar2 = 0; #endif -#ifdef NO_IMPLICIT_FUNCPTR_CONV void (*pfunc)(void); void (*p1func)(void); unsigned char (*pcfunc)(void); -#else -void (*pfunc)(); -void (*p1func)(); -unsigned char (*pcfunc)(); -#endif void done() { @@ -79,11 +73,7 @@ void docall1() } } -#ifdef NO_IMPLICIT_FUNCPTR_CONV -void docall2( void(*pf)(void) ) -#else void docall2( void(*pf)() ) -#endif { unsigned char i; for(i = 0; i < 2; i++) { From 44c82eb1c3f0360aa70d1ce77a16989e5567b59b Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 21 Jul 2020 18:25:26 -0400 Subject: [PATCH 115/806] Made da65 disassemble branch instructions with relative address expression operands if there's no label. --- src/da65/handler.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/da65/handler.c b/src/da65/handler.c index f8a778b22..255b8da86 100644 --- a/src/da65/handler.c +++ b/src/da65/handler.c @@ -346,7 +346,12 @@ void OH_Relative (const OpcDesc* D) GenerateLabel (D->Flags, Addr); /* Output the line */ - OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); + if (HaveLabel(Addr)) { + OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); + } else { + /* No label -- make a relative address expression */ + OneLine (D, "* + (%d)", (int) Offs + 2); + } } From e22e9c403c7bbbb99c46ff961d91d46a5ab03221 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 14:57:40 +0200 Subject: [PATCH 116/806] added testcase for issue #1098 --- test/misc/Makefile | 18 ++++++++++++++++++ test/misc/bug1098.c | 13 +++++++++++++ test/misc/bug1098a.c | 13 +++++++++++++ test/misc/bug1098b.c | 13 +++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 test/misc/bug1098.c create mode 100644 test/misc/bug1098a.c create mode 100644 test/misc/bug1098b.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 7e19d6360..19768d5aa 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -93,6 +93,24 @@ $(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1048.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# this should fail to compile, because there are errors in the code +$(WORKDIR)/bug1098.$1.$2.prg: bug1098.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiles but should give an error." + $(if $(QUIET),echo misc/bug1098.$1.$2.prg) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# this should fail to compile, because there are errors in the code +$(WORKDIR)/bug1098a.$1.$2.prg: bug1098a.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiles but should give an error." + $(if $(QUIET),echo misc/bug1098a.$1.$2.prg) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# this should fail to compile, because there are errors in the code +$(WORKDIR)/bug1098b.$1.$2.prg: bug1098b.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiles but should give an error." + $(if $(QUIET),echo misc/bug1098b.$1.$2.prg) + $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) diff --git a/test/misc/bug1098.c b/test/misc/bug1098.c new file mode 100644 index 000000000..c49296245 --- /dev/null +++ b/test/misc/bug1098.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +enum { +}; + +int main(void) +{ + return 0; +} diff --git a/test/misc/bug1098a.c b/test/misc/bug1098a.c new file mode 100644 index 000000000..63c1c8da0 --- /dev/null +++ b/test/misc/bug1098a.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +struct { +}; + +int main(void) +{ + return 0; +} diff --git a/test/misc/bug1098b.c b/test/misc/bug1098b.c new file mode 100644 index 000000000..ebd3e94c8 --- /dev/null +++ b/test/misc/bug1098b.c @@ -0,0 +1,13 @@ + +/* bug #1098 Empty enumerator-list */ + +/* The C Standard requires that something exists between the braces for + * enum, struct, and union. */ + +union { +}; + +int main(void) +{ + return 0; +} From 6abf24e25eac975765524441e7f0dc9528372911 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 15:12:02 +0200 Subject: [PATCH 117/806] move test for issue #1077 to test/val --- test/{todo => val}/bug1077.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{todo => val}/bug1077.c (100%) diff --git a/test/todo/bug1077.c b/test/val/bug1077.c similarity index 100% rename from test/todo/bug1077.c rename to test/val/bug1077.c From 844f5a9d337ad80cac3489e62499598bf4020182 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 15:12:29 +0200 Subject: [PATCH 118/806] hook up test/todo in the toplevel test makefile --- test/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Makefile b/test/Makefile index fc1ac246c..be6e60013 100644 --- a/test/Makefile +++ b/test/Makefile @@ -25,6 +25,7 @@ continue: @$(MAKE) -C ref all @$(MAKE) -C err all @$(MAKE) -C misc all + @$(MAKE) -C todo all mostlyclean: @$(MAKE) -C asm clean @@ -33,6 +34,7 @@ mostlyclean: @$(MAKE) -C ref clean @$(MAKE) -C err clean @$(MAKE) -C misc clean + @$(MAKE) -C todo clean clean: mostlyclean @$(call RMDIR,$(WORKDIR)) From ce06b20c6c696f781332c45958a082b5a7b1983e Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 15:31:39 +0200 Subject: [PATCH 119/806] add some details to the readme --- test/readme.txt | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/test/readme.txt b/test/readme.txt index 57ebfdd23..21b13fc62 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -2,29 +2,40 @@ This directory contains test code for automatic regression testing of the CC65 compiler. -/val - the bulk of tests are contained here, individual tests should exit with - an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error +/val - The bulk of tests are contained here, individual tests should exit with + an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error. -/ref - these tests produce output that must be compared with reference output +/ref - These tests produce output that must be compared with reference output. /err - contains tests that MUST NOT compile -/todo - these tests fail due to open compiler issues. - when an issue was fixed, the test should get moved to /var +/todo - These tests fail due to open compiler issues. + + The makefile in this directory _expects_ the tests to fail, because of + that when an issue was fixed it will break the CI. The test should get + moved to /var in the PR fixing the issue, which will make CI pass again. + No changes to makefiles are required! /asm - contains the assembler regression tests /dasm - contains the disassembler regression tests /misc - a few tests that need special care of some sort - tests that (incorrectly) fail to compile and other tests that fail and - do NOT return an exit code are collected here. + + Tests that (incorrectly) fail to compile and other tests that fail and + do NOT return an exit code are collected here. The makefile _expects_ + those tests to fail, so when an issue is fixed it will break the CI. + When this happens, the PR fixing the issue should also "invert" the + failing condition in the makefile by removing the $(NOT) before the + offending line (or removing it when it is already there), which will + make the CI pass again. The test should then be moved elsewhere later, + which will require additional changes to the makefile(s). -to run the tests use "make" in this (top) directory, the makefile should exit +To run the tests use "make" in this (top) directory, the makefile should exit with no error. -when a test failed you can use "make continue" to run further tests +When a test failed you can use "make continue" to run further tests. -------------------------------------------------------------------------------- From 98b2d43c2bd03be06da700b1f9dd4cac0ebf3f4c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 15:52:04 +0200 Subject: [PATCH 120/806] added tests related to pr #1110 --- test/err/pr1110.c | 15 +++++++++++++++ test/val/pr1110a.c | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/err/pr1110.c create mode 100644 test/val/pr1110a.c diff --git a/test/err/pr1110.c b/test/err/pr1110.c new file mode 100644 index 000000000..86955c720 --- /dev/null +++ b/test/err/pr1110.c @@ -0,0 +1,15 @@ + +/* pr #1110 - not only should the current test case for #975 compile and work, + * but also the code piece below fail to compile and generate errors like commented: */ + +static const unsigned char array[3]; /* OK */ +static const unsigned char array[] = { 0, 1, 2 }; /* OK - complete definition*/ +static const unsigned char array[3]; /* OK */ +static const unsigned char array[]; /* OK */ +static const unsigned char array[] = { 1, 2, 3 }; /* Error - redefinition */ +static const unsigned char array[4]; /* Error - conflicting size */ + +int main(void) +{ + return 0; +} diff --git a/test/val/pr1110a.c b/test/val/pr1110a.c new file mode 100644 index 000000000..0e0d91f85 --- /dev/null +++ b/test/val/pr1110a.c @@ -0,0 +1,19 @@ + +/* pr #1110 - the part of the redefinition that should compile */ + +static const unsigned char array[3]; /* OK */ +static const unsigned char array[] = { 0, 1, 2 }; /* OK - complete definition*/ +static const unsigned char array[3]; /* OK */ +static const unsigned char array[]; /* OK */ + +#include +#include + +int main(void) +{ + printf("%u %u %u\n", array[0], array[1], array[2]); + if ((array[0] != 0) || (array[1] != 1) || (array[2] != 2)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} From afe455238c17b0796365e4de5eb777be362798d2 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 22 Jul 2020 15:55:55 +0200 Subject: [PATCH 121/806] added test related to issue #1113 --- test/misc/Makefile | 7 +++++++ test/misc/bug1113.c | 12 ++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/misc/bug1113.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 19768d5aa..9045e4e4f 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -111,6 +111,13 @@ $(WORKDIR)/bug1098b.$1.$2.prg: bug1098b.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1098b.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# this should fail to compile, because there are errors in the code +# instead, the compiler crashes +$(WORKDIR)/bug1113.$1.$2.prg: bug1113.c | $(WORKDIR) + @echo "FIXME: " $$@ "compiler crashes but should give an error." + $(if $(QUIET),echo misc/bug1113.$1.$2.prg) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) diff --git a/test/misc/bug1113.c b/test/misc/bug1113.c new file mode 100644 index 000000000..d1aecaa0b --- /dev/null +++ b/test/misc/bug1113.c @@ -0,0 +1,12 @@ + +/* bug #1113 - Compiler crashes when calling functions "redefined" as other types */ + +void f() {} + +int f; + +int main(void) +{ + f(); + return 0; +} From 5df2de06290e9b7181c03eeb311f7687ef469b80 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Wed, 22 Jul 2020 23:27:04 +0200 Subject: [PATCH 122/806] Added test target. --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 206f853fc..3331f6fb9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples +.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples test .SUFFIXES: @@ -20,6 +20,9 @@ doc html info: samples: @$(MAKE) -C samples --no-print-directory $@ +test: + @$(MAKE) -C test --no-print-directory $@ + %65: @$(MAKE) -C src --no-print-directory $@ From 294b5d1cf1e79ed2b4a21000e37d4e423214cfa1 Mon Sep 17 00:00:00 2001 From: compyx Date: Wed, 15 Jul 2020 21:39:29 +0200 Subject: [PATCH 123/806] C64 soft80 conio: shave off a few bytes and cycles --- libsrc/c64/soft80_cgetc.s | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/libsrc/c64/soft80_cgetc.s b/libsrc/c64/soft80_cgetc.s index aa12cd8ca..1bca57bec 100644 --- a/libsrc/c64/soft80_cgetc.s +++ b/libsrc/c64/soft80_cgetc.s @@ -46,8 +46,18 @@ invertcursor: lda #$34 sta $01 - jsr setcolor - + ldy #0 + bcs @set + ; restore old value + lda tmp1 + bcc @lp0 +@set: + ; save old value + lda (CRAM_PTR),y ; vram + sta tmp1 + lda soft80_internal_cellcolor +@lp0: + sta (CRAM_PTR),y ; vram ldx soft80_internal_cursorxlsb ldy #7 @lp1: @@ -65,20 +75,7 @@ invertcursor: ; do not use soft80_putcolor here to make sure the cursor is always ; shown using the current textcolor without disturbing the "color voodoo" ; in soft80_cputc -setcolor: - ldy #0 - bcs @set - ; restore old value - lda tmp1 - sta (CRAM_PTR),y ; vram - rts -@set: - ; save old value - lda (CRAM_PTR),y ; vram - sta tmp1 - lda soft80_internal_cellcolor - sta (CRAM_PTR),y ; vram - rts + .rodata nibble: .byte $f0, $0f From 81d3dedb4109cd3da845ec77b1fddcc3f83c5aa3 Mon Sep 17 00:00:00 2001 From: compyx Date: Wed, 15 Jul 2020 22:44:34 +0200 Subject: [PATCH 124/806] Move comment block as requested --- libsrc/c64/soft80_cgetc.s | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libsrc/c64/soft80_cgetc.s b/libsrc/c64/soft80_cgetc.s index 1bca57bec..28e6b0042 100644 --- a/libsrc/c64/soft80_cgetc.s +++ b/libsrc/c64/soft80_cgetc.s @@ -46,6 +46,9 @@ invertcursor: lda #$34 sta $01 + ; do not use soft80_putcolor here to make sure the cursor is always + ; shown using the current textcolor without disturbing the "color voodoo" + ; in soft80_cputc ldy #0 bcs @set ; restore old value @@ -72,11 +75,6 @@ invertcursor: cli rts - ; do not use soft80_putcolor here to make sure the cursor is always - ; shown using the current textcolor without disturbing the "color voodoo" - ; in soft80_cputc - - .rodata nibble: .byte $f0, $0f From ed3f281b9e9983f381c1c72b029e525e220e68eb Mon Sep 17 00:00:00 2001 From: mrdudz Date: Mon, 27 Jul 2020 14:40:27 +0200 Subject: [PATCH 125/806] fix wording --- test/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/readme.txt b/test/readme.txt index 21b13fc62..a8746ba60 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -26,7 +26,7 @@ compiler. do NOT return an exit code are collected here. The makefile _expects_ those tests to fail, so when an issue is fixed it will break the CI. When this happens, the PR fixing the issue should also "invert" the - failing condition in the makefile by removing the $(NOT) before the + failing condition in the makefile by adding a $(NOT) before the offending line (or removing it when it is already there), which will make the CI pass again. The test should then be moved elsewhere later, which will require additional changes to the makefile(s). From 4316242d7ee3d1471bfdbef6469cee5a2190336a Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Mon, 27 Jul 2020 17:47:14 +0200 Subject: [PATCH 126/806] Adjusted to https://github.com/cc65/cc65/commit/5df2de06290e9b7181c03eeb311f7687ef469b80 (and slightly simplified). --- test/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/Makefile b/test/Makefile index be6e60013..abc70d58f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -12,11 +12,9 @@ endif WORKDIR = ../testwrk -.PHONY: all dotests continue mostlyclean clean +.PHONY: test continue mostlyclean clean -all: dotests - -dotests: mostlyclean continue +test: mostlyclean continue continue: @$(MAKE) -C asm all From cdd23edd37f44e730fe00acd66ccffeb626d3499 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Mon, 27 Jul 2020 17:59:24 +0200 Subject: [PATCH 127/806] Added building of samples. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c84eb0cc2..630466cc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,11 @@ install: script: - make bin USER_CFLAGS=-Werror - make lib QUIET=1 - - make -C test QUIET=1 + - make test QUIET=1 + - make samples - make -C src clean - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + - make -C samples clean - make doc zip after_success: - make -f Makefile.travis From 04d16b3740d962ecfe0f1c9cc83e3b702835fa58 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Tue, 28 Jul 2020 13:38:49 +0200 Subject: [PATCH 128/806] Make $WORKDIR for tests/err Without this, if there is a test that can compile, it will still fail because the WORKDIR does not exist: ``` pass.c(1): Fatal: Cannot open output file '../../testwrk/err/pass.s': No such file or directory ``` --- test/err/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/err/Makefile b/test/err/Makefile index 4b05ca5db..1273bbb2c 100644 --- a/test/err/Makefile +++ b/test/err/Makefile @@ -34,7 +34,10 @@ TESTS = $(patsubst %.c,$(WORKDIR)/%.s,$(SOURCES)) all: $(TESTS) -$(WORKDIR)/%.s: %.c +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +$(WORKDIR)/%.s: %.c | $(WORKDIR) $(if $(QUIET),echo err/$*.s) $(NOT) $(CC65) -o $@ $< $(NULLERR) From c0f2b69bef0c47cdbcbfa9b2291b68768c294529 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 19 Jul 2020 22:59:44 +0200 Subject: [PATCH 129/806] Add test case for sign extending < 1 byte --- test/todo/bug1095.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/todo/bug1095.c b/test/todo/bug1095.c index a00a10585..0803c2d1c 100644 --- a/test/todo/bug1095.c +++ b/test/todo/bug1095.c @@ -30,7 +30,8 @@ static struct signed_ints { signed int a : 3; signed int b : 3; signed int c : 3; -} si = {-4, -1, 3}; + signed int d : 10; +} si = {-4, -1, 3, -500}; static void test_signed_bitfield(void) { @@ -61,9 +62,19 @@ static void test_signed_bitfield(void) failures++; } + if (si.d >= 0) { + printf("Got si.d = %d, expected negative.\n", si.d); + failures++; + } + if (si.d != -500) { + printf("Got si.d = %d, expected -500.\n", si.d); + failures++; + } + si.a = -3; si.b = 1; si.c = -2; + si.d = 500; if (si.a >= 0) { printf("Got si.a = %d, expected negative.\n", si.a); @@ -91,6 +102,15 @@ static void test_signed_bitfield(void) printf("Got si.c = %d, expected -2.\n", si.c); failures++; } + + if (si.d <= 0) { + printf("Got si.d = %d, expected positive.\n", si.d); + failures++; + } + if (si.d != 500) { + printf("Got si.d = %d, expected 500.\n", si.d); + failures++; + } } int main(void) From c272c73686e940ef9caf608c6a437904bb874349 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Tue, 28 Jul 2020 13:47:47 +0200 Subject: [PATCH 130/806] Add err test for char bit-fields #1047 --- test/err/bug1047.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 test/err/bug1047.c diff --git a/test/err/bug1047.c b/test/err/bug1047.c new file mode 100644 index 000000000..3fb11250f --- /dev/null +++ b/test/err/bug1047.c @@ -0,0 +1,82 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of char bit-fields; see https://github.com/cc65/cc65/issues/1047 +*/ + +#include + +static unsigned char failures = 0; + +static struct chars { + unsigned char a : 3; + unsigned char b : 3; + unsigned char c : 3; +} cs = {4, 1, 3}; + +static void test_char_bitfield(void) +{ + if (sizeof (cs) != 2) { + printf("Got sizeof (cs) = %zu, expected 2.\n", sizeof (cs)); + failures++; + } + + if (cs.a != 4) { + printf("Got cs.a = %u, expected 4.\n", cs.a); + failures++; + } + + if (cs.b != 1) { + printf("Got cs.b = %u, expected 1.\n", cs.b); + failures++; + } + + if (cs.c != 3) { + printf("Got cs.c = %u, expected 3.\n", cs.c); + failures++; + } + + cs.a = -1; + cs.b = 6; + cs.c = 1; + + if (cs.a != 7) { + printf("Got cs.a = %u, expected 7.\n", cs.a); + failures++; + } + + if (cs.b != 6) { + printf("Got cs.b = %u, expected 6.\n", cs.b); + failures++; + } + + if (cs.c != 1) { + printf("Got cs.c = %u, expected 1.\n", cs.c); + failures++; + } +} + +int main(void) +{ + test_char_bitfield(); + printf("failures: %u\n", failures); + return failures; +} From daa65199b34974302eee410ae68e604291e48a69 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 26 Jul 2020 20:12:55 +0800 Subject: [PATCH 131/806] Fixed underlying types of enums. Made enumerator diagnostics more sensible. Fixed Issue #1048 as a natural result. --- src/cc65/datatype.c | 176 +++++++++++++++++++++++++++---- src/cc65/datatype.h | 158 +++++++++++++++++++--------- src/cc65/declare.c | 247 ++++++++++++++++++++++++++++++++++---------- src/cc65/function.c | 2 +- src/cc65/swstmt.c | 2 +- src/cc65/symentry.h | 6 ++ src/cc65/symtab.c | 48 ++++++++- src/cc65/symtab.h | 3 + src/cc65/typecmp.c | 8 +- 9 files changed, 526 insertions(+), 124 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2a989e9f8..3f2725659 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -83,14 +83,9 @@ const char* GetBasicTypeName (const Type* T) ** Return "type" for unknown basic types. */ { - switch (GetType (T)) { - case T_TYPE_CHAR: return "char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "integer"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; + switch (GetRawType (T)) { case T_TYPE_ENUM: return "enum"; - case T_TYPE_FLOAT: return "poinfloatter"; + case T_TYPE_FLOAT: return "float"; case T_TYPE_DOUBLE: return "double"; case T_TYPE_VOID: return "void"; case T_TYPE_STRUCT: return "struct"; @@ -99,8 +94,42 @@ const char* GetBasicTypeName (const Type* T) case T_TYPE_PTR: return "pointer"; case T_TYPE_FUNC: return "function"; case T_TYPE_NONE: /* FALLTHROUGH */ - default: return "type"; + default: break; } + if (IsClassInt (T)) { + if (IsSignSigned (T)) { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "signed char"; + case T_TYPE_SHORT: return "short"; + case T_TYPE_INT: return "int"; + case T_TYPE_LONG: return "long"; + case T_TYPE_LONGLONG: return "long long"; + default: + return "signed integer"; + } + } else if (IsSignUnsigned (T)) { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "unsigned char"; + case T_TYPE_SHORT: return "unsigned short"; + case T_TYPE_INT: return "unsigned int"; + case T_TYPE_LONG: return "unsigned long"; + case T_TYPE_LONGLONG: return "unsigned long long"; + default: + return "unsigned integer"; + } + } else { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "char"; + case T_TYPE_SHORT: return "short"; + case T_TYPE_INT: return "int"; + case T_TYPE_LONG: return "long"; + case T_TYPE_LONGLONG: return "long long"; + default: + return "integer"; + } + } + } + return "type"; } @@ -245,6 +274,49 @@ const Type* GetStructReplacementType (const Type* SType) +long GetIntegerTypeMin (const Type* Type) +/* Get the smallest possible value of the integer type */ +{ + if (IsSignSigned (Type)) { + return (long)(0xFFFFFFFF << (CHAR_BITS * SizeOf (Type) - 1U)); + } else { + return 0; + } +} + + + +unsigned long GetIntegerTypeMax (const Type* Type) +/* Get the largest possible value of the integer type */ +{ + if (IsSignSigned (Type)) { + return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; + } else { + return (1UL << (CHAR_BITS * SizeOf (Type))) - 1UL; + } +} + + + +static unsigned TypeOfBySize (const Type* Type) +/* Get the code generator replacement type of the object by its size */ +{ + unsigned NewType; + /* If the size is less than or equal to that of a a long, we will copy + ** the struct using the primary register, otherwise we use memcpy. + */ + switch (SizeOf (Type)) { + case 1: NewType = CF_CHAR; break; + case 2: NewType = CF_INT; break; + case 3: /* FALLTHROUGH */ + case 4: NewType = CF_LONG; break; + default: NewType = CF_NONE; break; + } + + return NewType; +} + + Type* PointerTo (const Type* T) /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. @@ -321,6 +393,9 @@ void PrintType (FILE* F, const Type* T) case T_TYPE_LONGLONG: fprintf (F, "long long"); break; + case T_TYPE_ENUM: + fprintf (F, "enum"); + break; case T_TYPE_FLOAT: fprintf (F, "float"); break; @@ -430,10 +505,68 @@ int TypeHasAttr (const Type* T) +const Type* GetUnderlyingType (const Type* Type) +/* Get the underlying type of an enum or other integer class type */ +{ + if (IsTypeEnum (Type)) { + + /* This should not happen, but just in case */ + if (Type->A.P == 0) { + Internal ("Enum tag type error in GetUnderlyingTypeCode"); + } + + return ((SymEntry*)Type->A.P)->V.E.Type; + } + + return Type; +} + + + +TypeCode GetUnderlyingTypeCode (const Type* Type) +/* Get the type code of the unqualified underlying type of TCode. +** Return UnqualifiedType (TCode) if TCode is not scalar. +*/ +{ + TypeCode Underlying = UnqualifiedType (Type->C); + TypeCode TCode; + + /* We could also support other T_CLASS_INT types, but just enums for now */ + if (IsTypeEnum (Type)) { + + /* This should not happen, but just in case */ + if (Type->A.P == 0) { + Internal ("Enum tag type error in GetUnderlyingTypeCode"); + } + + /* Inspect the underlying type of the enum */ + if (((SymEntry*)Type->A.P)->V.E.Type == 0) { + /* Incomplete enum type is used */ + return Underlying; + } + TCode = UnqualifiedType (((SymEntry*)Type->A.P)->V.E.Type->C); + + /* Replace the type code with integer */ + Underlying = (TCode & ~T_MASK_TYPE); + switch (TCode & T_MASK_SIZE) { + case T_SIZE_INT: Underlying |= T_TYPE_INT; break; + case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break; + case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break; + case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break; + case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; + default: Underlying |= T_TYPE_INT; break; + } + } + + return Underlying; +} + + + unsigned SizeOf (const Type* T) /* Compute size of object represented by type array. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -470,9 +603,6 @@ unsigned SizeOf (const Type* T) case T_ULONGLONG: return SIZEOF_LONGLONG; - case T_ENUM: - return SIZEOF_INT; - case T_FLOAT: return SIZEOF_FLOAT; @@ -491,7 +621,12 @@ unsigned SizeOf (const Type* T) return T->A.U * SizeOf (T + 1); } + case T_ENUM: + /* Incomplete enum type */ + return 0; + default: + Internal ("Unknown type in SizeOf: %04lX", T->C); return 0; @@ -547,7 +682,9 @@ unsigned CheckedPSizeOf (const Type* T) unsigned TypeOf (const Type* T) /* Get the code generator base type of the object */ { - switch (UnqualifiedType (T->C)) { + unsigned NewType; + + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -557,7 +694,6 @@ unsigned TypeOf (const Type* T) case T_SHORT: case T_INT: - case T_ENUM: return CF_INT; case T_USHORT: @@ -582,6 +718,10 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: + NewType = TypeOfBySize (T); + if (NewType != CF_NONE) { + return NewType; + } /* Address of ... */ return CF_INT | CF_UNSIGNED; @@ -726,8 +866,8 @@ Type* GetBaseElementType (Type* T) SymEntry* GetSymEntry (const Type* T) /* Return a SymEntry pointer from a type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Return the attribute */ return T->A.P; @@ -738,8 +878,8 @@ SymEntry* GetSymEntry (const Type* T) void SetSymEntry (Type* T, SymEntry* S) /* Set the SymEntry pointer for a type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Set the attribute */ T->A.P = S; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 3464e8a16..361f5e0a6 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -96,37 +96,39 @@ enum { /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_SHORT = 0x000200, - T_SIZE_LONG = 0x000400, - T_SIZE_LONGLONG = 0x000600, - T_MASK_SIZE = 0x000600, + T_SIZE_CHAR = 0x000200, + T_SIZE_SHORT = 0x000400, + T_SIZE_INT = 0x000600, + T_SIZE_LONG = 0x000800, + T_SIZE_LONGLONG = 0x000A00, + T_MASK_SIZE = 0x000E00, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x000800, - T_QUAL_VOLATILE = 0x001000, - T_QUAL_RESTRICT = 0x002000, - T_QUAL_NEAR = 0x004000, - T_QUAL_FAR = 0x008000, + T_QUAL_CONST = 0x001000, + T_QUAL_VOLATILE = 0x002000, + T_QUAL_RESTRICT = 0x004000, + T_QUAL_NEAR = 0x008000, + T_QUAL_FAR = 0x010000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x010000, - T_QUAL_CDECL = 0x020000, + T_QUAL_FASTCALL = 0x020000, + T_QUAL_CDECL = 0x040000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x03F800, + T_MASK_QUAL = 0x07F000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, - T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, + T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, + T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, T_SHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, T_USHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, - T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, + T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, T_LONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, T_ULONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, - T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, @@ -246,6 +248,12 @@ Type* GetImplicitFuncType (void); const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union in the primary register */ +long GetIntegerTypeMin (const Type* Type); +/* Get the smallest possible value of the integer type */ + +unsigned long GetIntegerTypeMax (const Type* Type); +/* Get the largest possible value of the integer type */ + Type* PointerTo (const Type* T); /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. @@ -283,6 +291,14 @@ INLINE TypeCode UnqualifiedType (TypeCode T) # define UnqualifiedType(T) ((T) & ~T_MASK_QUAL) #endif +const Type* GetUnderlyingType (const Type* Type); +/* Get the underlying type of an enum or other integer class type */ + +TypeCode GetUnderlyingTypeCode (const Type* Type); +/* Get the type code of the unqualified underlying type of TCode. +** Return TCode if it is not scalar. +*/ + unsigned SizeOf (const Type* T); /* Compute size of object represented by type array. */ @@ -312,123 +328,163 @@ Type* ArrayToPtr (Type* T); /* Convert an array to a pointer to it's first element */ #if defined(HAVE_INLINE) -INLINE TypeCode GetType (const Type* T) +INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ { return (T->C & T_MASK_TYPE); } #else -# define GetType(T) ((T)->C & T_MASK_TYPE) +# define GetRawType(T) ((T)->C & T_MASK_TYPE) #endif #if defined(HAVE_INLINE) INLINE int IsTypeChar (const Type* T) /* Return true if this is a character type */ { - return (GetType (T) == T_TYPE_CHAR); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); } #else -# define IsTypeChar(T) (GetType (T) == T_TYPE_CHAR) +# define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_INT); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT); } #else -# define IsTypeInt(T) (GetType (T) == T_TYPE_INT) +# define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeLong (const Type* T) /* Return true if this is a long type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_LONG); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); } #else -# define IsTypeLong(T) (GetType (T) == T_TYPE_LONG) +# define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeChar (const Type* T) +/* Return true if this is a character raw type */ +{ + return (GetRawType (T) == T_TYPE_CHAR); +} +#else +# define IsRawTypeChar(T) (GetRawType (T) == T_TYPE_CHAR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeInt (const Type* T) +/* Return true if this is an int raw type (signed or unsigned) */ +{ + return (GetRawType (T) == T_TYPE_INT); +} +#else +# define IsRawTypeInt(T) (GetRawType (T) == T_TYPE_INT) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeLong (const Type* T) +/* Return true if this is a long raw type (signed or unsigned) */ +{ + return (GetRawType (T) == T_TYPE_LONG); +} +#else +# define IsRawTypeLong(T) (GetRawType (T) == T_TYPE_LONG) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFloat (const Type* T) /* Return true if this is a float type */ { - return (GetType (T) == T_TYPE_FLOAT); + return (GetRawType (T) == T_TYPE_FLOAT); } #else -# define IsTypeFloat(T) (GetType (T) == T_TYPE_FLOAT) +# define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeDouble (const Type* T) /* Return true if this is a double type */ { - return (GetType (T) == T_TYPE_DOUBLE); + return (GetRawType (T) == T_TYPE_DOUBLE); } #else -# define IsTypeDouble(T) (GetType (T) == T_TYPE_DOUBLE) +# define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE) #endif #if defined(HAVE_INLINE) INLINE int IsTypePtr (const Type* T) /* Return true if this is a pointer type */ { - return (GetType (T) == T_TYPE_PTR); + return (GetRawType (T) == T_TYPE_PTR); } #else -# define IsTypePtr(T) (GetType (T) == T_TYPE_PTR) +# define IsTypePtr(T) (GetRawType (T) == T_TYPE_PTR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeEnum (const Type* T) +/* Return true if this is an enum type */ +{ + return (GetRawType (T) == T_TYPE_ENUM); +} +#else +# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) #endif #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ { - return (GetType (T) == T_TYPE_STRUCT); + return (GetRawType (T) == T_TYPE_STRUCT); } #else -# define IsTypeStruct(T) (GetType (T) == T_TYPE_STRUCT) +# define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnion (const Type* T) /* Return true if this is a union type */ { - return (GetType (T) == T_TYPE_UNION); + return (GetRawType (T) == T_TYPE_UNION); } #else -# define IsTypeUnion(T) (GetType (T) == T_TYPE_UNION) +# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION) #endif #if defined(HAVE_INLINE) INLINE int IsTypeArray (const Type* T) /* Return true if this is an array type */ { - return (GetType (T) == T_TYPE_ARRAY); + return (GetRawType (T) == T_TYPE_ARRAY); } #else -# define IsTypeArray(T) (GetType (T) == T_TYPE_ARRAY) +# define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY) #endif #if defined(HAVE_INLINE) INLINE int IsTypeVoid (const Type* T) /* Return true if this is a void type */ { - return (GetType (T) == T_TYPE_VOID); + return (GetRawType (T) == T_TYPE_VOID); } #else -# define IsTypeVoid(T) (GetType (T) == T_TYPE_VOID) +# define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFunc (const Type* T) /* Return true if this is a function class */ { - return (GetType (T) == T_TYPE_FUNC); + return (GetRawType (T) == T_TYPE_FUNC); } #else -# define IsTypeFunc(T) (GetType (T) == T_TYPE_FUNC) +# define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC) #endif #if defined(HAVE_INLINE) @@ -502,13 +558,23 @@ INLINE int IsClassFunc (const Type* T) #endif #if defined(HAVE_INLINE) -INLINE TypeCode GetSignedness (const Type* T) -/* Get the sign of a type */ +INLINE TypeCode GetRawSignedness (const Type* T) +/* Get the raw signedness of a type */ { - return (T->C & T_MASK_SIGN); + return ((T)->C & T_MASK_SIGN); } #else -# define GetSignedness(T) ((T)->C & T_MASK_SIGN) +# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetSignedness (const Type* T) +/* Get the signedness of a type */ +{ + return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); +} +#else +# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) #endif #if defined(HAVE_INLINE) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 323e9af14..734256b97 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -453,27 +453,99 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) -static void ParseEnumDecl (void) -/* Process an enum declaration . */ +static SymEntry* ESUForwardDecl (const char* Name, unsigned Type) +/* Handle an enum, struct or union forward decl */ { - int EnumVal; - ident Ident; + /* Try to find an enum/struct/union with the given name. If there is none, + ** insert a forward declaration into the current lexical level. + */ + SymEntry* Entry = FindTagSym (Name); + if (Entry == 0) { + if (Type != SC_ENUM) { + Entry = AddStructSym (Name, Type, 0, 0); + } else { + Entry = AddEnumSym (Name, 0, 0); + } + } else if ((Entry->Flags & SC_TYPEMASK) != Type) { + /* Already defined, but not the same type class */ + Error ("Symbol '%s' is already different kind", Name); + } + return Entry; +} + + + +static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) +/* GitHub #1093 - We use unsigned types to save spaces whenever possible. +** If both the signed and unsigned integer types of the same minimum size +** capable of representing all values of the enum, we prefer the unsigned +** one. +** Return 0 if impossible to represent Min and Max as the same integer type. +*/ +{ + const Type* Underlying = type_int; /* default type */ + + /* Change the underlying type if necessary */ + if (Min < 0 || Signed) { + /* We can't use unsigned types if there are any negative values */ + if (Max > (unsigned long)INT32_MAX) { + /* No way to represent both Min and Max as the same integer type */ + Underlying = 0; + } else if (Min < INT16_MIN || Max > (unsigned long)INT16_MAX) { + Underlying = type_long; + } else if (Min < INT8_MIN || Max > (unsigned long)INT8_MAX) { + Underlying = type_int; + } else { + Underlying = type_schar; + } + } else { + if (Max > UINT16_MAX) { + Underlying = type_ulong; + } else if (Max > UINT8_MAX) { + Underlying = type_uint; + } else { + Underlying = type_uchar; + } + } + + return Underlying; +} + + + +static SymEntry* ParseEnumDecl (const char* Name) +/* Process an enum declaration */ +{ + SymTable* FieldTab; + long EnumVal; + int IsSigned; + int IsIncremented; + ident Ident; + long MinConstant = 0; + unsigned long MaxConstant = 0; + const Type* NewType = type_int; /* new enumerator type */ + const Type* MemberType = type_int; /* default enumerator type */ /* Accept forward definitions */ if (CurTok.Tok != TOK_LCURLY) { - return; + return ESUForwardDecl (Name, SC_ENUM); } + /* Add the enum tag */ + AddEnumSym (Name, 0, 0); + /* Skip the opening curly brace */ NextToken (); /* Read the enum tags */ - EnumVal = 0; + EnumVal = -1L; while (CurTok.Tok != TOK_RCURLY) { /* We expect an identifier */ if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected"); + Error ("Identifier expected for enumerator declarator"); + /* Avoid excessive errors */ + NextToken (); continue; } @@ -483,21 +555,116 @@ static void ParseEnumDecl (void) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { + ExprDesc Expr; NextToken (); ConstAbsIntExpr (hie1, &Expr); - EnumVal = Expr.IVal; + EnumVal = Expr.IVal; + MemberType = Expr.Type; + IsSigned = IsSignSigned (MemberType); + IsIncremented = 0; + + } else { + + /* Defaulted with the same signedness as the previous member's */ + IsSigned = IsSignSigned (MemberType) && + (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); + + /* Enumerate. Signed integer overflow is UB but unsigned integers + ** are guaranteed to wrap around. + */ + EnumVal = (long)((unsigned long)EnumVal + 1UL); + + if (UnqualifiedType (MemberType->C) == T_ULONG && EnumVal == 0) { + /* Warn on 'unsigned long' overflow in enumeration */ + Warning ("Enumerator '%s' overflows the range of '%s'", + Ident, + GetBasicTypeName (type_ulong)); + } + + IsIncremented = 1; } - /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUMERATOR | SC_CONST, EnumVal++); + /* Track down the min/max values and evaluate the type of EnumVal + ** using GetEnumeratorType in a tricky way. + */ + if (!IsSigned || EnumVal >= 0) { + if ((unsigned long)EnumVal > MaxConstant) { + MaxConstant = (unsigned long)EnumVal; + } + NewType = GetEnumeratorType (0, EnumVal, IsSigned); + } else { + if (EnumVal < MinConstant) { + MinConstant = EnumVal; + } + NewType = GetEnumeratorType (EnumVal, 0, 1); + } + + /* GetEnumeratorType above should never fail, but just in case */ + if (NewType == 0) { + Internal ("Unexpected failure with GetEnumeratorType: %lx", EnumVal); + NewType = type_ulong; + } else if (SizeOf (NewType) < SizeOf (type_int)) { + /* Integer constants are not shorter than int */ + NewType = type_int; + } + + /* Warn if the incremented value exceeds the range of the previous + ** type. + */ + if (IsIncremented && + EnumVal >= 0 && + NewType->C != UnqualifiedType (MemberType->C)) { + /* The possible overflow here can only be when EnumVal > 0 */ + Warning ("Enumerator '%s' (value = %lu) is of type '%s'", + Ident, + (unsigned long)EnumVal, + GetBasicTypeName (NewType)); + } + + /* Warn if the value exceeds range of 'int' in standard mode */ + if (IS_Get (&Standard) != STD_CC65 && NewType->C != T_INT) { + if (!IsSigned || EnumVal >= 0) { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %lu) is too large", + Ident, + (unsigned long)EnumVal); + } else { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %ld) is too small", + Ident, + EnumVal); + } + } + + /* Add an entry of the enumerator to the symbol table */ + AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); + + /* Use this type for following members */ + MemberType = NewType; /* Check for end of definition */ - if (CurTok.Tok != TOK_COMMA) + if (CurTok.Tok != TOK_COMMA) { break; + } NextToken (); } ConsumeRCurly (); + + /* This evaluates the underlying type of the whole enum */ + MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); + if (MemberType == 0) { + /* It is very likely that the program is wrong */ + Error ("Enumeration values cannot be represented all as 'long'\n" + "\tMin enumerator value = %ld, Max enumerator value = %lu", + MinConstant, MaxConstant); + + /* Avoid more errors */ + MemberType = type_long; + } + + FieldTab = GetSymTab (); + return AddEnumSym (Name, MemberType, FieldTab); } @@ -541,24 +708,6 @@ static int ParseFieldWidth (Declaration* Decl) -static SymEntry* StructOrUnionForwardDecl (const char* Name, unsigned Type) -/* Handle a struct or union forward decl */ -{ - /* Try to find a struct/union with the given name. If there is none, - ** insert a forward declaration into the current lexical level. - */ - SymEntry* Entry = FindTagSym (Name); - if (Entry == 0) { - Entry = AddStructSym (Name, Type, 0, 0); - } else if ((Entry->Flags & SC_TYPEMASK) != Type) { - /* Already defined, but no struct */ - Error ("Symbol '%s' is already different kind", Name); - } - return Entry; -} - - - static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) /* Copy fields from an anon union/struct into the current lexical level. The ** function returns the size of the embedded struct/union. @@ -617,7 +766,7 @@ static SymEntry* ParseUnionDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_UNION); + return ESUForwardDecl (Name, SC_UNION); } /* Add a forward declaration for the struct in the current lexical level */ @@ -722,7 +871,7 @@ static SymEntry* ParseStructDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_STRUCT); + return ESUForwardDecl (Name, SC_STRUCT); } /* Add a forward declaration for the struct in the current lexical level */ @@ -1069,29 +1218,23 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_ENUM: NextToken (); - if (CurTok.Tok != TOK_LCURLY) { - /* Named enum */ - if (CurTok.Tok == TOK_IDENT) { - /* Find an entry with this name */ - Entry = FindTagSym (CurTok.Ident); - if (Entry) { - if (SymIsLocal (Entry) && (Entry->Flags & SC_ENUM) == 0) { - Error ("Symbol '%s' is already different kind", Entry->Name); - } - } else { - /* Insert entry into table ### */ - } - /* Skip the identifier */ - NextToken (); - } else { + /* Named enum */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else { + if (CurTok.Tok != TOK_LCURLY) { Error ("Identifier expected"); + } else { + AnonName (Ident, "enum"); } } /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - ParseEnumDecl (); - D->Type[0].C = T_INT; + Entry = ParseEnumDecl (Ident); + D->Type[0].C |= T_ENUM; + SetSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -1624,7 +1767,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* The return type must not be qualified */ if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - if (GetType (RetType) == T_TYPE_VOID) { + if (GetRawType (RetType) == T_TYPE_VOID) { /* A qualified void type is always an error */ Error ("function definition has qualified void return type"); } else { @@ -1916,7 +2059,7 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsTypeChar (ElementType) && + if (IsRawTypeChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { @@ -2180,7 +2323,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ConstExpr (hie1, &Expr); - switch (UnqualifiedType (Expr.Type[0].C)) { + switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -2244,7 +2387,7 @@ static unsigned ParseVoidInit (Type* T) static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/function.c b/src/cc65/function.c index ff4c23656..d6e11fea3 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -452,7 +452,7 @@ void NewFunc (SymEntry* Func) /* Determine if this is a main function in a C99 environment that ** returns an int. */ - if (IsTypeInt (F_GetReturnType (CurrentFunc)) && + if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 4a3730283..8cab93bba 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -132,7 +132,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = UnqualifiedType (SwitchExpr.Type[0].C); + SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 13829e958..b030d8b8b 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -165,6 +165,12 @@ struct SymEntry { unsigned Size; /* Size of the union/struct */ } S; + /* Data for enums */ + struct { + struct SymTable* SymTab; /* Member symbol table */ + const Type* Type; /* Underlying type */ + } E; + /* Data for bit fields */ struct { unsigned Offs; /* Byte offset into struct */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 26a09cd87..87a33760b 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -551,6 +551,50 @@ static void AddSymEntry (SymTable* T, SymEntry* S) +SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) +/* Add an enum entry and return it */ +{ + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (TagTab, Name, HashStr (Name)); + if (Entry) { + + /* We do have an entry. This may be a forward, so check it. */ + if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) { + /* Existing symbol is not an enum */ + Error ("Symbol '%s' is already different kind", Name); + } else { + /* Define the struct size if the underlying type is given. */ + if (Type != 0) { + if (Type !=0 && Entry->V.E.Type != 0) { + /* Both are definitions. */ + Error ("Multiple definition for enum '%s'", Name); + } + Entry->Type = 0; + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + } + } + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, SC_ENUM); + + /* Set the enum type data */ + Entry->Type = 0; + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + + /* Add it to the current table */ + AddSymEntry (TagTab, Entry); + } + + /* Return the entry */ + return Entry; +} + + + SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab) /* Add a struct/union entry and return it */ { @@ -649,10 +693,10 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val /* Create a new entry */ Entry = NewSymEntry (Name, Flags); - /* Enum values are ints */ + /* We only have integer constants for now */ Entry->Type = TypeDup (T); - /* Set the enum data */ + /* Set the constant data */ Entry->V.ConstVal = Val; /* Add the entry to the symbol table */ diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 94e5f2d9b..dd4b3aa37 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -146,6 +146,9 @@ unsigned short FindSPAdjustment (const char* Name); +SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab); +/* Add an enum entry and return it */ + SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab); /* Add a struct/union entry and return it */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 673dfa163..af04d8232 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -214,9 +214,9 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } - /* Get the raw left and right types, signs and qualifiers */ - LeftType = GetType (lhs); - RightType = GetType (rhs); + /* Get the left and right types, signs and qualifiers */ + LeftType = GetUnderlyingTypeCode (lhs); + RightType = GetUnderlyingTypeCode (rhs); LeftSign = GetSignedness (lhs); RightSign = GetSignedness (rhs); LeftQual = GetQualifier (lhs); @@ -229,7 +229,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) RightType = T_TYPE_PTR; } - /* If the raw types are not identical, the types are incompatible */ + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); return; From d8184fbe543d2e72538e6af179f900098bc19ce6 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 26 Jul 2020 20:12:59 +0800 Subject: [PATCH 132/806] No longer insert all enums in the global symbol table. --- src/cc65/symtab.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 87a33760b..4169e6280 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -676,11 +676,8 @@ SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsign SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val) /* Add an constant symbol to the symbol table and return it */ { - /* Enums must be inserted in the global symbol table */ - SymTable* Tab = ((Flags & SC_ENUMERATOR) == SC_ENUMERATOR) ? SymTab0 : SymTab; - /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); if (Entry) { if ((Entry->Flags & SC_CONST) != SC_CONST) { Error ("Symbol '%s' is already different kind", Name); @@ -700,7 +697,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val Entry->V.ConstVal = Val; /* Add the entry to the symbol table */ - AddSymEntry (Tab, Entry); + AddSymEntry (SymTab, Entry); /* Return the entry */ return Entry; From 7e243e0f2c8d4b210cd6d6b43d8914587f631d1f Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 26 Jul 2020 20:12:59 +0800 Subject: [PATCH 133/806] Allowed using all integer types including enum and char types to define bit-fields, but kept the currently behavior that all of them are treated as unsigned int. --- src/cc65/declare.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 734256b97..49e233a56 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -684,21 +684,31 @@ static int ParseFieldWidth (Declaration* Decl) /* Read the width */ NextToken (); ConstAbsIntExpr (hie1, &Expr); + if (!IsClassInt (Decl->Type)) { + /* Only integer types may be used for bit-fields */ + Error ("Bit-field has invalid type ''"); + return -1; + } if (Expr.IVal < 0) { Error ("Negative width in bit-field"); return -1; } - if (Expr.IVal > (int) INT_BITS) { + /* FIXME: We should compare with the width of the specified type */ +#if 0 + /* Use is when we really support non-uint16_t bit-fields */ + if (Expr.IVal > (long)(SizeOf (Decl->Type) * CHAR_BITS)) { Error ("Width of bit-field exceeds its type"); return -1; } - if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { - Error ("Zero width for named bit-field"); +#else + /* This is what we currenty do */ + if (Expr.IVal > (long)(SizeOf (type_uint) * CHAR_BITS)) { + Error ("Width of bit-field exceeds 16"); return -1; } - if (!IsTypeInt (Decl->Type)) { - /* Only integer types may be used for bit-fields */ - Error ("Bit-field has invalid type"); +#endif + if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { + Error ("Zero width for named bit-field"); return -1; } From 35e1efc7f2576bc579cb27a187d1a35aadbbe85b Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 26 Jul 2020 20:40:27 +0800 Subject: [PATCH 134/806] Moved misc/bug1048 as it is already correctly rejected by the compiler. --- test/{misc => err}/bug1048.c | 0 test/misc/Makefile | 6 ------ 2 files changed, 6 deletions(-) rename test/{misc => err}/bug1048.c (100%) diff --git a/test/misc/bug1048.c b/test/err/bug1048.c similarity index 100% rename from test/misc/bug1048.c rename to test/err/bug1048.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 9045e4e4f..3c5045753 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -87,12 +87,6 @@ $(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) $(if $(QUIET),echo misc/bug264.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# this should fail to compile, because there are errors in the code -$(WORKDIR)/bug1048.$1.$2.prg: bug1048.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiles but should give an error." - $(if $(QUIET),echo misc/bug1048.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # this should fail to compile, because there are errors in the code $(WORKDIR)/bug1098.$1.$2.prg: bug1098.c | $(WORKDIR) @echo "FIXME: " $$@ "compiles but should give an error." From 0f412b6beb1319255834b7062c000a1295140e4b Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 27 Jul 2020 18:25:28 +0800 Subject: [PATCH 135/806] Small fixes according to PR review. --- src/cc65/datatype.c | 10 ++++++++-- src/cc65/declare.c | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 3f2725659..4da9a8727 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -278,7 +278,8 @@ long GetIntegerTypeMin (const Type* Type) /* Get the smallest possible value of the integer type */ { if (IsSignSigned (Type)) { - return (long)(0xFFFFFFFF << (CHAR_BITS * SizeOf (Type) - 1U)); + /* The smallest possible signed value of N-byte integer is -pow(2, 8*N-1) */ + return (long)((unsigned long)(-1L) << (CHAR_BITS * SizeOf (Type) - 1U)); } else { return 0; } @@ -290,9 +291,14 @@ unsigned long GetIntegerTypeMax (const Type* Type) /* Get the largest possible value of the integer type */ { if (IsSignSigned (Type)) { + /* Min signed value of N-byte integer is pow(2, 8*N-1) - 1 */ return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; } else { - return (1UL << (CHAR_BITS * SizeOf (Type))) - 1UL; + /* Max signed value of N-byte integer is pow(2, 8*N) - 1. However, + ** workaround is needed as in ISO C it is UB if the shift count is + ** equal to the bit width of the left operand type. + */ + return (1UL << 1U << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; } } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 49e233a56..ba4091c3b 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -686,7 +686,7 @@ static int ParseFieldWidth (Declaration* Decl) ConstAbsIntExpr (hie1, &Expr); if (!IsClassInt (Decl->Type)) { /* Only integer types may be used for bit-fields */ - Error ("Bit-field has invalid type ''"); + Error ("Bit-field has invalid type '%s'", GetBasicTypeName (Decl->Type)); return -1; } if (Expr.IVal < 0) { From c37f9f1a41f9e6ab853cfbac7438cf5f41fb91f8 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 27 Jul 2020 19:20:07 +0800 Subject: [PATCH 136/806] Check if the integer size is known in GetIntegerTypeMin/Max() to prevent potential misuse. --- src/cc65/datatype.c | 16 ++++++++++++++-- src/cc65/datatype.h | 8 ++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 4da9a8727..1e2859ba7 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -275,8 +275,14 @@ const Type* GetStructReplacementType (const Type* SType) long GetIntegerTypeMin (const Type* Type) -/* Get the smallest possible value of the integer type */ +/* Get the smallest possible value of the integer type. +** The type must have a known size. +*/ { + if (SizeOf (Type) == 0) { + Internal ("Incomplete type used in GetIntegerTypeMin"); + } + if (IsSignSigned (Type)) { /* The smallest possible signed value of N-byte integer is -pow(2, 8*N-1) */ return (long)((unsigned long)(-1L) << (CHAR_BITS * SizeOf (Type) - 1U)); @@ -288,8 +294,14 @@ long GetIntegerTypeMin (const Type* Type) unsigned long GetIntegerTypeMax (const Type* Type) -/* Get the largest possible value of the integer type */ +/* Get the largest possible value of the integer type. +** The type must have a known size. +*/ { + if (SizeOf (Type) == 0) { + Internal ("Incomplete type used in GetIntegerTypeMax"); + } + if (IsSignSigned (Type)) { /* Min signed value of N-byte integer is pow(2, 8*N-1) - 1 */ return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 361f5e0a6..2f2ed136e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -249,10 +249,14 @@ const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union in the primary register */ long GetIntegerTypeMin (const Type* Type); -/* Get the smallest possible value of the integer type */ +/* Get the smallest possible value of the integer type. +** The type must have a known size. +*/ unsigned long GetIntegerTypeMax (const Type* Type); -/* Get the largest possible value of the integer type */ +/* Get the largest possible value of the integer type. +** The type must have a known size. +*/ Type* PointerTo (const Type* T); /* Return a type string that is "pointer to T". The type string is allocated From cbb33f86e85d82f3ebe22c486e9b1bbcacb584d9 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 28 Jul 2020 11:17:17 +0800 Subject: [PATCH 137/806] Disabled using non-int-size types to declare bit-fields. --- src/cc65/datatype.h | 30 ++++++++++++++++++++---------- src/cc65/declare.c | 7 +++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 2f2ed136e..1997b5c89 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -601,6 +601,26 @@ INLINE int IsSignSigned (const Type* T) # define IsSignSigned(T) (GetSignedness (T) == T_SIGN_SIGNED) #endif +#if defined(HAVE_INLINE) +INLINE TypeCode GetRawSizeModifier(const Type* T) +/* Get the size modifier of a raw type */ +{ + return (T->C & T_MASK_SIZE); +} +#else +# define GetRawSizeModifier(T) ((T)->C & T_MASK_SIZE) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetSizeModifier (const Type* T) +/* Get the size modifier of a type */ +{ + return (GetUnderlyingTypeCode (T) & T_MASK_SIZE); +} +#else +# define GetSizeModifier(T) (GetUnderlyingTypeCode (T) & T_MASK_SIZE) +#endif + #if defined(HAVE_INLINE) INLINE TypeCode GetQualifier (const Type* T) /* Get the qualifier from the given type string */ @@ -696,16 +716,6 @@ int IsVariadicFunc (const Type* T) attribute ((const)); ** variable parameter list */ -#if defined(HAVE_INLINE) -INLINE TypeCode GetSizeModifier (const Type* T) -/* Get the size modifier of a type */ -{ - return (T->C & T_MASK_SIZE); -} -#else -# define GetSizeModifier(T) ((T)->C & T_MASK_SIZE) -#endif - FuncDesc* GetFuncDesc (const Type* T) attribute ((const)); /* Get the FuncDesc pointer from a function or pointer-to-function type */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ba4091c3b..cb3aa0ab3 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -689,6 +689,13 @@ static int ParseFieldWidth (Declaration* Decl) Error ("Bit-field has invalid type '%s'", GetBasicTypeName (Decl->Type)); return -1; } + + if (SizeOf (Decl->Type) != SizeOf (type_uint)) { + /* Only int sized types may be used for bit-fields for now */ + Error ("CC65 currently only supports unsigned int bit-fields"); + return -1; + } + if (Expr.IVal < 0) { Error ("Negative width in bit-field"); return -1; From 8eab28012a36516bab4682cd1797193fe721d51a Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Tue, 28 Jul 2020 23:29:16 +0200 Subject: [PATCH 138/806] Adjusted project name. --- src/cc65/declare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index cb3aa0ab3..48db64e61 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -692,7 +692,7 @@ static int ParseFieldWidth (Declaration* Decl) if (SizeOf (Decl->Type) != SizeOf (type_uint)) { /* Only int sized types may be used for bit-fields for now */ - Error ("CC65 currently only supports unsigned int bit-fields"); + Error ("cc65 currently only supports unsigned int bit-fields"); return -1; } From 74dda01919dd1f84c1fcbd9ebb808312976de83a Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 29 Jul 2020 13:32:26 +0200 Subject: [PATCH 139/806] Add test that plain int bitfields are unsigned We want to make sure this doesn't change when #1095 is fixed; unsigned is much more efficient. --- test/val/plain-int-bitfield.c | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/val/plain-int-bitfield.c diff --git a/test/val/plain-int-bitfield.c b/test/val/plain-int-bitfield.c new file mode 100644 index 000000000..afc7121bb --- /dev/null +++ b/test/val/plain-int-bitfield.c @@ -0,0 +1,63 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests that plain int bit-fields are unsigned. +*/ + +#include + +static unsigned char failures = 0; + +static struct plain_ints { + int x : 4; + int y : 10; +} pi = {15, 700}; + +static void test_plain_int_bitfields (void) +{ + if (pi.x != 15) { + printf ("Got pi.x = %u, expected 15.\n", pi.x); + failures++; + } + if (pi.y != 700) { + printf ("Got pi.y = %u, expected 700.\n", pi.y); + failures++; + } + + pi.x = 3; + pi.y = 1023; + + if (pi.x != 3) { + printf ("Got pi.x = %u, expected 3.\n", pi.x); + failures++; + } + if (pi.y != 1023) { + printf ("Got pi.y = %u, expected 1023.\n", pi.y); + failures++; + } +} + +int main (void) +{ + test_plain_int_bitfields (); + printf ("failures: %u\n", failures); + return failures; +} From a2561d07f3b6c07de0db312ed32d26c01b429d89 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 29 Jul 2020 14:04:52 +0200 Subject: [PATCH 140/806] Remove special-case bit-field width code cbb33f8 restricted allowed bit-field types to int, so this is equivalent for now, but forward-compatible. Fixes FIXME Also move the int type check before parsing the colon. --- src/cc65/declare.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 48db64e61..b8ad09cfd 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -681,14 +681,16 @@ static int ParseFieldWidth (Declaration* Decl) return -1; } + if (!IsClassInt (Decl->Type)) { + /* Only integer types may be used for bit-fields */ + Error ("Bit-field has invalid type '%s', must be integral", + GetBasicTypeName (Decl->Type)); + return -1; + } + /* Read the width */ NextToken (); ConstAbsIntExpr (hie1, &Expr); - if (!IsClassInt (Decl->Type)) { - /* Only integer types may be used for bit-fields */ - Error ("Bit-field has invalid type '%s'", GetBasicTypeName (Decl->Type)); - return -1; - } if (SizeOf (Decl->Type) != SizeOf (type_uint)) { /* Only int sized types may be used for bit-fields for now */ @@ -700,20 +702,10 @@ static int ParseFieldWidth (Declaration* Decl) Error ("Negative width in bit-field"); return -1; } - /* FIXME: We should compare with the width of the specified type */ -#if 0 - /* Use is when we really support non-uint16_t bit-fields */ if (Expr.IVal > (long)(SizeOf (Decl->Type) * CHAR_BITS)) { Error ("Width of bit-field exceeds its type"); return -1; } -#else - /* This is what we currenty do */ - if (Expr.IVal > (long)(SizeOf (type_uint) * CHAR_BITS)) { - Error ("Width of bit-field exceeds 16"); - return -1; - } -#endif if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { Error ("Zero width for named bit-field"); return -1; From aaa0cf5448fc55e5b2bb925d0ac41779d8b50b56 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 29 Jul 2020 22:41:46 +0200 Subject: [PATCH 141/806] Add err tests for bitfield width errors ! ../../bin/cc65 -o ../../testwrk/err/bitfield-named-zero-width.s bitfield-named-zero-width.c bitfield-named-zero-width.c(27): Error: Zero width for named bit-field ! ../../bin/cc65 -o ../../testwrk/err/bitfield-negative-width.s bitfield-negative-width.c bitfield-negative-width.c(26): Error: Negative width in bit-field ! ../../bin/cc65 -o ../../testwrk/err/bitfield-too-wide.s bitfield-too-wide.c bitfield-too-wide.c(26): Error: Width of bit-field exceeds its type --- test/err/bitfield-named-zero-width.c | 29 ++++++++++++++++++++++++++++ test/err/bitfield-negative-width.c | 27 ++++++++++++++++++++++++++ test/err/bitfield-too-wide.c | 27 ++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 test/err/bitfield-named-zero-width.c create mode 100644 test/err/bitfield-negative-width.c create mode 100644 test/err/bitfield-too-wide.c diff --git a/test/err/bitfield-named-zero-width.c b/test/err/bitfield-named-zero-width.c new file mode 100644 index 000000000..b9b9db88d --- /dev/null +++ b/test/err/bitfield-named-zero-width.c @@ -0,0 +1,29 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of bit-field with zero-width named field +*/ + +struct s { + unsigned int x : 4; + unsigned int y : 0; + unsigned int z : 4; +}; diff --git a/test/err/bitfield-negative-width.c b/test/err/bitfield-negative-width.c new file mode 100644 index 000000000..dd83b3fc4 --- /dev/null +++ b/test/err/bitfield-negative-width.c @@ -0,0 +1,27 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of bit-field with negative width. +*/ + +struct s { + unsigned int x : -1; +}; diff --git a/test/err/bitfield-too-wide.c b/test/err/bitfield-too-wide.c new file mode 100644 index 000000000..6c9c229fc --- /dev/null +++ b/test/err/bitfield-too-wide.c @@ -0,0 +1,27 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of bit-field with too large width. +*/ + +struct s { + unsigned int x : 17; +}; From fb9b50ff9c63c2eefcef298348882fc3c2261587 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 29 Jul 2020 22:45:59 +0200 Subject: [PATCH 142/806] Move type checks before bit-field width parsing --- src/cc65/declare.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index b8ad09cfd..5c5c5c01e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -688,16 +688,16 @@ static int ParseFieldWidth (Declaration* Decl) return -1; } - /* Read the width */ - NextToken (); - ConstAbsIntExpr (hie1, &Expr); - if (SizeOf (Decl->Type) != SizeOf (type_uint)) { /* Only int sized types may be used for bit-fields for now */ Error ("cc65 currently only supports unsigned int bit-fields"); return -1; } + /* Read the width */ + NextToken (); + ConstAbsIntExpr (hie1, &Expr); + if (Expr.IVal < 0) { Error ("Negative width in bit-field"); return -1; From 19c81ed8665f1062c176bcc75cb2f23a32f9ae03 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 19:51:47 +0800 Subject: [PATCH 143/806] Fixed type mask usage. --- src/cc65/typecmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index af04d8232..66dd9039b 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -215,8 +215,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } /* Get the left and right types, signs and qualifiers */ - LeftType = GetUnderlyingTypeCode (lhs); - RightType = GetUnderlyingTypeCode (rhs); + LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE); + RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE); LeftSign = GetSignedness (lhs); RightSign = GetSignedness (rhs); LeftQual = GetQualifier (lhs); From 92de4fa0d0f8ae1c4d863a7191037bd0dea8cc0b Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 15:08:54 +0800 Subject: [PATCH 144/806] Enabled to recognize labels when parsing local variable declarations. --- src/cc65/declare.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 5c5c5c01e..1ebae4123 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1248,11 +1248,23 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) break; case TOK_IDENT: - Entry = FindSym (CurTok.Ident); - if (Entry && SymIsTypeDef (Entry)) { - /* It's a typedef */ - NextToken (); - TypeCopy (D->Type, Entry->Type); + /* This could be a label */ + if (NextTok.Tok != TOK_COLON) { + Entry = FindSym (CurTok.Ident); + if (Entry && SymIsTypeDef (Entry)) { + /* It's a typedef */ + NextToken (); + TypeCopy (D->Type, Entry->Type); + break; + } + } else { + /* This is a label. Use the default type flag to end the loop + ** in DeclareLocals. The type code used here doesn't matter as + ** long as it has no qualifiers. + */ + D->Flags |= DS_DEF_TYPE; + D->Type[0].C = T_QUAL_NONE; + D->Type[1].C = T_END; break; } /* FALL THROUGH */ From d6d667a688b3831e17ec0b4ec4b77a99cc0def88 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 18:07:16 +0800 Subject: [PATCH 145/806] Improved error handling with symbol redefinitions. --- src/cc65/symtab.c | 282 ++++++++++++++++++++++++++++++---------------- 1 file changed, 183 insertions(+), 99 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 4169e6280..6472a0792 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -93,6 +93,7 @@ static SymTable* TagTab0 = 0; static SymTable* TagTab = 0; static SymTable* LabelTab = 0; static SymTable* SPAdjustTab = 0; +static SymTable* FailSafeTab = 0; /* For errors */ /*****************************************************************************/ @@ -141,7 +142,6 @@ static void FreeSymTable (SymTable* S) } - /*****************************************************************************/ /* Check symbols in a table */ /*****************************************************************************/ @@ -228,6 +228,9 @@ void EnterGlobalLevel (void) /* Create and assign the table of SP adjustment symbols */ SPAdjustTab = NewSymTable (SYMTAB_SIZE_GLOBAL); + + /* Create and assign the table of fictitious symbols used with errors */ + FailSafeTab = NewSymTable (SYMTAB_SIZE_GLOBAL); } @@ -523,6 +526,111 @@ SymEntry* FindStructField (const Type* T, const char* Name) +static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags) +/* Check and handle redefinition of existing symbols. +** Return ture if there *is* an error. +*/ +{ + /* Get the type info of the existing symbol */ + Type* E_Type = Entry->Type; + unsigned E_SCType = Entry->Flags & SC_TYPEMASK; + unsigned SCType = Flags & SC_TYPEMASK; + + /* Some symbols may be redeclared if certain requirements are met */ + if (IsTypeArray (T) && IsTypeArray (E_Type)) { + + /* Get the array sizes */ + long Size = GetElementCount (T); + long ESize = GetElementCount (E_Type); + + /* If we are handling arrays, the old entry or the new entry may be + ** an incomplete declaration. Accept this, and if the exsting entry + ** is incomplete, complete it. + */ + if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || + TypeCmp (T + 1, E_Type + 1) < TC_EQUAL) { + /* Types not identical: Conflicting types */ + Error ("Conflicting array types for '%s[]'", Entry->Name); + Entry = 0; + } else { + /* Check if we have a size in the existing definition */ + if (ESize == UNSPECIFIED) { + /* Existing, size not given, use size from new def */ + SetElementCount (E_Type, Size); + } + } + + } else { + + /* We have a symbol with this name already */ + if ((Entry->Flags & SC_FUNC) == SC_FUNC) { + + /* In case of a function, use the new type descriptor, since it + ** contains pointers to the new symbol tables that are needed if + ** an actual function definition follows. Be sure not to use the + ** new descriptor if it contains a function declaration with an + ** empty parameter list. + */ + if (IsTypeFunc (T)) { + + /* New type must be identical */ + if (TypeCmp (Entry->Type, T) < TC_EQUAL) { + Error ("Conflicting function types for '%s'", Entry->Name); + Entry = 0; + } else { + /* Get the function descriptor from the new type */ + FuncDesc* F = GetFuncDesc (T); + /* Use this new function descriptor if it doesn't contain + ** an empty parameter list. + */ + if ((F->Flags & FD_EMPTY) == 0) { + Entry->V.F.Func = F; + SetFuncDesc (E_Type, F); + } + } + + } else { + Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } + } else if (E_SCType == SC_TYPEDEF) { + + if (SCType == SC_TYPEDEF) { + /* New typedef must be identical */ + if (TypeCmp (E_Type, T) < TC_EQUAL) { + Error ("Conflicting types for typedef '%s'", Entry->Name); + Entry = 0; + } + + } else { + + Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } + + } else { + + /* New type must be identical */ + if (SCType != E_SCType) { + Error ("Redefinition of '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } else if (TypeCmp (E_Type, T) < TC_EQUAL) { + Error ("Conflicting types for '%s'", Entry->Name); + Entry = 0; + } else if (E_SCType == SC_ENUMERATOR) { + /* Current code logic won't reach here, but still... */ + Error ("Redeclaration of enumerator constant '%s'", Entry->Name); + Entry = 0; + } + } + } + + /* Return if there are any errors */ + return Entry == 0; +} + + + static void AddSymEntry (SymTable* T, SymEntry* S) /* Add a symbol to a symbol table */ { @@ -554,28 +662,39 @@ static void AddSymEntry (SymTable* T, SymEntry* S) SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) /* Add an enum entry and return it */ { + SymTable* CurTagTab = TagTab; + /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (TagTab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); if (Entry) { /* We do have an entry. This may be a forward, so check it. */ if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) { /* Existing symbol is not an enum */ Error ("Symbol '%s' is already different kind", Name); + Entry = 0; } else { /* Define the struct size if the underlying type is given. */ if (Type != 0) { if (Type !=0 && Entry->V.E.Type != 0) { /* Both are definitions. */ Error ("Multiple definition for enum '%s'", Name); + Entry = 0; + } else { + Entry->Type = 0; + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; } - Entry->Type = 0; - Entry->V.E.SymTab = Tab; - Entry->V.E.Type = Type; } } - } else { + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + CurTagTab = FailSafeTab; + } + } + + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, SC_ENUM); @@ -586,7 +705,7 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) Entry->V.E.Type = Type; /* Add it to the current table */ - AddSymEntry (TagTab, Entry); + AddSymEntry (CurTagTab, Entry); } /* Return the entry */ @@ -598,22 +717,25 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab) /* Add a struct/union entry and return it */ { + SymTable* CurTagTab = TagTab; SymEntry* Entry; /* Type must be struct or union */ PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); /* Do we have an entry with this name already? */ - Entry = FindSymInTable (TagTab, Name, HashStr (Name)); + Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); if (Entry) { /* We do have an entry. This may be a forward, so check it. */ if ((Entry->Flags & SC_TYPEMASK) != Type) { /* Existing symbol is not a struct */ Error ("Symbol '%s' is already different kind", Name); + Entry = 0; } else if (Size > 0 && Entry->V.S.Size > 0) { /* Both structs are definitions. */ Error ("Multiple definition for '%s'", Name); + Entry = 0; } else { /* Define the struct size if it is given */ if (Size > 0) { @@ -622,7 +744,13 @@ SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable } } - } else { + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + CurTagTab = FailSafeTab; + } + } + + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, Type); @@ -631,8 +759,8 @@ SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable Entry->V.S.SymTab = Tab; Entry->V.S.Size = Size; - /* Add it to the current table */ - AddSymEntry (TagTab, Entry); + /* Add it to the current tag table */ + AddSymEntry (CurTagTab, Entry); } /* Return the entry */ @@ -704,6 +832,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val } + DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) /* Add definition or reference to the SymEntry and preserve its attributes */ { @@ -721,6 +850,8 @@ DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) return DOR; } + + unsigned short FindSPAdjustment (const char* Name) /* Search for an entry in the table of SP adjustments */ { @@ -733,6 +864,8 @@ unsigned short FindSPAdjustment (const char* Name) return Entry->V.SPAdjustment; } + + SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Add a goto label to the label table */ { @@ -848,15 +981,21 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs) /* Add a local symbol and return the symbol entry */ { + SymTable* Tab = SymTab; + /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { /* We have a symbol with this name already */ - Error ("Multiple definition for '%s'", Name); - - } else { - + if (HandleSymRedefinition (Entry, T, Flags)) { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; + Entry = 0; + } + } + + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -881,7 +1020,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs } /* Add the entry to the symbol table */ - AddSymEntry (SymTab, Entry); + AddSymEntry (Tab, Entry); } @@ -898,101 +1037,46 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) int IsFunc = IsTypeFunc (T); /* Functions must be inserted in the global symbol table */ - SymTable* Tab = IsFunc? SymTab0 : SymTab; + SymTable* Tab = IsFunc ? SymTab0 : SymTab; /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); if (Entry) { - Type* EType; - - /* If the existing symbol is an enumerated constant, - ** then avoid a compiler crash. See GitHub issue #728. - */ - if (Entry->Flags & SC_ENUMERATOR) { - Fatal ("Can't redeclare enumerator constant '%s' as global variable", Name); - } /* We have a symbol with this name already */ - if (Entry->Flags & SC_TYPEMASK) { - Error ("Multiple definition for '%s'", Name); - return Entry; - } - - /* Get the type string of the existing symbol */ - EType = Entry->Type; - - /* If we are handling arrays, the old entry or the new entry may be an - ** incomplete declaration. Accept this, and if the exsting entry is - ** incomplete, complete it. - */ - if (IsTypeArray (T) && IsTypeArray (EType)) { - - /* Get the array sizes */ - long Size = GetElementCount (T); - long ESize = GetElementCount (EType); - - if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || - TypeCmp (T + 1, EType + 1) < TC_EQUAL) { - /* Types not identical: Conflicting types */ - Error ("Conflicting types for '%s'", Name); - return Entry; - } else { - /* Check if we have a size in the existing definition */ - if (ESize == UNSPECIFIED) { - /* Existing, size not given, use size from new def */ - SetElementCount (EType, Size); - } - } + if (HandleSymRedefinition (Entry, T, Flags)) { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; + Entry = 0; } else { - /* New type must be identical */ - if (TypeCmp (EType, T) < TC_EQUAL) { - Error ("Conflicting types for '%s'", Name); - return Entry; - } - /* In case of a function, use the new type descriptor, since it - ** contains pointers to the new symbol tables that are needed if - ** an actual function definition follows. Be sure not to use the - ** new descriptor if it contains a function declaration with an - ** empty parameter list. + /* If a static declaration follows a non-static declaration, then + ** warn about the conflict. (It will compile a public declaration.) */ - if (IsFunc) { - /* Get the function descriptor from the new type */ - FuncDesc* F = GetFuncDesc (T); - /* Use this new function descriptor if it doesn't contain - ** an empty parameter list. - */ - if ((F->Flags & FD_EMPTY) == 0) { - Entry->V.F.Func = F; - SetFuncDesc (EType, F); - } + if ((Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { + Warning ("static declaration follows non-static declaration of '%s'.", Name); } + + /* An extern declaration must not change the current linkage. */ + if (IsFunc || (Flags & (SC_EXTERN | SC_STORAGE)) == SC_EXTERN) { + Flags &= ~SC_EXTERN; + } + + /* If a public declaration follows a static declaration, then + ** warn about the conflict. (It will compile a public declaration.) + */ + if ((Flags & SC_EXTERN) != 0 && (Entry->Flags & SC_EXTERN) == 0) { + Warning ("public declaration follows static declaration of '%s'.", Name); + } + + /* Add the new flags */ + Entry->Flags |= Flags; } - /* If a static declaration follows a non-static declaration, then - ** warn about the conflict. (It will compile a public declaration.) - */ - if ((Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { - Warning ("static declaration follows non-static declaration of '%s'.", Name); - } - - /* An extern declaration must not change the current linkage. */ - if (IsFunc || (Flags & (SC_EXTERN | SC_STORAGE)) == SC_EXTERN) { - Flags &= ~SC_EXTERN; - } - - /* If a public declaration follows a static declaration, then - ** warn about the conflict. (It will compile a public declaration.) - */ - if ((Flags & SC_EXTERN) != 0 && (Entry->Flags & SC_EXTERN) == 0) { - Warning ("public declaration follows static declaration of '%s'.", Name); - } - - /* Add the new flags */ - Entry->Flags |= Flags; - - } else { + } + + if (Entry == 0) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); From 25d10d9d9ad033c793f0857b465774a42c3613a3 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 15:09:53 +0800 Subject: [PATCH 146/806] Fixed nested struct/union initialization. Fixed bit-fields offsets in anonymous structs. --- src/cc65/anonname.c | 11 +++ src/cc65/anonname.h | 5 ++ src/cc65/declare.c | 203 +++++++++++++++++++++++++++----------------- src/cc65/expr.c | 31 ++++--- src/cc65/symentry.h | 10 +++ src/cc65/symtab.c | 39 +++++++-- src/cc65/symtab.h | 7 +- 7 files changed, 201 insertions(+), 105 deletions(-) diff --git a/src/cc65/anonname.c b/src/cc65/anonname.c index d02253d82..23891bf0e 100644 --- a/src/cc65/anonname.c +++ b/src/cc65/anonname.c @@ -61,6 +61,17 @@ static const char AnonTag[] = "$anon"; +char* AnonFieldName (char* Buf, const char* Spec, int ANumber) +/* Get a name for an anonymous field of a struct or union. The given buffer is +** expected to be IDENTSIZE characters long. A pointer to the buffer is returned. +*/ +{ + xsprintf (Buf, IDENTSIZE, "%s-%s-%04X", AnonTag, Spec, ANumber); + return Buf; +} + + + char* AnonName (char* Buf, const char* Spec) /* Get a name for an anonymous variable or type. The given buffer is expected ** to be IDENTSIZE characters long. A pointer to the buffer is returned. diff --git a/src/cc65/anonname.h b/src/cc65/anonname.h index dfaa1bdaa..a88b131b5 100644 --- a/src/cc65/anonname.h +++ b/src/cc65/anonname.h @@ -44,6 +44,11 @@ +char* AnonFieldName (char* Buf, const char* Spec, int ANumber); +/* Get a name for an anonymous field of a struct or union. The given buffer is +** expected to be IDENTSIZE characters long. A pointer to the buffer is returned. +*/ + char* AnonName (char* Buf, const char* Spec); /* Get a name for an anonymous variable or type. The given buffer is expected ** to be IDENTSIZE characters long. A pointer to the buffer is returned. diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 1ebae4123..a1d23c20b 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -89,7 +89,7 @@ struct StructInitData { static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); /* Parse a type specifier */ -static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers); +static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers); /* Parse initialization of variables. Return the number of data bytes. */ @@ -717,24 +717,24 @@ static int ParseFieldWidth (Declaration* Decl) -static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) -/* Copy fields from an anon union/struct into the current lexical level. The -** function returns the size of the embedded struct/union. +static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) +/* Create alias fields from an anon union/struct in the current lexical level. +** The function returns the count of created aliases. */ { + unsigned Count = 0; + SymEntry* Alias; + /* Get the pointer to the symbol table entry of the anon struct */ SymEntry* Entry = GetSymEntry (Decl->Type); - /* Get the size of the anon struct */ - unsigned Size = Entry->V.S.Size; - /* Get the symbol table containing the fields. If it is empty, there has ** been an error before, so bail out. */ SymTable* Tab = Entry->V.S.SymTab; if (Tab == 0) { /* Incomplete definition - has been flagged before */ - return Size; + return 0; } /* Get a pointer to the list of symbols. Then walk the list adding copies @@ -743,10 +743,13 @@ static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) Entry = Tab->SymHead; while (Entry) { - /* Enter a copy of this symbol adjusting the offset. We will just - ** reuse the type string here. - */ - AddLocalSym (Entry->Name, Entry->Type, SC_STRUCTFIELD, Offs + Entry->V.Offs); + /* Enter an alias of this symbol */ + if (!IsAnonName (Entry->Name)) { + Alias = AddLocalSym (Entry->Name, Entry->Type, SC_STRUCTFIELD|SC_ALIAS, 0); + Alias->V.A.Field = Entry; + Alias->V.A.Offs = Anon->V.Offs + Entry->V.Offs; + ++Count; + } /* Currently, there can not be any attributes, but if there will be ** some in the future, we want to know this. @@ -757,8 +760,8 @@ static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) Entry = Entry->NextSym; } - /* Return the size of the embedded struct */ - return Size; + /* Return the count of created aliases */ + return Count; } @@ -771,6 +774,8 @@ static SymEntry* ParseUnionDecl (const char* Name) unsigned FieldSize; int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; + SymEntry* StructTypeEntry; + SymEntry* Entry; if (CurTok.Tok != TOK_LCURLY) { @@ -779,7 +784,9 @@ static SymEntry* ParseUnionDecl (const char* Name) } /* Add a forward declaration for the struct in the current lexical level */ - AddStructSym (Name, SC_UNION, 0, 0); + StructTypeEntry = AddStructSym (Name, SC_UNION, 0, 0); + + StructTypeEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -821,16 +828,11 @@ static SymEntry* ParseUnionDecl (const char* Name) /* This is an anonymous struct or union. Copy the fields ** into the current level. */ - FieldSize = CopyAnonStructFields (&Decl, 0); - if (FieldSize > UnionSize) { - UnionSize = FieldSize; - } - + AnonFieldName (Decl.Ident, "field", StructTypeEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); } - goto NextMember; } /* Handle sizes */ @@ -843,7 +845,13 @@ static SymEntry* ParseUnionDecl (const char* Name) if (FieldWidth > 0) { AddBitField (Decl.Ident, 0, 0, FieldWidth); } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + if (IsAnonName (Decl.Ident)) { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + Entry->V.A.ANumber = StructTypeEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Entry); + } else { + AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + } } NextMember: if (CurTok.Tok != TOK_COMMA) { @@ -876,6 +884,8 @@ static SymEntry* ParseStructDecl (const char* Name) unsigned BitOffs; /* Bit offset for bit-fields */ int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; + SymEntry* StructTypeEntry; + SymEntry* Entry; if (CurTok.Tok != TOK_LCURLY) { @@ -884,7 +894,9 @@ static SymEntry* ParseStructDecl (const char* Name) } /* Add a forward declaration for the struct in the current lexical level */ - AddStructSym (Name, SC_STRUCT, 0, 0); + StructTypeEntry = AddStructSym (Name, SC_STRUCT, 0, 0); + + StructTypeEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -981,13 +993,11 @@ static SymEntry* ParseStructDecl (const char* Name) /* This is an anonymous struct or union. Copy the ** fields into the current level. */ - StructSize += CopyAnonStructFields (&Decl, StructSize); - + AnonFieldName (Decl.Ident, "field", StructTypeEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); } - goto NextMember; } else { /* A bit-field without a name will get an anonymous one */ AnonName (Decl.Ident, "bit-field"); @@ -1009,7 +1019,13 @@ static SymEntry* ParseStructDecl (const char* Name) StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + if (IsAnonName (Decl.Ident)) { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + Entry->V.A.ANumber = StructTypeEntry->V.S.ACount++; + AliasAnonStructFields (&Decl, Entry); + } else { + AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + } if (!FlexibleMember) { StructSize += CheckedSizeOf (Decl.Type); } @@ -1874,18 +1890,20 @@ void CheckEmptyDecl (const DeclSpec* D) -static void SkipInitializer (unsigned BracesExpected) +static void SkipInitializer (int BracesExpected) /* Skip the remainder of an initializer in case of errors. Try to be somewhat ** smart so we don't have too many following errors. */ { - while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected > 0) { + while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) { switch (CurTok.Tok) { case TOK_RCURLY: --BracesExpected; break; case TOK_LCURLY: ++BracesExpected; break; default: break; } - NextToken (); + if (BracesExpected >= 0) { + NextToken (); + } } } @@ -1917,6 +1935,7 @@ static void ClosingCurlyBraces (unsigned BracesExpected) */ { while (BracesExpected) { + /* TODO: Skip all excess initializers until next closing curly brace */ if (CurTok.Tok == TOK_RCURLY) { NextToken (); } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) { @@ -2069,7 +2088,7 @@ static unsigned ParsePointerInit (Type* T) -static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) +static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initializaton for arrays. Return the number of data bytes. */ { int Count; @@ -2135,7 +2154,7 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) ** an array (because the size of each element may differ ** otherwise). */ - ParseInitInternal (ElementType, 0); + ParseInitInternal (ElementType, Braces, 0); ++Count; if (CurTok.Tok != TOK_COMMA) break; @@ -2158,23 +2177,29 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) } else if (Count < ElementCount) { g_zerobytes ((ElementCount - Count) * ElementSize); } else if (Count > ElementCount) { - Error ("Too many initializers"); + Error ("Excess elements in array initializer"); } return ElementCount * ElementSize; } -static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) +static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of a struct or union. Return the number of data bytes. */ { SymEntry* Entry; SymTable* Tab; StructInitData SI; + int HasCurly = 0; + int SkipComma = 0; - /* Consume the opening curly brace */ - ConsumeLCurly (); + /* Fields can be initialized without a pair of curly braces */ + if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { + /* Consume the opening curly brace */ + HasCurly = ConsumeLCurly (); + *Braces += HasCurly; + } /* Get a pointer to the struct entry from the type */ Entry = GetSymEntry (T); @@ -2189,7 +2214,7 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) if (Tab == 0) { Error ("Cannot initialize variables with incomplete type"); /* Try error recovery */ - SkipInitializer (1); + SkipInitializer (HasCurly); /* Nothing initialized */ return 0; } @@ -2203,18 +2228,50 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) SI.ValBits = 0; while (CurTok.Tok != TOK_RCURLY) { - /* */ + /* Check for excess elements */ if (Entry == 0) { - Error ("Too many initializers"); - SkipInitializer (1); + if (HasCurly) { + Error ("Excess elements in %s initializer", GetBasicTypeName (T)); + SkipInitializer (HasCurly); + } return SI.Offs; } - /* Parse initialization of one field. Bit-fields need a special - ** handling. + /* Check for special members that don't consume the initializer */ + if ((Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Just skip */ + goto NextMember; + } + + /* This may be an anonymous bit-field, in which case it doesn't + ** have an initializer. */ + if (SymIsBitField (Entry) && (IsAnonName (Entry->Name))) { + /* Account for the data and output it if we have at least a full + ** word. We may have more if there was storage unit overlap, for + ** example two consecutive 10 bit fields. These will be packed + ** into 3 bytes. + */ + SI.ValBits += Entry->V.B.BitWidth; + CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); + while (SI.ValBits >= CHAR_BITS) { + OutputBitFieldData (&SI); + } + /* Avoid consuming the comma if any */ + goto NextMember; + } + + /* Skip comma this round */ + if (SkipComma) { + NextToken (); + SkipComma = 0; + } + if (SymIsBitField (Entry)) { + /* Parse initialization of one field. Bit-fields need a special + ** handling. + */ ExprDesc ED; unsigned Val; unsigned Shift; @@ -2226,36 +2283,17 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == SI.Offs * CHAR_BITS + SI.ValBits); - /* This may be an anonymous bit-field, in which case it doesn't - ** have an initializer. - */ - if (IsAnonName (Entry->Name)) { - /* Account for the data and output it if we have at least a - ** full word. We may have more if there was storage unit - ** overlap, for example two consecutive 10 bit fields. - ** These will be packed into 3 bytes. - */ - SI.ValBits += Entry->V.B.BitWidth; - CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); - while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); - } - goto NextMember; - } else { - /* Read the data, check for a constant integer, do a range - ** check. - */ - ParseScalarInitInternal (type_uint, &ED); - if (!ED_IsConstAbsInt (&ED)) { - Error ("Constant initializer expected"); - ED_MakeConstAbsInt (&ED, 1); - } - if (ED.IVal > (long) Mask) { - Warning ("Truncating value in bit-field initializer"); - ED.IVal &= (long) Mask; - } - Val = (unsigned) ED.IVal; + /* Read the data, check for a constant integer, do a range check */ + ParseScalarInitInternal (type_uint, &ED); + if (!ED_IsConstAbsInt (&ED)) { + Error ("Constant initializer expected"); + ED_MakeConstAbsInt (&ED, 1); } + if (ED.IVal > (long) Mask) { + Warning ("Truncating value in bit-field initializer"); + ED.IVal &= (long) Mask; + } + Val = (unsigned) ED.IVal; /* Add the value to the currently stored bit-field value */ Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; @@ -2285,7 +2323,7 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) /* Flexible array members may only be initialized if they are ** the last field (or part of the last struct field). */ - SI.Offs += ParseInitInternal (Entry->Type, AllowFlexibleMembers && Entry->NextSym == 0); + SI.Offs += ParseInitInternal (Entry->Type, Braces, AllowFlexibleMembers && Entry->NextSym == 0); } /* More initializers? */ @@ -2293,8 +2331,8 @@ static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) break; } - /* Skip the comma */ - NextToken (); + /* Skip the comma next round */ + SkipComma = 1; NextMember: /* Next member. For unions, only the first one can be initialized */ @@ -2307,8 +2345,10 @@ NextMember: } } - /* Consume the closing curly brace */ - ConsumeRCurly (); + if (HasCurly) { + /* Consume the closing curly brace */ + ConsumeRCurly (); + } /* If we have data from a bit-field left, output it now */ CHECK (SI.ValBits < CHAR_BITS); @@ -2405,7 +2445,7 @@ static unsigned ParseVoidInit (Type* T) -static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) +static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { switch (GetUnderlyingTypeCode (T)) { @@ -2426,11 +2466,11 @@ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) return ParsePointerInit (T); case T_ARRAY: - return ParseArrayInit (T, AllowFlexibleMembers); + return ParseArrayInit (T, Braces, AllowFlexibleMembers); case T_STRUCT: case T_UNION: - return ParseStructInit (T, AllowFlexibleMembers); + return ParseStructInit (T, Braces, AllowFlexibleMembers); case T_VOID: if (IS_Get (&Standard) == STD_CC65) { @@ -2451,10 +2491,13 @@ static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) unsigned ParseInit (Type* T) /* Parse initialization of variables. Return the number of data bytes. */ { + /* Current curly braces layers */ + int Braces = 0; + /* Parse the initialization. Flexible array members can only be initialized ** in cc65 mode. */ - unsigned Size = ParseInitInternal (T, IS_Get (&Standard) == STD_CC65); + unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65); /* The initialization may not generate code on global level, because code ** outside function scope will never get executed. diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a35ad59a9..fd8a48002 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1201,7 +1201,6 @@ static void StructRef (ExprDesc* Expr) /* Process struct/union field after . or ->. */ { ident Ident; - SymEntry* Field; Type* FinalType; TypeCode Q; @@ -1217,8 +1216,8 @@ static void StructRef (ExprDesc* Expr) /* Get the symbol table entry and check for a struct/union field */ strcpy (Ident, CurTok.Ident); NextToken (); - Field = FindStructField (Expr->Type, Ident); - if (Field == 0) { + const SymEntry Field = FindStructField (Expr->Type, Ident); + if (Field.Type == 0) { Error ("No field named '%s' found in %s", Ident, GetBasicTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); @@ -1264,10 +1263,10 @@ static void StructRef (ExprDesc* Expr) } else { Q = GetQualifier (Indirect (Expr->Type)); } - if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) { - FinalType = Field->Type; + if (GetQualifier (Field.Type) == (GetQualifier (Field.Type) | Q)) { + FinalType = Field.Type; } else { - FinalType = TypeDup (Field->Type); + FinalType = TypeDup (Field.Type); FinalType->C |= Q; } @@ -1278,10 +1277,10 @@ static void StructRef (ExprDesc* Expr) /* Get the size of the type */ unsigned StructSize = SizeOf (Expr->Type); - unsigned FieldSize = SizeOf (Field->Type); + unsigned FieldSize = SizeOf (Field.Type); /* Safety check */ - CHECK (Field->V.Offs + FieldSize <= StructSize); + CHECK (Field.V.Offs + FieldSize <= StructSize); /* The type of the operation depends on the type of the struct/union */ switch (StructSize) { @@ -1304,16 +1303,16 @@ static void StructRef (ExprDesc* Expr) /* Generate a shift to get the field in the proper position in the ** primary. For bit fields, mask the value. */ - BitOffs = Field->V.Offs * CHAR_BITS; - if (SymIsBitField (Field)) { - BitOffs += Field->V.B.BitOffs; + BitOffs = Field.V.Offs * CHAR_BITS; + if (SymIsBitField (&Field)) { + BitOffs += Field.V.B.BitOffs; g_asr (Flags, BitOffs); /* Mask the value. This is unnecessary if the shift executed above ** moved only zeroes into the value. */ - if (BitOffs + Field->V.B.BitWidth != FieldSize * CHAR_BITS) { + if (BitOffs + Field.V.B.BitWidth != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, - (0x0001U << Field->V.B.BitWidth) - 1U); + (0x0001U << Field.V.B.BitWidth) - 1U); } } else { g_asr (Flags, BitOffs); @@ -1325,7 +1324,7 @@ static void StructRef (ExprDesc* Expr) } else { /* Set the struct/union field offset */ - Expr->IVal += Field->V.Offs; + Expr->IVal += Field.V.Offs; /* Use the new type */ Expr->Type = FinalType; @@ -1341,8 +1340,8 @@ static void StructRef (ExprDesc* Expr) } /* Make the expression a bit field if necessary */ - if (SymIsBitField (Field)) { - ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth); + if (SymIsBitField (&Field)) { + ED_MakeBitField (Expr, Field.V.B.BitOffs, Field.V.B.BitWidth); } } diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index b030d8b8b..e59bf2a35 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -105,6 +105,7 @@ struct CodeEntry; #define SC_SPADJUSTMENT 0x400000U #define SC_GOTO_IND 0x800000U /* Indirect goto */ +#define SC_ALIAS 0x01000000U /* Alias of anonymous field */ @@ -138,6 +139,14 @@ struct SymEntry { /* Offset for locals or struct members */ int Offs; + /* Data for anonymous struct or union members */ + struct { + int Offs; /* Byte offset into struct */ + unsigned ANumber; /* Numeric ID */ + SymEntry* Field; /* The real field aliased */ + } A; + + /* Label name for static symbols */ struct { unsigned Label; @@ -163,6 +172,7 @@ struct SymEntry { struct { struct SymTable* SymTab; /* Member symbol table */ unsigned Size; /* Size of the union/struct */ + unsigned ACount; /* Count of anonymous fields */ } S; /* Data for enums */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 6472a0792..f7a7862cf 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -490,10 +490,15 @@ SymEntry* FindTagSym (const char* Name) -SymEntry* FindStructField (const Type* T, const char* Name) -/* Find a struct/union field in the fields list */ +SymEntry FindStructField (const Type* T, const char* Name) +/* Find a struct/union field in the fields list. +** Return the info about the found field symbol filled in an entry struct by +** value, or an empty entry struct if the field is not found. +*/ { - SymEntry* Field = 0; + SymEntry* Entry = 0; + SymEntry Field; + int Offs = 0; /* The given type may actually be a pointer to struct/union */ if (IsTypePtr (T)) { @@ -511,10 +516,26 @@ SymEntry* FindStructField (const Type* T, const char* Name) ** not exist. */ if (Struct->V.S.SymTab) { - Field = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + Entry = FindSymInTable (Struct->V.S.SymTab, Name, HashStr (Name)); + + if (Entry != 0) { + Offs = Entry->V.Offs; + } + + while (Entry != 0 && (Entry->Flags & SC_ALIAS) == SC_ALIAS) { + /* Get the real field */ + Entry = Entry->V.A.Field; + } } } + if (Entry != 0) { + Field = *Entry; + Field.V.Offs = Offs; + } else { + memset (&Field, 0, sizeof(SymEntry)); + } + return Field; } @@ -1001,7 +1022,13 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Set the symbol attributes */ Entry->Type = TypeDup (T); - if ((Flags & SC_AUTO) == SC_AUTO || (Flags & SC_TYPEMASK) == SC_TYPEDEF) { + + if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || + (Flags & SC_TYPEDEF) == SC_TYPEDEF) { + if ((Flags & SC_ALIAS) != SC_ALIAS) { + Entry->V.Offs = Offs; + } + } else if ((Flags & SC_AUTO) == SC_AUTO) { Entry->V.Offs = Offs; } else if ((Flags & SC_REGISTER) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; @@ -1013,8 +1040,6 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Generate the assembler name from the label number */ Entry->V.L.Label = Offs; Entry->AsmName = xstrdup (LocalLabelName (Entry->V.L.Label)); - } else if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD) { - Entry->V.Offs = Offs; } else { Internal ("Invalid flags in AddLocalSym: %04X", Flags); } diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index dd4b3aa37..79f656f95 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -133,8 +133,11 @@ SymEntry* FindLocalSym (const char* Name); SymEntry* FindTagSym (const char* Name); /* Find the symbol with the given name in the tag table */ -SymEntry* FindStructField (const Type* TypeArray, const char* Name); -/* Find a struct/union field in the fields list */ +SymEntry FindStructField (const Type* TypeArray, const char* Name); +/* Find a struct/union field in the fields list. +** Return the info about the found field symbol filled in an entry struct by +** value, or an empty entry struct if the field is not found. +*/ unsigned short FindSPAdjustment (const char* Name); /* Search for an entry in the table of SP adjustments */ From 9075a853dc919e2d028b4cf67af61183555dad13 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 15:09:53 +0800 Subject: [PATCH 147/806] Allows one trailing comma before the closing curly of a struct/union initializer. --- src/cc65/declare.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index a1d23c20b..0c489dab4 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2230,6 +2230,13 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Check for excess elements */ if (Entry == 0) { + /* Is there just one trailing comma before a closing curly? */ + if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) { + /* Skip comma and exit scope */ + NextToken (); + break; + } + if (HasCurly) { Error ("Excess elements in %s initializer", GetBasicTypeName (T)); SkipInitializer (HasCurly); From 8a511bb63ddcfefa71700bada01a02cd863396a0 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 15:09:53 +0800 Subject: [PATCH 148/806] Fixed nested array initializers. --- src/cc65/declare.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 0c489dab4..c15041cbf 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2092,6 +2092,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initializaton for arrays. Return the number of data bytes. */ { int Count; + int HasCurly = 0; /* Get the array data */ Type* ElementType = GetElementType (T); @@ -2144,8 +2145,12 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } else { - /* Curly brace */ - ConsumeLCurly (); + /* Arrays can be initialized without a pair of curly braces */ + if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) { + /* Consume the opening curly brace */ + HasCurly = ConsumeLCurly (); + *Braces += HasCurly; + } /* Initialize the array members */ Count = 0; @@ -2161,8 +2166,10 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) NextToken (); } - /* Closing curly braces */ - ConsumeRCurly (); + if (HasCurly) { + /* Closing curly braces */ + ConsumeRCurly (); + } } if (ElementCount == UNSPECIFIED) { @@ -2176,7 +2183,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) ElementCount = Count; } else if (Count < ElementCount) { g_zerobytes ((ElementCount - Count) * ElementSize); - } else if (Count > ElementCount) { + } else if (Count > ElementCount && HasCurly) { Error ("Excess elements in array initializer"); } return ElementCount * ElementSize; From e38f601fcc5db52355a2c8afb498723699c6e3bf Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 29 Jul 2020 15:09:53 +0800 Subject: [PATCH 149/806] Fixed padding at the ends of structs with bit-fields. --- src/cc65/declare.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index c15041cbf..9e77ea63e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -717,6 +717,29 @@ static int ParseFieldWidth (Declaration* Decl) +static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) +/* Pad the current struct with an anonymous bit-field aligned to the next byte. +** Return how many bits are used to pad. +*/ +{ + /* MSVC complains about unary negation of unsigned, + ** so it has been rewritten as subtraction. + */ + unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; + + /* We need an anonymous name */ + ident Ident; + AnonName (Ident, "bit-field"); + + /* Add an anonymous bit-field that aligns to the next + ** byte. + */ + AddBitField (Ident, StructSize, BitOffs, PaddingBits); + + return PaddingBits; +} + + static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) /* Create alias fields from an anon union/struct in the current lexical level. ** The function returns the count of created aliases. @@ -919,7 +942,6 @@ static SymEntry* ParseStructDecl (const char* Name) while (1) { Declaration Decl; - ident Ident; /* If we had a flexible array member before, no other fields can ** follow. @@ -942,19 +964,10 @@ static SymEntry* ParseStructDecl (const char* Name) */ if (BitOffs > 0) { if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { - /* Bits needed to byte-align the next field. - ** MSVC complains about unary negation of unsigned, - ** so it has been rewritten as subtraction. - */ - unsigned PaddingBits = (0 - BitOffs) % CHAR_BITS; - - /* We need an anonymous name */ - AnonName (Ident, "bit-field"); - /* Add an anonymous bit-field that aligns to the next ** byte. */ - AddBitField (Ident, StructSize, BitOffs, PaddingBits); + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); /* No bits left */ StructSize += (BitOffs + PaddingBits) / CHAR_BITS; @@ -1039,9 +1052,12 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { ConsumeSemi (); } - /* If we have bits from bit-fields left, add them to the size. */ if (BitOffs > 0) { - StructSize += ((BitOffs + CHAR_BITS - 1) / CHAR_BITS); + /* If we have bits from bit-fields left, pad the struct to next byte */ + unsigned PaddingBits = PadWithBitField (StructSize, BitOffs); + + /* No bits left */ + StructSize += (BitOffs + PaddingBits) / CHAR_BITS; } /* Skip the closing brace */ From 2d5fd0fc6301ab0d0ee4cb3517f9e0c9dce540a5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 30 Jul 2020 12:00:21 +0200 Subject: [PATCH 150/806] Use char ops if possible for bit-field loads Set CF_FORCECHAR and change type to char once we have shifted into a char. This saves some unnecessary ldx #0 instructions. --- src/cc65/loadexpr.c | 20 +++++++++- test/val/bitfield.c | 92 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index bc0ee1dd0..807a5cbe5 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -193,10 +193,26 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** operations. */ if (ED_IsBitField (Expr)) { - unsigned F = CF_INT | CF_UNSIGNED | CF_CONST | (Flags & CF_TEST); + /* If the field was loaded as a char, force the shift/mask ops to be char ops. + ** If it is a char, the load has already put 0 in the upper byte, so that can remain. + ** CF_FORCECHAR does nothing if the type is not CF_CHAR. + */ + unsigned F = Flags | CF_FORCECHAR | CF_CONST; + /* Shift right by the bit offset */ g_asr (F, Expr->BitOffs); - /* And by the width if the field doesn't end on an int boundary */ + + /* Since we have now shifted down, we can do char ops as long as the width fits in + ** a char. + */ + if (Expr->BitWidth <= CHAR_BITS) { + F |= CF_CHAR; + } + + /* And by the width if the field doesn't end on a char or int boundary. If it does + ** end on a boundary, then zeros have already been shifted in. g_and emits no code + ** if the mask is all ones. + */ if (Expr->BitOffs + Expr->BitWidth != CHAR_BITS && Expr->BitOffs + Expr->BitWidth != INT_BITS) { g_and (F, (0x0001U << Expr->BitWidth) - 1U); diff --git a/test/val/bitfield.c b/test/val/bitfield.c index 81d5b2aa1..67747ed5b 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -182,12 +182,104 @@ static void test_overlap_with_int(void) } } +static struct full_width { + unsigned int x : 8; + unsigned int y : 16; +} fw = {255, 17}; + +static void test_full_width(void) +{ + if (sizeof(struct full_width) != 3) { + printf("Got sizeof(struct full_width) = %zu, expected 3.\n", + sizeof(struct full_width)); + failures++; + } + + if (fw.x != 255) { + printf("Got fw.x = %u, expected 255.\n", fw.x); + failures++; + } + + if (fw.y != 17) { + printf("Got fw.y = %u, expected 17.\n", fw.y); + failures++; + } + + fw.x = 42; + fw.y = 1023; + + if (fw.x != 42) { + printf("Got fw.x = %u, expected 42.\n", fw.x); + failures++; + } + + if (fw.y != 1023) { + printf("Got fw.y = %u, expected 1023.\n", fw.y); + failures++; + } +} + +static struct aligned_end { + unsigned int : 2; + unsigned int x : 6; + unsigned int : 3; + unsigned int y : 13; + /* z crosses a byte boundary, but fits in a byte when shifted. */ + unsigned int : 6; + unsigned int z : 7; +} ae = {63, 17, 100}; + +static void test_aligned_end(void) +{ + if (sizeof(struct aligned_end) != 5) { + printf("Got sizeof(struct aligned_end) = %zu, expected 5.\n", + sizeof(struct aligned_end)); + failures++; + } + + if (ae.x != 63) { + printf("Got ae.x = %u, expected 63.\n", ae.x); + failures++; + } + + if (ae.y != 17) { + printf("Got ae.y = %u, expected 17.\n", ae.y); + failures++; + } + + if (ae.z != 100) { + printf("Got ae.z = %u, expected 100.\n", ae.z); + failures++; + } + + ae.x = 42; + ae.y = 1023; + ae.z = 66; + + if (ae.x != 42) { + printf("Got ae.x = %u, expected 42.\n", ae.x); + failures++; + } + + if (ae.y != 1023) { + printf("Got ae.y = %u, expected 1023.\n", ae.y); + failures++; + } + + if (ae.z != 66) { + printf("Got ae.z = %u, expected 66.\n", ae.z); + failures++; + } +} + int main(void) { test_four_bits(); test_four_bits_with_int(); test_overlap(); test_overlap_with_int(); + test_full_width(); + test_aligned_end(); printf("failures: %u\n", failures); return failures; } From c72fa735b96a832852e2b7f60c3677463e3f8f20 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 30 Jul 2020 17:21:56 +0200 Subject: [PATCH 151/806] Add test for #1139 --- test/todo/bug1139.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/todo/bug1139.c diff --git a/test/todo/bug1139.c b/test/todo/bug1139.c new file mode 100644 index 000000000..222a9ab51 --- /dev/null +++ b/test/todo/bug1139.c @@ -0,0 +1,52 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests bit-field in if condition; see https://github.com/cc65/cc65/issues/1139 +*/ + +#include + +static unsigned char failures = 0; + +static struct overlap { + unsigned int x : 10; + unsigned int y : 10; +} o = {11, 22}; + +/* Test using bit-fields in if conditions. */ +static void test_if(void) +{ + o.x = 0; + o.y = 44; + if (o.x) { + printf("Bad, o.x is false\n"); + failures++; + } else { + printf("Good\n"); + } +} + +int main(void) +{ + test_if(); + printf("failures: %u\n", failures); + return failures; +} From 3df6c383c0b9c387ef5d9be8f6fe479ce714c51f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 26 Jul 2020 22:16:47 +0200 Subject: [PATCH 152/806] Add support for static_assert Add C11's _Static_assert and static_assert macro. This is like #error, but is handled at a later stage of translation, so it is possible to check sizes of types, values of enums, etc. https://en.cppreference.com/w/c/language/_Static_assert https://port70.net/~nsz/c/c11/n1570.html#6.7.10 --- include/assert.h | 6 +++ src/cc65/compile.c | 7 +++ src/cc65/declare.c | 8 ++++ src/cc65/locals.c | 8 ++++ src/cc65/scanner.c | 1 + src/cc65/scanner.h | 1 + src/cc65/staticassert.c | 96 +++++++++++++++++++++++++++++++++++++++++ src/cc65/staticassert.h | 51 ++++++++++++++++++++++ test/err/staticassert.c | 26 +++++++++++ test/val/staticassert.c | 70 ++++++++++++++++++++++++++++++ 10 files changed, 274 insertions(+) create mode 100644 src/cc65/staticassert.c create mode 100644 src/cc65/staticassert.h create mode 100644 test/err/staticassert.c create mode 100644 test/val/staticassert.c diff --git a/include/assert.h b/include/assert.h index 504964dc2..3f8b46ac0 100644 --- a/include/assert.h +++ b/include/assert.h @@ -46,6 +46,12 @@ extern void __fastcall__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif +/* +** TODO: Guard with #if __STDC_VERSION__ >= 201112L or similar when there +** is a C11 mode. +*/ +#define static_assert _Static_assert + /* End of assert.h */ diff --git a/src/cc65/compile.c b/src/cc65/compile.c index f7e4bd35d..3d2f82c51 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -61,6 +61,7 @@ #include "pragma.h" #include "preproc.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" @@ -108,6 +109,12 @@ static void Parse (void) continue; } + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + /* Read variable defs and functions */ ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 9e77ea63e..ada3ddaae 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -58,6 +58,7 @@ #include "pragma.h" #include "scanner.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "wrappedcall.h" #include "typeconv.h" @@ -935,6 +936,13 @@ static SymEntry* ParseStructDecl (const char* Name) /* Get the type of the entry */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + InitDeclSpec (&Spec); ParseTypeSpec (&Spec, -1, T_QUAL_NONE); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 0007879d8..3fa26021f 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -50,6 +50,7 @@ #include "locals.h" #include "stackptr.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "typeconv.h" #include "input.h" @@ -511,6 +512,13 @@ void DeclareLocals (void) ** declarations. */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + ParseDeclSpec (&Spec, SC_AUTO, T_INT); if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 5040cdf08..70203d027 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -86,6 +86,7 @@ static const struct Keyword { unsigned char Std; /* Token supported in which standards? */ } Keywords [] = { { "_Pragma", TOK_PRAGMA, TT_C89 | TT_C99 | TT_CC65 }, /* !! */ + { "_Static_assert", TOK_STATIC_ASSERT, TT_CC65 }, /* C11 */ { "__AX__", TOK_AX, TT_C89 | TT_C99 | TT_CC65 }, { "__A__", TOK_A, TT_C89 | TT_C99 | TT_CC65 }, { "__EAX__", TOK_EAX, TT_C89 | TT_C99 | TT_CC65 }, diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 1242d78fd..1c95b3d33 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -173,6 +173,7 @@ typedef enum token_t { TOK_WCSCONST, TOK_ATTRIBUTE, + TOK_STATIC_ASSERT, TOK_FAR, TOK_NEAR, TOK_A, diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c new file mode 100644 index 000000000..d7e608b06 --- /dev/null +++ b/src/cc65/staticassert.c @@ -0,0 +1,96 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 Google LLC */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "error.h" +#include "expr.h" +#include "litpool.h" +#include "scanner.h" +#include "staticassert.h" + + + +/*****************************************************************************/ +/* _Static_assert handling functions */ +/*****************************************************************************/ + + + +void ParseStaticAssert () +{ + /* + ** static_assert-declaration ::= + ** _Static_assert ( constant-expression , string-literal ) ; + */ + ExprDesc Expr; + int failed; + + /* Skip the _Static_assert token itself */ + CHECK (CurTok.Tok == TOK_STATIC_ASSERT); + NextToken (); + + /* We expect an opening paren */ + if (!ConsumeLParen ()) { + return; + } + + /* Parse assertion condition */ + ConstAbsIntExpr (hie1, &Expr); + failed = !Expr.IVal; + + /* We expect a comma */ + if (!ConsumeComma ()) { + return; + } + + /* String literal */ + if (CurTok.Tok != TOK_SCONST) { + Error ("String literal expected for static_assert message"); + return; + } + + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } + + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + if (!Consume (TOK_SCONST, "String literal expected")) { + return; + } + + /* Closing paren and semi-colon needed */ + ConsumeRParen (); + ConsumeSemi (); +} diff --git a/src/cc65/staticassert.h b/src/cc65/staticassert.h new file mode 100644 index 000000000..955255bfe --- /dev/null +++ b/src/cc65/staticassert.h @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 Google LLC */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef STATICASSERT_H +#define STATICASSERT_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ParseStaticAssert (void); +/* Handle _Static_assert. These are a C11 feature. */ + + + +/* End of staticassert.h */ + +#endif diff --git a/test/err/staticassert.c b/test/err/staticassert.c new file mode 100644 index 000000000..df9bab6d8 --- /dev/null +++ b/test/err/staticassert.c @@ -0,0 +1,26 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* +** Test of failing _Static_assert. +**/ + + +_Static_assert(0, "0 should be false."); diff --git a/test/val/staticassert.c b/test/val/staticassert.c new file mode 100644 index 000000000..a02ba27e8 --- /dev/null +++ b/test/val/staticassert.c @@ -0,0 +1,70 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* +** Tests of passing _Static_asserts. +**/ + + + +#include + +_Static_assert (1, "1 should be true."); +_Static_assert (!0, "!0 should be true."); +_Static_assert (1 == 1, "1 == 1 should be true."); +_Static_assert (1 == 1L, "1 == 1L should be true."); +_Static_assert (1 != 0, "1 != 0 should be true."); +_Static_assert (sizeof (char) == 1, "sizeof (char) should be 1."); +_Static_assert (sizeof (int) == 2, "sizeof (int) should be 2."); + +/* Make sure we can also do structs. */ +struct sc { char a; }; +_Static_assert (sizeof (struct sc) == 1, "sizeof (struct sc) should be 1."); +struct si { int a; }; +_Static_assert (sizeof (struct si) == 2, "sizeof (struct si) should be 2."); + +/* Try enums. */ +enum { k = 1 }; +_Static_assert (k == 1, "k should be 1."); + +/* Just test the macro version once. */ +static_assert (1, "1 should be true."); + +/* _Static_assert can appear anywhere a declaration can. */ +void f (void) +{ + _Static_assert (1, "1 should still be true."); + if (1) { + _Static_assert (1, "1 should still be true."); + } +} + +/* _Static_assert can also appear in structs. */ +struct S { + int a; + _Static_assert (1, "1 should still be true."); + int b; +}; + + +int main (void) +{ + return 0; +} From d0c7108dcf64403d4256372ed9b5a1faa7b76c14 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 30 Jul 2020 13:27:57 +0200 Subject: [PATCH 153/806] Change copyright notice to "The cc65 Authors" --- src/cc65/staticassert.c | 2 +- src/cc65/staticassert.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index d7e608b06..95a2d4a6e 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* Copyright 2020 Google LLC */ +/* Copyright 2020 The cc65 Authors */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ diff --git a/src/cc65/staticassert.h b/src/cc65/staticassert.h index 955255bfe..cf6314424 100644 --- a/src/cc65/staticassert.h +++ b/src/cc65/staticassert.h @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* Copyright 2020 Google LLC */ +/* Copyright 2020 The cc65 Authors */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ From 847982c6bf1323f801140b71f16d0477cb19ea7e Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 31 Jul 2020 08:41:05 +0200 Subject: [PATCH 154/806] Handle bit-field test after shift/mask Previously, bit-field tests were incorrectly combined with load in `if (x.bitfield)`. Delay the test until after the shift/mask is done. Still combine tests with load if no shift/mask is required. Fixes #1139 --- src/cc65/loadexpr.c | 77 +++++++--- test/todo/bug1139.c | 52 ------- test/val/bug1139.c | 353 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+), 73 deletions(-) delete mode 100644 test/todo/bug1139.c create mode 100644 test/val/bug1139.c diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 807a5cbe5..394e0a562 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -33,6 +33,8 @@ +#include + /* cc65 */ #include "codegen.h" #include "error.h" @@ -113,13 +115,23 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** field is completely contained in the lower byte, we will throw away ** the high byte anyway and may therefore load just the low byte. */ + unsigned EndBit = 0; /* End bit for bit-fields, or zero if non-bit-field. */ + int AdjustBitField = 0; if (ED_IsBitField (Expr)) { - Flags |= (Expr->BitOffs + Expr->BitWidth <= CHAR_BITS) ? CF_CHAR : CF_INT; + EndBit = Expr->BitOffs + Expr->BitWidth; + AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + + Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; Flags |= CF_UNSIGNED; } else if ((Flags & CF_TYPEMASK) == 0) { Flags |= TypeOf (Expr->Type); } - if (ED_NeedsTest (Expr)) { + + /* Setting CF_TEST will cause the load to perform optimizations and not actually load all + ** bits of the bit-field, instead just computing the condition codes. Therefore, if + ** adjustment is required, we do not set CF_TEST here, but handle it below. + */ + if (ED_NeedsTest (Expr) && !AdjustBitField) { Flags |= CF_TEST; } @@ -188,34 +200,57 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) Internal ("Invalid location in LoadExpr: 0x%04X", ED_GetLoc (Expr)); } - /* Handle bit fields. The actual type may have been casted or - ** converted, so be sure to always use unsigned ints for the - ** operations. + /* Handle bit fields if necessary. The actual type may have been casted or converted, + ** so be sure to always use unsigned ints for the operations. */ - if (ED_IsBitField (Expr)) { + if (AdjustBitField) { /* If the field was loaded as a char, force the shift/mask ops to be char ops. ** If it is a char, the load has already put 0 in the upper byte, so that can remain. ** CF_FORCECHAR does nothing if the type is not CF_CHAR. */ unsigned F = Flags | CF_FORCECHAR | CF_CONST; - /* Shift right by the bit offset */ - g_asr (F, Expr->BitOffs); - - /* Since we have now shifted down, we can do char ops as long as the width fits in - ** a char. + /* We always need to do something with the low byte, so there is no opportunity + ** for optimization by skipping it. */ - if (Expr->BitWidth <= CHAR_BITS) { - F |= CF_CHAR; - } + CHECK (Expr->BitOffs < CHAR_BITS); - /* And by the width if the field doesn't end on a char or int boundary. If it does - ** end on a boundary, then zeros have already been shifted in. g_and emits no code - ** if the mask is all ones. - */ - if (Expr->BitOffs + Expr->BitWidth != CHAR_BITS && - Expr->BitOffs + Expr->BitWidth != INT_BITS) { - g_and (F, (0x0001U << Expr->BitWidth) - 1U); + if (ED_NeedsTest (Expr)) { + /* If we need to do a test, then we avoid shifting (ASR only shifts one bit + ** at a time, so is slow) and just AND with the appropriate mask, then test + ** the result of that. + */ + + /* Avoid overly large shift. */ + if (EndBit == sizeof (unsigned long) * CHAR_BIT) { + g_and (F, (~0UL << Expr->BitOffs)); + } else { + g_and (F, ((1UL << EndBit) - 1) & (~0UL << Expr->BitOffs)); + } + + /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes + ** when EndBit <= 24 is possible. + */ + g_test (F); + ED_TestDone (Expr); + } else { + /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ + g_asr (F, Expr->BitOffs); + + /* Since we have now shifted down, we can do char ops as long as the width fits in + ** a char. + */ + if (Expr->BitWidth <= CHAR_BITS) { + F |= CF_CHAR; + } + + /* And by the width if the field doesn't end on a char or int boundary. + ** If it does end on a boundary, then zeros have already been shifted in. + ** g_and emits no code if the mask is all ones. + */ + if (EndBit != CHAR_BITS && EndBit != INT_BITS) { + g_and (F, (0x0001U << Expr->BitWidth) - 1U); + } } } diff --git a/test/todo/bug1139.c b/test/todo/bug1139.c deleted file mode 100644 index 222a9ab51..000000000 --- a/test/todo/bug1139.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2020 The cc65 Authors - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -/* - Tests bit-field in if condition; see https://github.com/cc65/cc65/issues/1139 -*/ - -#include - -static unsigned char failures = 0; - -static struct overlap { - unsigned int x : 10; - unsigned int y : 10; -} o = {11, 22}; - -/* Test using bit-fields in if conditions. */ -static void test_if(void) -{ - o.x = 0; - o.y = 44; - if (o.x) { - printf("Bad, o.x is false\n"); - failures++; - } else { - printf("Good\n"); - } -} - -int main(void) -{ - test_if(); - printf("failures: %u\n", failures); - return failures; -} diff --git a/test/val/bug1139.c b/test/val/bug1139.c new file mode 100644 index 000000000..5aef3924d --- /dev/null +++ b/test/val/bug1139.c @@ -0,0 +1,353 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests bit-field in if condition; see https://github.com/cc65/cc65/issues/1139 +*/ + +#include + +static unsigned char failures = 0; + +static struct four_bits { + unsigned int x : 4; +} fb = {1}; + +static struct overlap { + unsigned int x : 10; + unsigned int y : 10; +} o = {11, 22}; + +static struct full_width { + unsigned int x : 8; + unsigned int y : 16; +} fw = {255, 17}; + +static struct aligned_end { + unsigned int : 2; + unsigned int x : 6; + unsigned int : 3; + unsigned int y : 13; + /* z crosses a byte boundary, but fits in a byte when shifted. */ + unsigned int : 6; + unsigned int z : 7; +} ae = {63, 17, 100}; + +/* Test using bit-fields in if conditions. */ +static void test_if(void) +{ + /* Original test case for the bug. */ + o.x = 0; + o.y = 44; + if (o.x) { + printf("Bad, o.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* Additionally, test most fields from bitfield.c to try to cover all start/end situations. */ + /* four_bits */ + fb.x = 1; + if (fb.x) { + printf("Good\n"); + } else { + printf("Bad, fb.x is true (1)\n"); + failures++; + } + + fb.x = 0; + if (fb.x) { + printf("Bad, fb.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* overlap */ + o.x = 123; + if (o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is true (123)\n"); + failures++; + } + + o.x = 0; + if (o.x) { + printf("Bad, o.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + o.y = 321; + if (o.y) { + printf("Good\n"); + } else { + printf("Bad, o.y is true (321)\n"); + failures++; + } + + o.y = 0; + if (o.y) { + printf("Bad, o.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* full_width */ + fw.x = 117; + if (fw.x) { + printf("Good\n"); + } else { + printf("Bad, fw.x is true (117)\n"); + failures++; + } + + fw.x = 0; + if (fw.x) { + printf("Bad, fw.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.y = 32123; + if (fw.y) { + printf("Good\n"); + } else { + printf("Bad, fw.y is true (32123)\n"); + failures++; + } + + fw.y = 0; + if (fw.y) { + printf("Bad, fw.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + /* aligned_end */ + ae.x = 2; + if (ae.x) { + printf("Good\n"); + } else { + printf("Bad, ae.x is true (2)\n"); + failures++; + } + + ae.x = 0; + if (ae.x) { + printf("Bad, ae.x is false\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.y = 2222; + if (ae.y) { + printf("Good\n"); + } else { + printf("Bad, ae.y is true (2222)\n"); + failures++; + } + + ae.y = 0; + if (ae.y) { + printf("Bad, ae.y is false\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.z = 111; + if (ae.z) { + printf("Good\n"); + } else { + printf("Bad, ae.z is true (111)\n"); + failures++; + } + + ae.z = 0; + if (ae.z) { + printf("Bad, ae.z is false\n"); + failures++; + } else { + printf("Good\n"); + } +} + +/* Test using bit-fields in inverted if conditions. */ +static void test_if_not(void) +{ + /* Original test case for the bug, inverted. */ + o.x = 0; + o.y = 44; + if (!o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is false\n"); + failures++; + } + + /* Additionally, test most fields from bitfield.c to try to cover all start/end situations. */ + /* four_bits */ + fb.x = 1; + if (!fb.x) { + printf("Bad, fb.x is true (1)\n"); + failures++; + } else { + printf("Good\n"); + } + + fb.x = 0; + if (!fb.x) { + printf("Good\n"); + } else { + printf("Bad, fb.x is false\n"); + failures++; + } + + /* overlap */ + o.x = 123; + if (!o.x) { + printf("Bad, o.x is true (123)\n"); + failures++; + } else { + printf("Good\n"); + } + + o.x = 0; + if (!o.x) { + printf("Good\n"); + } else { + printf("Bad, o.x is false\n"); + failures++; + } + + o.y = 321; + if (!o.y) { + printf("Bad, o.y is true (321)\n"); + failures++; + } else { + printf("Good\n"); + } + + o.y = 0; + if (!o.y) { + printf("Good\n"); + } else { + printf("Bad, o.y is false\n"); + failures++; + } + + /* full_width */ + fw.x = 117; + if (!fw.x) { + printf("Bad, fw.x is true (117)\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.x = 0; + if (!fw.x) { + printf("Good\n"); + } else { + printf("Bad, fw.x is false\n"); + failures++; + } + + fw.y = 32123; + if (!fw.y) { + printf("Bad, fw.y is true (32123)\n"); + failures++; + } else { + printf("Good\n"); + } + + fw.y = 0; + if (!fw.y) { + printf("Good\n"); + } else { + printf("Bad, fw.y is false\n"); + failures++; + } + + /* aligned_end */ + ae.x = 2; + if (!ae.x) { + printf("Bad, ae.x is true (2)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.x = 0; + if (!ae.x) { + printf("Good\n"); + } else { + printf("Bad, ae.x is false\n"); + failures++; + } + + ae.y = 2222; + if (!ae.y) { + printf("Bad, ae.y is true (2222)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.y = 0; + if (!ae.y) { + printf("Good\n"); + } else { + printf("Bad, ae.y is false\n"); + failures++; + } + + ae.z = 111; + if (!ae.z) { + printf("Bad, ae.z is true (111)\n"); + failures++; + } else { + printf("Good\n"); + } + + ae.z = 0; + if (!ae.z) { + printf("Good\n"); + } else { + printf("Bad, ae.z is false\n"); + failures++; + } +} + +int main(void) +{ + test_if(); + test_if_not(); + printf("failures: %u\n", failures); + return failures; +} From 9c70bd44a6fecf99aefe888f95e0645b82324b24 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 31 Jul 2020 16:52:22 +0200 Subject: [PATCH 155/806] Clarify comment about large shift This is to avoid overflow on host platform. --- src/cc65/loadexpr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 394e0a562..15be997df 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -221,7 +221,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** the result of that. */ - /* Avoid overly large shift. */ + /* Avoid overly large shift on host platform. */ if (EndBit == sizeof (unsigned long) * CHAR_BIT) { g_and (F, (~0UL << Expr->BitOffs)); } else { From e1043fac12151cdec415e76d78e23114d8f0b070 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sat, 1 Aug 2020 10:55:49 +0200 Subject: [PATCH 156/806] Adjusted to https://github.com/cc65/cc65/pull/1124. --- src/cc65.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index a56ecf880..d566c8bc1 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -135,6 +135,7 @@ + @@ -209,6 +210,7 @@ + From 633cd17a3e8df1f20fd918a1529b61049efa0594 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 1 Aug 2020 22:30:36 +0200 Subject: [PATCH 157/806] Add enum size test case for #1050 --- test/val/bug1050.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/val/bug1050.c diff --git a/test/val/bug1050.c b/test/val/bug1050.c new file mode 100644 index 000000000..af9e4f6b2 --- /dev/null +++ b/test/val/bug1050.c @@ -0,0 +1,32 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests enum sizes; see https://github.com/cc65/cc65/issues/1050 +*/ + +enum e { k }; +_Static_assert(sizeof(enum e) == 1, "This should fit in a byte."); +_Static_assert(sizeof(k) == 2, "Enumerators are still ints."); + +int main (void) +{ + return 0; +} From e526cbbff6c2588287dad7af94b3f2326300fd58 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 1 Aug 2020 22:08:19 +0800 Subject: [PATCH 158/806] Fixed handling multiple storage specifiers in one declaration. --- src/cc65/declare.c | 89 +++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ada3ddaae..a525e1f86 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -410,6 +410,48 @@ static void FixQualifiers (Type* DataType) +static unsigned ParseOneStorageClass (void) +/* Parse and return a storage class */ +{ + unsigned StorageClass = 0; + + /* Check the storage class given */ + switch (CurTok.Tok) { + + case TOK_EXTERN: + StorageClass = SC_EXTERN | SC_STATIC; + NextToken (); + break; + + case TOK_STATIC: + StorageClass = SC_STATIC; + NextToken (); + break; + + case TOK_REGISTER: + StorageClass = SC_REGISTER | SC_STATIC; + NextToken (); + break; + + case TOK_AUTO: + StorageClass = SC_AUTO; + NextToken (); + break; + + case TOK_TYPEDEF: + StorageClass = SC_TYPEDEF; + NextToken (); + break; + + default: + break; + } + + return StorageClass; +} + + + static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) /* Parse a storage class */ { @@ -417,38 +459,21 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) D->Flags &= ~DS_DEF_STORAGE; /* Check the storage class given */ - switch (CurTok.Tok) { - - case TOK_EXTERN: - D->StorageClass = SC_EXTERN | SC_STATIC; - NextToken (); - break; - - case TOK_STATIC: - D->StorageClass = SC_STATIC; - NextToken (); - break; - - case TOK_REGISTER: - D->StorageClass = SC_REGISTER | SC_STATIC; - NextToken (); - break; - - case TOK_AUTO: - D->StorageClass = SC_AUTO; - NextToken (); - break; - - case TOK_TYPEDEF: - D->StorageClass = SC_TYPEDEF; - NextToken (); - break; - - default: - /* No storage class given, use default */ - D->Flags |= DS_DEF_STORAGE; - D->StorageClass = DefStorage; - break; + D->StorageClass = ParseOneStorageClass (); + if (D->StorageClass == 0) { + /* No storage class given, use default */ + D->Flags |= DS_DEF_STORAGE; + D->StorageClass = DefStorage; + } else { + unsigned StorageClass = ParseOneStorageClass (); + while (StorageClass != 0) { + if (D->StorageClass == StorageClass) { + Warning ("Duplicate storage class specifier"); + } else { + Error ("Conflicting storage class specifier"); + } + StorageClass = ParseOneStorageClass (); + } } } From 30fd8592ae6332b9b4eaa64abd87c11d81f2d586 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 1 Aug 2020 22:06:14 +0800 Subject: [PATCH 159/806] Avoid internal errors when using function-type objects in expressions. --- src/cc65/datatype.c | 19 ++++++++++++++++++- src/cc65/datatype.h | 3 +++ src/cc65/expr.c | 6 +++--- src/cc65/function.c | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 1e2859ba7..18dd0a6b1 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -732,7 +732,8 @@ unsigned TypeOf (const Type* T) return CF_FLOAT; case T_FUNC: - return (((FuncDesc*) T->A.P)->Flags & FD_VARIADIC)? 0 : CF_FIXARGC; + /* Treat this as a function pointer */ + return CF_INT | CF_UNSIGNED; case T_STRUCT: case T_UNION: @@ -751,6 +752,22 @@ unsigned TypeOf (const Type* T) +unsigned FuncTypeOf (const Type* T) +/* Get the code generator flag for calling the function */ +{ + switch (GetUnderlyingTypeCode (T)) { + + case T_FUNC: + return (((FuncDesc*) T->A.P)->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; + + default: + Error ("Illegal function type %04lX", T->C); + return 0; + } +} + + + Type* Indirect (Type* T) /* Do one indirection for the given type, that is, return the type where the ** given type points to. diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 1997b5c89..33c66d65d 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -323,6 +323,9 @@ unsigned CheckedPSizeOf (const Type* T); unsigned TypeOf (const Type* T); /* Get the code generator base type of the object */ +unsigned FuncTypeOf (const Type* T); +/* Get the code generator flag for calling the function */ + Type* Indirect (Type* T); /* Do one indirection for the given type, that is, return the type where the ** given type points to. diff --git a/src/cc65/expr.c b/src/cc65/expr.c index fd8a48002..1d123ce9b 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -585,7 +585,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Call the function */ - g_callind (TypeOf (Expr->Type+1), ParamSize, PtrOffs); + g_callind (FuncTypeOf (Expr->Type+1), ParamSize, PtrOffs); } else { @@ -646,9 +646,9 @@ static void FunctionCall (ExprDesc* Expr) SB_Done (&S); - g_call (TypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize); + g_call (FuncTypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize); } else { - g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize); + g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ParamSize); } } diff --git a/src/cc65/function.c b/src/cc65/function.c index d6e11fea3..e6a974f98 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -487,7 +487,7 @@ void NewFunc (SymEntry* Func) } /* Generate function entry code if needed */ - g_enter (TypeOf (Func->Type), F_GetParamSize (CurrentFunc)); + g_enter (FuncTypeOf (Func->Type), F_GetParamSize (CurrentFunc)); /* If stack checking code is requested, emit a call to the helper routine */ if (IS_Get (&CheckStack)) { From 2a555d198cd4bf5ac916dc77ad1c095077627551 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 13:24:46 +0800 Subject: [PATCH 160/806] Changed 'switch' to 'if' according PR review comments. --- src/cc65/datatype.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 18dd0a6b1..444902e60 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -755,14 +755,11 @@ unsigned TypeOf (const Type* T) unsigned FuncTypeOf (const Type* T) /* Get the code generator flag for calling the function */ { - switch (GetUnderlyingTypeCode (T)) { - - case T_FUNC: - return (((FuncDesc*) T->A.P)->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; - - default: - Error ("Illegal function type %04lX", T->C); - return 0; + if (GetUnderlyingTypeCode (T) == T_FUNC) { + return (((FuncDesc*) T->A.P)->Flags & FD_VARIADIC) ? 0 : CF_FIXARGC; + } else { + Error ("Illegal function type %04lX", T->C); + return 0; } } From adda28f5c5ce8de5a7a778723361b36545dcf92e Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 2 Aug 2020 09:03:21 +0200 Subject: [PATCH 161/806] LoadExpr: Set CF_FORCECHAR if test is required If we are testing, we do not need to load the high byte(s). --- src/cc65/loadexpr.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 15be997df..1a6e43779 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -127,12 +127,19 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) Flags |= TypeOf (Expr->Type); } - /* Setting CF_TEST will cause the load to perform optimizations and not actually load all - ** bits of the bit-field, instead just computing the condition codes. Therefore, if - ** adjustment is required, we do not set CF_TEST here, but handle it below. - */ - if (ED_NeedsTest (Expr) && !AdjustBitField) { - Flags |= CF_TEST; + if (ED_NeedsTest (Expr)) { + /* If we're only testing, we do not need to promote char to int. + ** CF_FORCECHAR does nothing if the type is not CF_CHAR. + */ + Flags |= CF_FORCECHAR; + + /* Setting CF_TEST will cause the load to perform optimizations and not actually load + ** all bits of the bit-field, instead just computing the condition codes. Therefore, + ** if adjustment is required, we do not set CF_TEST here, but handle it below. + */ + if (!AdjustBitField) { + Flags |= CF_TEST; + } } /* Load the content of Expr */ @@ -206,7 +213,6 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) if (AdjustBitField) { /* If the field was loaded as a char, force the shift/mask ops to be char ops. ** If it is a char, the load has already put 0 in the upper byte, so that can remain. - ** CF_FORCECHAR does nothing if the type is not CF_CHAR. */ unsigned F = Flags | CF_FORCECHAR | CF_CONST; From ce19d7b84f6ad8780135d374d63377f30214bdf3 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 2 Aug 2020 15:50:17 +0200 Subject: [PATCH 162/806] Made use of Travis CI's Windows support. At least for now there seems to be no point in trying to build the libraries (and run the tests) on Windows. Rather we only want to check that the MSVC solution is still valid. --- .travis.yml | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 630466cc0..efeca96b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,24 @@ -language: - - c -install: - - sudo apt-get update - - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass -script: - - make bin USER_CFLAGS=-Werror - - make lib QUIET=1 - - make test QUIET=1 - - make samples - - make -C src clean - - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- - - make -C samples clean - - make doc zip -after_success: - - make -f Makefile.travis +language: c + +jobs: + include: + + - os: linux + install: + - sudo apt-get update + - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass + script: + - make bin USER_CFLAGS=-Werror + - make lib QUIET=1 + - make test QUIET=1 + - make samples + - make -C src clean + - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + - make -C samples clean + - make doc zip + after_success: + - make -f Makefile.travis + + - os: windows + script: + - MSBuild.exe scr/cc65.sln From 992596c981a3027b1d9320b854ea1fd5fb523b53 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 2 Aug 2020 17:32:36 +0200 Subject: [PATCH 163/806] Initialize MSVC 2017 environment before build. --- .travis.yml | 4 +++- src/msbuild.cmd | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/msbuild.cmd diff --git a/.travis.yml b/.travis.yml index efeca96b4..4096d783a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ jobs: include: - os: linux + name: Linux install: - sudo apt-get update - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass @@ -20,5 +21,6 @@ jobs: - make -f Makefile.travis - os: windows + name: Windows script: - - MSBuild.exe scr/cc65.sln + - src/msbuild.cmd scr/cc65.sln diff --git a/src/msbuild.cmd b/src/msbuild.cmd new file mode 100644 index 000000000..129817d6a --- /dev/null +++ b/src/msbuild.cmd @@ -0,0 +1,2 @@ +call "%VS140COMNTOOLS%vsvars32.bat" +msbuild.exe %* From 35d6a75b37d021a2084edfb96c2bbd3dd9791d84 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 2 Aug 2020 17:49:18 +0200 Subject: [PATCH 164/806] Fixed Windows path notation. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4096d783a..0b25e5d16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,4 +23,4 @@ jobs: - os: windows name: Windows script: - - src/msbuild.cmd scr/cc65.sln + - src/msbuild.cmd src\\cc65.sln From c831f40e9b1c2bcc3d1fdb5c5dfff5d51524447a Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 2 Aug 2020 18:44:13 +0200 Subject: [PATCH 165/806] Unfortunately there's no other way than using the absolute path to init the correct MSVC 2017 environment. --- src/msbuild.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msbuild.cmd b/src/msbuild.cmd index 129817d6a..5736846da 100644 --- a/src/msbuild.cmd +++ b/src/msbuild.cmd @@ -1,2 +1,2 @@ -call "%VS140COMNTOOLS%vsvars32.bat" +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" msbuild.exe %* From 0df45fe2f2db73eb0e09985381462bb4f91d7566 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:08:50 +0800 Subject: [PATCH 166/806] Utility for getting ESU tag type symbols. --- src/cc65/datatype.c | 8 ++++---- src/cc65/datatype.h | 8 ++++---- src/cc65/declare.c | 11 ++++++----- src/cc65/symentry.c | 32 ++++++++++++++++++++++++++++++++ src/cc65/symentry.h | 10 ++++++++++ src/cc65/symtab.c | 2 +- src/cc65/typecmp.c | 4 ++-- 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 444902e60..5ac2ec4b4 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -895,8 +895,8 @@ Type* GetBaseElementType (Type* T) -SymEntry* GetSymEntry (const Type* T) -/* Return a SymEntry pointer from a type */ +SymEntry* GetESUSymEntry (const Type* T) +/* Return a SymEntry pointer from an enum/struct/union type */ { /* Only enums, structs or unions have a SymEntry attribute */ CHECK (IsClassStruct (T) || IsTypeEnum (T)); @@ -907,8 +907,8 @@ SymEntry* GetSymEntry (const Type* T) -void SetSymEntry (Type* T, SymEntry* S) -/* Set the SymEntry pointer for a type */ +void SetESUSymEntry (Type* T, SymEntry* S) +/* Set the SymEntry pointer for an enum/struct/union type */ { /* Only enums, structs or unions have a SymEntry attribute */ CHECK (IsClassStruct (T) || IsTypeEnum (T)); diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 33c66d65d..4839be5d2 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -747,11 +747,11 @@ Type* GetBaseElementType (Type* T); ** the element type that is not an array. */ -struct SymEntry* GetSymEntry (const Type* T) attribute ((const)); -/* Return a SymEntry pointer from a type */ +struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const)); +/* Return a SymEntry pointer from an enum/struct/union type */ -void SetSymEntry (Type* T, struct SymEntry* S); -/* Set the SymEntry pointer for a type */ +void SetESUSymEntry (Type* T, struct SymEntry* S); +/* Set the SymEntry pointer for an enum/struct/union type */ Type* IntPromotion (Type* T); /* Apply the integer promotions to T and return the result. The returned type diff --git a/src/cc65/declare.c b/src/cc65/declare.c index a525e1f86..5ecfeb04f 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -766,6 +766,7 @@ static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) } + static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) /* Create alias fields from an anon union/struct in the current lexical level. ** The function returns the count of created aliases. @@ -775,7 +776,7 @@ static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) SymEntry* Alias; /* Get the pointer to the symbol table entry of the anon struct */ - SymEntry* Entry = GetSymEntry (Decl->Type); + SymEntry* Entry = GetESUSymEntry (Decl->Type); /* Get the symbol table containing the fields. If it is empty, there has ** been an error before, so bail out. @@ -1267,7 +1268,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) Entry = ParseUnionDecl (Ident); /* Encode the union entry into the type */ D->Type[0].C = T_UNION; - SetSymEntry (D->Type, Entry); + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -1286,7 +1287,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) Entry = ParseStructDecl (Ident); /* Encode the struct entry into the type */ D->Type[0].C = T_STRUCT; - SetSymEntry (D->Type, Entry); + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -1308,7 +1309,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) /* Parse the enum decl */ Entry = ParseEnumDecl (Ident); D->Type[0].C |= T_ENUM; - SetSymEntry (D->Type, Entry); + SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -2258,7 +2259,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the struct entry from the type */ - Entry = GetSymEntry (T); + Entry = GetESUSymEntry (T); /* Get the size of the struct from the symbol table entry */ SI.Size = Entry->V.S.Size; diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index c8d4f9e2a..6486ab764 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -278,6 +278,38 @@ void CvtRegVarToAuto (SymEntry* Sym) +SymEntry* GetSymType (const Type* T) +/* Get the symbol entry of the enum/struct/union type +** Return 0 if it is not an enum/struct/union. +*/ +{ + if ((IsClassStruct (T) || IsTypeEnum (T))) { + return T->A.P; + } + return 0; +} + + + +const char* GetSymTypeName (const Type* T) +/* Return a name string of the type or the symbol name if it is an ESU type. +** Note: This may use a static buffer that could be overwritten by other calls. +*/ +{ + static char TypeName [IDENTSIZE + 16]; + SymEntry* Sym; + + Sym = GetSymType (T); + if (Sym == 0) { + return GetBasicTypeName (T); + } + sprintf (TypeName, "%s %s", GetBasicTypeName (T), Sym->Name ? Sym->Name : ""); + + return TypeName; +} + + + void ChangeSymType (SymEntry* Entry, Type* T) /* Change the type of the given symbol */ { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index e59bf2a35..1d8af3f50 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -304,6 +304,16 @@ void SymSetAsmName (SymEntry* Sym); void CvtRegVarToAuto (SymEntry* Sym); /* Convert a register variable to an auto variable */ +SymEntry* GetSymType (const Type* T); +/* Get the symbol entry of the enum/struct/union type +** Return 0 if it is not an enum/struct/union. +*/ + +const char* GetSymTypeName (const Type* T); +/* Return a name string of the type or the symbol name if it is an ESU type. +** Note: This may use a static buffer that could be overwritten by other calls. +*/ + void ChangeSymType (SymEntry* Entry, Type* T); /* Change the type of the given symbol */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index f7a7862cf..735cb854f 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -509,7 +509,7 @@ SymEntry FindStructField (const Type* T, const char* Name) if (IsClassStruct (T)) { /* Get a pointer to the struct/union type */ - const SymEntry* Struct = GetSymEntry (T); + const SymEntry* Struct = GetESUSymEntry (T); CHECK (Struct != 0); /* Now search in the struct/union symbol table. Beware: The table may diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 66dd9039b..0eddf2988 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -368,8 +368,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) ** pointer to the struct definition from the type, and compare ** the fields. */ - Sym1 = GetSymEntry (lhs); - Sym2 = GetSymEntry (rhs); + Sym1 = GetESUSymEntry (lhs); + Sym2 = GetESUSymEntry (rhs); /* If one symbol has a name, the names must be identical */ if (!HasAnonName (Sym1) || !HasAnonName (Sym2)) { From 4ccf10f3fa43f28555168b9b7730deb73a7aaa6a Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:51:26 +0800 Subject: [PATCH 167/806] Utility to get full type names. --- src/cc65/datatype.c | 233 ++++++++++++++++++++++++++++++++++++++++++++ src/cc65/datatype.h | 16 +++ 2 files changed, 249 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 5ac2ec4b4..2c2167188 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -78,6 +78,144 @@ Type type_double[] = { TYPE(T_DOUBLE), TYPE(T_END) }; +static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBuf* East, const Type* T) +/* Return the name string of the given type split into a western part and an +** eastern part. +*/ +{ + struct StrBuf Buf = STATIC_STRBUF_INITIALIZER; + + if (IsTypeArray (T)) { + + long Count = GetElementCount (T); + if (!SB_IsEmpty (East)) { + if (Count > 0) { + SB_Printf (&Buf, "[%ld]", Count); + } else { + SB_Printf (&Buf, "[]"); + } + SB_Append (East, &Buf); + SB_Terminate (East); + + } else { + if (Count > 0) { + SB_Printf (East, "[%ld]", Count); + } else { + SB_Printf (East, "[]"); + } + + if (!SB_IsEmpty (West)) { + /* Add parentheses to West */ + SB_Printf (&Buf, "(%s)", SB_GetConstBuf (West)); + SB_Copy (West, &Buf); + SB_Terminate (West); + } + } + + /* Get element type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else if (IsTypeFunc (T)) { + + FuncDesc* F = GetFuncDesc (T); + struct StrBuf ParamList = STATIC_STRBUF_INITIALIZER; + + /* First argument */ + SymEntry* Param = F->SymTab->SymHead; + for (unsigned I = 1; I < F->ParamCount; ++I) { + CHECK (Param->NextSym != 0 && (Param->Flags & SC_PARAM) != 0); + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_AppendStr (&ParamList, ", "); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((F->Flags & FD_VARIADIC) == 0) { + if (F->ParamCount > 0) { + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + } else { + SB_AppendStr (&ParamList, "void"); + } + } else { + if (F->ParamCount > 0) { + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); + } + } + SB_Terminate (&ParamList); + + /* Join the existing West and East together */ + if (!SB_IsEmpty (East)) { + SB_Append (West, East); + SB_Terminate (West); + SB_Clear (East); + } + + if (SB_IsEmpty (West)) { + /* Just use the param list */ + SB_Printf (West, "(%s)", SB_GetConstBuf (&ParamList)); + } else { + /* Append the param list to the existing West */ + SB_Printf (&Buf, "(%s)(%s)", SB_GetConstBuf (West), SB_GetConstBuf (&ParamList)); + SB_Printf (West, "%s", SB_GetConstBuf (&Buf)); + } + SB_Done (&ParamList); + + /* Return type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else if (IsTypePtr (T)) { + + int QualCount = 0; + + SB_Printf (&Buf, "*"); + + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != T_QUAL_NONE) { + QualCount = GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR); + } + + if (!SB_IsEmpty (West)) { + if (QualCount > 0) { + SB_AppendChar (&Buf, ' '); + } + SB_Append (&Buf, West); + } + + SB_Copy (West, &Buf); + SB_Terminate (West); + + /* Get indirection type */ + GetFullTypeNameWestEast (West, East, T + 1); + + } else { + + /* Add qualifiers */ + if ((GetQualifier (T) & ~T_QUAL_NEAR) != 0) { + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NEAR) > 0) { + SB_AppendChar (&Buf, ' '); + } + } + + SB_AppendStr (&Buf, GetSymTypeName (T)); + + if (!SB_IsEmpty (West)) { + SB_AppendChar (&Buf, ' '); + SB_Append (&Buf, West); + } + + SB_Copy (West, &Buf); + SB_Terminate (West); + } + + SB_Done (&Buf); + return West; +} + + + const char* GetBasicTypeName (const Type* T) /* Return a const name string of the basic type. ** Return "type" for unknown basic types. @@ -134,6 +272,101 @@ const char* GetBasicTypeName (const Type* T) +const char* GetFullTypeName (const Type* T) +/* Return the full name string of the given type. +** Note: This may use a static buffer that could be overwritten by other calls. +*/ +{ + static struct StrBuf Buf = STATIC_STRBUF_INITIALIZER; + SB_Clear (&Buf); + GetFullTypeNameBuf (&Buf, T); + + return SB_GetConstBuf (&Buf); +} + + + +struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T) +/* Return the full name string of the given type */ +{ + struct StrBuf East = STATIC_STRBUF_INITIALIZER; + GetFullTypeNameWestEast (S, &East, T); + + /* Join West and East */ + SB_Append (S, &East); + SB_Terminate (S); + SB_Done (&East); + + return S; +} + + + +int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual) +/* Return the names of the qualifiers of the type. +** Qualifiers to be ignored can be specified with the IgnoredQual flags. +** Return the count of added qualifier names. +*/ +{ + int Count = 0; + + Qual &= T_MASK_QUAL & ~IgnoredQual; + if (Qual & T_QUAL_CONST) { + if (!SB_IsEmpty (S)) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "const"); + ++Count; + } + if (Qual & T_QUAL_VOLATILE) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "volatile"); + ++Count; + } + if (Qual & T_QUAL_RESTRICT) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "restrict"); + ++Count; + } + if (Qual & T_QUAL_NEAR) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__near__"); + ++Count; + } + if (Qual & T_QUAL_FAR) { + SB_AppendStr (S, "__far__"); + ++Count; + } + if (Qual & T_QUAL_FASTCALL) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__fastcall__"); + ++Count; + } + if (Qual & T_QUAL_CDECL) { + if (Count > 0) { + SB_AppendChar (S, ' '); + } + SB_AppendStr (S, "__cdecl__"); + ++Count; + } + + if (Count > 0) { + SB_Terminate (S); + } + + return Count; +} + + + unsigned TypeLen (const Type* T) /* Return the length of the type string */ { diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 4839be5d2..8e32d6404 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -203,6 +203,8 @@ extern Type type_double[]; /* Forward for the SymEntry struct */ struct SymEntry; +/* Forward for the StrBuf struct */ +struct StrBuf; /*****************************************************************************/ @@ -216,6 +218,20 @@ const char* GetBasicTypeName (const Type* T); ** Return "type" for unknown basic types. */ +const char* GetFullTypeName (const Type* T); +/* Return the full name string of the given type. +** Note: This may use a static buffer that could be overwritten by other calls. +*/ + +struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T); +/* Return the full name string of the given type */ + +int GetQualifierTypeCodeNameBuf (struct StrBuf* S, TypeCode Qual, TypeCode IgnoredQual); +/* Return the names of the qualifiers of the type. +** Qualifiers to be ignored can be specified with the IgnoredQual flags. +** Return the count of added qualifier names. +*/ + unsigned TypeLen (const Type* T); /* Return the length of the type string */ From 52051f444e6b1bfff20d5dd8967cd1f3effad610 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:51:32 +0800 Subject: [PATCH 168/806] Using tracked individual string buffers instead of a shared static string buffer for full type names. --- src/cc65/datatype.c | 11 ++++------ src/cc65/datatype.h | 4 +--- src/cc65/error.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/error.h | 15 +++++++++++++ src/cc65/main.c | 6 ++++++ 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2c2167188..d1c34c825 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -273,15 +273,12 @@ const char* GetBasicTypeName (const Type* T) const char* GetFullTypeName (const Type* T) -/* Return the full name string of the given type. -** Note: This may use a static buffer that could be overwritten by other calls. -*/ +/* Return the full name string of the given type */ { - static struct StrBuf Buf = STATIC_STRBUF_INITIALIZER; - SB_Clear (&Buf); - GetFullTypeNameBuf (&Buf, T); + struct StrBuf* Buf = NewDiagnosticStrBuf (); + GetFullTypeNameBuf (Buf, T); - return SB_GetConstBuf (&Buf); + return SB_GetConstBuf (Buf); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 8e32d6404..ad382d50f 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -219,9 +219,7 @@ const char* GetBasicTypeName (const Type* T); */ const char* GetFullTypeName (const Type* T); -/* Return the full name string of the given type. -** Note: This may use a static buffer that could be overwritten by other calls. -*/ +/* Return the full name string of the given type */ struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T); /* Return the full name string of the given type */ diff --git a/src/cc65/error.c b/src/cc65/error.c index c3ebebf77..10d20a34b 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -38,7 +38,9 @@ #include /* common */ +#include "coll.h" #include "print.h" +#include "strbuf.h" /* cc65 */ #include "global.h" @@ -92,6 +94,8 @@ static WarnMapEntry WarnMap[] = { { &WarnUnusedVar, "unused-var" }, }; +Collection DiagnosticStrBufs; + /*****************************************************************************/ @@ -323,3 +327,50 @@ void ErrorReport (void) { Print (stdout, 1, "%u errors, %u warnings\n", ErrorCount, WarningCount); } + + + +/*****************************************************************************/ +/* Tracked StrBufs */ +/*****************************************************************************/ + + + +void InitDiagnosticStrBufs (void) +/* Init tracking string buffers used for diagnostics */ +{ + InitCollection (&DiagnosticStrBufs); +} + + + +void DoneDiagnosticStrBufs (void) +/* Done with tracked string buffers used for diagnostics */ +{ + ClearDiagnosticStrBufs (); + DoneCollection (&DiagnosticStrBufs); +} + + + +void ClearDiagnosticStrBufs (void) +/* Free all tracked string buffers */ +{ + unsigned I; + + for (I = 0; I < CollCount (&DiagnosticStrBufs); ++I) { + SB_Done (CollAtUnchecked (&DiagnosticStrBufs, I)); + } + + CollDeleteAll (&DiagnosticStrBufs); +} + + + +struct StrBuf* NewDiagnosticStrBuf (void) +/* Get a new tracked string buffer */ +{ + StrBuf *Buf = NewStrBuf (); + CollAppend (&DiagnosticStrBufs, Buf); + return Buf; +} diff --git a/src/cc65/error.h b/src/cc65/error.h index 97ee09591..a443aeff8 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -72,6 +72,9 @@ extern IntStack WarnUnusedLabel; /* - unused labels */ extern IntStack WarnUnusedParam; /* - unused parameters */ extern IntStack WarnUnusedVar; /* - unused variables */ +/* Forward */ +struct StrBuf; + /*****************************************************************************/ @@ -115,6 +118,18 @@ void ListWarnings (FILE* F); void ErrorReport (void); /* Report errors (called at end of compile) */ +void InitDiagnosticStrBufs (void); +/* Init tracking string buffers used for diagnostics */ + +void DoneDiagnosticStrBufs (void); +/* Done with tracked string buffers used for diagnostics */ + +void ClearDiagnosticStrBufs (void); +/* Free all tracked string buffers */ + +struct StrBuf* NewDiagnosticStrBuf (void); +/* Get a new tracked string buffer */ + /* End of error.h */ diff --git a/src/cc65/main.c b/src/cc65/main.c index e86ae13ba..ed2e9d7ba 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -1060,6 +1060,9 @@ int main (int argc, char* argv[]) IS_Set (&Standard, STD_DEFAULT); } + /* Track string buffer allocation */ + InitDiagnosticStrBufs (); + /* Go! */ Compile (InputFile); @@ -1083,6 +1086,9 @@ int main (int argc, char* argv[]) CreateDependencies (); } + /* Done with tracked string buffer allocation */ + DoneDiagnosticStrBufs (); + /* Return an apropriate exit code */ return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; } From d841bbe49825f604d0d52365675d2332ed76fae7 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:51:32 +0800 Subject: [PATCH 169/806] Utility to check for castability. --- src/cc65/datatype.c | 16 ++++++++++++++++ src/cc65/datatype.h | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index d1c34c825..cffec7880 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1018,6 +1018,22 @@ Type* ArrayToPtr (Type* T) +int IsClassArithmetic (const Type* T) +/* Return true if this is an arithmetic type */ +{ + return IsClassInt (T) || IsClassFloat (T); +} + + + +int IsCastType (const Type* T) +/* Return true if this type can be used for casting */ +{ + return IsClassArithmetic (T) || IsClassPtr (T) || IsTypeVoid (T); +} + + + int IsVariadicFunc (const Type* T) /* Return true if this is a function type or pointer to function type with ** variable parameter list diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index ad382d50f..66f1b8b05 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -578,6 +578,12 @@ INLINE int IsClassFunc (const Type* T) # define IsClassFunc(T) (GetClass (T) == T_CLASS_FUNC) #endif +int IsClassArithmetic (const Type* T); +/* Return true if this is an arithmetic type */ + +int IsCastType (const Type* T); +/* Return true if this type can be used for casting */ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawSignedness (const Type* T) /* Get the raw signedness of a type */ From 003d47cc8b661cfb8dcfd89ae02eb7e7022e0196 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:51:32 +0800 Subject: [PATCH 170/806] Improved type conversion diagnostic messages. Allowed incompatible pointer assignments with warnings. Fixed Issue #1089. --- src/cc65/assignment.c | 3 +- src/cc65/expr.c | 12 ++++-- src/cc65/typeconv.c | 96 +++++++++++++++++++++++++++++++------------ src/cc65/typeconv.h | 3 ++ 4 files changed, 82 insertions(+), 32 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index ed374ef8a..c67ac1ce5 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -78,7 +78,8 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Check for equality of the structs */ if (TypeCmp (ltype, RExpr->Type) < TC_STRICT_COMPATIBLE) { - Error ("Incompatible types"); + TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, + "Incompatible types in assignment to '%s' from '%s'"); } /* Do we copy using the primary? */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 1d123ce9b..a99f17b55 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -2242,7 +2242,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Make sure, the types are compatible */ if (IsClassInt (Expr->Type)) { if (!IsClassInt (Expr2.Type) && !(IsClassPtr(Expr2.Type) && ED_IsNullPtr(Expr))) { - Error ("Incompatible types"); + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Incompatible types comparing '%s' with '%s'"); } } else if (IsClassPtr (Expr->Type)) { if (IsClassPtr (Expr2.Type)) { @@ -2253,10 +2254,12 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ Type* right = Indirect (Expr2.Type); if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) { /* Incompatible pointers */ - Error ("Incompatible types"); + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Incompatible pointer types comparing '%s' with '%s'"); } } else if (!ED_IsNullPtr (&Expr2)) { - Error ("Incompatible types"); + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing pointer type '%s' with '%s'"); } } @@ -3324,7 +3327,8 @@ static void hieQuest (ExprDesc* Expr) /* Result type is void */ ResultType = Expr3.Type; } else { - Error ("Incompatible types"); + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, + "Incompatible types in ternary '%s' with '%s'"); ResultType = Expr2.Type; /* Doesn't matter here */ } diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index e915d7392..c3c1a2528 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -55,6 +55,24 @@ +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg) +/* Print error or warning message about type conversion with proper type names */ +{ + StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER; + StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER; + GetFullTypeNameBuf (&NewTypeName, NewType); + GetFullTypeNameBuf (&OldTypeName, OldType); + if (IsError) { + Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); + } else { + Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName)); + } + SB_Done (&OldTypeName); + SB_Done (&NewTypeName); +} + + + static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Emit code to convert the given expression to a new type. */ { @@ -189,6 +207,11 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) printf ("\n"); PrintRawType (stdout, NewType); #endif + int HasWarning = 0; + int HasError = 0; + const char* Msg = 0; + const Type* OldType = Expr->Type; + /* First, do some type checking */ if (IsTypeVoid (NewType) || IsTypeVoid (Expr->Type)) { @@ -199,7 +222,7 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) } /* If Expr is a function, convert it to pointer to function */ - if (IsTypeFunc(Expr->Type)) { + if (IsTypeFunc (Expr->Type)) { Expr->Type = PointerTo (Expr->Type); } @@ -220,15 +243,12 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) } Warning ("Converting pointer to integer without a cast"); } else if (!IsClassInt (Expr->Type) && !IsClassFloat (Expr->Type)) { - Error ("Incompatible types"); + HasError = 1; } - } else if (IsClassFloat (NewType)) { - if (!IsClassFloat (Expr->Type) && !IsClassInt (Expr->Type)) { - Error ("Incompatible types"); + HasError = 1; } - } else if (IsClassPtr (NewType)) { /* Handle conversions to pointer type */ @@ -248,17 +268,19 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) /* Compare the types */ switch (TypeCmp (NewType, Expr->Type)) { - case TC_INCOMPATIBLE: - Error ("Incompatible pointer types at '%s'", (Expr->Sym? Expr->Sym->Name : "Unknown")); - break; + case TC_INCOMPATIBLE: + HasWarning = 1; + Msg = "Incompatible pointer assignment to '%s' from '%s'"; + break; - case TC_QUAL_DIFF: - Error ("Pointer types differ in type qualifiers"); - break; + case TC_QUAL_DIFF: + HasWarning = 1; + Msg = "Pointer assignment to '%s' from '%s' discards qualifiers"; + break; - default: - /* Ok */ - break; + default: + /* Ok */ + break; } } @@ -268,18 +290,28 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) Warning ("Converting integer to pointer without a cast"); } } else { - Error ("Incompatible types"); + HasError = 1; } } else { - - /* Invalid automatic conversion */ - Error ("Incompatible types"); - + /* Invalid automatic conversion */ + HasError = 1; } - /* Do the actual conversion */ - DoConversion (Expr, NewType); + if (Msg == 0) { + Msg = "Converting to '%s' from '%s'"; + } + + if (HasError) { + TypeCompatibilityDiagnostic (NewType, OldType, 1, Msg); + } else { + if (HasWarning) { + TypeCompatibilityDiagnostic (NewType, OldType, 0, Msg); + } + + /* Do the actual conversion */ + DoConversion (Expr, NewType); + } } @@ -301,9 +333,19 @@ void TypeCast (ExprDesc* Expr) /* Read the expression we have to cast */ hie10 (Expr); - /* Convert functions and arrays to "pointer to" object */ - Expr->Type = PtrConversion (Expr->Type); - - /* Convert the value. */ - DoConversion (Expr, NewType); + /* Only allow casts to arithmetic or pointer types, or just changing the + ** qualifiers. + */ + if (TypeCmp (NewType, Expr->Type) >= TC_QUAL_DIFF) { + /* The expression has always the new type */ + ReplaceType (Expr, NewType); + } else if (IsCastType (NewType)) { + /* Convert functions and arrays to "pointer to" object */ + Expr->Type = PtrConversion (Expr->Type); + /* Convert the value */ + DoConversion (Expr, NewType); + } else { + Error ("Arithmetic or pointer type expected but '%s' is used", + GetFullTypeName (NewType)); + } } diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 8321aded4..5c94069a3 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -49,6 +49,9 @@ +void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); +/* Print error or warning message about type conversion with proper type names */ + void TypeConversion (ExprDesc* Expr, Type* NewType); /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or From 80b0e57543eeddd32485a015208aee160f7594f9 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 2 Aug 2020 21:51:32 +0800 Subject: [PATCH 171/806] Changed parameter constness of TypeConversion(). --- src/cc65/datatype.c | 14 ++++++++++++++ src/cc65/datatype.h | 5 +++++ src/cc65/typeconv.c | 4 ++-- src/cc65/typeconv.h | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index cffec7880..005e6109b 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1009,6 +1009,20 @@ Type* Indirect (Type* T) +const Type* IndirectConst (const Type* T) +/* Do one indirection for the given type, that is, return the type where the +** given type points to. +*/ +{ + /* We are expecting a pointer expression */ + CHECK (IsClassPtr (T)); + + /* Skip the pointer or array token itself */ + return T + 1; +} + + + Type* ArrayToPtr (Type* T) /* Convert an array to a pointer to it's first element */ { diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 66f1b8b05..6cbad302f 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -345,6 +345,11 @@ Type* Indirect (Type* T); ** given type points to. */ +const Type* IndirectConst (const Type* T); +/* Do one indirection for the given type, that is, return the type where the +** given type points to. +*/ + Type* ArrayToPtr (Type* T); /* Convert an array to a pointer to it's first element */ diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index c3c1a2528..7e2787529 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -192,7 +192,7 @@ ExitPoint: -void TypeConversion (ExprDesc* Expr, Type* NewType) +void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or ** impossible. @@ -264,7 +264,7 @@ void TypeConversion (ExprDesc* Expr, Type* NewType) ** - the rhs pointer is a void pointer, or ** - the lhs pointer is a void pointer. */ - if (!IsTypeVoid (Indirect (NewType)) && !IsTypeVoid (Indirect (Expr->Type))) { + if (!IsTypeVoid (IndirectConst (NewType)) && !IsTypeVoid (Indirect (Expr->Type))) { /* Compare the types */ switch (TypeCmp (NewType, Expr->Type)) { diff --git a/src/cc65/typeconv.h b/src/cc65/typeconv.h index 5c94069a3..0a82eac27 100644 --- a/src/cc65/typeconv.h +++ b/src/cc65/typeconv.h @@ -52,7 +52,7 @@ void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg); /* Print error or warning message about type conversion with proper type names */ -void TypeConversion (ExprDesc* Expr, Type* NewType); +void TypeConversion (ExprDesc* Expr, const Type* NewType); /* Do an automatic conversion of the given expression to the new type. Output ** warnings or errors where this automatic conversion is suspicious or ** impossible. From 00c16d34a4ea1a7906ef716c6db5d52effdb8ef1 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 172/806] Minor fixes for HandleSymRedefinition(). --- src/cc65/symtab.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 735cb854f..2104c017b 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -549,7 +549,8 @@ SymEntry FindStructField (const Type* T, const char* Name) static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags) /* Check and handle redefinition of existing symbols. -** Return ture if there *is* an error. +** Complete array sizes and function descriptors as well. +** Return true if there *is* an error. */ { /* Get the type info of the existing symbol */ @@ -594,8 +595,8 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags */ if (IsTypeFunc (T)) { - /* New type must be identical */ - if (TypeCmp (Entry->Type, T) < TC_EQUAL) { + /* New type must be equivalent */ + if (TypeCmp (E_Type, T) < TC_EQUAL) { Error ("Conflicting function types for '%s'", Entry->Name); Entry = 0; } else { @@ -614,6 +615,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); Entry = 0; } + } else if (E_SCType == SC_TYPEDEF) { if (SCType == SC_TYPEDEF) { @@ -622,9 +624,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Error ("Conflicting types for typedef '%s'", Entry->Name); Entry = 0; } - } else { - Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); Entry = 0; } @@ -639,7 +639,10 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Error ("Conflicting types for '%s'", Entry->Name); Entry = 0; } else if (E_SCType == SC_ENUMERATOR) { - /* Current code logic won't reach here, but still... */ + /* Enumerators aren't allowed to be redeclared at all, even if + ** all occurences are identical. The current code logic won't + ** get here, but let's just do it. + */ Error ("Redeclaration of enumerator constant '%s'", Entry->Name); Entry = 0; } From 44e3080ea93676e761093da37e4d16465e9c27b3 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 173/806] Increased upper limit of allowed errors before aborting. --- src/cc65/error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index 10d20a34b..b0ca97c89 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -184,7 +184,7 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } ++ErrorCount; - if (ErrorCount > 10) { + if (ErrorCount > 20) { Fatal ("Too many errors"); } } From 99ac1c46da0e9d19c32855409200067992e7b1a4 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 174/806] Made errors/warnings statistic message visible when there are errors. --- src/cc65/error.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index b0ca97c89..132bf331d 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -325,7 +325,8 @@ void ListWarnings (FILE* F) void ErrorReport (void) /* Report errors (called at end of compile) */ { - Print (stdout, 1, "%u errors, %u warnings\n", ErrorCount, WarningCount); + unsigned int V = (ErrorCount != 0 ? 0 : 1); + Print (stdout, V, "%u errors and %u warnings generated.\n", ErrorCount, WarningCount); } From 2ab727267385f4e51e26c682e69ab98af607a562 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 175/806] Improved warning on comparison of unsigned type < 0. --- src/cc65/codegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 20bfdf434..8a879745d 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3642,7 +3642,7 @@ void g_lt (unsigned flags, unsigned long val) /* Give a warning in some special cases */ if (val == 0) { - Warning ("Condition is never true"); + Warning ("Comparison of unsigned type < 0 is always false"); AddCodeLine ("jsr return0"); return; } From ef5a4db12e547d4758151f58606d4eb5f5b59297 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 176/806] Improved warning messages on UB shifts. --- src/cc65/shiftexpr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index c7aea5255..9fe9d1188 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -139,9 +139,14 @@ void ShiftExpr (struct ExprDesc* Expr) ** the operand, the behaviour is undefined according to the ** standard. */ - if (Expr2.IVal < 0 || Expr2.IVal >= (long) ExprBits) { + if (Expr2.IVal < 0) { - Warning ("Shift count too large for operand type"); + Warning ("Shift count '%ld' is negative", Expr2.IVal); + Expr2.IVal &= ExprBits - 1; + + } else if (Expr2.IVal >= (long) ExprBits) { + + Warning ("Shift count '%ld' >= width of type", Expr2.IVal); Expr2.IVal &= ExprBits - 1; } From e8c28864554248095ff71f10be383a258755838e Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 177/806] Improved error messages on redefinitions of constants and bit-fields. --- src/cc65/symtab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 2104c017b..ee95a77b7 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -801,7 +801,7 @@ SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsign if (Entry) { /* We have a symbol with this name already */ - Error ("Multiple definition for '%s'", Name); + Error ("Multiple definition for bit-field '%s'", Name); } else { @@ -834,7 +834,7 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val if ((Entry->Flags & SC_CONST) != SC_CONST) { Error ("Symbol '%s' is already different kind", Name); } else { - Error ("Multiple definition for '%s'", Name); + Error ("Multiple definition for constant '%s'", Name); } return Entry; } From 7e68a246251d04027319c0f5305bcab4b292018f Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 178/806] Clearer warning messages on unused symbols. --- src/cc65/symtab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index ee95a77b7..a673bc3dd 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -172,7 +172,7 @@ static void CheckSymTable (SymTable* Tab) } } else { if (IS_Get (&WarnUnusedVar)) { - Warning ("'%s' is defined but never used", Entry->Name); + Warning ("Variable '%s' is defined but never used", Entry->Name); } } } @@ -186,7 +186,7 @@ static void CheckSymTable (SymTable* Tab) } else if (!SymIsRef (Entry)) { /* Defined but not used */ if (IS_Get (&WarnUnusedLabel)) { - Warning ("'%s' is defined but never used", Entry->Name); + Warning ("Label '%s' is defined but never used", Entry->Name); } } } From 11a5f0edf156bf2bb828ede170a0d03aa2b4befd Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:15:57 +0800 Subject: [PATCH 179/806] No "Statement has no effect" warnings on statements with errors. --- src/cc65/stmt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index b02ae1cd6..27665e619 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -581,6 +581,7 @@ int Statement (int* PendingToken) ExprDesc Expr; int GotBreak; CodeMark Start, End; + unsigned PrevErrorCount = ErrorCount; /* Assume no pending token */ if (PendingToken) { @@ -681,7 +682,8 @@ int Statement (int* PendingToken) GetCodePos (&End); if (CodeRangeIsEmpty (&Start, &End) && !IsTypeVoid (Expr.Type) && - IS_Get (&WarnNoEffect)) { + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { Warning ("Statement has no effect"); } CheckSemi (PendingToken); From 6df4f1996b52e1d09111ff6078fab1ff1ea76f3d Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:16:00 +0800 Subject: [PATCH 180/806] Improved diagnostics with more detailed type names. --- src/cc65/assignment.c | 8 +++++++- src/cc65/datatype.c | 4 ++-- src/cc65/expr.c | 4 ++-- src/cc65/function.c | 2 +- src/cc65/stmt.c | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index c67ac1ce5..207e01588 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -127,7 +127,13 @@ void Assignment (ExprDesc* Expr) /* We must have an lvalue for an assignment */ if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); + if (IsTypeArray (Expr->Type)) { + Error ("Array type '%s' is not assignable", GetFullTypeName (Expr->Type)); + } else if (IsTypeFunc (Expr->Type)) { + Error ("Function type '%s' is not assignable", GetFullTypeName (Expr->Type)); + } else { + Error ("Assignment to rvalue"); + } } /* Check for assignment to const */ diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 005e6109b..aaee7260f 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -903,7 +903,7 @@ unsigned CheckedSizeOf (const Type* T) { unsigned Size = SizeOf (T); if (Size == 0) { - Error ("Size of data type is unknown"); + Error ("Size of type '%s' is unknown", GetFullTypeName (T)); Size = SIZEOF_CHAR; /* Don't return zero */ } return Size; @@ -919,7 +919,7 @@ unsigned CheckedPSizeOf (const Type* T) { unsigned Size = PSizeOf (T); if (Size == 0) { - Error ("Size of data type is unknown"); + Error ("Size of type '%s' is unknown", GetFullTypeName (T)); Size = SIZEOF_CHAR; /* Don't return zero */ } return Size; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a99f17b55..8cd332173 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1218,7 +1218,7 @@ static void StructRef (ExprDesc* Expr) NextToken (); const SymEntry Field = FindStructField (Expr->Type, Ident); if (Field.Type == 0) { - Error ("No field named '%s' found in %s", Ident, GetBasicTypeName (Expr->Type)); + Error ("No field named '%s' found in '%s'", Ident, GetFullTypeName (Expr->Type)); /* Make the expression an integer at address zero */ ED_MakeConstAbs (Expr, 0, type_int); return; @@ -1296,7 +1296,7 @@ static void StructRef (ExprDesc* Expr) Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break; default: - Internal ("Invalid %s size: %u", GetBasicTypeName (Expr->Type), StructSize); + Internal ("Invalid '%s' size: %u", GetFullTypeName (Expr->Type), StructSize); break; } diff --git a/src/cc65/function.c b/src/cc65/function.c index e6a974f98..5d0b09380 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -513,7 +513,7 @@ void NewFunc (SymEntry* Func) ** We don't currently support this case. */ if (RType == Param->Type) { - Error ("Passing %s of this size by value is not supported", GetBasicTypeName (Param->Type)); + Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); } } diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 27665e619..036cc2d89 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -333,7 +333,7 @@ static void ReturnStatement (void) /* Handle struct/union specially */ ReturnType = GetStructReplacementType (Expr.Type); if (ReturnType == Expr.Type) { - Error ("Returning %s of this size by value is not supported", GetBasicTypeName (Expr.Type)); + Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); } LoadExpr (TypeOf (ReturnType), &Expr); From d6aa446b541cbfa909187b6eb0243e95712c37e4 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:24:38 +0800 Subject: [PATCH 181/806] Error info for loading expressions of incomplete enum types. No more "Illegal type 0016". --- src/cc65/datatype.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index aaee7260f..bcaf3ab64 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -974,6 +974,11 @@ unsigned TypeOf (const Type* T) /* Address of ... */ return CF_INT | CF_UNSIGNED; + case T_ENUM: + /* Incomplete enum type */ + Error ("Incomplete enum type"); + return CF_INT; + default: Error ("Illegal type %04lX", T->C); return CF_INT; From e3d913b81aced6b50976b6aa5d24ef523527a0eb Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 02:23:35 +0800 Subject: [PATCH 182/806] Fixed the reference output of test/misc/goto.c (test/misc/goto.ref). --- test/misc/goto.ref | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/misc/goto.ref b/test/misc/goto.ref index 85dc20a61..d0a978436 100644 --- a/test/misc/goto.ref +++ b/test/misc/goto.ref @@ -1,7 +1,7 @@ goto.c(8): Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration -goto.c(97): Warning: 'a' is defined but never used -goto.c(117): Warning: 'a' is defined but never used -goto.c(137): Warning: 'a' is defined but never used +goto.c(97): Warning: Variable 'a' is defined but never used +goto.c(117): Warning: Variable 'a' is defined but never used +goto.c(137): Warning: Variable 'a' is defined but never used goto.c(159): Warning: Goto at line 23 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c(159): Warning: Goto at line 44 to label l8 jumps into a block with initialization of an object that has automatic storage duration goto.c(159): Warning: Goto at line 65 to label l8 jumps into a block with initialization of an object that has automatic storage duration From bae431eab0f3cdb3d47aa47164f0d870a0ecc2ba Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 08:09:50 +0800 Subject: [PATCH 183/806] Fixed error message of CheckedPSizeOf(). --- src/cc65/datatype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index bcaf3ab64..18bf41bca 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -919,7 +919,7 @@ unsigned CheckedPSizeOf (const Type* T) { unsigned Size = PSizeOf (T); if (Size == 0) { - Error ("Size of type '%s' is unknown", GetFullTypeName (T)); + Error ("Size of type '%s' is unknown", GetFullTypeName (T + 1)); Size = SIZEOF_CHAR; /* Don't return zero */ } return Size; From 0c72647edda327df6970773cefb28d3c7c6a41d9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 3 Aug 2020 09:07:36 +0200 Subject: [PATCH 184/806] Remove extra ED_TestDone call Accidentally added in #1141. --- src/cc65/loadexpr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 1a6e43779..1ea86fa2d 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -238,7 +238,6 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** when EndBit <= 24 is possible. */ g_test (F); - ED_TestDone (Expr); } else { /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ g_asr (F, Expr->BitOffs); From d8f9201ecd82165299b8fe758f57c1c515da313f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 2 Aug 2020 09:35:33 +0200 Subject: [PATCH 185/806] LoadExpr: Optimize <= 8-bit bit-field loads Set CF_FORCECHAR to do as many operations as char ops as possible. Clear high byte at the end. --- src/cc65/loadexpr.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 1ea86fa2d..ba585d3e3 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -117,12 +117,27 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ unsigned EndBit = 0; /* End bit for bit-fields, or zero if non-bit-field. */ int AdjustBitField = 0; + unsigned BitFieldFullWidthFlags = 0; if (ED_IsBitField (Expr)) { EndBit = Expr->BitOffs + Expr->BitWidth; AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are + ** supported. + */ Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; Flags |= CF_UNSIGNED; + + /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ + BitFieldFullWidthFlags = Flags; + + /* If we're adjusting, then only load a char (not an int) and do only char ops; + ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the + ** type is not CF_CHAR. + */ + if (AdjustBitField) { + Flags |= CF_FORCECHAR; + } } else if ((Flags & CF_TYPEMASK) == 0) { Flags |= TypeOf (Expr->Type); } @@ -211,10 +226,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** so be sure to always use unsigned ints for the operations. */ if (AdjustBitField) { - /* If the field was loaded as a char, force the shift/mask ops to be char ops. - ** If it is a char, the load has already put 0 in the upper byte, so that can remain. - */ - unsigned F = Flags | CF_FORCECHAR | CF_CONST; + unsigned F = Flags | CF_CONST; /* We always need to do something with the low byte, so there is no opportunity ** for optimization by skipping it. @@ -242,19 +254,21 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ g_asr (F, Expr->BitOffs); - /* Since we have now shifted down, we can do char ops as long as the width fits in - ** a char. + /* Since we have now shifted down, we could do char ops when the width fits in + ** a char, but we also need to clear the high byte since we've been using + ** CF_FORCECHAR up to now. */ - if (Expr->BitWidth <= CHAR_BITS) { - F |= CF_CHAR; - } /* And by the width if the field doesn't end on a char or int boundary. - ** If it does end on a boundary, then zeros have already been shifted in. - ** g_and emits no code if the mask is all ones. + ** If it does end on a boundary, then zeros have already been shifted in, + ** but we need to clear the high byte for char. g_and emits no code if the mask + ** is all ones. */ - if (EndBit != CHAR_BITS && EndBit != INT_BITS) { - g_and (F, (0x0001U << Expr->BitWidth) - 1U); + if (EndBit == CHAR_BITS) { + /* We need to clear the high byte, since CF_FORCECHAR was set. */ + g_and (BitFieldFullWidthFlags | CF_CONST, 0xFF); + } else if (EndBit != INT_BITS) { + g_and (BitFieldFullWidthFlags | CF_CONST, (0x0001U << Expr->BitWidth) - 1U); } } } From cdfc1afd89be8fdcd14377aac996589432345fce Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 5 Aug 2020 12:57:49 +0200 Subject: [PATCH 186/806] Fix vacuous comparison warning from 0df45fe cc65/symentry.c:306:60: warning: address of array 'Sym->Name' will always evaluate to 'true' [-Wpointer-bool-conversion] sprintf (TypeName, "%s %s", GetBasicTypeName (T), Sym->Name ? Sym->Name : ""); ~~~~~^~~~ ~ --- src/cc65/symentry.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc65/symentry.c b/src/cc65/symentry.c index 6486ab764..ea08a860c 100644 --- a/src/cc65/symentry.c +++ b/src/cc65/symentry.c @@ -303,7 +303,8 @@ const char* GetSymTypeName (const Type* T) if (Sym == 0) { return GetBasicTypeName (T); } - sprintf (TypeName, "%s %s", GetBasicTypeName (T), Sym->Name ? Sym->Name : ""); + sprintf (TypeName, "%s %s", GetBasicTypeName (T), + Sym->Name[0] != '\0' ? Sym->Name : ""); return TypeName; } From f59d6b8f6ab79a0ed1e6d1e473f325cc5d8974c3 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:31:52 +0800 Subject: [PATCH 187/806] Redefining enums/structs/unions of 0 size is no longer treated as declarations and thus forbidden. --- src/cc65/declare.c | 4 ++-- src/cc65/symtab.c | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 5ecfeb04f..5f5fdeac4 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -920,7 +920,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { LeaveStructLevel (); /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_UNION, UnionSize, FieldTab); + return AddStructSym (Name, SC_UNION | SC_DEF, UnionSize, FieldTab); } @@ -1102,7 +1102,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { LeaveStructLevel (); /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_STRUCT, StructSize, FieldTab); + return AddStructSym (Name, SC_STRUCT | SC_DEF, StructSize, FieldTab); } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index a673bc3dd..08c909121 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -52,13 +52,13 @@ #include "declare.h" #include "error.h" #include "funcdesc.h" +#include "function.h" #include "global.h" +#include "input.h" #include "stackptr.h" #include "symentry.h" #include "typecmp.h" #include "symtab.h" -#include "function.h" -#include "input.h" @@ -728,6 +728,10 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) Entry->V.E.SymTab = Tab; Entry->V.E.Type = Type; + if (Type != 0) { + Entry->Flags |= SC_DEF; + } + /* Add it to the current table */ AddSymEntry (CurTagTab, Entry); } @@ -738,11 +742,12 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) -SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab) +SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab) /* Add a struct/union entry and return it */ { SymTable* CurTagTab = TagTab; SymEntry* Entry; + unsigned Type = (Flags & SC_TYPEMASK); /* Type must be struct or union */ PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); @@ -756,13 +761,14 @@ SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable /* Existing symbol is not a struct */ Error ("Symbol '%s' is already different kind", Name); Entry = 0; - } else if (Size > 0 && Entry->V.S.Size > 0) { + } else if ((Entry->Flags & Flags & SC_DEF) == SC_DEF) { /* Both structs are definitions. */ Error ("Multiple definition for '%s'", Name); Entry = 0; } else { - /* Define the struct size if it is given */ - if (Size > 0) { + /* Define the struct size if it is a definition */ + if ((Flags & SC_DEF) == SC_DEF) { + Entry->Flags = Flags; Entry->V.S.SymTab = Tab; Entry->V.S.Size = Size; } @@ -777,7 +783,7 @@ SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable if (Entry == 0) { /* Create a new entry */ - Entry = NewSymEntry (Name, Type); + Entry = NewSymEntry (Name, Flags); /* Set the struct data */ Entry->V.S.SymTab = Tab; From fdd120db4905611e552cadd1bbeca984a3100e0b Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:36:19 +0800 Subject: [PATCH 188/806] Enabled to output errors and warnings about tentative definitions. --- src/cc65/compile.c | 52 ++++++++++++++++++++++++++-------------------- src/cc65/compile.h | 2 +- src/cc65/main.c | 2 +- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 3d2f82c51..d70e38e00 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -415,9 +415,36 @@ void Compile (const char* FileName) } else { + /* Used for emitting externals */ + SymEntry* Entry; + /* Ok, start the ball rolling... */ Parse (); + /* Reset the BSS segment name to its default; so that the below strcmp() + ** will work as expected, at the beginning of the list of variables + */ + SetSegName (SEG_BSS, SEGNAME_BSS); + + /* Walk over all global symbols and generate code for uninitialized + ** global variables. + */ + for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { + if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { + /* Assembly definition of uninitialized global variable */ + + /* Set the segment name only when it changes */ + if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { + SetSegName (SEG_BSS, Entry->V.BssName); + g_segname (SEG_BSS); + } + g_usebss (); + g_defgloblabel (Entry->Name); + g_res (SizeOf (Entry->Type)); + /* Mark as defined; so that it will be exported, not imported */ + Entry->Flags |= SC_DEF; + } + } } if (Debug) { @@ -431,18 +458,12 @@ void Compile (const char* FileName) void FinishCompile (void) -/* Emit literals, externals, debug info, do cleanup and optimizations */ +/* Emit literals, debug info, do cleanup and optimizations */ { SymEntry* Entry; - /* Reset the BSS segment name to its default; so that the below strcmp() - ** will work as expected, at the beginning of the list of variables - */ - SetSegName (SEG_BSS, SEGNAME_BSS); - - /* Walk over all global symbols: - ** - for functions, do clean-up and optimizations - ** - generate code for uninitialized global variables + /* Walk over all global symbols and do clean-up and optimizations for + ** functions. */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { if (SymIsOutputFunc (Entry)) { @@ -450,19 +471,6 @@ void FinishCompile (void) MoveLiteralPool (Entry->V.F.LitPool); CS_MergeLabels (Entry->V.F.Seg->Code); RunOpt (Entry->V.F.Seg->Code); - } else if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { - /* Assembly definition of uninitialized global variable */ - - /* Set the segment name only when it changes */ - if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { - SetSegName (SEG_BSS, Entry->V.BssName); - g_segname (SEG_BSS); - } - g_usebss (); - g_defgloblabel (Entry->Name); - g_res (SizeOf (Entry->Type)); - /* Mark as defined; so that it will be exported, not imported */ - Entry->Flags |= SC_DEF; } } diff --git a/src/cc65/compile.h b/src/cc65/compile.h index 2d15c8200..7af14ef7e 100644 --- a/src/cc65/compile.h +++ b/src/cc65/compile.h @@ -48,7 +48,7 @@ void Compile (const char* FileName); /* Top level compile routine. Will setup things and call the parser. */ void FinishCompile (void); -/* Emit literals, externals, do cleanup and optimizations */ +/* Emit literals, debug info, do cleanup and optimizations */ diff --git a/src/cc65/main.c b/src/cc65/main.c index ed2e9d7ba..3e60bcb95 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -1069,7 +1069,7 @@ int main (int argc, char* argv[]) /* Create the output file if we didn't had any errors */ if (PreprocessOnly == 0 && (ErrorCount == 0 || Debug)) { - /* Emit literals, externals, do cleanup and optimizations */ + /* Emit literals, do cleanup and optimizations */ FinishCompile (); /* Open the file */ From fdef06762938c3edbfcba45ef96a05b04d42e0f3 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:36:19 +0800 Subject: [PATCH 189/806] Fixed tentative definition of variables of incomplete types that may be completed later. Tenative arrays that never get completed are now assumed each to have one element. --- src/cc65/compile.c | 50 ++++++++++++++++++++++++++++++++-------------- src/cc65/expr.c | 21 +++++++++++++++++-- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index d70e38e00..a28c3e848 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -248,16 +248,11 @@ static void Parse (void) /* We cannot declare variables of type void */ Error ("Illegal type for variable '%s'", Decl.Ident); Entry->Flags &= ~(SC_STORAGE | SC_DEF); - } else if (Size == 0) { + } else if (Size == 0 && SymIsDef (Entry)) { /* Size is unknown. Is it an array? */ if (!IsTypeArray (Decl.Type)) { Error ("Variable '%s' has unknown size", Decl.Ident); } - /* Do this only if the same array has not been defined */ - if (!SymIsDef (Entry)) { - Entry->Flags &= ~(SC_STORAGE | SC_DEF); - Entry->Flags |= SC_DECL; - } } else { /* A global (including static) uninitialized variable is ** only a tentative definition. For example, this is valid: @@ -432,17 +427,42 @@ void Compile (const char* FileName) for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { if ((Entry->Flags & (SC_STORAGE | SC_DEF | SC_STATIC)) == (SC_STORAGE | SC_STATIC)) { /* Assembly definition of uninitialized global variable */ + SymEntry* Sym = GetSymType (Entry->Type); + unsigned Size = SizeOf (Entry->Type); + if (Size == 0 && IsTypeArray (Entry->Type)) { + if (GetElementCount (Entry->Type) == UNSPECIFIED) { + /* Assume array size of 1 */ + SetElementCount (Entry->Type, 1); + Size = SizeOf (Entry->Type); + Warning ("Tentative array '%s[]' assumed to have one element", Entry->Name); + } - /* Set the segment name only when it changes */ - if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { - SetSegName (SEG_BSS, Entry->V.BssName); - g_segname (SEG_BSS); + Sym = GetSymType (GetElementType (Entry->Type)); + if (Size == 0 && Sym != 0 && SymIsDef (Sym)) { + /* Array of 0-size elements */ + Warning ("Array '%s[]' has 0-sized elements", Entry->Name); + } + } + + /* For non-ESU types, Size != 0 */ + if (Size != 0 || (Sym != 0 && SymIsDef (Sym))) { + /* Set the segment name only when it changes */ + if (strcmp (GetSegName (SEG_BSS), Entry->V.BssName) != 0) { + SetSegName (SEG_BSS, Entry->V.BssName); + g_segname (SEG_BSS); + } + g_usebss (); + g_defgloblabel (Entry->Name); + g_res (Size); + + /* Mark as defined; so that it will be exported, not imported */ + Entry->Flags |= SC_DEF; + } else { + /* Tentative declared variable is still of incomplete type */ + Error ("Tentative definition of '%s' of type '%s' is incomplete", + Entry->Name, + GetFullTypeName (Entry->Type)); } - g_usebss (); - g_defgloblabel (Entry->Name); - g_res (SizeOf (Entry->Type)); - /* Mark as defined; so that it will be exported, not imported */ - Entry->Flags |= SC_DEF; } } } diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 8cd332173..65238af61 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -244,6 +244,23 @@ static int TypeSpecAhead (void) +static unsigned ExprCheckedSizeOf (const Type* T) +/* Specially checked SizeOf() used in 'sizeof' expressions */ +{ + unsigned Size = SizeOf (T); + SymEntry* Sym; + + if (Size == 0) { + Sym = GetSymType (T); + if (Sym == 0 || !SymIsDef (Sym)) { + Error ("Cannot apply 'sizeof' to incomplete type '%s'", GetFullTypeName (T)); + } + } + return Size; +} + + + void PushAddr (const ExprDesc* Expr) /* If the expression contains an address that was somehow evaluated, ** push this address on the stack. This is a helper function for all @@ -1890,7 +1907,7 @@ void hie10 (ExprDesc* Expr) if (TypeSpecAhead ()) { Type T[MAXTYPELEN]; NextToken (); - Size = CheckedSizeOf (ParseType (T)); + Size = ExprCheckedSizeOf (ParseType (T)); ConsumeRParen (); } else { /* Remember the output queue pointer */ @@ -1908,7 +1925,7 @@ void hie10 (ExprDesc* Expr) ReleaseLiteral (Expr->LVal); } /* Calculate the size */ - Size = CheckedSizeOf (Expr->Type); + Size = ExprCheckedSizeOf (Expr->Type); } /* Remove any generated code */ RemoveCode (&Mark); From 8cdffc1944d743455310fae7dac7470229106759 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:36:19 +0800 Subject: [PATCH 190/806] No storage for unsuccessfully parsed variables. --- src/cc65/compile.c | 6 ++++++ src/cc65/declare.c | 11 +++++++++-- src/cc65/locals.c | 21 +++++++++++---------- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index a28c3e848..ef7dea149 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -148,6 +148,11 @@ static void Parse (void) break; } + if ((Decl.StorageClass & SC_ALIAS) == SC_ALIAS) { + /* Failed parsing */ + goto SkipOneDecl; + } + /* Check if we must reserve storage for the variable. We do this, ** ** - if it is not a typedef or function, @@ -276,6 +281,7 @@ static void Parse (void) } +SkipOneDecl: /* Check for end of declaration list */ if (CurTok.Tok == TOK_COMMA) { NextToken (); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 5f5fdeac4..7b868380d 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1814,6 +1814,9 @@ Type* ParseType (Type* T) void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Parse a variable, type or function declaration */ { + /* Used to check if we have any errors during parsing this */ + unsigned PrevErrorCount = ErrorCount; + /* Initialize the Declaration struct */ InitDeclaration (D); @@ -1891,8 +1894,8 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) } } - /* Check the size of the generated type */ if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type)) { + /* Check the size of the generated type */ unsigned Size = SizeOf (D->Type); if (Size >= 0x10000) { if (D->Ident[0] != '\0') { @@ -1901,8 +1904,12 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) Error ("Invalid size in declaration (0x%06X)", Size); } } - } + if (PrevErrorCount != ErrorCount) { + /* Don't give storage if the declaration is not parsed correctly */ + D->StorageClass |= SC_DECL | SC_ALIAS; + } + } } diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 3fa26021f..03bc80f21 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -443,13 +443,14 @@ static void ParseOneDecl (const DeclSpec* Spec) } /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_EXTERN) == 0) { + if ((Decl.StorageClass & SC_ALIAS) == 0 && + (Decl.StorageClass & SC_EXTERN) == 0) { Decl.StorageClass |= SC_DEF; } /* Handle anything that needs storage (no functions, no typdefs) */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && - (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + if ((Decl.StorageClass & SC_DEF) == SC_DEF && + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { /* If we have a register variable, try to allocate a register and ** convert the declaration to "auto" if this is not possible. @@ -468,13 +469,6 @@ static void ParseOneDecl (const DeclSpec* Spec) } else if ((Decl.StorageClass & SC_AUTO) == SC_AUTO) { /* Auto variable */ ParseAutoDecl (&Decl); - } else if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { - /* External identifier - may not get initialized */ - if (CurTok.Tok == TOK_ASSIGN) { - Error ("Cannot initialize externals"); - } - /* Add the external symbol to the symbol table */ - AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); } else if ((Decl.StorageClass & SC_STATIC) == SC_STATIC) { /* Static variable */ ParseStaticDecl (&Decl); @@ -484,6 +478,13 @@ static void ParseOneDecl (const DeclSpec* Spec) } else { + if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { + /* External identifier - may not get initialized */ + if (CurTok.Tok == TOK_ASSIGN) { + Error ("Cannot initialize externals"); + } + } + /* Add the symbol to the symbol table */ AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); From b2d3b8379cd3aee9d99ac07080da2d0f73b03638 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:38:54 +0800 Subject: [PATCH 191/806] Warning about forward declaration of enum types in non-cc65 modes. --- src/cc65/compile.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index ef7dea149..5332caedb 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -276,6 +276,15 @@ static void Parse (void) Entry->Name, Entry->V.BssName); } Entry->V.BssName = xstrdup (bssName); + + /* Check for enum forward declaration. + ** Warn about it when extensions are not allowed. + */ + if (Size == 0 && IsTypeEnum (Decl.Type)) { + if (IS_Get (&Standard) != STD_CC65) { + Warning ("ISO C forbids forward references to 'enum' types"); + } + } } } From 43efc256f16c96d27a750793547b8d99e4fada66 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 5 Aug 2020 00:19:28 +0800 Subject: [PATCH 192/806] Changed error/warning messages not using the term 'tentative' according to PR reviews. --- src/cc65/compile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 5332caedb..d045473ff 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -449,7 +449,7 @@ void Compile (const char* FileName) /* Assume array size of 1 */ SetElementCount (Entry->Type, 1); Size = SizeOf (Entry->Type); - Warning ("Tentative array '%s[]' assumed to have one element", Entry->Name); + Warning ("Incomplete array '%s[]' assumed to have one element", Entry->Name); } Sym = GetSymType (GetElementType (Entry->Type)); @@ -474,7 +474,7 @@ void Compile (const char* FileName) Entry->Flags |= SC_DEF; } else { /* Tentative declared variable is still of incomplete type */ - Error ("Tentative definition of '%s' of type '%s' is incomplete", + Error ("Definition of '%s' has type '%s' that is never completed", Entry->Name, GetFullTypeName (Entry->Type)); } From d68cd90e47bfe96a5b85bd57328a25687a59f016 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 6 Aug 2020 17:22:19 +0800 Subject: [PATCH 193/806] Function declaration in functions cannot have storage classes other than 'extern'. --- src/cc65/locals.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 03bc80f21..6b4c13fce 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -426,13 +426,25 @@ static void ParseOneDecl (const DeclSpec* Spec) /* Read the declaration */ ParseDecl (Spec, &Decl, DM_NEED_IDENT); - /* Set the correct storage class for functions */ + /* Check if there are any non-extern storage classes set for function + ** declarations. The only valid storage class for function declarations + ** inside functions is 'extern'. + */ if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { - /* Function prototypes are always external */ - if ((Decl.StorageClass & SC_EXTERN) == 0) { - Warning ("Function must be extern"); + + /* Check if there are explicitly specified non-external storage classes */ + if ((Spec->Flags & DS_DEF_STORAGE) != DS_DEF_STORAGE && + (Decl.StorageClass & SC_EXTERN) == 0 && + (Decl.StorageClass & SC_STORAGEMASK) != 0) { + Error ("Illegal storage class on function"); } + + /* The default storage class could be wrong. Just use 'extern' in all + ** cases. + */ + Decl.StorageClass &= ~SC_STORAGEMASK; Decl.StorageClass |= SC_EXTERN; + } /* If we don't have a name, this was flagged as an error earlier. From 1dd899c7c9e78597f065b4ec085e7bddd244254e Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:44:36 +0800 Subject: [PATCH 194/806] Fixed non-file-scope multiple definition checking. --- src/cc65/symtab.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 08c909121..25f3b2428 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1019,9 +1019,18 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { - /* Use the fail-safe table for fictitious symbols */ - Tab = FailSafeTab; Entry = 0; + } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { + /* Redefinitions are not allowed */ + if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { + Error ("Multiple definition of '%s'", Entry->Name); + Entry = 0; + } + } + + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; } } @@ -1033,7 +1042,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry->Type = TypeDup (T); if ((Flags & SC_STRUCTFIELD) == SC_STRUCTFIELD || - (Flags & SC_TYPEDEF) == SC_TYPEDEF) { + (Flags & SC_ESUTYPEMASK) == SC_TYPEDEF) { if ((Flags & SC_ALIAS) != SC_ALIAS) { Entry->V.Offs = Offs; } From 0f1a5e0520eccf6d97c0de1c0d1b92a3679274fa Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:45:45 +0800 Subject: [PATCH 195/806] Set enum tag definition flags. --- src/cc65/symtab.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 25f3b2428..de17c6487 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -697,18 +697,17 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) /* Existing symbol is not an enum */ Error ("Symbol '%s' is already different kind", Name); Entry = 0; - } else { + } else if (Type != 0) { /* Define the struct size if the underlying type is given. */ - if (Type != 0) { - if (Type !=0 && Entry->V.E.Type != 0) { - /* Both are definitions. */ - Error ("Multiple definition for enum '%s'", Name); - Entry = 0; - } else { - Entry->Type = 0; - Entry->V.E.SymTab = Tab; - Entry->V.E.Type = Type; - } + if (Entry->V.E.Type != 0) { + /* Both are definitions. */ + Error ("Multiple definition for 'enum %s'", Name); + Entry = 0; + } else { + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + Entry->Flags &= ~SC_DECL; + Entry->Flags |= SC_DEF; } } @@ -724,7 +723,6 @@ SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) Entry = NewSymEntry (Name, SC_ENUM); /* Set the enum type data */ - Entry->Type = 0; Entry->V.E.SymTab = Tab; Entry->V.E.Type = Type; From 68d63b089d9be2e69defac2b9f6002af459bda3a Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:34:02 +0800 Subject: [PATCH 196/806] Reduced error flood raised by misplaced variable declarations. --- src/cc65/declare.c | 2 +- src/cc65/declare.h | 3 +++ src/cc65/expr.c | 31 +++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 7b868380d..f6aa9c430 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -263,7 +263,7 @@ static void OptionalSigned (void) -static void InitDeclSpec (DeclSpec* D) +void InitDeclSpec (DeclSpec* D) /* Initialize the DeclSpec struct for use */ { D->StorageClass = 0; diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 117ac14a6..615f16a4a 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -93,6 +93,9 @@ typedef enum { +void InitDeclSpec (DeclSpec* D); +/* Initialize the DeclSpec struct for use */ + Type* ParseType (Type* Type); /* Parse a complete type specification */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 65238af61..7b9fd0b9d 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -920,8 +920,35 @@ static void Primary (ExprDesc* E) /* Illegal primary. Be sure to skip the token to avoid endless ** error loops. */ - Error ("Expression expected"); - NextToken (); + { + /* Let's see if this is a C99-style declaration */ + DeclSpec Spec; + InitDeclSpec (&Spec); + ParseDeclSpec (&Spec, -1, T_QUAL_NONE); + + if (Spec.Type->C != T_END) { + + Error ("Mixed declarations and code are not supported in cc65"); + while (CurTok.Tok != TOK_SEMI) { + Declaration Decl; + + /* Parse one declaration */ + ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + if (CurTok.Tok == TOK_ASSIGN) { + NextToken (); + ParseInit (Decl.Type); + } + if (CurTok.Tok == TOK_COMMA) { + NextToken (); + } else { + break; + } + } + } else { + Error ("Expression expected"); + NextToken (); + } + } ED_MakeConstAbsInt (E, 1); break; } From 9fcfa3fc49f97c7cbbd6d5840cfa3c9097b77c08 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:46:22 +0800 Subject: [PATCH 197/806] Fixed full type names of functions with "empty" parameter list. --- src/cc65/datatype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 18bf41bca..65a0c0cbb 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -133,7 +133,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu if ((F->Flags & FD_VARIADIC) == 0) { if (F->ParamCount > 0) { SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - } else { + } else if ((F->Flags & FD_EMPTY) == 0) { SB_AppendStr (&ParamList, "void"); } } else { From 9317db6642a512cb0318d134daeb6d7f07299ab7 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:46:22 +0800 Subject: [PATCH 198/806] Slightly improved type error messages of 'op='. --- src/cc65/expr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 7b9fd0b9d..817234ec8 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3408,7 +3408,7 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) /* There must be an integer or pointer on the left side */ if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand type"); + Error ("Invalid left operand for binary operator '%s'", Op); /* Continue. Wrong code will be generated, but the compiler won't ** break, so this is the best error recovery. */ @@ -3532,7 +3532,7 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) /* There must be an integer or pointer on the left side */ if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand type"); + Error ("Invalid left operand for binary operator '%s'", Op); /* Continue. Wrong code will be generated, but the compiler won't ** break, so this is the best error recovery. */ From 0d53806490cf65bccc9420507795a3be2d973847 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:46:22 +0800 Subject: [PATCH 199/806] Avoided excess errors in incomplete struct assignment. --- src/cc65/assignment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 207e01588..53e0653ed 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -107,13 +107,13 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) g_push (CF_PTR | CF_UNSIGNED, 0); /* Load the size of the struct or union into the primary */ - g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, CheckedSizeOf (ltype), 0); + g_getimmed (CF_INT | CF_UNSIGNED | CF_CONST, SizeOf (ltype), 0); /* Call the memcpy function */ g_call (CF_FIXARGC, Func_memcpy, 4); } - return 0; + return 1; } From fe3f726fd695860d1534cd7819632e0fbf3bc944 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 07:04:29 +0800 Subject: [PATCH 200/806] Improved incomplete enum typed diagnostics. --- src/cc65/datatype.c | 2 +- src/cc65/declare.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 65a0c0cbb..5794bf5a6 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -976,7 +976,7 @@ unsigned TypeOf (const Type* T) case T_ENUM: /* Incomplete enum type */ - Error ("Incomplete enum type"); + Error ("Incomplete type '%s'", GetFullTypeName (T)); return CF_INT; default: diff --git a/src/cc65/declare.c b/src/cc65/declare.c index f6aa9c430..9539a09b6 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2543,6 +2543,12 @@ static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMember case T_UNION: return ParseStructInit (T, Braces, AllowFlexibleMembers); + case T_ENUM: + /* Incomplete enum type must have already raised errors. + ** Just proceed to consume the value. + */ + return ParseScalarInit (T); + case T_VOID: if (IS_Get (&Standard) == STD_CC65) { /* Special cc65 extension in non-ANSI mode */ From b62b1650f57d10611368dc7f70b75599b96888e1 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 20:12:04 +0800 Subject: [PATCH 201/806] Improved error messages on struct/union type multiple definitions. --- src/cc65/symtab.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index de17c6487..30fab0593 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -761,7 +761,11 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl Entry = 0; } else if ((Entry->Flags & Flags & SC_DEF) == SC_DEF) { /* Both structs are definitions. */ - Error ("Multiple definition for '%s'", Name); + if (Type == SC_STRUCT) { + Error ("Multiple definition for 'struct %s'", Name); + } else { + Error ("Multiple definition for 'union %s'", Name); + } Entry = 0; } else { /* Define the struct size if it is a definition */ From bde5be67933e247a6362de21e011c7f4147fbea6 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 9 Aug 2020 06:25:36 +0800 Subject: [PATCH 202/806] Improved error message on initializing extern variables inside functions. --- src/cc65/locals.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 6b4c13fce..de2074211 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -493,7 +493,10 @@ static void ParseOneDecl (const DeclSpec* Spec) if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN) { /* External identifier - may not get initialized */ if (CurTok.Tok == TOK_ASSIGN) { - Error ("Cannot initialize externals"); + Error ("Cannot initialize extern variable '%s'", Decl.Ident); + /* Avoid excess errors */ + NextToken (); + ParseInit (Decl.Type); } } From 4dfc1a5ded78e4c2b75e3bf34d3f6ae749ecd5a2 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 10 Aug 2020 09:32:47 +0800 Subject: [PATCH 203/806] Using a dedicated SC_FICTITIOUS flag in case of parsing errors. --- src/cc65/compile.c | 2 +- src/cc65/declare.c | 2 +- src/cc65/locals.c | 2 +- src/cc65/symentry.h | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index d045473ff..6c881d9a8 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -148,7 +148,7 @@ static void Parse (void) break; } - if ((Decl.StorageClass & SC_ALIAS) == SC_ALIAS) { + if ((Decl.StorageClass & SC_FICTITIOUS) == SC_FICTITIOUS) { /* Failed parsing */ goto SkipOneDecl; } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 9539a09b6..ac58948bf 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1907,7 +1907,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) if (PrevErrorCount != ErrorCount) { /* Don't give storage if the declaration is not parsed correctly */ - D->StorageClass |= SC_DECL | SC_ALIAS; + D->StorageClass |= SC_DECL | SC_FICTITIOUS; } } } diff --git a/src/cc65/locals.c b/src/cc65/locals.c index de2074211..12caa7aa2 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -455,7 +455,7 @@ static void ParseOneDecl (const DeclSpec* Spec) } /* If the symbol is not marked as external, it will be defined now */ - if ((Decl.StorageClass & SC_ALIAS) == 0 && + if ((Decl.StorageClass & SC_FICTITIOUS) == 0 && (Decl.StorageClass & SC_EXTERN) == 0) { Decl.StorageClass |= SC_DEF; } diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 1d8af3f50..cfc6379d3 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -106,6 +106,7 @@ struct CodeEntry; #define SC_GOTO_IND 0x800000U /* Indirect goto */ #define SC_ALIAS 0x01000000U /* Alias of anonymous field */ +#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious */ From 97065faf1a8ca3906d587ef404b6395bee111f83 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 5 Aug 2020 00:19:28 +0800 Subject: [PATCH 204/806] Disallowed struct/union types of 0 size as cc65 is not ready to support them. --- src/cc65/declare.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ac58948bf..636e0d61d 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -919,6 +919,11 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { FieldTab = GetSymTab (); LeaveStructLevel (); + /* Empty union is not supported now */ + if (UnionSize == 0) { + Error ("Empty union type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_UNION | SC_DEF, UnionSize, FieldTab); } @@ -1101,6 +1106,11 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { FieldTab = GetSymTab (); LeaveStructLevel (); + /* Empty struct is not supported now */ + if (StructSize == 0) { + Error ("Empty struct type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_STRUCT | SC_DEF, StructSize, FieldTab); } From fe44fe963fa2285096ba8c59d975feef474bd830 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 7 Aug 2020 17:40:40 +0800 Subject: [PATCH 205/806] Disallowed empty enums. --- src/cc65/declare.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 636e0d61d..41f96c20a 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -549,8 +549,8 @@ static SymEntry* ParseEnumDecl (const char* Name) ident Ident; long MinConstant = 0; unsigned long MaxConstant = 0; - const Type* NewType = type_int; /* new enumerator type */ - const Type* MemberType = type_int; /* default enumerator type */ + const Type* NewType = 0; /* new member type */ + const Type* MemberType = type_int; /* default member type */ /* Accept forward definitions */ if (CurTok.Tok != TOK_LCURLY) { @@ -677,6 +677,11 @@ static SymEntry* ParseEnumDecl (const char* Name) } ConsumeRCurly (); + /* Check if there have been any members. Error if none */ + if (NewType == 0) { + Error ("Empty enum is invalid"); + } + /* This evaluates the underlying type of the whole enum */ MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); if (MemberType == 0) { From 8b8561161c09139df076a19609d58312d111a3ce Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 7 Aug 2020 19:35:37 +0800 Subject: [PATCH 206/806] Moved #1098 bug tests from test/misc to test/err as they are fixed now. --- test/{misc => err}/bug1098.c | 0 test/{misc => err}/bug1098a.c | 0 test/{misc => err}/bug1098b.c | 0 test/misc/Makefile | 18 ------------------ 4 files changed, 18 deletions(-) rename test/{misc => err}/bug1098.c (100%) rename test/{misc => err}/bug1098a.c (100%) rename test/{misc => err}/bug1098b.c (100%) diff --git a/test/misc/bug1098.c b/test/err/bug1098.c similarity index 100% rename from test/misc/bug1098.c rename to test/err/bug1098.c diff --git a/test/misc/bug1098a.c b/test/err/bug1098a.c similarity index 100% rename from test/misc/bug1098a.c rename to test/err/bug1098a.c diff --git a/test/misc/bug1098b.c b/test/err/bug1098b.c similarity index 100% rename from test/misc/bug1098b.c rename to test/err/bug1098b.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 3c5045753..1d98e2d62 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -87,24 +87,6 @@ $(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) $(if $(QUIET),echo misc/bug264.$1.$2.prg) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# this should fail to compile, because there are errors in the code -$(WORKDIR)/bug1098.$1.$2.prg: bug1098.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiles but should give an error." - $(if $(QUIET),echo misc/bug1098.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# this should fail to compile, because there are errors in the code -$(WORKDIR)/bug1098a.$1.$2.prg: bug1098a.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiles but should give an error." - $(if $(QUIET),echo misc/bug1098a.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - -# this should fail to compile, because there are errors in the code -$(WORKDIR)/bug1098b.$1.$2.prg: bug1098b.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiles but should give an error." - $(if $(QUIET),echo misc/bug1098b.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # this should fail to compile, because there are errors in the code # instead, the compiler crashes $(WORKDIR)/bug1113.$1.$2.prg: bug1113.c | $(WORKDIR) From eb4464e8283cfeedeb560b7b0e0cc0561e4e3c3d Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Aug 2020 01:38:54 +0800 Subject: [PATCH 207/806] Fixed type comparisons of ESU types with stricter rules. --- src/cc65/typecmp.c | 111 +++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 65 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 0eddf2988..39f13b79e 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -148,41 +148,6 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2) -static int EqualSymTables (SymTable* Tab1, SymTable* Tab2) -/* Compare two symbol tables. Return 1 if they are equal and 0 otherwise */ -{ - /* Compare the parameter lists */ - SymEntry* Sym1 = Tab1->SymHead; - SymEntry* Sym2 = Tab2->SymHead; - - /* Compare the fields */ - while (Sym1 && Sym2) { - - /* Compare the names of this field */ - if (!HasAnonName (Sym1) || !HasAnonName (Sym2)) { - if (strcmp (Sym1->Name, Sym2->Name) != 0) { - /* Names are not identical */ - return 0; - } - } - - /* Compare the types of this field */ - if (TypeCmp (Sym1->Type, Sym2->Type) < TC_EQUAL) { - /* Field types not equal */ - return 0; - } - - /* Get the pointers to the next fields */ - Sym1 = Sym1->NextSym; - Sym2 = Sym2->NextSym; - } - - /* Check both pointers against NULL to compare the field count */ - return (Sym1 == 0 && Sym2 == 0); -} - - - static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Recursively compare two types. */ { @@ -190,8 +155,6 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) unsigned ElementCount; SymEntry* Sym1; SymEntry* Sym2; - SymTable* Tab1; - SymTable* Tab2; FuncDesc* F1; FuncDesc* F2; @@ -235,6 +198,40 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } + /* Enums must be handled specially */ + if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { + + /* Compare the tag types */ + Sym1 = GetESUSymEntry (lhs); + Sym2 = GetESUSymEntry (rhs); + + if (Sym1 != Sym2) { + if (Sym1 == 0 || Sym2 == 0) { + + /* Only one is an enum. So they can't be identical */ + SetResult (Result, TC_STRICT_COMPATIBLE); + + } else { + /* For the two to be identical, they must be in the same + ** scope and have the same name. + */ + if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { + + /* If any one of the two is incomplete, we can't guess + ** their underlying types and have to assume that they + ** be incompatible. + */ + if (SizeOf (lhs) == 0 || SizeOf (rhs) == 0) { + SetResult (Result, TC_INCOMPATIBLE); + return; + } + } + } + } + + } + /* On indirection level zero, a qualifier or sign difference is ** accepted. The types are no longer equal, but compatible. */ @@ -364,41 +361,25 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) case T_TYPE_STRUCT: case T_TYPE_UNION: - /* Compare the fields recursively. To do that, we fetch the - ** pointer to the struct definition from the type, and compare - ** the fields. - */ + /* Compare the tag types */ Sym1 = GetESUSymEntry (lhs); Sym2 = GetESUSymEntry (rhs); - /* If one symbol has a name, the names must be identical */ - if (!HasAnonName (Sym1) || !HasAnonName (Sym2)) { - if (strcmp (Sym1->Name, Sym2->Name) != 0) { - /* Names are not identical */ + CHECK (Sym1 != 0 || Sym2 != 0); + + if (Sym1 != Sym2) { + /* Both must be in the same scope and have the same name to + ** be identical. This shouldn't happen in the current code + ** base, but we still do this to be future-proof. + */ + if (Sym1->Owner != Sym2->Owner || + strcmp (Sym1->Name, Sym2->Name) != 0) { SetResult (Result, TC_INCOMPATIBLE); return; } } - /* Get the field tables from the struct entry */ - Tab1 = Sym1->V.S.SymTab; - Tab2 = Sym2->V.S.SymTab; - - /* One or both structs may be forward definitions. In this case, - ** the symbol tables are both non existant. Assume that the - ** structs are equal in this case. - */ - if (Tab1 != 0 && Tab2 != 0) { - - if (EqualSymTables (Tab1, Tab2) == 0) { - /* Field lists are not equal */ - SetResult (Result, TC_INCOMPATIBLE); - return; - } - - } - - /* Structs are equal */ + /* Both are identical */ break; } @@ -410,7 +391,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check if end of rhs reached */ if (rhs->C == T_END) { - SetResult (Result, TC_EQUAL); + SetResult (Result, TC_IDENTICAL); } else { SetResult (Result, TC_INCOMPATIBLE); } From 03b37cf7127deab8c5c7a659550e08b793610302 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 8 Aug 2020 06:46:55 +0800 Subject: [PATCH 208/806] Fixed type comparisons of typedefs and arrays. --- src/cc65/symtab.c | 71 +++++++++++++++++++++++----------------------- src/cc65/typecmp.c | 15 ++++++---- src/cc65/typecmp.h | 2 +- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 30fab0593..316963ee9 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -558,34 +558,46 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags unsigned E_SCType = Entry->Flags & SC_TYPEMASK; unsigned SCType = Flags & SC_TYPEMASK; - /* Some symbols may be redeclared if certain requirements are met */ - if (IsTypeArray (T) && IsTypeArray (E_Type)) { + /* Existing typedefs cannot be redeclared as anything different */ + if (E_SCType == SC_TYPEDEF) { - /* Get the array sizes */ - long Size = GetElementCount (T); - long ESize = GetElementCount (E_Type); - - /* If we are handling arrays, the old entry or the new entry may be - ** an incomplete declaration. Accept this, and if the exsting entry - ** is incomplete, complete it. - */ - if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || - TypeCmp (T + 1, E_Type + 1) < TC_EQUAL) { - /* Types not identical: Conflicting types */ - Error ("Conflicting array types for '%s[]'", Entry->Name); - Entry = 0; - } else { - /* Check if we have a size in the existing definition */ - if (ESize == UNSPECIFIED) { - /* Existing, size not given, use size from new def */ - SetElementCount (E_Type, Size); + if (SCType == SC_TYPEDEF) { + if (TypeCmp (E_Type, T) < TC_IDENTICAL) { + Error ("Conflicting types for typedef '%s'", Entry->Name); + Entry = 0; } + } else { + Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); + Entry = 0; } } else { - /* We have a symbol with this name already */ - if ((Entry->Flags & SC_FUNC) == SC_FUNC) { + /* Some symbols may be redeclared if certain requirements are met */ + if (IsTypeArray (T) && IsTypeArray (E_Type)) { + + /* Get the array sizes */ + long Size = GetElementCount (T); + long ESize = GetElementCount (E_Type); + + /* If we are handling arrays, the old entry or the new entry may be + ** an incomplete declaration. Accept this, and if the exsting entry + ** is incomplete, complete it. + */ + if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) || + TypeCmp (T + 1, E_Type + 1) < TC_EQUAL) { + /* Conflicting element types */ + Error ("Conflicting array types for '%s[]'", Entry->Name); + Entry = 0; + } else { + /* Check if we have a size in the existing definition */ + if (ESize == UNSPECIFIED) { + /* Existing, size not given, use size from new def */ + SetElementCount (E_Type, Size); + } + } + + } else if ((Entry->Flags & SC_FUNC) == SC_FUNC) { /* In case of a function, use the new type descriptor, since it ** contains pointers to the new symbol tables that are needed if @@ -616,22 +628,9 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Entry = 0; } - } else if (E_SCType == SC_TYPEDEF) { - - if (SCType == SC_TYPEDEF) { - /* New typedef must be identical */ - if (TypeCmp (E_Type, T) < TC_EQUAL) { - Error ("Conflicting types for typedef '%s'", Entry->Name); - Entry = 0; - } - } else { - Error ("Redefinition of typedef '%s' as different kind of symbol", Entry->Name); - Entry = 0; - } - } else { - /* New type must be identical */ + /* New type must be equivalent */ if (SCType != E_SCType) { Error ("Redefinition of '%s' as different kind of symbol", Entry->Name); Entry = 0; diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 39f13b79e..e14ace6b9 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -190,6 +190,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) */ if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) { RightType = T_TYPE_PTR; + SetResult (Result, TC_STRICT_COMPATIBLE); } /* If the underlying types are not identical, the types are incompatible */ @@ -350,12 +351,14 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) /* Check member count */ LeftCount = GetElementCount (lhs); RightCount = GetElementCount (rhs); - if (LeftCount != UNSPECIFIED && - RightCount != UNSPECIFIED && - LeftCount != RightCount) { - /* Member count given but different */ - SetResult (Result, TC_INCOMPATIBLE); - return; + if (LeftCount != RightCount) { + if (LeftCount != UNSPECIFIED && + RightCount != UNSPECIFIED) { + /* Member count given but different */ + SetResult (Result, TC_INCOMPATIBLE); + return; + } + SetResult (Result, TC_EQUAL); } break; diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index 5f95e42a1..3e95adb3f 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -55,7 +55,7 @@ typedef enum { TC_COMPATIBLE = TC_SIGN_DIFF, /* Compatible types */ TC_QUAL_DIFF, /* Types differ in qualifier of pointer */ TC_STRICT_COMPATIBLE, /* Strict compatibility */ - TC_EQUAL, /* Types are equal */ + TC_EQUAL, /* Types are equivalent */ TC_IDENTICAL /* Types are identical */ } typecmp_t; From 0fa18886c066278b6e1129b588dc9ff9e408f4d0 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 13 Aug 2020 09:27:53 +0800 Subject: [PATCH 209/806] Fixed copying structs/unions > 4 bytes. --- src/cc65/assignment.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 53e0653ed..6e25eaa56 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -58,22 +58,23 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Copy the struct/union represented by RExpr to the one represented by LExpr */ { /* If the size is that of a basic type (char, int, long), we will copy - ** the struct using the primary register, otherwise we use memcpy. In - ** the former case, push the address only if really needed. + ** the struct using the primary register, otherwise we will use memcpy. */ const Type* ltype = LExpr->Type; const Type* stype = GetStructReplacementType (ltype); int UseReg = (stype != ltype); if (UseReg) { + /* Back up the address of lhs only if it is in the primary */ PushAddr (LExpr); } else { - ED_MarkExprAsRVal (LExpr); + /* Push the address of lhs as the destination of memcpy */ + ED_AddrExpr (LExpr); LoadExpr (CF_NONE, LExpr); g_push (CF_PTR | CF_UNSIGNED, 0); } - /* Get the expression on the right of the '=' into the primary */ + /* Get the expression on the right of the '=' */ hie1 (RExpr); /* Check for equality of the structs */ @@ -82,28 +83,27 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) "Incompatible types in assignment to '%s' from '%s'"); } - /* Do we copy using the primary? */ + /* Do we copy the value directly using the primary? */ if (UseReg) { - /* Check if the right hand side is an lvalue */ - if (ED_IsLVal (RExpr)) { + /* Check if the value of the rhs is not in the primary yet */ + if (!ED_IsLocPrimary (RExpr)) { /* Just load the value into the primary as the replacement type. */ LoadExpr (TypeOf (stype) | CF_FORCECHAR, RExpr); } - /* Store it into the new location */ + /* Store it into the location referred in the primary */ Store (LExpr, stype); } else { - /* Check if the right hand side is an lvalue */ - if (ED_IsLVal (RExpr)) { - /* We will use memcpy. Push the address of the rhs */ - ED_MarkExprAsRVal (RExpr); + /* The only way this can happen is in chained assignments */ + if (!ED_IsLocPrimary (RExpr)) { + ED_AddrExpr (RExpr); LoadExpr (CF_NONE, RExpr); } - /* Push the address (or whatever is in ax in case of errors) */ + /* Push the address of the rhs as the source of memcpy */ g_push (CF_PTR | CF_UNSIGNED, 0); /* Load the size of the struct or union into the primary */ @@ -111,6 +111,9 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Call the memcpy function */ g_call (CF_FIXARGC, Func_memcpy, 4); + + /* Restore the indirection level of lhs */ + ED_IndExpr (LExpr); } return 1; @@ -257,6 +260,6 @@ void Assignment (ExprDesc* Expr) } - /* Value is still in primary and not an lvalue */ + /* Value might be still in primary and not an lvalue */ ED_FinalizeRValLoad (Expr); } From 0dfe9ff5fe8ac5056aa37d727b2f986e4732faab Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 14 Aug 2020 08:32:22 +0800 Subject: [PATCH 210/806] Fixed testing 'struct->field'. --- src/cc65/assignment.c | 8 +++++++- src/cc65/expr.c | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 6e25eaa56..0151a9000 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -77,7 +77,7 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Get the expression on the right of the '=' */ hie1 (RExpr); - /* Check for equality of the structs */ + /* Check for equality of the structs/unions */ if (TypeCmp (ltype, RExpr->Type) < TC_STRICT_COMPATIBLE) { TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, "Incompatible types in assignment to '%s' from '%s'"); @@ -114,6 +114,12 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Restore the indirection level of lhs */ ED_IndExpr (LExpr); + + /* Clear the tested flag set during loading. This is not neccessary + ** currently (and probably ever) as a struct/union cannot be converted + ** to a boolean in C, but there is no harm to be future-proof. + */ + ED_MarkAsUntested (LExpr); } return 1; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 817234ec8..f75015305 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1301,6 +1301,9 @@ static void StructRef (ExprDesc* Expr) LoadExpr (CF_NONE, Expr); } + /* Clear the tested flag set during loading */ + ED_MarkAsUntested (Expr); + /* The type is the field type plus any qualifiers from the struct/union */ if (IsClassStruct (Expr->Type)) { Q = GetQualifier (Expr->Type); From dc83eb15aff2eaf0cab5f3964d93846740eb7feb Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 14 Aug 2020 16:12:17 +0200 Subject: [PATCH 211/806] added test related to issue #1181 --- test/val/bug1181.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/val/bug1181.c diff --git a/test/val/bug1181.c b/test/val/bug1181.c new file mode 100644 index 000000000..4ea2d54bf --- /dev/null +++ b/test/val/bug1181.c @@ -0,0 +1,86 @@ + +/* bug #1181 - Testing struct member against NULL is broken */ + +#include +#include + +struct { + int a; +} s = { 256 }, *ps = &s; + +int res = EXIT_SUCCESS; + +void test1(void) +{ + if (ps->a) { + printf("OK\n"); + } else { + printf("ERROR: %d\n", ps->a); + res = EXIT_FAILURE; + } +} + +typedef struct _MENUITEM +{ + char *name; +} MENUITEM; + +typedef struct _MENU +{ + struct _MENUITEM *items; +} MENU; + +/* note: the behaviour changes when these strings are changed! */ +static unsigned char oi31[] = {"Browser Exec Setup"}; +static unsigned char oi36[] = {"Browser auto sort"}; +static unsigned char oi47[] = {"Browser startup"}; +static unsigned char oi49[] = {"Browser charset"}; +static unsigned char oi55[] = {"Menu color scheme"}; +static unsigned char oi63[] = {"Menu input scheme"}; +static unsigned char oi35[] = {"back"}; + +MENUITEM optionsitems_menu[] = { + {oi31}, + {oi36}, + {oi47}, + {oi49}, + {oi55}, + {oi63}, + {oi35}, + {NULL} +}; + +static MENU optionsmenu_menu = { + &optionsitems_menu[0], +}; + +unsigned char __fastcall__ menu_getnumitems(MENU *menu) +{ +static unsigned char numitems; +MENUITEM *items; + numitems = 0; + items = menu->items; + while(items->name) + { + ++numitems; + ++items; + } + return numitems; +} + +int main(void) +{ + unsigned char i = 0; + + i = menu_getnumitems(&optionsmenu_menu); + printf("numitems (expected 7): %d\n", i); + + if (i != 7) { + printf("failed\n"); + res = EXIT_FAILURE; + } + printf("passed\n"); + + test1(); + return res; +} From 8a417ff03952b30ed478d4414c42fbf3bed5c28b Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 12 Aug 2020 21:33:46 +0800 Subject: [PATCH 212/806] Improved ESU declaration failure handling. --- src/cc65/declare.c | 73 ++++++++++++++++++++++++++++++---------------- src/cc65/symtab.c | 25 ++++++++++++---- src/cc65/symtab.h | 4 +-- 3 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 41f96c20a..259f8b8ac 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -479,7 +479,7 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) -static SymEntry* ESUForwardDecl (const char* Name, unsigned Type) +static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags) /* Handle an enum, struct or union forward decl */ { /* Try to find an enum/struct/union with the given name. If there is none, @@ -487,12 +487,12 @@ static SymEntry* ESUForwardDecl (const char* Name, unsigned Type) */ SymEntry* Entry = FindTagSym (Name); if (Entry == 0) { - if (Type != SC_ENUM) { - Entry = AddStructSym (Name, Type, 0, 0); + if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { + Entry = AddStructSym (Name, Flags, 0, 0); } else { - Entry = AddEnumSym (Name, 0, 0); + Entry = AddEnumSym (Name, Flags, 0, 0); } - } else if ((Entry->Flags & SC_TYPEMASK) != Type) { + } else if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { /* Already defined, but not the same type class */ Error ("Symbol '%s' is already different kind", Name); } @@ -551,14 +551,17 @@ static SymEntry* ParseEnumDecl (const char* Name) unsigned long MaxConstant = 0; const Type* NewType = 0; /* new member type */ const Type* MemberType = type_int; /* default member type */ + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; + - /* Accept forward definitions */ if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward definition */ return ESUForwardDecl (Name, SC_ENUM); } - /* Add the enum tag */ - AddEnumSym (Name, 0, 0); + /* Add a forward declaration for the enum tag in the current lexical level */ + AddEnumSym (Name, 0, 0, 0); /* Skip the opening curly brace */ NextToken (); @@ -695,7 +698,13 @@ static SymEntry* ParseEnumDecl (const char* Name) } FieldTab = GetSymTab (); - return AddEnumSym (Name, MemberType, FieldTab); + + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + + return AddEnumSym (Name, Flags, MemberType, FieldTab); } @@ -829,19 +838,21 @@ static SymEntry* ParseUnionDecl (const char* Name) unsigned FieldSize; int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; - SymEntry* StructTypeEntry; + SymEntry* UnionTagEntry; SymEntry* Entry; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration. */ + /* Just a forward declaration */ return ESUForwardDecl (Name, SC_UNION); } - /* Add a forward declaration for the struct in the current lexical level */ - StructTypeEntry = AddStructSym (Name, SC_UNION, 0, 0); + /* Add a forward declaration for the union tag in the current lexical level */ + UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0); - StructTypeEntry->V.S.ACount = 0; + UnionTagEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -883,7 +894,7 @@ static SymEntry* ParseUnionDecl (const char* Name) /* This is an anonymous struct or union. Copy the fields ** into the current level. */ - AnonFieldName (Decl.Ident, "field", StructTypeEntry->V.S.ACount); + AnonFieldName (Decl.Ident, "field", UnionTagEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); @@ -902,7 +913,7 @@ static SymEntry* ParseUnionDecl (const char* Name) } else { if (IsAnonName (Decl.Ident)) { Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); - Entry->V.A.ANumber = StructTypeEntry->V.S.ACount++; + Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++; AliasAnonStructFields (&Decl, Entry); } else { AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); @@ -929,8 +940,13 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { Error ("Empty union type '%s' is not supported", Name); } + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_UNION | SC_DEF, UnionSize, FieldTab); + return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab); } @@ -944,19 +960,21 @@ static SymEntry* ParseStructDecl (const char* Name) unsigned BitOffs; /* Bit offset for bit-fields */ int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; - SymEntry* StructTypeEntry; + SymEntry* StructTagEntry; SymEntry* Entry; + unsigned Flags = 0; + unsigned PrevErrorCount = ErrorCount; if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration. */ + /* Just a forward declaration */ return ESUForwardDecl (Name, SC_STRUCT); } - /* Add a forward declaration for the struct in the current lexical level */ - StructTypeEntry = AddStructSym (Name, SC_STRUCT, 0, 0); + /* Add a forward declaration for the struct tag in the current lexical level */ + StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0); - StructTypeEntry->V.S.ACount = 0; + StructTagEntry->V.S.ACount = 0; /* Skip the curly brace */ NextToken (); @@ -1050,7 +1068,7 @@ static SymEntry* ParseStructDecl (const char* Name) /* This is an anonymous struct or union. Copy the ** fields into the current level. */ - AnonFieldName (Decl.Ident, "field", StructTypeEntry->V.S.ACount); + AnonFieldName (Decl.Ident, "field", StructTagEntry->V.S.ACount); } else { /* A non bit-field without a name is legal but useless */ Warning ("Declaration does not declare anything"); @@ -1078,7 +1096,7 @@ static SymEntry* ParseStructDecl (const char* Name) } else { if (IsAnonName (Decl.Ident)) { Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); - Entry->V.A.ANumber = StructTypeEntry->V.S.ACount++; + Entry->V.A.ANumber = StructTagEntry->V.S.ACount++; AliasAnonStructFields (&Decl, Entry); } else { AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); @@ -1116,8 +1134,13 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { Error ("Empty struct type '%s' is not supported", Name); } + /* Return a fictitious symbol if errors occurred during parsing */ + if (PrevErrorCount != ErrorCount) { + Flags |= SC_FICTITIOUS; + } + /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_STRUCT | SC_DEF, StructSize, FieldTab); + return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab); } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 316963ee9..53c30254d 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -682,13 +682,21 @@ static void AddSymEntry (SymTable* T, SymEntry* S) -SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab) /* Add an enum entry and return it */ { SymTable* CurTagTab = TagTab; + SymEntry* Entry; + + if ((Flags & SC_FICTITIOUS) == 0) { + /* Do we have an entry with this name already? */ + Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + } else { + /* Add a fictitious symbol in the fail-safe table */ + Entry = 0; + CurTagTab = FailSafeTab; + } - /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); if (Entry) { /* We do have an entry. This may be a forward, so check it. */ @@ -749,8 +757,15 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl /* Type must be struct or union */ PRECONDITION (Type == SC_STRUCT || Type == SC_UNION); - /* Do we have an entry with this name already? */ - Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + if ((Flags & SC_FICTITIOUS) == 0) { + /* Do we have an entry with this name already? */ + Entry = FindSymInTable (CurTagTab, Name, HashStr (Name)); + } else { + /* Add a fictitious symbol in the fail-safe table */ + Entry = 0; + CurTagTab = FailSafeTab; + } + if (Entry) { /* We do have an entry. This may be a forward, so check it. */ diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 79f656f95..f04517fed 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -149,10 +149,10 @@ unsigned short FindSPAdjustment (const char* Name); -SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab); +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab); /* Add an enum entry and return it */ -SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab); +SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab); /* Add a struct/union entry and return it */ SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsigned BitWidth); From 44d52935dafde30886caf03d8c4b1fe1f3832870 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 12 Aug 2020 21:35:44 +0800 Subject: [PATCH 213/806] Utility for getting the composite types of functions. --- src/cc65/typecmp.c | 134 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/typecmp.h | 2 + 2 files changed, 136 insertions(+) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index e14ace6b9..e3e42e67f 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -421,3 +421,137 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs) /* Return the result */ return Result; } + + + +static Type* DoComposite (Type* lhs, const Type* rhs); + +static void CompositeFuncParams (const FuncDesc* F1, const FuncDesc* F2) +/* Composite two function symbol tables regarding function parameters */ +{ + /* Get the symbol tables */ + const SymTable* Tab1 = F1->SymTab; + const SymTable* Tab2 = F2->SymTab; + + /* Composite the parameter lists */ + const SymEntry* Sym1 = Tab1->SymHead; + const SymEntry* Sym2 = Tab2->SymHead; + + /* Composite the fields */ + while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) { + + /* Get the symbol types */ + Type* Type1 = Sym1->Type; + Type* Type2 = Sym2->Type; + + /* If either of both functions is old style, apply the default + ** promotions to the parameter type. + */ + if (F1->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type1)) { + Type1 = IntPromotion (Type1); + } + } + if (F2->Flags & FD_OLDSTYLE) { + if (IsClassInt (Type2)) { + Type2 = IntPromotion (Type2); + } + } + + /* Composite this field */ + DoComposite (Type1, Type2); + + /* Get the pointers to the next fields */ + Sym1 = Sym1->NextSym; + Sym2 = Sym2->NextSym; + } +} + + + +static Type* DoComposite (Type* lhs, const Type* rhs) +/* Recursively composite two types into lhs */ +{ + FuncDesc* F1; + FuncDesc* F2; + long LeftCount, RightCount; + + /* Composite two types */ + while (lhs->C != T_END) { + + /* Check if the end of the type string is reached */ + if (rhs->C == T_END) { + return lhs; + } + + /* Check for sanity */ + CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs)); + + /* Check for special type elements */ + + if (IsTypeFunc (lhs)) { + /* Composite the function descriptors */ + F1 = GetFuncDesc (lhs); + F2 = GetFuncDesc (rhs); + + /* If one of both functions has an empty parameter list (which + ** does also mean, it is not a function definition, because the + ** flag is reset in this case), it is replaced by the other + ** definition, provided that the other has no default + ** promotions in the parameter list. If none of both parameter + ** lists is empty, we have to composite the parameter lists and + ** other attributes. + */ + if ((F1->Flags & FD_EMPTY) == FD_EMPTY) { + if ((F2->Flags & FD_EMPTY) == 0) { + /* Copy the parameters and flags */ + TypeCopy (lhs, rhs); + F1->Flags = F2->Flags; + } + } else if ((F2->Flags & FD_EMPTY) == 0) { + /* Composite the parameter lists */ + CompositeFuncParams (F1, F2); + } + + } else if (IsTypeArray (lhs)) { + /* Check member count */ + LeftCount = GetElementCount (lhs); + RightCount = GetElementCount (rhs); + + /* Set composite type if it is requested */ + if (LeftCount != UNSPECIFIED) { + SetElementCount (lhs, LeftCount); + } else if (RightCount != UNSPECIFIED) { + SetElementCount (lhs, RightCount); + } + } + + /* Next type string element */ + ++lhs; + ++rhs; + } + + return lhs; +} + + + +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType) +/* Refine the existing function descriptor with a new one */ +{ + FuncDesc* Old = GetFuncDesc (OldType); + FuncDesc* New = GetFuncDesc (NewType); + + CHECK (Old != 0 && New != 0); + + if ((New->Flags & FD_EMPTY) == 0) { + if ((Old->Flags & FD_EMPTY) == 0) { + DoComposite (OldType, NewType); + } else { + TypeCopy (OldType, NewType); + Old->Flags &= ~FD_EMPTY; + } + } + + return Old; +} diff --git a/src/cc65/typecmp.h b/src/cc65/typecmp.h index 3e95adb3f..f4a87bcf6 100644 --- a/src/cc65/typecmp.h +++ b/src/cc65/typecmp.h @@ -70,6 +70,8 @@ typedef enum { typecmp_t TypeCmp (const Type* lhs, const Type* rhs); /* Compare two types and return the result */ +FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType); +/* Refine the existing function descriptor with a new one */ /* End of typecmp.h */ From 13ed557b92586765ccc35b43ddc2359521bcca70 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 12 Aug 2020 21:35:47 +0800 Subject: [PATCH 214/806] Fixed compatibility checking of function declarations by using the composite types of them. --- src/cc65/codeinfo.c | 2 +- src/cc65/compile.c | 32 ++++++------ src/cc65/datatype.c | 22 ++++++++ src/cc65/datatype.h | 6 +++ src/cc65/declare.c | 2 +- src/cc65/function.c | 4 +- src/cc65/locals.c | 22 +++++--- src/cc65/pragma.c | 2 +- src/cc65/symentry.h | 2 +- src/cc65/symtab.c | 123 ++++++++++++++++++++++++-------------------- 10 files changed, 129 insertions(+), 88 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index b1e5668ce..c9dea5071 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -392,7 +392,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { - FuncDesc* D = E->V.F.Func; + FuncDesc* D = GetFuncCompositeDesc (E); /* A variadic function will use the Y register (the parameter list ** size is passed there). A fastcall function will use the A or A/X diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 6c881d9a8..54918fb21 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -177,18 +177,23 @@ static void Parse (void) } /* If this is a function declarator that is not followed by a comma - ** or semicolon, it must be followed by a function body. If this is - ** the case, convert an empty parameter list into one accepting no - ** parameters (same as void) as required by the standard. + ** or semicolon, it must be followed by a function body. */ - if ((Decl.StorageClass & SC_FUNC) != 0 && - (CurTok.Tok != TOK_COMMA) && - (CurTok.Tok != TOK_SEMI)) { + if ((Decl.StorageClass & SC_FUNC) != 0) { + if (CurTok.Tok != TOK_COMMA && CurTok.Tok != TOK_SEMI) { + /* A definition */ + Decl.StorageClass |= SC_DEF; - FuncDesc* D = GetFuncDesc (Decl.Type); - - if (D->Flags & FD_EMPTY) { - D->Flags = (D->Flags & ~FD_EMPTY) | FD_VOID_PARAM; + /* Convert an empty parameter list into one accepting no + ** parameters (same as void) as required by the standard. + */ + FuncDesc* D = GetFuncDesc (Decl.Type); + if (D->Flags & FD_EMPTY) { + D->Flags = (D->Flags & ~FD_EMPTY) | FD_VOID_PARAM; + } + } else { + /* Just a declaration */ + Decl.StorageClass |= SC_DECL; } } @@ -309,13 +314,6 @@ SkipOneDecl: /* Prototype only */ NextToken (); } else { - - /* Function body. Check for duplicate function definitions */ - if (SymIsDef (Entry)) { - Error ("Body for function '%s' has already been defined", - Entry->Name); - } - /* Parse the function body */ NewFunc (Entry); } diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 5794bf5a6..94aea9cc1 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1115,6 +1115,28 @@ Type* GetFuncReturn (Type* T) +Type* GetFuncCompositeType (SymEntry* Func) +/* Get the composite function type */ +{ + /* Be sure it's a function type */ + CHECK (IsClassFunc (Func->Type)); + + return Func->V.F.Composite->Type; +} + + + +FuncDesc* GetFuncCompositeDesc (SymEntry* Func) +/* Get the composite function type descriptor */ +{ + /* Be sure it's a function type */ + CHECK (IsClassFunc (Func->Type)); + + return GetFuncDesc (Func->V.F.Composite->Type); +} + + + long GetElementCount (const Type* T) /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 6cbad302f..1ff8333c6 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -753,6 +753,12 @@ void SetFuncDesc (Type* T, FuncDesc* F); Type* GetFuncReturn (Type* T) attribute ((const)); /* Return a pointer to the return type of a function or pointer-to-function type */ +Type* GetFuncCompositeType (struct SymEntry* Func); +/* Get the composite function type */ + +FuncDesc* GetFuncCompositeDesc (struct SymEntry* Func); +/* Get the composite function type descriptor */ + long GetElementCount (const Type* T); /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 259f8b8ac..36286b6d5 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1749,7 +1749,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Was there a previous entry? If so, copy WrappedCall info from it */ PrevEntry = FindGlobalSym (D->Ident); if (PrevEntry && PrevEntry->Flags & SC_FUNC) { - FuncDesc* D = PrevEntry->V.F.Func; + FuncDesc* D = GetFuncDesc (PrevEntry->Type); if (D->WrappedCall && !F->WrappedCall) { F->WrappedCall = D->WrappedCall; F->WrappedCallData = D->WrappedCallData; diff --git a/src/cc65/function.c b/src/cc65/function.c index 5d0b09380..b2291b312 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -295,7 +295,7 @@ static void F_RestoreRegVars (Function* F) } /* Get the first symbol from the function symbol table */ - Sym = F->FuncEntry->V.F.Func->SymTab->SymHead; + Sym = GetFuncDesc (F->FuncEntry->Type)->SymTab->SymHead; /* Walk through all symbols checking for register variables */ while (Sym) { @@ -383,7 +383,7 @@ void NewFunc (SymEntry* Func) const Type* RType; /* Real type used for struct parameters */ /* Get the function descriptor from the function entry */ - FuncDesc* D = Func->V.F.Func; + FuncDesc* D = GetFuncDesc (Func->Type); /* Allocate the function activation record for the function */ CurrentFunc = NewFunction (Func); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 12caa7aa2..a21a09e8e 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -427,8 +427,8 @@ static void ParseOneDecl (const DeclSpec* Spec) ParseDecl (Spec, &Decl, DM_NEED_IDENT); /* Check if there are any non-extern storage classes set for function - ** declarations. The only valid storage class for function declarations - ** inside functions is 'extern'. + ** declarations. Function can only be declared inside functions with the + ** 'extern' storage class specifier or no storage class specifier at all. */ if ((Decl.StorageClass & SC_FUNC) == SC_FUNC) { @@ -439,12 +439,11 @@ static void ParseOneDecl (const DeclSpec* Spec) Error ("Illegal storage class on function"); } - /* The default storage class could be wrong. Just use 'extern' in all - ** cases. - */ + /* The default storage class could be wrong. Just clear them */ Decl.StorageClass &= ~SC_STORAGEMASK; - Decl.StorageClass |= SC_EXTERN; + /* This is always a declaration */ + Decl.StorageClass |= SC_DECL; } /* If we don't have a name, this was flagged as an error earlier. @@ -456,6 +455,7 @@ static void ParseOneDecl (const DeclSpec* Spec) /* If the symbol is not marked as external, it will be defined now */ if ((Decl.StorageClass & SC_FICTITIOUS) == 0 && + (Decl.StorageClass & SC_DECL) == 0 && (Decl.StorageClass & SC_EXTERN) == 0) { Decl.StorageClass |= SC_DEF; } @@ -500,8 +500,14 @@ static void ParseOneDecl (const DeclSpec* Spec) } } - /* Add the symbol to the symbol table */ - AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + if ((Decl.StorageClass & SC_EXTERN) == SC_EXTERN || + (Decl.StorageClass & SC_FUNC) == SC_FUNC) { + /* Add the global symbol to the local symbol table */ + AddGlobalSym (Decl.Ident, Decl.Type, Decl.StorageClass); + } else { + /* Add the local symbol to the local symbol table */ + AddLocalSym (Decl.Ident, Decl.Type, Decl.StorageClass, 0); + } } } diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 93c83906f..a0876a550 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -527,7 +527,7 @@ static void WrappedCallPragma (StrBuf* B) PushWrappedCall(Entry, (unsigned char) Val); Entry->Flags |= SC_REF; - Entry->V.F.Func->Flags |= FD_CALL_WRAPPER; + GetFuncCompositeDesc (Entry)->Flags |= FD_CALL_WRAPPER; } else { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index cfc6379d3..6b05dd72b 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -191,7 +191,7 @@ struct SymEntry { /* Data for functions */ struct { - struct FuncDesc* Func; /* Function descriptor */ + SymEntry* Composite;/* Entry to hold composite function type */ struct Segments* Seg; /* Segments for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ } F; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 53c30254d..10ec88d79 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -45,6 +45,7 @@ #include "xmalloc.h" /* cc65 */ +#include "anonname.h" #include "asmcode.h" #include "asmlabel.h" #include "codegen.h" @@ -558,9 +559,10 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags unsigned E_SCType = Entry->Flags & SC_TYPEMASK; unsigned SCType = Flags & SC_TYPEMASK; - /* Existing typedefs cannot be redeclared as anything different */ + /* Some symbols may be redeclared if certain requirements are met */ if (E_SCType == SC_TYPEDEF) { + /* Existing typedefs cannot be redeclared as anything different */ if (SCType == SC_TYPEDEF) { if (TypeCmp (E_Type, T) < TC_IDENTICAL) { Error ("Conflicting types for typedef '%s'", Entry->Name); @@ -571,9 +573,42 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Entry = 0; } + } else if ((Entry->Flags & SC_FUNC) == SC_FUNC) { + + /* In case of a function, use the new type descriptor, since it + ** contains pointers to the new symbol tables that are needed if + ** an actual function definition follows. Be sure not to use the + ** new descriptor if it contains a function declaration with an + ** empty parameter list. + */ + if (IsTypeFunc (T)) { + + /* Check for duplicate function definitions */ + if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { + Error ("Body for function '%s' has already been defined", + Entry->Name); + Entry = 0; + } else { + /* New type must be compatible with the composite prototype */ + if (TypeCmp (GetFuncCompositeType (Entry), T) < TC_EQUAL) { + Error ("Conflicting function types for '%s'", Entry->Name); + Entry = 0; + } else { + /* Refine the existing composite prototype with this new + ** one. + */ + RefineFuncDesc (GetFuncCompositeType (Entry), T); + } + } + + } else { + Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); + Entry = 0; + } + } else { - /* Some symbols may be redeclared if certain requirements are met */ + /* Redeclarations of ESU types are checked elsewhere */ if (IsTypeArray (T) && IsTypeArray (E_Type)) { /* Get the array sizes */ @@ -597,37 +632,6 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags } } - } else if ((Entry->Flags & SC_FUNC) == SC_FUNC) { - - /* In case of a function, use the new type descriptor, since it - ** contains pointers to the new symbol tables that are needed if - ** an actual function definition follows. Be sure not to use the - ** new descriptor if it contains a function declaration with an - ** empty parameter list. - */ - if (IsTypeFunc (T)) { - - /* New type must be equivalent */ - if (TypeCmp (E_Type, T) < TC_EQUAL) { - Error ("Conflicting function types for '%s'", Entry->Name); - Entry = 0; - } else { - /* Get the function descriptor from the new type */ - FuncDesc* F = GetFuncDesc (T); - /* Use this new function descriptor if it doesn't contain - ** an empty parameter list. - */ - if ((F->Flags & FD_EMPTY) == 0) { - Entry->V.F.Func = F; - SetFuncDesc (E_Type, F); - } - } - - } else { - Error ("Redefinition of function '%s' as different kind of symbol", Entry->Name); - Entry = 0; - } - } else { /* New type must be equivalent */ @@ -1067,7 +1071,8 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs } else if ((Flags & SC_REGISTER) == SC_REGISTER) { Entry->V.R.RegOffs = Offs; Entry->V.R.SaveOffs = StackPtr; - } else if ((Flags & SC_EXTERN) == SC_EXTERN) { + } else if ((Flags & SC_EXTERN) == SC_EXTERN || + (Flags & SC_FUNC) == SC_FUNC) { Entry->V.L.Label = Offs; SymSetAsmName (Entry); } else if ((Flags & SC_STATIC) == SC_STATIC) { @@ -1080,7 +1085,6 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Add the entry to the symbol table */ AddSymEntry (Tab, Entry); - } /* Return the entry */ @@ -1092,16 +1096,12 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Add an external or global symbol to the symbol table and return the entry */ { - /* There is some special handling for functions, so check if it is one */ - int IsFunc = IsTypeFunc (T); - - /* Functions must be inserted in the global symbol table */ - SymTable* Tab = IsFunc ? SymTab0 : SymTab; + /* Use the global symbol table */ + SymTable* Tab = SymTab; /* Do we have an entry with this name already? */ - SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); + SymEntry* Entry = FindSymInTree (Tab, Name); if (Entry) { - /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { /* Use the fail-safe table for fictitious symbols */ @@ -1111,31 +1111,34 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } else { /* If a static declaration follows a non-static declaration, then - ** warn about the conflict. (It will compile a public declaration.) + ** warn about the conflict. It will compile an extern declaration. */ if ((Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { Warning ("static declaration follows non-static declaration of '%s'.", Name); } - /* An extern declaration must not change the current linkage. */ - if (IsFunc || (Flags & (SC_EXTERN | SC_STORAGE)) == SC_EXTERN) { - Flags &= ~SC_EXTERN; - } - - /* If a public declaration follows a static declaration, then - ** warn about the conflict. (It will compile a public declaration.) + /* If an extern declaration follows a static declaration, then + ** warn about the conflict. It will compile an extern declaration. */ if ((Flags & SC_EXTERN) != 0 && (Entry->Flags & SC_EXTERN) == 0) { - Warning ("public declaration follows static declaration of '%s'.", Name); + Warning ("extern declaration follows static declaration of '%s'.", Name); + } + + /* Update existing function type if this is a definition */ + if (IsTypeFunc (Entry->Type) && + !SymIsDef (Entry) && + (Flags & SC_DEF) == SC_DEF) { + TypeFree (Entry->Type); + Entry->Type = TypeDup (T); } /* Add the new flags */ - Entry->Flags |= Flags; + Entry->Flags |= Flags & ~SC_EXTERN; } } - if (Entry == 0) { + if (Entry == 0 || Entry->Owner != Tab) { /* Create a new entry */ Entry = NewSymEntry (Name, Flags); @@ -1143,12 +1146,18 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Set the symbol attributes */ Entry->Type = TypeDup (T); - /* If this is a function, set the function descriptor and clear + /* If this is a function, set the function composite typeand clear ** additional fields. */ - if (IsFunc) { - Entry->V.F.Func = GetFuncDesc (Entry->Type); - Entry->V.F.Seg = 0; + if (IsTypeFunc (T)) { + /* GitHub #1167 - Make a composite prototype */ + ident Ident; + AnonName (Ident, "prototype"); + Entry->V.F.Composite = NewSymEntry (Ident, SC_EXTERN | SC_DECL | SC_ALIAS | SC_FUNC); + Entry->V.F.Composite->Type = TypeDup (T); + AddSymEntry (SymTab0, Entry->V.F.Composite); + + Entry->V.F.Seg = 0; } /* Add the assembler name of the symbol */ From b19bb14348b2394bbd504c5c987c475bfb35de3a Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 12 Aug 2020 21:35:50 +0800 Subject: [PATCH 215/806] Fixed checking conflitcing declarations with external vs other linkage. --- src/cc65/symtab.c | 86 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 10ec88d79..443d0a21c 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1045,6 +1045,22 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs if (SymIsDef (Entry) && (Flags & SC_DEF) == SC_DEF) { Error ("Multiple definition of '%s'", Entry->Name); Entry = 0; + } else if ((Flags & (SC_AUTO | SC_REGISTER)) != 0 && + (Entry->Flags & SC_EXTERN) != 0) { + /* Check for local storage class conflict */ + Error ("Declaration of '%s' with no linkage follows extern declaration", + Name); + Entry = 0; + } else { + /* If a static declaration follows a non-static declaration, + ** then it is an error. + */ + if ((Flags & SC_DEF) && + (Flags & SC_EXTERN) == 0 && + (Entry->Flags & SC_EXTERN) != 0) { + Error ("Static declaration of '%s' follows extern declaration", Name); + Entry = 0; + } } } @@ -1104,38 +1120,62 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) if (Entry) { /* We have a symbol with this name already */ if (HandleSymRedefinition (Entry, T, Flags)) { - /* Use the fail-safe table for fictitious symbols */ - Tab = FailSafeTab; Entry = 0; - - } else { - + } else if ((Entry->Flags & (SC_AUTO | SC_REGISTER)) != 0) { + /* Check for local storage class conflict */ + Error ("Extern declaration of '%s' follows declaration with no linkage", + Name); + Entry = 0; + } else if ((Flags & SC_ESUTYPEMASK) != SC_TYPEDEF) { /* If a static declaration follows a non-static declaration, then - ** warn about the conflict. It will compile an extern declaration. + ** diagnose the conflict. It will warn and compile an extern + ** declaration if both declarations are global, otherwise give an + ** error. */ - if ((Flags & SC_EXTERN) == 0 && (Entry->Flags & SC_EXTERN) != 0) { - Warning ("static declaration follows non-static declaration of '%s'.", Name); + if (Tab == SymTab0 && + (Flags & SC_EXTERN) == 0 && + (Entry->Flags & SC_EXTERN) != 0) { + Warning ("Static declaration of '%s' follows non-static declaration", Name); + } else if ((Flags & SC_EXTERN) != 0 && + (Entry->Owner == SymTab0 || (Entry->Flags & SC_DEF) != 0) && + (Entry->Flags & SC_EXTERN) == 0) { + /* It is OK if a global extern declaration follows a global + ** non-static declaration, but an error if either of them is + ** local, as the two would be referring to different objects. + ** It is an error as well if a global non-static declaration + ** follows a global static declaration. + */ + if (Entry->Owner == SymTab0) { + if ((Flags & SC_STORAGE) == 0) { + /* Linkage must be unchanged */ + Flags &= ~SC_EXTERN; + } else { + Error ("Non-static declaration of '%s' follows static declaration", Name); + } + } else { + Error ("Extern declaration of '%s' follows static declaration", Name); + Entry = 0; + } } - /* If an extern declaration follows a static declaration, then - ** warn about the conflict. It will compile an extern declaration. - */ - if ((Flags & SC_EXTERN) != 0 && (Entry->Flags & SC_EXTERN) == 0) { - Warning ("extern declaration follows static declaration of '%s'.", Name); - } + if (Entry) { + /* Update existing function type if this is a definition */ + if (IsTypeFunc (Entry->Type) && + !SymIsDef (Entry) && + (Flags & SC_DEF) == SC_DEF) { + TypeFree (Entry->Type); + Entry->Type = TypeDup (T); + } - /* Update existing function type if this is a definition */ - if (IsTypeFunc (Entry->Type) && - !SymIsDef (Entry) && - (Flags & SC_DEF) == SC_DEF) { - TypeFree (Entry->Type); - Entry->Type = TypeDup (T); + /* Add the new flags */ + Entry->Flags |= Flags; } - - /* Add the new flags */ - Entry->Flags |= Flags & ~SC_EXTERN; } + if (Entry == 0) { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; + } } if (Entry == 0 || Entry->Owner != Tab) { From 1d28e8e3deff8d69c4e9b8ab09b5fbe0bf726a72 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 13 Aug 2020 00:00:32 +0800 Subject: [PATCH 216/806] Improved test case for Issue #191. --- test/val/static-1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/val/static-1.c b/test/val/static-1.c index ae2ba6289..6918e0033 100644 --- a/test/val/static-1.c +++ b/test/val/static-1.c @@ -13,6 +13,7 @@ static int n = 0; extern int n; /* should not give an error */ +static int n; /* should not give an error */ int main(void) { From f0e4053a0d5a5a3749b4043283f912801d3914f8 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 14 Aug 2020 19:58:58 +0200 Subject: [PATCH 217/806] added test related to issue #1178 --- test/val/bug1178.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/val/bug1178.c diff --git a/test/val/bug1178.c b/test/val/bug1178.c new file mode 100644 index 000000000..043767e4c --- /dev/null +++ b/test/val/bug1178.c @@ -0,0 +1,81 @@ + +/* bug #1178 - copying structs/unions > 4 bytes is broken */ + +#include +#include + +typedef struct +{ + unsigned char a; + unsigned char b; + unsigned char c; + unsigned char d; + unsigned char e; +} +TestStruct1; + +TestStruct1 StructArray1[2]; +TestStruct1 test1; + +typedef struct +{ + unsigned char a; + unsigned char b; + unsigned char c; + unsigned char d; +} +TestStruct2; + +TestStruct2 StructArray2[2]; +TestStruct2 test2; + +int res = EXIT_SUCCESS; + +void dotest1(void) +{ + test1.a = 42; + test1.b = 11; + test1.c = 23; + test1.d = 47; + test1.e = 13; + + StructArray1[0] = test1; + + printf ("test1: %d, %d, %d, %d, %d\n", + (int)StructArray1[0].a, (int)StructArray1[0].b, (int)StructArray1[0].c, + (int)StructArray1[0].d, (int)StructArray1[0].e); + if ((StructArray1[0].a != 42) || + (StructArray1[0].b != 11) || + (StructArray1[0].c != 23) || + (StructArray1[0].d != 47) || + (StructArray1[0].e != 13)) { + res = EXIT_FAILURE; + } +} + +void dotest2(void) +{ + test2.a = 42; + test2.b = 11; + test2.c = 23; + test2.d = 47; + + StructArray2[0] = test2; + + printf ("test2: %d, %d, %d, %d, %d\n", + (int)StructArray2[0].a, (int)StructArray2[0].b, + (int)StructArray2[0].c, (int)StructArray2[0].d); + if ((StructArray2[0].a != 42) || + (StructArray2[0].b != 11) || + (StructArray2[0].c != 23) || + (StructArray2[0].d != 47)) { + res = EXIT_FAILURE; + } +} + +int main(void) +{ + dotest1(); + dotest2(); + return res; +} From 8197e3c7cdeb373ae69ce80f368eb76ada6d6102 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 14 Aug 2020 21:07:39 +0200 Subject: [PATCH 218/806] make it actually compile again :) --- testcode/lib/strdup-test.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/testcode/lib/strdup-test.c b/testcode/lib/strdup-test.c index 5514471f9..e30841c3e 100644 --- a/testcode/lib/strdup-test.c +++ b/testcode/lib/strdup-test.c @@ -3,34 +3,25 @@ #include #include - -/* From _heap.h */ -extern unsigned _horg; /* Bottom of heap */ -extern unsigned _hptr; /* Current top */ -extern unsigned _hend; /* Upper limit */ -extern unsigned _hfirst; /* First free block in list */ -extern unsigned _hlast; /* Last free block in list */ - +#include <_heap.h> static unsigned char* V[256]; - - static void ShowInfo (void) /* Show heap info */ { /* Count free blocks */ unsigned Count = 0; - unsigned** P = (unsigned**) _hfirst; + unsigned** P = (unsigned**) _heapfirst; while (P) { ++Count; P = P[1]; } printf ("%04X %04X %04X %04X %04X %u\n", - _horg, _hptr, _hend, _hfirst, _hlast, Count); + _heaporg, _heapptr, _heapend, _heapfirst, _heaplast, Count); if (Count) { - P = (unsigned**) _hfirst; + P = (unsigned**) _heapfirst; while (P) { printf ("%04X %04X %04X %04X(%u)\n", (unsigned) P, P[2], P[1], P[0], P[0]); From c4698dfd07383e4ffa50d6c1fa5d158052b167a9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 14 Aug 2020 14:54:10 +0200 Subject: [PATCH 219/806] Use C89 semantics for integer conversions Previously, the following rules were used for binary operators: * If one of the values is a long, the result is long. * If one of the values is unsigned, the result is also unsigned. * Otherwise the result is an int. C89 specifies the "usual arithmetic conversions" as: * The integral promotions are performed on both operands. * Then the following rules are applied: * If either operand has type unsigned long int, the other operand is converted to unsigned long int. * Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. * Otherwise, if either operand has type long int, the other operand is converted to long int. * Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. * Otherwise, both operands have type int. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 As one example, these rules give a different result for an operator with one long operand and one unsigned int operand. Previously, the result type was unsigned long. With C89 semantics, it is just long, since long can represent all unsigned ints. Integral promotions convert types shorter than int to int (or unsigned int). Both char and unsigned char are promoted to int since int can represent all unsigned chars. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 Rename promoteint to ArithmeticConvert, since this is more accurate. Fixes #170 --- src/cc65/codegen.c | 111 ++++++++++++++++++++++++++++-------- src/cc65/datatype.c | 18 +++++- src/cc65/datatype.h | 10 ++++ src/cc65/expr.c | 78 ++++++++++++++++++------- test/{todo => val}/bug170.c | 0 5 files changed, 170 insertions(+), 47 deletions(-) rename test/{todo => val}/bug170.c (100%) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 8a879745d..2c6624a5c 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1327,26 +1327,52 @@ void g_reglong (unsigned Flags) +static unsigned g_intpromotion (unsigned flags) +/* Return new flags for integral promotions for types smaller than int. */ +{ + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 + ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or an + ** object that has enumeration type, may be used in an expression wherever an int or + ** unsigned int may be used. If an int can represent all values of the original type, the value + ** is converted to an int; otherwise it is converted to an unsigned int. + ** These are called the integral promotions. + */ + + if ((flags & CF_TYPEMASK) == CF_CHAR) { + /* int can represent all unsigned chars, so unsigned char is promoted to int. */ + flags &= ~CF_TYPEMASK; + flags &= ~CF_UNSIGNED; + flags |= CF_INT; + return flags; + } else if ((flags & CF_TYPEMASK) == CF_SHORT) { + /* int cannot represent all unsigned shorts, so unsigned short is promoted to + ** unsigned int. + */ + flags &= ~CF_TYPEMASK; + flags |= CF_INT; + return flags; + } else { + /* Otherwise, the type is not smaller than int, so leave it alone. */ + return flags; + } +} + + + unsigned g_typeadjust (unsigned lhs, unsigned rhs) /* Adjust the integer operands before doing a binary operation. lhs is a flags ** value, that corresponds to the value on TOS, rhs corresponds to the value ** in (e)ax. The return value is the the flags value for the resulting type. */ { - unsigned ltype, rtype; - unsigned result; - /* Get the type spec from the flags */ - ltype = lhs & CF_TYPEMASK; - rtype = rhs & CF_TYPEMASK; + unsigned ltype = lhs & CF_TYPEMASK; + unsigned rtype = rhs & CF_TYPEMASK; /* Check if a conversion is needed */ if (ltype == CF_LONG && rtype != CF_LONG && (rhs & CF_CONST) == 0) { /* We must promote the primary register to long */ g_reglong (rhs); - /* Get the new rhs type */ - rhs = (rhs & ~CF_TYPEMASK) | CF_LONG; - rtype = CF_LONG; } else if (ltype != CF_LONG && (lhs & CF_CONST) == 0 && rtype == CF_LONG) { /* We must promote the lhs to long */ if (lhs & CF_PRIMARY) { @@ -1354,25 +1380,64 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) } else { g_toslong (lhs); } - /* Get the new rhs type */ - lhs = (lhs & ~CF_TYPEMASK) | CF_LONG; - ltype = CF_LONG; } - /* Determine the result type for the operation: - ** - The result is const if both operands are const. - ** - The result is unsigned if one of the operands is unsigned. - ** - The result is long if one of the operands is long. - ** - Otherwise the result is int sized. + /* Result is const if both operands are const. */ + unsigned const_flag = (lhs & CF_CONST) & (rhs & CF_CONST); + + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 + ** Many binary operators that expect operands of arithmetic type cause conversions and yield + ** result types in a similar way. The purpose is to yield a common type, which is also the type + ** of the result. This pattern is called the usual arithmetic conversions. */ - result = (lhs & CF_CONST) & (rhs & CF_CONST); - result |= (lhs & CF_UNSIGNED) | (rhs & CF_UNSIGNED); - if (rtype == CF_LONG || ltype == CF_LONG) { - result |= CF_LONG; - } else { - result |= CF_INT; + + /* Note that this logic is largely duplicated by ArithmeticConvert. */ + + /* Apply integral promotions for types char/short. */ + lhs = g_intpromotion (lhs); + rhs = g_intpromotion (rhs); + ltype = lhs & CF_TYPEMASK; + rtype = rhs & CF_TYPEMASK; + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((ltype == CF_LONG && (lhs & CF_UNSIGNED)) || + (rtype == CF_LONG && (rhs & CF_UNSIGNED))) { + return const_flag | CF_UNSIGNED | CF_LONG; } - return result; + + /* Otherwise, if one operand has type long int and the other has type unsigned int, + ** if a long int can represent all values of an unsigned int, the operand of type unsigned int + ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, + ** both operands are converted to unsigned long int. + */ + if ((ltype == CF_LONG && rtype == CF_INT && (rhs & CF_UNSIGNED)) || + (rtype == CF_LONG && ltype == CF_INT && (rhs & CF_UNSIGNED))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return const_flag | CF_LONG; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. + */ + if (ltype == CF_LONG || rtype == CF_LONG) { + return const_flag | CF_LONG; + } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((ltype == CF_INT && (lhs & CF_UNSIGNED)) || + (rtype == CF_INT && (rhs & CF_UNSIGNED))) { + return const_flag | CF_UNSIGNED | CF_INT; + } + + /* Otherwise, both operands have type int. */ + CHECK (ltype == CF_INT); + CHECK (!(lhs & CF_UNSIGNED)); + CHECK (rtype == CF_INT); + CHECK (!(rhs & CF_UNSIGNED)); + return const_flag | CF_INT; } diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 94aea9cc1..58b673fab 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1214,12 +1214,26 @@ Type* IntPromotion (Type* T) /* We must have an int to apply int promotions */ PRECONDITION (IsClassInt (T)); - /* An integer can represent all values from either signed or unsigned char, - ** so convert chars to int and leave all other types alone. + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 + ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or an + ** object that has enumeration type, may be used in an expression wherever an int or + ** unsigned int may be used. If an int can represent all values of the original type, the value + ** is converted to an int; otherwise it is converted to an unsigned int. + ** These are called the integral promotions. */ + if (IsTypeChar (T)) { + /* An integer can represent all values from either signed or unsigned char, so convert + ** chars to int. + */ return type_int; + } else if (IsTypeShort (T)) { + /* An integer cannot represent all values from unsigned short, so convert unsigned short + ** to unsigned int. + */ + return IsSignUnsigned (T) ? type_uint : type_int; } else { + /* Otherwise, the type is not smaller than int, so leave it alone. */ return T; } } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 1ff8333c6..fbd52e761 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -373,6 +373,16 @@ INLINE int IsTypeChar (const Type* T) # define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeShort (const Type* T) +/* Return true if this is a short type (signed or unsigned) */ +{ + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT); +} +#else +# define IsTypeShort(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_SHORT) +#endif + #if defined(HAVE_INLINE) INLINE int IsTypeInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index f75015305..efed934f8 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -140,27 +140,61 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) -static Type* promoteint (Type* lhst, Type* rhst) -/* In an expression with two ints, return the type of the result */ +static Type* ArithmeticConvert (Type* lhst, Type* rhst) +/* Perform the usual arithmetic conversions for binary operators. */ { - /* Rules for integer types: - ** - If one of the values is a long, the result is long. - ** - If one of the values is unsigned, the result is also unsigned. - ** - Otherwise the result is an int. + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 + ** Many binary operators that expect operands of arithmetic type cause conversions and yield + ** result types in a similar way. The purpose is to yield a common type, which is also the type + ** of the result. This pattern is called the usual arithmetic conversions. + */ + + /* There are additional rules for floating point types that we don't bother with, since + ** floating point types are not (yet) supported. + ** The integral promotions are performed on both operands. + */ + lhst = IntPromotion (lhst); + rhst = IntPromotion (rhst); + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || + (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { + return type_ulong; + } + + /* Otherwise, if one operand has type long int and the other has type unsigned int, + ** if a long int can represent all values of an unsigned int, the operand of type unsigned int + ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, + ** both operands are converted to unsigned long int. + */ + if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || + (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return type_long; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. */ if (IsTypeLong (lhst) || IsTypeLong (rhst)) { - if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) { - return type_ulong; - } else { - return type_long; - } - } else { - if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) { - return type_uint; - } else { - return type_int; - } + return type_long; } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || + (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { + return type_uint; + } + + /* Otherwise, both operands have type int. */ + CHECK (IsTypeInt (lhst)); + CHECK (IsSignSigned (lhst)); + CHECK (IsTypeInt (rhst)); + CHECK (IsSignSigned (rhst)); + return type_int; } @@ -198,7 +232,7 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) flags = g_typeadjust (ltype, rtype); /* Set the type of the result */ - lhs->Type = promoteint (lhst, rhst); + lhs->Type = ArithmeticConvert (lhst, rhst); /* Return the code generator flags */ return flags; @@ -2066,7 +2100,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ RemoveCode (&Mark1); /* Get the type of the result */ - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Handle the op differently for signed and unsigned types */ if (IsSignSigned (Expr->Type)) { @@ -2163,7 +2197,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Determine the type of the operation result. */ type |= g_typeadjust (ltype, rtype); - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Generate code */ Gen->Func (type, Expr->IVal); @@ -2196,7 +2230,7 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ /* Determine the type of the operation result. */ type |= g_typeadjust (ltype, rtype); - Expr->Type = promoteint (Expr->Type, Expr2.Type); + Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type); /* Generate code */ Gen->Func (type, Expr2.IVal); @@ -3340,7 +3374,7 @@ static void hieQuest (ExprDesc* Expr) /* Get common type */ - ResultType = promoteint (Expr2.Type, Expr3.Type); + ResultType = ArithmeticConvert (Expr2.Type, Expr3.Type); /* Convert the third expression to this type if needed */ TypeConversion (&Expr3, ResultType); diff --git a/test/todo/bug170.c b/test/val/bug170.c similarity index 100% rename from test/todo/bug170.c rename to test/val/bug170.c From 1cf9404c19d504fa7230f6fbb14bfa8ee8204282 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 15 Aug 2020 21:05:51 +0200 Subject: [PATCH 220/806] Support C2X _Static_assert(expr) syntax This makes the message in _Static_assert(expr, message) optional. Fixes #1188. --- src/cc65/staticassert.c | 49 ++++++++++++++++++++++++----------------- test/val/staticassert.c | 2 ++ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 95a2d4a6e..68f0a41bc 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -49,6 +49,7 @@ void ParseStaticAssert () { /* ** static_assert-declaration ::= + ** _Static_assert ( constant-expression ) ; ** _Static_assert ( constant-expression , string-literal ) ; */ ExprDesc Expr; @@ -67,27 +68,35 @@ void ParseStaticAssert () ConstAbsIntExpr (hie1, &Expr); failed = !Expr.IVal; - /* We expect a comma */ - if (!ConsumeComma ()) { - return; - } - - /* String literal */ - if (CurTok.Tok != TOK_SCONST) { - Error ("String literal expected for static_assert message"); - return; - } - - /* Issue an error including the message if the static_assert failed. */ - if (failed) { - Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); - } - - /* Consume the string constant, now that we don't need it anymore. - ** This should never fail since we checked the token type above. + /* If there is a comma, we also have an error message. The message is optional because we + ** support the C2X syntax with only an expression. */ - if (!Consume (TOK_SCONST, "String literal expected")) { - return; + if (CurTok.Tok == TOK_COMMA) { + /* Skip the comma. */ + NextToken (); + + /* String literal */ + if (CurTok.Tok != TOK_SCONST) { + Error ("String literal expected for static_assert message"); + return; + } + + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } + + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + if (!Consume (TOK_SCONST, "String literal expected")) { + return; + } + } else { + /* No message. */ + if (failed) { + Error ("static_assert failed"); + } } /* Closing paren and semi-colon needed */ diff --git a/test/val/staticassert.c b/test/val/staticassert.c index a02ba27e8..5d44fed6b 100644 --- a/test/val/staticassert.c +++ b/test/val/staticassert.c @@ -27,6 +27,7 @@ #include _Static_assert (1, "1 should be true."); +_Static_assert (1); /* Support C2x syntax with no message. */ _Static_assert (!0, "!0 should be true."); _Static_assert (1 == 1, "1 == 1 should be true."); _Static_assert (1 == 1L, "1 == 1L should be true."); @@ -46,6 +47,7 @@ _Static_assert (k == 1, "k should be 1."); /* Just test the macro version once. */ static_assert (1, "1 should be true."); +static_assert (1); /* _Static_assert can appear anywhere a declaration can. */ void f (void) From 7e61b11f23abe1a2b0f5e826c0832a291895df4a Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 15 Aug 2020 21:22:31 +0200 Subject: [PATCH 221/806] Add _Static_assert docs to cc65 extensions Fixes #1149. --- doc/cc65.sgml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index dba0a0288..a81358510 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -792,6 +792,21 @@ This cc65 version has some extensions to the ISO C standard. size zero, even if it is initialized.

+ cc65 supports + /* C11 version with message. */ + _Static_assert (sizeof (int) == 2, "Expected 2-bytes ints."); + + /* C2X version without message. */ + _Static_assert (sizeof (int) == 2); + + + Computed gotos, a GCC extension, has limited support. With it you can use fast jump tables from C. You can take the address of a label with a double ampersand, putting them in a static const array of type void *. From 11cd3e5cbd29ddfd796452a4d30a116adcedce47 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 12 Aug 2020 21:50:34 +0800 Subject: [PATCH 222/806] Utility for checking general datatype categories, incomplete ESU types and arrays of unknown sizes. --- src/cc65/datatype.c | 100 +++++++++++++++++++++++++++++++++++++++++++- src/cc65/datatype.h | 35 +++++++++++++++- 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 58b673fab..9b30eba70 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1037,18 +1037,114 @@ Type* ArrayToPtr (Type* T) +int IsClassObject (const Type* T) +/* Return true if this is a fully described object type */ +{ + return !IsTypeFunc (T) && !IsClassIncomplete (T); +} + + + +int IsClassIncomplete (const Type* T) +/* Return true if this is an object type lacking size info */ +{ + if (IsTypeArray (T)) { + return GetElementCount (T) == UNSPECIFIED; + } + return IsTypeVoid (T) || IsIncompleteESUType (T); +} + + + int IsClassArithmetic (const Type* T) -/* Return true if this is an arithmetic type */ +/* Return true if this is an integer or real floating type */ { return IsClassInt (T) || IsClassFloat (T); } +int IsClassBasic (const Type* T) +/* Return true if this is a char, integer or floating type */ +{ + return IsRawTypeChar (T) || IsClassInt (T) || IsClassFloat (T); +} + + + +int IsClassScalar (const Type* T) +/* Return true if this is an arithmetic or pointer type */ +{ + return IsClassArithmetic (T) || IsTypePtr (T); +} + + + +int IsClassDerived (const Type* T) +/* Return true if this is an array, struct, union, function or pointer type */ +{ + return IsTypeArray (T) || IsClassStruct (T) || IsClassFunc (T) || IsTypePtr (T); +} + + + +int IsClassAggregate (const Type* T) +/* Return true if this is an array or struct type */ +{ + return IsTypeArray (T) || IsTypeStruct (T); +} + + + +int IsRelationType (const Type* T) +/* Return true if this is an arithmetic, array or pointer type */ +{ + return IsClassArithmetic (T) || IsClassPtr (T); +} + + + int IsCastType (const Type* T) /* Return true if this type can be used for casting */ { - return IsClassArithmetic (T) || IsClassPtr (T) || IsTypeVoid (T); + return IsClassScalar (T) || IsTypeVoid (T); +} + + + +int IsESUType (const Type* T) +/* Return true if this is an enum/struct/union type */ +{ + return IsClassStruct (T) || IsTypeEnum (T); +} + + + +int IsIncompleteESUType (const Type* T) +/* Return true if this is an incomplete ESU type */ +{ + SymEntry* Sym = GetSymType (T); + + return Sym != 0 && !SymIsDef (Sym); +} + + + +int IsEmptiableObjectType (const Type* T) +/* Return true if this is a struct/union/void type that can have zero size */ +{ + return IsClassStruct (T) || IsTypeVoid (T); +} + + + +int HasUnknownSize (const Type* T) +/* Return true if this is an incomplete ESU type or an array of unknown size */ +{ + if (IsTypeArray (T)) { + return GetElementCount (T) == UNSPECIFIED; + } + return IsIncompleteESUType (T); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index fbd52e761..7aa1d77be 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -593,12 +593,45 @@ INLINE int IsClassFunc (const Type* T) # define IsClassFunc(T) (GetClass (T) == T_CLASS_FUNC) #endif +int IsClassObject (const Type* T); +/* Return true if this is a fully described object type */ + +int IsClassIncomplete (const Type* T); +/* Return true if this is an object type lacking size info */ + int IsClassArithmetic (const Type* T); -/* Return true if this is an arithmetic type */ +/* Return true if this is an integer or real floating type */ + +int IsClassBasic (const Type* T); +/* Return true if this is a char, integer or floating type */ + +int IsClassScalar (const Type* T); +/* Return true if this is an arithmetic or pointer type */ + +int IsClassDerived (const Type* T); +/* Return true if this is an array, struct, union, function or pointer type */ + +int IsClassAggregate (const Type* T); +/* Return true if this is an array or struct type */ + +int IsRelationType (const Type* T); +/* Return true if this is an arithmetic, array or pointer type */ int IsCastType (const Type* T); /* Return true if this type can be used for casting */ +int IsESUType (const Type* T); +/* Return true if this is an enum/struct/union type */ + +int IsIncompleteESUType (const Type* T); +/* Return true if this is an incomplete ESU type */ + +int IsEmptiableObjectType (const Type* T); +/* Return true if this is a struct/union/void type that can have zero size */ + +int HasUnknownSize (const Type* T); +/* Return true if this is an incomplete ESU type or an array of unknown size */ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawSignedness (const Type* T) /* Get the raw signedness of a type */ From 15f28c3a8cbf95a6bb9abb908b17de90576c7a49 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 13 Aug 2020 08:24:40 +0800 Subject: [PATCH 223/806] Fixed getting the basic raw type names. --- src/cc65/datatype.c | 4 ++-- src/cc65/datatype.h | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9b30eba70..20c4abc56 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -235,7 +235,7 @@ const char* GetBasicTypeName (const Type* T) default: break; } if (IsClassInt (T)) { - if (IsSignSigned (T)) { + if (IsRawSignSigned (T)) { switch (GetRawType (T)) { case T_TYPE_CHAR: return "signed char"; case T_TYPE_SHORT: return "short"; @@ -245,7 +245,7 @@ const char* GetBasicTypeName (const Type* T) default: return "signed integer"; } - } else if (IsSignUnsigned (T)) { + } else if (IsRawSignUnsigned (T)) { switch (GetRawType (T)) { case T_TYPE_CHAR: return "unsigned char"; case T_TYPE_SHORT: return "unsigned short"; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 7aa1d77be..660681909 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -652,6 +652,16 @@ INLINE TypeCode GetSignedness (const Type* T) # define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) #endif +#if defined(HAVE_INLINE) +INLINE int IsRawSignUnsigned (const Type* T) +/* Return true if this is an unsigned raw type */ +{ + return (GetRawSignedness (T) == T_SIGN_UNSIGNED); +} +#else +# define IsRawSignUnsigned(T) (GetRawSignedness (T) == T_SIGN_UNSIGNED) +#endif + #if defined(HAVE_INLINE) INLINE int IsSignUnsigned (const Type* T) /* Return true if this is an unsigned type */ @@ -662,6 +672,16 @@ INLINE int IsSignUnsigned (const Type* T) # define IsSignUnsigned(T) (GetSignedness (T) == T_SIGN_UNSIGNED) #endif +#if defined(HAVE_INLINE) +INLINE int IsRawSignSigned (const Type* T) +/* Return true if this is a signed raw type */ +{ + return (GetRawSignedness (T) == T_SIGN_SIGNED); +} +#else +# define IsRawSignSigned(T) (GetRawSignedness (T) == T_SIGN_SIGNED) +#endif + #if defined(HAVE_INLINE) INLINE int IsSignSigned (const Type* T) /* Return true if this is a signed type */ @@ -673,7 +693,7 @@ INLINE int IsSignSigned (const Type* T) #endif #if defined(HAVE_INLINE) -INLINE TypeCode GetRawSizeModifier(const Type* T) +INLINE TypeCode GetRawSizeModifier (const Type* T) /* Get the size modifier of a raw type */ { return (T->C & T_MASK_SIZE); From 6db93d58cffc8bfd2a320111d96f7ca5a1751519 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 17 Aug 2020 09:22:15 +0200 Subject: [PATCH 224/806] Add test of union of bit-field from mailing list https://sourceforge.net/p/cc65/mailman/message/36152700/ This currently works, but add a test to prevent future regressions. --- test/val/bitfield-union.c | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 test/val/bitfield-union.c diff --git a/test/val/bitfield-union.c b/test/val/bitfield-union.c new file mode 100644 index 000000000..1fd201456 --- /dev/null +++ b/test/val/bitfield-union.c @@ -0,0 +1,71 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of union of bit-fields; see https://sourceforge.net/p/cc65/mailman/message/36152700/ +*/ + +#include + +typedef union { + unsigned int bf; + + struct { + unsigned int a : 1; + unsigned int b : 1; + unsigned int c : 1; + }; +} bitfield_t; + +static unsigned char failures = 0; + +int main (void) +{ + bitfield_t bitfield = {0}; + + printf ("Bitfield: %u\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.a = bitfield.a ^ 1; + printf ("a=1: %u\n", bitfield.bf); + if (bitfield.bf != 1) failures++; + + bitfield.a = bitfield.a ^ 1; + printf ("a=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.b = bitfield.b ^ 1; + printf ("b=1: %u\n", bitfield.bf); + if (bitfield.bf != 2) failures++; + + bitfield.b = bitfield.b ^ 1; + printf ("b=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + bitfield.c = bitfield.c ^ 1; + printf ("c=1: %u\n", bitfield.bf); + if (bitfield.bf != 4) failures++; + + bitfield.c = bitfield.c ^ 1; + printf ("c=0: %u\n\n", bitfield.bf); + if (bitfield.bf != 0) failures++; + + return failures; +} From ebae994dc9e384aa680ab297eec33b9161e7ca92 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 22:59:26 +0800 Subject: [PATCH 225/806] Fixed CHECK failure when calling functions defined with repeated parameter names. Clarified the terms "parameter" vs "argument" in FunctionParamList(). --- src/cc65/expr.c | 55 +++++++++++++++++++++++------------------------ src/cc65/symtab.c | 10 +++++++-- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index efed934f8..db50e14b0 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -330,32 +330,30 @@ static void WarnConstCompareResult (void) static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) -/* Parse a function parameter list, and pass the parameters to the called +/* Parse a function parameter list, and pass the arguments to the called ** function. Depending on several criteria, this may be done by just pushing -** each parameter separately, or creating the parameter frame once, and then -** storing into this frame. -** The function returns the size of the parameters pushed. +** into each parameter separately, or creating the parameter frame once, and +** then storing into this frame. +** The function returns the size of the arguments pushed in bytes. */ { - ExprDesc Expr; - /* Initialize variables */ SymEntry* Param = 0; /* Keep gcc silent */ - unsigned ParamSize = 0; /* Size of parameters pushed */ - unsigned ParamCount = 0; /* Number of parameters pushed */ + unsigned PushedSize = 0; /* Size of arguments pushed */ + unsigned PushedCount = 0; /* Number of arguments pushed */ unsigned FrameSize = 0; /* Size of parameter frame */ - unsigned FrameParams = 0; /* Number of params in frame */ + unsigned FrameParams = 0; /* Number of parameters in frame */ int FrameOffs = 0; /* Offset into parameter frame */ int Ellipsis = 0; /* Function is variadic */ /* As an optimization, we may allocate the complete parameter frame at - ** once instead of pushing each parameter as it comes. We may do that, + ** once instead of pushing into each parameter as it comes. We may do that, ** if... ** ** - optimizations that increase code size are enabled (allocating the ** stack frame at once gives usually larger code). - ** - we have more than one parameter to push (don't count the last param - ** for __fastcall__ functions). + ** - we have more than one parameter to push into (don't count the last + ** parameter for __fastcall__ functions). ** ** The FrameSize variable will contain a value > 0 if storing into a frame ** (instead of pushing) is enabled. @@ -367,7 +365,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) FrameParams = Func->ParamCount; FrameSize = Func->ParamSize; if (FrameParams > 0 && IsFastcall) { - /* Last parameter is not pushed */ + /* Last parameter is not pushed into */ FrameSize -= CheckedSizeOf (Func->LastParam->Type); --FrameParams; } @@ -384,25 +382,26 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } } - /* Parse the actual parameter list */ + /* Parse the actual argument list */ while (CurTok.Tok != TOK_RPAREN) { unsigned Flags; + ExprDesc Expr; /* Count arguments */ - ++ParamCount; + ++PushedCount; /* Fetch the pointer to the next argument, check for too many args */ - if (ParamCount <= Func->ParamCount) { + if (PushedCount <= Func->ParamCount) { /* Beware: If there are parameters with identical names, they ** cannot go into the same symbol table, which means that, in this ** case of errorneous input, the number of nodes in the symbol - ** table and ParamCount are NOT equal. We have to handle this case + ** table and PushedCount are NOT equal. We have to handle this case ** below to avoid segmentation violations. Since we know that this ** problem can only occur if there is more than one parameter, ** we will just use the last one. */ - if (ParamCount == 1) { + if (PushedCount == 1) { /* First argument */ Param = Func->SymTab->SymHead; } else if (Param->NextSym != 0) { @@ -422,11 +421,11 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) Ellipsis = 1; } - /* Evaluate the parameter expression */ + /* Evaluate the argument expression */ hie1 (&Expr); - /* If we don't have an argument spec., accept anything; otherwise, - ** convert the actual argument to the type needed. + /* If we don't have a prototype, accept anything; otherwise, convert + ** the actual argument to the parameter type needed. */ Flags = CF_NONE; if (!Ellipsis) { @@ -483,7 +482,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } /* Calculate total parameter size */ - ParamSize += ArgSize; + PushedSize += ArgSize; } /* Check for end of argument list */ @@ -499,20 +498,20 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) } } - /* Check if we had enough parameters */ - if (ParamCount < Func->ParamCount) { + /* Check if we had enough arguments */ + if (PushedCount < Func->ParamCount) { Error ("Too few arguments in function call"); } - /* The function returns the size of all parameters pushed onto the stack. - ** However, if there are parameters missing (which is an error, and was + /* The function returns the size of all arguments pushed onto the stack. + ** However, if there are parameters missed (which is an error, and was ** flagged by the compiler), AND a stack frame was preallocated above, ** we would loose track of the stackpointer, and generate an internal error ** later. So we correct the value by the parameters that should have been - ** pushed, to avoid an internal compiler error. Since an error was + ** pushed into, to avoid an internal compiler error. Since an error was ** generated before, no code will be output anyway. */ - return ParamSize + FrameSize; + return PushedSize + FrameSize; } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 443d0a21c..02e96cf21 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -1032,6 +1032,7 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs /* Add a local symbol and return the symbol entry */ { SymTable* Tab = SymTab; + ident Ident; /* Do we have an entry with this name already? */ SymEntry* Entry = FindSymInTable (Tab, Name, HashStr (Name)); @@ -1065,8 +1066,13 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs } if (Entry == 0) { - /* Use the fail-safe table for fictitious symbols */ - Tab = FailSafeTab; + if ((Flags & SC_PARAM) != 0) { + /* Use anonymous names */ + Name = AnonName (Ident, "param"); + } else { + /* Use the fail-safe table for fictitious symbols */ + Tab = FailSafeTab; + } } } From 0afa1a9a95815430b444e0221f5836d8f97020db Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 01:50:04 +0800 Subject: [PATCH 226/806] Fixed function signatures in asm output. Improved full type name production. --- src/cc65/datatype.c | 213 +++++++++++++++----------------------------- src/cc65/datatype.h | 10 +-- 2 files changed, 76 insertions(+), 147 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 20c4abc56..48773cba5 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -83,7 +83,7 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu ** eastern part. */ { - struct StrBuf Buf = STATIC_STRBUF_INITIALIZER; + struct StrBuf Buf = AUTO_STRBUF_INITIALIZER; if (IsTypeArray (T)) { @@ -117,28 +117,27 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } else if (IsTypeFunc (T)) { - FuncDesc* F = GetFuncDesc (T); - struct StrBuf ParamList = STATIC_STRBUF_INITIALIZER; + FuncDesc* D = GetFuncDesc (T); + struct StrBuf ParamList = AUTO_STRBUF_INITIALIZER; /* First argument */ - SymEntry* Param = F->SymTab->SymHead; - for (unsigned I = 1; I < F->ParamCount; ++I) { - CHECK (Param->NextSym != 0 && (Param->Flags & SC_PARAM) != 0); + SymEntry* Param = D->SymTab->SymHead; + for (unsigned I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - SB_AppendStr (&ParamList, ", "); SB_Clear (&Buf); /* Next argument */ Param = Param->NextSym; } - if ((F->Flags & FD_VARIADIC) == 0) { - if (F->ParamCount > 0) { - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - } else if ((F->Flags & FD_EMPTY) == 0) { + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { SB_AppendStr (&ParamList, "void"); } } else { - if (F->ParamCount > 0) { - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + if (D->ParamCount > 0) { SB_AppendStr (&ParamList, ", ..."); } else { SB_AppendStr (&ParamList, "..."); @@ -286,7 +285,7 @@ const char* GetFullTypeName (const Type* T) struct StrBuf* GetFullTypeNameBuf (struct StrBuf* S, const Type* T) /* Return the full name string of the given type */ { - struct StrBuf East = STATIC_STRBUF_INITIALIZER; + struct StrBuf East = AUTO_STRBUF_INITIALIZER; GetFullTypeNameWestEast (S, &East, T); /* Join West and East */ @@ -586,155 +585,85 @@ Type* PointerTo (const Type* T) -static TypeCode PrintTypeComp (FILE* F, TypeCode C, TypeCode Mask, const char* Name) -/* Check for a specific component of the type. If it is there, print the -** name and remove it. Return the type with the component removed. -*/ -{ - if ((C & Mask) == Mask) { - fprintf (F, "%s ", Name); - C &= ~Mask; - } - return C; -} - - - void PrintType (FILE* F, const Type* T) -/* Output translation of type array. */ +/* Print fulle name of the type */ { - /* Walk over the type string */ - while (T->C != T_END) { - - /* Get the type code */ - TypeCode C = T->C; - - /* Print any qualifiers */ - C = PrintTypeComp (F, C, T_QUAL_CONST, "const"); - C = PrintTypeComp (F, C, T_QUAL_VOLATILE, "volatile"); - C = PrintTypeComp (F, C, T_QUAL_RESTRICT, "restrict"); - C = PrintTypeComp (F, C, T_QUAL_NEAR, "__near__"); - C = PrintTypeComp (F, C, T_QUAL_FAR, "__far__"); - C = PrintTypeComp (F, C, T_QUAL_FASTCALL, "__fastcall__"); - C = PrintTypeComp (F, C, T_QUAL_CDECL, "__cdecl__"); - - /* Signedness. Omit the signedness specifier for long and int */ - if ((C & T_MASK_TYPE) != T_TYPE_INT && (C & T_MASK_TYPE) != T_TYPE_LONG) { - C = PrintTypeComp (F, C, T_SIGN_SIGNED, "signed"); - } - C = PrintTypeComp (F, C, T_SIGN_UNSIGNED, "unsigned"); - - /* Now check the real type */ - switch (C & T_MASK_TYPE) { - case T_TYPE_CHAR: - fprintf (F, "char"); - break; - case T_TYPE_SHORT: - fprintf (F, "short"); - break; - case T_TYPE_INT: - fprintf (F, "int"); - break; - case T_TYPE_LONG: - fprintf (F, "long"); - break; - case T_TYPE_LONGLONG: - fprintf (F, "long long"); - break; - case T_TYPE_ENUM: - fprintf (F, "enum"); - break; - case T_TYPE_FLOAT: - fprintf (F, "float"); - break; - case T_TYPE_DOUBLE: - fprintf (F, "double"); - break; - case T_TYPE_VOID: - fprintf (F, "void"); - break; - case T_TYPE_STRUCT: - fprintf (F, "struct %s", ((SymEntry*) T->A.P)->Name); - break; - case T_TYPE_UNION: - fprintf (F, "union %s", ((SymEntry*) T->A.P)->Name); - break; - case T_TYPE_ARRAY: - /* Recursive call */ - PrintType (F, T + 1); - if (T->A.L == UNSPECIFIED) { - fprintf (F, " []"); - } else { - fprintf (F, " [%ld]", T->A.L); - } - return; - case T_TYPE_PTR: - /* Recursive call */ - PrintType (F, T + 1); - fprintf (F, " *"); - return; - case T_TYPE_FUNC: - fprintf (F, "function returning "); - break; - default: - fprintf (F, "unknown type: %04lX", T->C); - } - - /* Next element */ - ++T; - } + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); + SB_Done (&Buf); } void PrintFuncSig (FILE* F, const char* Name, Type* T) -/* Print a function signature. */ +/* Print a function signature */ { + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + StrBuf East = AUTO_STRBUF_INITIALIZER; + StrBuf West = AUTO_STRBUF_INITIALIZER; + /* Get the function descriptor */ const FuncDesc* D = GetFuncDesc (T); - /* Print a comment with the function signature */ - PrintType (F, GetFuncReturn (T)); - if (IsQualNear (T)) { - fprintf (F, " __near__"); + /* Get the parameter list string. Start from the first parameter */ + SymEntry* Param = D->SymTab->SymHead; + for (unsigned I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!HasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; } - if (IsQualFar (T)) { - fprintf (F, " __far__"); - } - if (IsQualFastcall (T)) { - fprintf (F, " __fastcall__"); - } - if (IsQualCDecl (T)) { - fprintf (F, " __cdecl__"); - } - fprintf (F, " %s (", Name); - - /* Parameters */ - if (D->Flags & FD_VOID_PARAM) { - fprintf (F, "void"); + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (&ParamList, "void"); + } } else { - unsigned I; - SymEntry* E = D->SymTab->SymHead; - for (I = 0; I < D->ParamCount; ++I) { - if (I > 0) { - fprintf (F, ", "); - } - if (SymIsRegVar (E)) { - fprintf (F, "register "); - } - PrintType (F, E->Type); - E = E->NextSym; + if (D->ParamCount > 0) { + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); } } + SB_Terminate (&ParamList); - /* End of parameter list */ - fprintf (F, ")"); + /* Get the function qualifiers */ + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { + /* Append a space between the qualifiers and the name */ + SB_AppendChar (&Buf, ' '); + } + SB_Terminate (&Buf); + + /* Get the signature string without the return type */ + SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); + SB_Done (&Buf); + SB_Done (&ParamList); + + /* Complete with the return type */ + GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + SB_Append (&West, &East); + SB_Terminate (&West); + + /* Output */ + fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&East); + SB_Done (&West); } void PrintRawType (FILE* F, const Type* T) -/* Print a type string in raw format (for debugging) */ +/* Print a type string in raw hex format (for debugging) */ { while (T->C != T_END) { fprintf (F, "%04lX ", T->C); diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 660681909..00a43fbe1 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -278,13 +278,13 @@ Type* PointerTo (const Type* T); */ void PrintType (FILE* F, const Type* T); -/* Output translation of type array. */ - -void PrintRawType (FILE* F, const Type* T); -/* Print a type string in raw format (for debugging) */ +/* Print fulle name of the type */ void PrintFuncSig (FILE* F, const char* Name, Type* T); -/* Print a function signature. */ +/* Print a function signature */ + +void PrintRawType (FILE* F, const Type* T); +/* Print a type string in raw hex format (for debugging) */ int TypeHasAttr (const Type* T); /* Return true if the given type has attribute data */ From f206833a2028362557da2e06dc982a1aace943d3 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 09:10:17 +0800 Subject: [PATCH 227/806] Alternative fix for Issue #1167. --- src/cc65/codeinfo.c | 2 +- src/cc65/compile.c | 9 +++++---- src/cc65/datatype.c | 22 ---------------------- src/cc65/datatype.h | 6 ------ src/cc65/function.c | 13 +++++-------- src/cc65/function.h | 5 ++++- src/cc65/pragma.c | 2 +- src/cc65/symentry.h | 1 - src/cc65/symtab.c | 23 +++-------------------- 9 files changed, 19 insertions(+), 64 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index c9dea5071..3e1d58709 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -392,7 +392,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { - FuncDesc* D = GetFuncCompositeDesc (E); + FuncDesc* D = GetFuncDesc (E->Type); /* A variadic function will use the Y register (the parameter list ** size is passed there). A fastcall function will use the A or A/X diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 54918fb21..82dc7ec63 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -77,6 +77,7 @@ static void Parse (void) { int comma; SymEntry* Entry; + FuncDesc* FuncDef = 0; /* Go... */ NextToken (); @@ -187,9 +188,9 @@ static void Parse (void) /* Convert an empty parameter list into one accepting no ** parameters (same as void) as required by the standard. */ - FuncDesc* D = GetFuncDesc (Decl.Type); - if (D->Flags & FD_EMPTY) { - D->Flags = (D->Flags & ~FD_EMPTY) | FD_VOID_PARAM; + FuncDef = GetFuncDesc (Decl.Type); + if (FuncDef->Flags & FD_EMPTY) { + FuncDef->Flags = (FuncDef->Flags & ~FD_EMPTY) | FD_VOID_PARAM; } } else { /* Just a declaration */ @@ -315,7 +316,7 @@ SkipOneDecl: NextToken (); } else { /* Parse the function body */ - NewFunc (Entry); + NewFunc (Entry, FuncDef); } } diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 48773cba5..0f3c3e5f9 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1140,28 +1140,6 @@ Type* GetFuncReturn (Type* T) -Type* GetFuncCompositeType (SymEntry* Func) -/* Get the composite function type */ -{ - /* Be sure it's a function type */ - CHECK (IsClassFunc (Func->Type)); - - return Func->V.F.Composite->Type; -} - - - -FuncDesc* GetFuncCompositeDesc (SymEntry* Func) -/* Get the composite function type descriptor */ -{ - /* Be sure it's a function type */ - CHECK (IsClassFunc (Func->Type)); - - return GetFuncDesc (Func->V.F.Composite->Type); -} - - - long GetElementCount (const Type* T) /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 00a43fbe1..54f601364 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -816,12 +816,6 @@ void SetFuncDesc (Type* T, FuncDesc* F); Type* GetFuncReturn (Type* T) attribute ((const)); /* Return a pointer to the return type of a function or pointer-to-function type */ -Type* GetFuncCompositeType (struct SymEntry* Func); -/* Get the composite function type */ - -FuncDesc* GetFuncCompositeDesc (struct SymEntry* Func); -/* Get the composite function type descriptor */ - long GetElementCount (const Type* T); /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/function.c b/src/cc65/function.c index b2291b312..fc113b29a 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -72,7 +72,7 @@ Function* CurrentFunc = 0; -static Function* NewFunction (struct SymEntry* Sym) +static Function* NewFunction (struct SymEntry* Sym, FuncDesc* D) /* Create a new function activation structure and return it */ { /* Allocate a new structure */ @@ -81,7 +81,7 @@ static Function* NewFunction (struct SymEntry* Sym) /* Initialize the fields */ F->FuncEntry = Sym; F->ReturnType = GetFuncReturn (Sym->Type); - F->Desc = GetFuncDesc (Sym->Type); + F->Desc = D; F->Reserved = 0; F->RetLab = GetLocalLabel (); F->TopLevelSP = 0; @@ -295,7 +295,7 @@ static void F_RestoreRegVars (Function* F) } /* Get the first symbol from the function symbol table */ - Sym = GetFuncDesc (F->FuncEntry->Type)->SymTab->SymHead; + Sym = F->Desc->SymTab->SymHead; /* Walk through all symbols checking for register variables */ while (Sym) { @@ -375,18 +375,15 @@ static void F_EmitDebugInfo (void) -void NewFunc (SymEntry* Func) +void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ - /* Get the function descriptor from the function entry */ - FuncDesc* D = GetFuncDesc (Func->Type); - /* Allocate the function activation record for the function */ - CurrentFunc = NewFunction (Func); + CurrentFunc = NewFunction (Func, D); /* Reenter the lexical level */ ReenterFunctionLevel (D); diff --git a/src/cc65/function.h b/src/cc65/function.h index 0954322ac..8231a1970 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -67,6 +67,9 @@ struct Function { /* Structure that holds all data needed for function activation */ typedef struct Function Function; +/* Forward declaration */ +struct FuncDesc; + /* Function activation data for current function (or NULL) */ extern Function* CurrentFunc; @@ -138,7 +141,7 @@ int F_AllocRegVar (Function* F, const Type* Type); ** bank (zero page storage). If there is no register space left, return -1. */ -void NewFunc (struct SymEntry* Func); +void NewFunc (struct SymEntry* Func, struct FuncDesc* D); /* Parse argument declarations and function body. */ diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index a0876a550..c614bbfdd 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -527,7 +527,7 @@ static void WrappedCallPragma (StrBuf* B) PushWrappedCall(Entry, (unsigned char) Val); Entry->Flags |= SC_REF; - GetFuncCompositeDesc (Entry)->Flags |= FD_CALL_WRAPPER; + GetFuncDesc (Entry->Type)->Flags |= FD_CALL_WRAPPER; } else { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 6b05dd72b..94fe66032 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -191,7 +191,6 @@ struct SymEntry { /* Data for functions */ struct { - SymEntry* Composite;/* Entry to hold composite function type */ struct Segments* Seg; /* Segments for this function */ struct LiteralPool* LitPool; /* Literal pool for this function */ } F; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 02e96cf21..6e0654576 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -590,14 +590,14 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags Entry = 0; } else { /* New type must be compatible with the composite prototype */ - if (TypeCmp (GetFuncCompositeType (Entry), T) < TC_EQUAL) { + if (TypeCmp (Entry->Type, T) < TC_EQUAL) { Error ("Conflicting function types for '%s'", Entry->Name); Entry = 0; } else { /* Refine the existing composite prototype with this new ** one. */ - RefineFuncDesc (GetFuncCompositeType (Entry), T); + RefineFuncDesc (Entry->Type, T); } } @@ -1165,14 +1165,6 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) } if (Entry) { - /* Update existing function type if this is a definition */ - if (IsTypeFunc (Entry->Type) && - !SymIsDef (Entry) && - (Flags & SC_DEF) == SC_DEF) { - TypeFree (Entry->Type); - Entry->Type = TypeDup (T); - } - /* Add the new flags */ Entry->Flags |= Flags; } @@ -1192,17 +1184,8 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags) /* Set the symbol attributes */ Entry->Type = TypeDup (T); - /* If this is a function, set the function composite typeand clear - ** additional fields. - */ + /* If this is a function, clear additional fields */ if (IsTypeFunc (T)) { - /* GitHub #1167 - Make a composite prototype */ - ident Ident; - AnonName (Ident, "prototype"); - Entry->V.F.Composite = NewSymEntry (Ident, SC_EXTERN | SC_DECL | SC_ALIAS | SC_FUNC); - Entry->V.F.Composite->Type = TypeDup (T); - AddSymEntry (SymTab0, Entry->V.F.Composite); - Entry->V.F.Seg = 0; } From cf41fccc0aee2c67fc47baffdc376a8122cdff5c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 00:40:29 +0200 Subject: [PATCH 228/806] added test related to issue #1143 --- test/err/bug1143err.c | 11 +++++++++++ test/val/bug1143warn.c | 9 +++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/err/bug1143err.c create mode 100644 test/val/bug1143warn.c diff --git a/test/err/bug1143err.c b/test/err/bug1143err.c new file mode 100644 index 000000000..03a6e6d35 --- /dev/null +++ b/test/err/bug1143err.c @@ -0,0 +1,11 @@ + +/* bug #1143 - Multiple storage class specifiers in one declaration? */ + +static static void* y[1]; /* warning */ +extern static int a; /* error */ +extern typedef int A; /* error */ + +int main(void) +{ + return 0; +} diff --git a/test/val/bug1143warn.c b/test/val/bug1143warn.c new file mode 100644 index 000000000..9bbc8ea8b --- /dev/null +++ b/test/val/bug1143warn.c @@ -0,0 +1,9 @@ + +/* bug #1143 - Multiple storage class specifiers in one declaration? */ + +static static void* y[1]; /* warning */ + +int main(void) +{ + return 0; +} From 6d8860b9de994607e4cebaf38350a7ac0b1c8c50 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 00:41:07 +0200 Subject: [PATCH 229/806] added test related to issue #1145 --- test/err/bug1145.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/err/bug1145.c diff --git a/test/err/bug1145.c b/test/err/bug1145.c new file mode 100644 index 000000000..23401f91b --- /dev/null +++ b/test/err/bug1145.c @@ -0,0 +1,13 @@ + +/* bug #1145 - Internal error with function type object */ + +void f() +{ + f = 0; /* internal error */ +} + +int main(void) +{ + f(); + return 0; +} From 2663561c623c9666a89d82ca9d435680312e4f71 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 00:41:35 +0200 Subject: [PATCH 230/806] added test related to pr #1135 --- test/err/pr1135.c | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/err/pr1135.c diff --git a/test/err/pr1135.c b/test/err/pr1135.c new file mode 100644 index 000000000..01eff7d93 --- /dev/null +++ b/test/err/pr1135.c @@ -0,0 +1,8 @@ + +void f(void) {} +void f(int); /* Should fail */ + +int main(void) +{ + return 0; +} From 87889df9e90646db40a86eadf34c50d5fde1f324 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 231/806] Fixed type checking in relation operations. --- src/cc65/expr.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index db50e14b0..84d709eb9 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -2303,16 +2303,21 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ LoadExpr (CF_NONE, &Expr2); } + /* Check if operands have allowed types for this operation */ + if (!IsRelationType (Expr->Type) || !IsRelationType (Expr2.Type)) { + /* Output only one message even if both sides are wrong */ + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + /* Avoid further errors */ + ED_MakeConstAbsInt (Expr, 0); + ED_MakeConstAbsInt (&Expr2, 0); + } + /* Some operations aren't allowed on function pointers */ if ((Gen->Flags & GEN_NOFUNC) != 0) { - /* Output only one message even if both sides are wrong */ - if (IsTypeFuncPtr (Expr->Type)) { - Error ("Invalid left operand for relational operator"); - /* Avoid further errors */ - ED_MakeConstAbsInt (Expr, 0); - ED_MakeConstAbsInt (&Expr2, 0); - } else if (IsTypeFuncPtr (Expr2.Type)) { - Error ("Invalid right operand for relational operator"); + if ((IsTypeFuncPtr (Expr->Type) || IsTypeFuncPtr (Expr2.Type))) { + /* Output only one message even if both sides are wrong */ + Error ("Cannot use function pointers in this relation operation"); /* Avoid further errors */ ED_MakeConstAbsInt (Expr, 0); ED_MakeConstAbsInt (&Expr2, 0); @@ -2321,9 +2326,14 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Make sure, the types are compatible */ if (IsClassInt (Expr->Type)) { - if (!IsClassInt (Expr2.Type) && !(IsClassPtr(Expr2.Type) && ED_IsNullPtr(Expr))) { - TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, - "Incompatible types comparing '%s' with '%s'"); + if (!IsClassInt (Expr2.Type) && !ED_IsNullPtr (Expr)) { + if (IsClassPtr (Expr2.Type)) { + TypeCompatibilityDiagnostic (Expr->Type, PtrConversion (Expr2.Type), 0, + "Comparing integer '%s' with pointer '%s'"); + } else { + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + } } } else if (IsClassPtr (Expr->Type)) { if (IsClassPtr (Expr2.Type)) { @@ -2334,12 +2344,17 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ Type* right = Indirect (Expr2.Type); if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) { /* Incompatible pointers */ - TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0, "Incompatible pointer types comparing '%s' with '%s'"); } } else if (!ED_IsNullPtr (&Expr2)) { - TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, - "Comparing pointer type '%s' with '%s'"); + if (IsClassInt (Expr2.Type)) { + TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), Expr2.Type, 0, + "Comparing pointer type '%s' with integer type '%s'"); + } else { + TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1, + "Comparing types '%s' with '%s' is invalid"); + } } } From 56b659c0be743a2630993bf4b7eaaff1c8415a2d Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 232/806] Made char type a distinct type. --- src/cc65/datatype.c | 25 +++++++++++-------------- src/cc65/datatype.h | 34 +++++++++++++++++++++++++++------- src/cc65/declare.c | 4 ++-- src/cc65/global.h | 2 +- src/cc65/main.c | 2 +- src/cc65/stdfunc.c | 10 +++++----- src/cc65/typecmp.c | 6 ++++++ 7 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 0f3c3e5f9..b95e37f87 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -59,6 +59,7 @@ /* Predefined type strings */ +Type type_char[] = { TYPE(T_CHAR), TYPE(T_END) }; Type type_schar[] = { TYPE(T_SCHAR), TYPE(T_END) }; Type type_uchar[] = { TYPE(T_UCHAR), TYPE(T_END) }; Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; @@ -431,14 +432,6 @@ int SignExtendChar (int C) -TypeCode GetDefaultChar (void) -/* Return the default char type (signed/unsigned) depending on the settings */ -{ - return IS_Get (&SignedChars)? T_SCHAR : T_UCHAR; -} - - - Type* GetCharArrayType (unsigned Len) /* Return the type for a char array of the given length */ { @@ -448,7 +441,7 @@ Type* GetCharArrayType (unsigned Len) /* Fill the type string */ T[0].C = T_ARRAY; T[0].A.L = Len; /* Array length is in the L attribute */ - T[1].C = GetDefaultChar (); + T[1].C = T_CHAR; T[2].C = T_END; /* Return the new type */ @@ -685,8 +678,9 @@ int TypeHasAttr (const Type* T) const Type* GetUnderlyingType (const Type* Type) /* Get the underlying type of an enum or other integer class type */ { - if (IsTypeEnum (Type)) { - + if (IsISOChar (Type)) { + return IS_Get (&SignedChars) ? type_schar : type_uchar; + } else if (IsTypeEnum (Type)) { /* This should not happen, but just in case */ if (Type->A.P == 0) { Internal ("Enum tag type error in GetUnderlyingTypeCode"); @@ -708,8 +702,11 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) TypeCode Underlying = UnqualifiedType (Type->C); TypeCode TCode; - /* We could also support other T_CLASS_INT types, but just enums for now */ - if (IsTypeEnum (Type)) { + if (IsISOChar (Type)) { + + return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; + + } else if (IsTypeEnum (Type)) { /* This should not happen, but just in case */ if (Type->A.P == 0) { @@ -996,7 +993,7 @@ int IsClassArithmetic (const Type* T) int IsClassBasic (const Type* T) /* Return true if this is a char, integer or floating type */ { - return IsRawTypeChar (T) || IsClassInt (T) || IsClassFloat (T); + return IsClassChar (T) || IsClassInt (T) || IsClassFloat (T); } diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 54f601364..472098788 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -117,7 +117,7 @@ enum { T_MASK_QUAL = 0x07F000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, + T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, T_SHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, @@ -189,6 +189,7 @@ struct Type { #define PTR_BITS (8 * SIZEOF_PTR) /* Predefined type strings */ +extern Type type_char[]; extern Type type_schar[]; extern Type type_uchar[]; extern Type type_int[]; @@ -250,9 +251,6 @@ void TypeFree (Type* T); int SignExtendChar (int C); /* Do correct sign extension of a character */ -TypeCode GetDefaultChar (void); -/* Return the default char type (signed/unsigned) depending on the settings */ - Type* GetCharArrayType (unsigned Len); /* Return the type for a char array of the given length */ @@ -365,7 +363,7 @@ INLINE TypeCode GetRawType (const Type* T) #if defined(HAVE_INLINE) INLINE int IsTypeChar (const Type* T) -/* Return true if this is a character type */ +/* Return true if this is a char type */ { return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); } @@ -395,7 +393,7 @@ INLINE int IsTypeInt (const Type* T) #if defined(HAVE_INLINE) INLINE int IsTypeLong (const Type* T) -/* Return true if this is a long type (signed or unsigned) */ +/* Return true if this is a long int type (signed or unsigned) */ { return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); } @@ -403,9 +401,31 @@ INLINE int IsTypeLong (const Type* T) # define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG) #endif +#if defined(HAVE_INLINE) +INLINE int IsISOChar (const Type* T) +/* Return true if this is a narrow character type (without signed/unsigned) */ +{ + return (UnqualifiedType (T->C) == T_CHAR); +} +#else +# define IsISOChar(T) (UnqualifiedType ((T)->C) == T_CHAR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsClassChar (const Type* T) +/* Return true if this is a narrow character type (including signed/unsigned). +** For now this is the same as IsRawTypeChar(T). +*/ +{ + return (GetRawType (T) == T_TYPE_CHAR); +} +#else +# define IsClassChar(T) (GetRawType (T) == T_TYPE_CHAR) +#endif + #if defined(HAVE_INLINE) INLINE int IsRawTypeChar (const Type* T) -/* Return true if this is a character raw type */ +/* Return true if this is a char raw type (including signed/unsigned) */ { return (GetRawType (T) == T_TYPE_CHAR); } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 36286b6d5..44d4e4142 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1169,7 +1169,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_CHAR: NextToken (); - D->Type[0].C = GetDefaultChar(); + D->Type[0].C = T_CHAR; D->Type[1].C = T_END; break; @@ -2195,7 +2195,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsRawTypeChar (ElementType) && + if (IsClassChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { diff --git a/src/cc65/global.h b/src/cc65/global.h index 4ffc84a39..b9bcf5550 100644 --- a/src/cc65/global.h +++ b/src/cc65/global.h @@ -67,7 +67,7 @@ extern IntStack EnableRegVars; /* Enable register variables */ extern IntStack AllowRegVarAddr; /* Allow taking addresses of register vars */ extern IntStack RegVarsToCallStack; /* Save reg variables on call stack */ extern IntStack StaticLocals; /* Make local variables static */ -extern IntStack SignedChars; /* Make characters signed by default */ +extern IntStack SignedChars; /* Use 'signed char' as the underlying type of 'char' */ extern IntStack CheckStack; /* Generate stack overflow checks */ extern IntStack Optimize; /* Optimize flag */ extern IntStack CodeSizeFactor; /* Size factor for generated code */ diff --git a/src/cc65/main.c b/src/cc65/main.c index 3e60bcb95..26dd721be 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -731,7 +731,7 @@ static void OptRodataName (const char* Opt attribute ((unused)), const char* Arg static void OptSignedChars (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) -/* Make default characters signed */ +/* Use 'signed char' as the underlying type of 'char' */ { IS_Set (&SignedChars, 1); } diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 6d61f2750..94e56a625 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -783,8 +783,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) int Offs; /* Setup the argument type string */ - Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST; - Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; + Arg1Type[1].C = T_CHAR | T_QUAL_CONST; + Arg2Type[1].C = T_CHAR | T_QUAL_CONST; /* Argument #1 */ ParseArg (&Arg1, Arg1Type); @@ -983,8 +983,8 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) unsigned L1; /* Setup the argument type string */ - Arg1Type[1].C = GetDefaultChar (); - Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; + Arg1Type[1].C = T_CHAR; + Arg2Type[1].C = T_CHAR | T_QUAL_CONST; /* Argument #1 */ ParseArg (&Arg1, Arg1Type); @@ -1181,7 +1181,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) unsigned L; /* Setup the argument type string */ - ArgType[1].C = GetDefaultChar () | T_QUAL_CONST; + ArgType[1].C = T_CHAR | T_QUAL_CONST; /* Evaluate the parameter */ hie1 (&Arg); diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index e3e42e67f..24d9a385f 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -233,6 +233,12 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) } + /* 'char' is neither 'signed char' nor 'unsigned char' */ + if ((IsISOChar (lhs) && !IsISOChar (rhs)) || + (!IsISOChar (lhs) && IsISOChar (rhs))) { + SetResult (Result, TC_COMPATIBLE); + } + /* On indirection level zero, a qualifier or sign difference is ** accepted. The types are no longer equal, but compatible. */ From 55cebc7b9e8d5e34dc80d638405b29968e89c309 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 17 Aug 2020 22:29:52 +0200 Subject: [PATCH 233/806] Move bit-field adjustment to codegen.c Extract functions g_testbitfield and g_extractbitfield from LoadExpr. This helps prepare for #1192, since g_extractbitfield will get much longer and call AddCodeLine. --- src/cc65/codegen.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/codegen.h | 11 +++++++++ src/cc65/loadexpr.c | 44 +++------------------------------- 3 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 2c6624a5c..a81b2cf46 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -33,6 +33,7 @@ +#include #include #include #include @@ -4424,6 +4425,63 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size) +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + + + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth) +/* Test bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* If we need to do a test, then we avoid shifting (ASR only shifts one bit at a time, + ** so is slow) and just AND with the appropriate mask, then test the result of that. + */ + + /* Avoid overly large shift on host platform. */ + if (EndBit == sizeof (unsigned long) * CHAR_BIT) { + g_and (Flags | CF_CONST, (~0UL << BitOffs)); + } else { + g_and (Flags | CF_CONST, ((1UL << EndBit) - 1) & (~0UL << BitOffs)); + } + + /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes when + ** EndBit <= 24 is possible. + */ + g_test (Flags | CF_CONST); +} + + + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, + unsigned BitOffs, unsigned BitWidth) +/* Extract bits from bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ + g_asr (Flags | CF_CONST, BitOffs); + + /* Since we have now shifted down, we could do char ops when the width fits in a char, but we + ** also need to clear the high byte since we've been using CF_FORCECHAR up to now. + */ + + /* And by the width if the field doesn't end on a char or int boundary. If it does end on + ** a boundary, then zeros have already been shifted in, but we need to clear the high byte + ** for char. g_and emits no code if the mask is all ones. + */ + if (EndBit == CHAR_BITS) { + /* We need to clear the high byte, since CF_FORCECHAR was set. */ + g_and (FullWidthFlags | CF_CONST, 0xFF); + } else if (EndBit != INT_BITS) { + g_and (FullWidthFlags | CF_CONST, (0x0001U << BitWidth) - 1U); + } +} + + + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index c63fc5398..6581fc54e 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -471,6 +471,17 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size); +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth); +/* Test bit-field in ax. */ + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, + unsigned BitOffs, unsigned BitWidth); +/* Extract bits from bit-field in ax. */ + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index ba585d3e3..452d9a9a3 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -33,8 +33,6 @@ -#include - /* cc65 */ #include "codegen.h" #include "error.h" @@ -115,11 +113,10 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** field is completely contained in the lower byte, we will throw away ** the high byte anyway and may therefore load just the low byte. */ - unsigned EndBit = 0; /* End bit for bit-fields, or zero if non-bit-field. */ int AdjustBitField = 0; unsigned BitFieldFullWidthFlags = 0; if (ED_IsBitField (Expr)) { - EndBit = Expr->BitOffs + Expr->BitWidth; + unsigned EndBit = Expr->BitOffs + Expr->BitWidth; AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are @@ -226,50 +223,15 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** so be sure to always use unsigned ints for the operations. */ if (AdjustBitField) { - unsigned F = Flags | CF_CONST; - /* We always need to do something with the low byte, so there is no opportunity ** for optimization by skipping it. */ CHECK (Expr->BitOffs < CHAR_BITS); if (ED_NeedsTest (Expr)) { - /* If we need to do a test, then we avoid shifting (ASR only shifts one bit - ** at a time, so is slow) and just AND with the appropriate mask, then test - ** the result of that. - */ - - /* Avoid overly large shift on host platform. */ - if (EndBit == sizeof (unsigned long) * CHAR_BIT) { - g_and (F, (~0UL << Expr->BitOffs)); - } else { - g_and (F, ((1UL << EndBit) - 1) & (~0UL << Expr->BitOffs)); - } - - /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes - ** when EndBit <= 24 is possible. - */ - g_test (F); + g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); } else { - /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ - g_asr (F, Expr->BitOffs); - - /* Since we have now shifted down, we could do char ops when the width fits in - ** a char, but we also need to clear the high byte since we've been using - ** CF_FORCECHAR up to now. - */ - - /* And by the width if the field doesn't end on a char or int boundary. - ** If it does end on a boundary, then zeros have already been shifted in, - ** but we need to clear the high byte for char. g_and emits no code if the mask - ** is all ones. - */ - if (EndBit == CHAR_BITS) { - /* We need to clear the high byte, since CF_FORCECHAR was set. */ - g_and (BitFieldFullWidthFlags | CF_CONST, 0xFF); - } else if (EndBit != INT_BITS) { - g_and (BitFieldFullWidthFlags | CF_CONST, (0x0001U << Expr->BitWidth) - 1U); - } + g_extractbitfield (Flags, BitFieldFullWidthFlags, Expr->BitOffs, Expr->BitWidth); } } From ff535b8e1a79205fe877904d922068ab6d3467d2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 19 Jul 2020 22:59:44 +0200 Subject: [PATCH 234/806] Treat signed int bit-fields as signed Prior to this PR, `int`, `signed int`, and `unsigned int` bitfields are all treated as `unsigned int`. With this PR, `signed int` will be treated as `signed int`, and the others remain unsigned. Since `Type` does not distinguish between `int` and `signed int`, add an extra `int* SignenessSpecified` param to `ParseTypeSpec` so we can tell these apart for bit-fields and treat plain `int : N` as `unsigned int : N` since it is more efficient to zero-extend than sign-extend. Fixes #1095 --- src/cc65/codegen.c | 75 +++++++++++++++++++++++--- src/cc65/codegen.h | 2 +- src/cc65/declare.c | 102 +++++++++++++++++++++++++++-------- src/cc65/loadexpr.c | 13 +++-- src/cc65/symtab.c | 16 ++++-- src/cc65/symtab.h | 3 +- test/{todo => val}/bug1095.c | 47 ++++++++++++++-- 7 files changed, 218 insertions(+), 40 deletions(-) rename test/{todo => val}/bug1095.c (74%) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index a81b2cf46..a0dbc498b 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -4455,7 +4455,7 @@ void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth) -void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned BitOffs, unsigned BitWidth) /* Extract bits from bit-field in ax. */ { @@ -4465,18 +4465,79 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, g_asr (Flags | CF_CONST, BitOffs); /* Since we have now shifted down, we could do char ops when the width fits in a char, but we - ** also need to clear the high byte since we've been using CF_FORCECHAR up to now. + ** also need to clear (or set) the high byte since we've been using CF_FORCECHAR up to now. */ + unsigned Mask = (1U << BitWidth) - 1; - /* And by the width if the field doesn't end on a char or int boundary. If it does end on - ** a boundary, then zeros have already been shifted in, but we need to clear the high byte - ** for char. g_and emits no code if the mask is all ones. + /* To zero-extend, we will and by the width if the field doesn't end on a char or + ** int boundary. If it does end on a boundary, then zeros will have already been shifted in, + ** but we need to clear the high byte for char. g_and emits no code if the mask is all ones. + ** This is here so the signed and unsigned branches can use it. */ + unsigned ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */ if (EndBit == CHAR_BITS) { /* We need to clear the high byte, since CF_FORCECHAR was set. */ - g_and (FullWidthFlags | CF_CONST, 0xFF); + ZeroExtendMask = 0xFF; } else if (EndBit != INT_BITS) { - g_and (FullWidthFlags | CF_CONST, (0x0001U << BitWidth) - 1U); + ZeroExtendMask = (1U << BitWidth) - 1; + } + + /* Handle signed bit-fields. */ + if (IsSigned) { + /* Push A, since the sign bit test will destroy it. */ + AddCodeLine ("pha"); + + /* Check sign bit */ + unsigned SignBitPos = BitWidth - 1U; + unsigned SignBitByte = SignBitPos / CHAR_BITS; + unsigned SignBitPosInByte = SignBitPos % CHAR_BITS; + unsigned SignBitMask = 1U << SignBitPosInByte; + + /* Move the correct byte to A. This can only be X for now, + ** but more cases will be needed to support long. + */ + switch (SignBitByte) { + case 0: + break; + case 1: + AddCodeLine ("txa"); + break; + default: + FAIL ("Invalid Byte for sign bit"); + } + + /* Test the sign bit */ + AddCodeLine ("and #$%02X", SignBitMask); + unsigned ZeroExtendLabel = GetLocalLabel (); + AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel)); + + /* Pop A back and sign extend if required; operating on the full result need + ** to sign-extend into high byte, too. + */ + AddCodeLine ("pla"); + g_or (FullWidthFlags | CF_CONST, ~Mask); + + /* Apparently, there is no unconditional branch BRA, so use JMP. */ + unsigned DoneLabel = GetLocalLabel (); + AddCodeLine ("jmp %s", LocalLabelName (DoneLabel)); + + /* Pop A back, then zero-extend; we need to duplicate the PLA rather than move it before + ** the branch to share with the other label because PLA sets the condition codes. + */ + g_defcodelabel (ZeroExtendLabel); + AddCodeLine ("pla"); + + /* Zero the upper bits, the same as the unsigned path. */ + if (ZeroExtendMask != 0) { + g_and (FullWidthFlags | CF_CONST, ZeroExtendMask); + } + + g_defcodelabel (DoneLabel); + } else { + /* Unsigned bit-field, only needs zero-extension. */ + if (ZeroExtendMask != 0) { + g_and (FullWidthFlags | CF_CONST, ZeroExtendMask); + } } } diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 6581fc54e..ec4756f2d 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -478,7 +478,7 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size); void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth); /* Test bit-field in ax. */ -void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned BitOffs, unsigned BitWidth); /* Extract bits from bit-field in ax. */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 44d4e4142..ccd4e9004 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -41,6 +41,7 @@ /* common */ #include "addrsize.h" #include "mmodel.h" +#include "shift.h" #include "xmalloc.h" /* cc65 */ @@ -87,7 +88,8 @@ struct StructInitData { -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, + int* SignednessSpecified); /* Parse a type specifier */ static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers); @@ -252,12 +254,15 @@ static void OptionalInt (void) -static void OptionalSigned (void) +static void OptionalSigned (int* SignednessSpecified) /* Eat an optional "signed" token */ { if (CurTok.Tok == TOK_SIGNED) { /* Skip it */ NextToken (); + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } } } @@ -728,6 +733,9 @@ static int ParseFieldWidth (Declaration* Decl) return -1; } + /* TODO: This can be relaxed to be any integral type, but + ** ParseStructInit currently only supports up to int. + */ if (SizeOf (Decl->Type) != SizeOf (type_uint)) { /* Only int sized types may be used for bit-fields for now */ Error ("cc65 currently only supports unsigned int bit-fields"); @@ -774,7 +782,8 @@ static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) /* Add an anonymous bit-field that aligns to the next ** byte. */ - AddBitField (Ident, StructSize, BitOffs, PaddingBits); + AddBitField (Ident, type_uchar, StructSize, BitOffs, PaddingBits, + /*SignednessSpecified=*/1); return PaddingBits; } @@ -866,8 +875,9 @@ static SymEntry* ParseUnionDecl (const char* Name) /* Get the type of the entry */ DeclSpec Spec; + int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { @@ -909,7 +919,11 @@ static SymEntry* ParseUnionDecl (const char* Name) /* Add a field entry to the table. */ if (FieldWidth > 0) { - AddBitField (Decl.Ident, 0, 0, FieldWidth); + /* For a union, allocate space for the type specified by the + ** bit-field. + */ + AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, + SignednessSpecified); } else { if (IsAnonName (Decl.Ident)) { Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); @@ -997,8 +1011,9 @@ static SymEntry* ParseStructDecl (const char* Name) continue; } + int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { @@ -1020,12 +1035,13 @@ static SymEntry* ParseStructDecl (const char* Name) FieldWidth = ParseFieldWidth (&Decl); /* If this is not a bit field, or the bit field is too large for - ** the remainder of the current member, or we have a bit field + ** the remainder of the allocated unit, or we have a bit field ** with width zero, align the struct to the next member by adding ** a member with an anonymous name. */ if (BitOffs > 0) { - if (FieldWidth <= 0 || (BitOffs + FieldWidth) > INT_BITS) { + if (FieldWidth <= 0 || + (BitOffs + FieldWidth) > CHAR_BITS * SizeOf (Decl.Type)) { /* Add an anonymous bit-field that aligns to the next ** byte. */ @@ -1087,9 +1103,10 @@ static SymEntry* ParseStructDecl (const char* Name) ** bit-field as a char type in expressions. */ CHECK (BitOffs < CHAR_BITS); - AddBitField (Decl.Ident, StructSize, BitOffs, FieldWidth); + AddBitField (Decl.Ident, Decl.Type, StructSize, BitOffs, + FieldWidth, SignednessSpecified); BitOffs += FieldWidth; - CHECK (BitOffs <= INT_BITS); + CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); /* Add any full bytes to the struct size. */ StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; @@ -1145,12 +1162,20 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) -/* Parse a type specifier */ +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, + int* SignednessSpecified) +/* Parse a type specifier. Store whether one of "signed" or "unsigned" was +** specified, so bit-fields of unspecified signedness can be treated as +** unsigned; without special handling, it would be treated as signed. +*/ { ident Ident; SymEntry* Entry; + if (SignednessSpecified != NULL) { + *SignednessSpecified = 0; + } + /* Assume we have an explicit type */ D->Flags &= ~DS_DEF_TYPE; @@ -1176,12 +1201,15 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_LONG: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); OptionalInt (); D->Type[0].C = T_ULONG; D->Type[1].C = T_END; } else { - OptionalSigned (); + OptionalSigned (SignednessSpecified); OptionalInt (); D->Type[0].C = T_LONG; D->Type[1].C = T_END; @@ -1191,12 +1219,15 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_SHORT: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); OptionalInt (); D->Type[0].C = T_USHORT; D->Type[1].C = T_END; } else { - OptionalSigned (); + OptionalSigned (SignednessSpecified); OptionalInt (); D->Type[0].C = T_SHORT; D->Type[1].C = T_END; @@ -1210,6 +1241,9 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) break; case TOK_SIGNED: + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); switch (CurTok.Tok) { @@ -1245,6 +1279,9 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) break; case TOK_UNSIGNED: + if (SignednessSpecified != NULL) { + *SignednessSpecified = 1; + } NextToken (); switch (CurTok.Tok) { @@ -1835,7 +1872,7 @@ Type* ParseType (Type* T) /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL); /* Parse additional declarators */ ParseDecl (&Spec, &Decl, DM_NO_IDENT); @@ -1967,7 +2004,7 @@ void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType) ParseStorageClass (D, DefStorage); /* Parse the type specifiers passing any initial type qualifiers */ - ParseTypeSpec (D, DefType, Qualifiers); + ParseTypeSpec (D, DefType, Qualifiers, NULL); } @@ -2362,6 +2399,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** into 3 bytes. */ SI.ValBits += Entry->V.B.BitWidth; + /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { OutputBitFieldData (&SI); @@ -2393,16 +2431,34 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ParseScalarInitInternal (type_uint, &ED); + ParseScalarInitInternal (Entry->Type, &ED); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); } - if (ED.IVal > (long) Mask) { - Warning ("Truncating value in bit-field initializer"); - ED.IVal &= (long) Mask; + + /* Truncate the initializer value to the width of the bit-field and check if we lost + ** any useful bits. + */ + Val = (unsigned) ED.IVal & Mask; + if (IsSignUnsigned (Entry->Type)) { + if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { + Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %ld to %u", + GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), + Entry->V.B.BitWidth, ED.IVal, Val); + } + } else { + /* Sign extend back to full width of host long. */ + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Entry->V.B.BitWidth; + long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); + if (ED.IVal != RestoredVal) { + Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " + "changes value from %ld to %d", + GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), + Entry->V.B.BitWidth, ED.IVal, Val); + } } - Val = (unsigned) ED.IVal; /* Add the value to the currently stored bit-field value */ Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; @@ -2417,6 +2473,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** aligned, so will have padding before it. */ CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); + /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { OutputBitFieldData (&SI); @@ -2425,7 +2482,8 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } else { /* Standard member. We should never have stuff from a - ** bit-field left + ** bit-field left because an anonymous member was added + ** for padding by ParseStructDecl. */ CHECK (SI.ValBits == 0); diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 452d9a9a3..f3a1a6add 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -123,7 +123,9 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** supported. */ Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; - Flags |= CF_UNSIGNED; + if (IsSignUnsigned (Expr->Type)) { + Flags |= CF_UNSIGNED; + } /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ BitFieldFullWidthFlags = Flags; @@ -133,7 +135,11 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** type is not CF_CHAR. */ if (AdjustBitField) { - Flags |= CF_FORCECHAR; + /* If adjusting, then we're sign extending manually, so do everything unsigned + ** to make shifts faster. + */ + Flags |= CF_UNSIGNED | CF_FORCECHAR; + BitFieldFullWidthFlags |= CF_UNSIGNED; } } else if ((Flags & CF_TYPEMASK) == 0) { Flags |= TypeOf (Expr->Type); @@ -231,7 +237,8 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) if (ED_NeedsTest (Expr)) { g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); } else { - g_extractbitfield (Flags, BitFieldFullWidthFlags, Expr->BitOffs, Expr->BitWidth); + g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), + Expr->BitOffs, Expr->BitWidth); } } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 6e0654576..7e6a9f5bb 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -819,7 +819,8 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl -SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsigned BitWidth) +SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, + unsigned BitOffs, unsigned BitWidth, int SignednessSpecified) /* Add a bit field to the local symbol table and return the symbol entry */ { /* Do we have an entry with this name already? */ @@ -834,12 +835,21 @@ SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsign /* Create a new entry */ Entry = NewSymEntry (Name, SC_BITFIELD); - /* Set the symbol attributes. Bit-fields are always of type unsigned */ - Entry->Type = type_uint; + /* Set the symbol attributes. Bit-fields are always integral types. */ + Entry->Type = TypeDup (T); Entry->V.B.Offs = Offs; Entry->V.B.BitOffs = BitOffs; Entry->V.B.BitWidth = BitWidth; + if (!SignednessSpecified) { + /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, + ** since this is allowed for bit-fields and avoids sign-extension, so is much faster. + */ + CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED); + Entry->Type->C &= ~T_MASK_SIGN; + Entry->Type->C |= T_SIGN_UNSIGNED; + } + /* Add the entry to the symbol table */ AddSymEntry (SymTab, Entry); diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index f04517fed..750e41b54 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -155,7 +155,8 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab); /* Add a struct/union entry and return it */ -SymEntry* AddBitField (const char* Name, unsigned Offs, unsigned BitOffs, unsigned BitWidth); +SymEntry* AddBitField (const char* Name, const Type* Type, unsigned Offs, + unsigned BitOffs, unsigned BitWidth, int SignednessSpecified); /* Add a bit field to the local symbol table and return the symbol entry */ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val); diff --git a/test/todo/bug1095.c b/test/val/bug1095.c similarity index 74% rename from test/todo/bug1095.c rename to test/val/bug1095.c index 0803c2d1c..8c184cd1d 100644 --- a/test/todo/bug1095.c +++ b/test/val/bug1095.c @@ -31,7 +31,10 @@ static struct signed_ints { signed int b : 3; signed int c : 3; signed int d : 10; -} si = {-4, -1, 3, -500}; + signed int : 0; + signed int e : 8; + signed int f : 16; +} si = {-4, -1, 3, -500, -100, -5000}; static void test_signed_bitfield(void) { @@ -53,7 +56,7 @@ static void test_signed_bitfield(void) failures++; } - if (si.b <= 0) { + if (si.c <= 0) { printf("Got si.c = %d, expected positive.\n", si.c); failures++; } @@ -71,10 +74,30 @@ static void test_signed_bitfield(void) failures++; } + if (si.e >= 0) { + printf("Got si.e = %d, expected negative.\n", si.e); + failures++; + } + if (si.e != -100) { + printf("Got si.e = %d, expected -100.\n", si.e); + failures++; + } + + if (si.f >= 0) { + printf("Got si.f = %d, expected negative.\n", si.f); + failures++; + } + if (si.f != -5000) { + printf("Got si.f = %d, expected -5000.\n", si.f); + failures++; + } + si.a = -3; si.b = 1; si.c = -2; si.d = 500; + si.e = 100; + si.f = 5000; if (si.a >= 0) { printf("Got si.a = %d, expected negative.\n", si.a); @@ -94,7 +117,7 @@ static void test_signed_bitfield(void) failures++; } - if (si.b >= 0) { + if (si.c >= 0) { printf("Got si.c = %d, expected negative.\n", si.c); failures++; } @@ -111,6 +134,24 @@ static void test_signed_bitfield(void) printf("Got si.d = %d, expected 500.\n", si.d); failures++; } + + if (si.e <= 0) { + printf("Got si.e = %d, expected positive.\n", si.e); + failures++; + } + if (si.e != 100) { + printf("Got si.e = %d, expected 100.\n", si.e); + failures++; + } + + if (si.f <= 0) { + printf("Got si.f = %d, expected positive.\n", si.f); + failures++; + } + if (si.f != 5000) { + printf("Got si.f = %d, expected 5000.\n", si.f); + failures++; + } } int main(void) From ab89c168decdb959251c28314bd29f20943fba33 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 17:47:35 +0200 Subject: [PATCH 235/806] replace "Copyright 2020 Google LLC" by "Copyright 2020 the cc65 authors" --- test/err/bitfield-named-zero-width.c | 2 +- test/err/bitfield-negative-width.c | 2 +- test/err/bitfield-too-wide.c | 2 +- test/err/bug1047.c | 2 +- test/err/staticassert.c | 2 +- test/val/bitfield.c | 2 +- test/val/bug1095.c | 2 +- test/val/plain-int-bitfield.c | 2 +- test/val/staticassert.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/err/bitfield-named-zero-width.c b/test/err/bitfield-named-zero-width.c index b9b9db88d..323c3d49c 100644 --- a/test/err/bitfield-named-zero-width.c +++ b/test/err/bitfield-named-zero-width.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bitfield-negative-width.c b/test/err/bitfield-negative-width.c index dd83b3fc4..5b14ea7e3 100644 --- a/test/err/bitfield-negative-width.c +++ b/test/err/bitfield-negative-width.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bitfield-too-wide.c b/test/err/bitfield-too-wide.c index 6c9c229fc..da7f69dc2 100644 --- a/test/err/bitfield-too-wide.c +++ b/test/err/bitfield-too-wide.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bug1047.c b/test/err/bug1047.c index 3fb11250f..4020189e0 100644 --- a/test/err/bug1047.c +++ b/test/err/bug1047.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/staticassert.c b/test/err/staticassert.c index df9bab6d8..be991e744 100644 --- a/test/err/staticassert.c +++ b/test/err/staticassert.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/bitfield.c b/test/val/bitfield.c index 67747ed5b..cf96d8929 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/bug1095.c b/test/val/bug1095.c index 8c184cd1d..6872d151c 100644 --- a/test/val/bug1095.c +++ b/test/val/bug1095.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/plain-int-bitfield.c b/test/val/plain-int-bitfield.c index afc7121bb..75e65590c 100644 --- a/test/val/plain-int-bitfield.c +++ b/test/val/plain-int-bitfield.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/staticassert.c b/test/val/staticassert.c index 5d44fed6b..a31a9e646 100644 --- a/test/val/staticassert.c +++ b/test/val/staticassert.c @@ -1,5 +1,5 @@ /* - Copyright 2020 Google LLC + Copyright 2020 the cc65 authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages From 0c22d5011e9d8d92d251072c9dc59009c773ac91 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 17:58:29 +0200 Subject: [PATCH 236/806] added test related to pr #1190 --- test/err/staticassert-nomsg.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/err/staticassert-nomsg.c diff --git a/test/err/staticassert-nomsg.c b/test/err/staticassert-nomsg.c new file mode 100644 index 000000000..e0a1ede69 --- /dev/null +++ b/test/err/staticassert-nomsg.c @@ -0,0 +1,26 @@ +/* + Copyright 2020 the cc65 authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* +** Test of failing _Static_assert. +**/ + + +_Static_assert(0 == 1); From 0690a12ad2fb998ad6e012ed6e9f391f0580b914 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 18 Aug 2020 22:41:42 +0200 Subject: [PATCH 237/806] change "the cc65 authors" to "The cc65 Authors" as per jmrs request --- test/err/bitfield-named-zero-width.c | 2 +- test/err/bitfield-negative-width.c | 2 +- test/err/bitfield-too-wide.c | 2 +- test/err/bug1047.c | 2 +- test/err/staticassert-nomsg.c | 2 +- test/err/staticassert.c | 2 +- test/val/bitfield.c | 2 +- test/val/bug1095.c | 2 +- test/val/plain-int-bitfield.c | 2 +- test/val/staticassert.c | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/err/bitfield-named-zero-width.c b/test/err/bitfield-named-zero-width.c index 323c3d49c..108b195d0 100644 --- a/test/err/bitfield-named-zero-width.c +++ b/test/err/bitfield-named-zero-width.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bitfield-negative-width.c b/test/err/bitfield-negative-width.c index 5b14ea7e3..a90199f38 100644 --- a/test/err/bitfield-negative-width.c +++ b/test/err/bitfield-negative-width.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bitfield-too-wide.c b/test/err/bitfield-too-wide.c index da7f69dc2..424cf9c05 100644 --- a/test/err/bitfield-too-wide.c +++ b/test/err/bitfield-too-wide.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/bug1047.c b/test/err/bug1047.c index 4020189e0..3f1d3cf63 100644 --- a/test/err/bug1047.c +++ b/test/err/bug1047.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/staticassert-nomsg.c b/test/err/staticassert-nomsg.c index e0a1ede69..8cdcb09a4 100644 --- a/test/err/staticassert-nomsg.c +++ b/test/err/staticassert-nomsg.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/err/staticassert.c b/test/err/staticassert.c index be991e744..60cb37529 100644 --- a/test/err/staticassert.c +++ b/test/err/staticassert.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/bitfield.c b/test/val/bitfield.c index cf96d8929..939d90dff 100644 --- a/test/val/bitfield.c +++ b/test/val/bitfield.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/bug1095.c b/test/val/bug1095.c index 6872d151c..cecbf0329 100644 --- a/test/val/bug1095.c +++ b/test/val/bug1095.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/plain-int-bitfield.c b/test/val/plain-int-bitfield.c index 75e65590c..4d158eca9 100644 --- a/test/val/plain-int-bitfield.c +++ b/test/val/plain-int-bitfield.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/test/val/staticassert.c b/test/val/staticassert.c index a31a9e646..e43eeec8d 100644 --- a/test/val/staticassert.c +++ b/test/val/staticassert.c @@ -1,5 +1,5 @@ /* - Copyright 2020 the cc65 authors + Copyright 2020 The cc65 Authors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages From 36dd82f0e61a2c288bf4dda16ef9a3a69bc6c044 Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 18 Aug 2020 19:07:29 -0400 Subject: [PATCH 238/806] Added g_branch() to cc65's code generator. It uses BRA if the platform's CPU has BRA. Else, it generates a JMP. (Used it in the bitfield sign-extending code.) --- src/cc65/codegen.c | 34 +++++++++++++++++++++++++--------- src/cc65/codegen.h | 7 ++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index a0dbc498b..6b6b292b0 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2207,7 +2207,7 @@ void g_restore (unsigned flags) void g_cmp (unsigned flags, unsigned long val) -/* Immidiate compare. The primary register will not be changed, Z flag +/* Immediate compare. The primary register will not be changed, Z flag ** will be set. */ { @@ -2455,6 +2455,21 @@ void g_falsejump (unsigned flags attribute ((unused)), unsigned label) } + +void g_branch (unsigned Label) +/* Branch unconditionally to Label if the CPU has the BRA instruction. +** Otherwise, jump to Label. +*/ +{ + if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0) { + AddCodeLine ("bra %s", LocalLabelName (Label)); + } else { + g_jump (Label); + } +} + + + void g_lateadjustSP (unsigned label) /* Adjust stack based on non-immediate data */ { @@ -2761,12 +2776,14 @@ void g_div (unsigned flags, unsigned long val) AddCodeLine ("lsr a"); g_restore (flags); AddCodeLine ("bcs %s", LocalLabelName (DoShiftLabel)); - + /* The result is 0. We can just load 0 and skip the shifting. */ g_getimmed (flags | CF_ABSOLUTE, 0, 0); + + /* TODO: replace with BEQ? Would it be optimized? */ g_jump (EndLabel); - /* Do the shift. The sign of the result may need be corrected + /* Do the shift. The sign of the result may need to be corrected ** later. */ g_defcodelabel (DoShiftLabel); @@ -4511,18 +4528,17 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned ZeroExtendLabel = GetLocalLabel (); AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel)); - /* Pop A back and sign extend if required; operating on the full result need + /* Pop A back and sign-extend if required; operating on the full result needs ** to sign-extend into high byte, too. */ AddCodeLine ("pla"); g_or (FullWidthFlags | CF_CONST, ~Mask); - /* Apparently, there is no unconditional branch BRA, so use JMP. */ unsigned DoneLabel = GetLocalLabel (); - AddCodeLine ("jmp %s", LocalLabelName (DoneLabel)); + g_branch (DoneLabel); - /* Pop A back, then zero-extend; we need to duplicate the PLA rather than move it before - ** the branch to share with the other label because PLA sets the condition codes. + /* Pop A back, then zero-extend. We need to duplicate the PLA, rather than move it before + ** the branch to share with the other label, because PLA changes some condition codes. */ g_defcodelabel (ZeroExtendLabel); AddCodeLine ("pla"); @@ -4534,7 +4550,7 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, g_defcodelabel (DoneLabel); } else { - /* Unsigned bit-field, only needs zero-extension. */ + /* Unsigned bit-field, needs only zero-extension. */ if (ZeroExtendMask != 0) { g_and (FullWidthFlags | CF_CONST, ZeroExtendMask); } diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index ec4756f2d..d946a9a10 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -380,7 +380,7 @@ void g_restore (unsigned flags); /* Copy hold register to primary. */ void g_cmp (unsigned flags, unsigned long val); -/* Immidiate compare. The primary register will not be changed, Z flag +/* Immediate compare. The primary register will not be changed, Z flag ** will be set. */ @@ -410,6 +410,11 @@ void g_truejump (unsigned flags, unsigned label); void g_falsejump (unsigned flags, unsigned label); /* Jump to label if zero flag set */ +void g_branch (unsigned Label); +/* Branch unconditionally to Label if the CPU has the BRA instruction. +** Otherwise, jump to Label. +*/ + void g_lateadjustSP (unsigned label); /* Adjust stack based on non-immediate data */ From 9fcde120aa1fca60c19f09210adf2557f8888502 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 18 Aug 2020 20:43:19 +0800 Subject: [PATCH 239/806] Made function signatures in asm output use the parameter lists from original definitions instead of the composites. --- src/cc65/datatype.c | 18 ++++++++++++++++-- src/cc65/datatype.h | 3 +++ src/cc65/funcdesc.c | 1 + src/cc65/funcdesc.h | 1 + src/cc65/function.c | 3 +++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index b95e37f87..ad008cfd3 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -596,8 +596,8 @@ void PrintFuncSig (FILE* F, const char* Name, Type* T) StrBuf East = AUTO_STRBUF_INITIALIZER; StrBuf West = AUTO_STRBUF_INITIALIZER; - /* Get the function descriptor */ - const FuncDesc* D = GetFuncDesc (T); + /* Get the function descriptor used in definition */ + const FuncDesc* D = GetFuncDefinitionDesc (T); /* Get the parameter list string. Start from the first parameter */ SymEntry* Param = D->SymTab->SymHead; @@ -1137,6 +1137,20 @@ Type* GetFuncReturn (Type* T) +FuncDesc* GetFuncDefinitionDesc (Type* T) +/* Get the function descriptor of the function definition */ +{ + FuncDesc* D; + + /* Be sure it's a function type */ + CHECK (IsClassFunc (T)); + + D = GetFuncDesc (T); + return D->FuncDef != 0 ? D->FuncDef : D; +} + + + long GetElementCount (const Type* T) /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 472098788..8e9ee4c33 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -836,6 +836,9 @@ void SetFuncDesc (Type* T, FuncDesc* F); Type* GetFuncReturn (Type* T) attribute ((const)); /* Return a pointer to the return type of a function or pointer-to-function type */ +FuncDesc* GetFuncDefinitionDesc (struct Type* T); +/* Get the function descriptor of the function definition */ + long GetElementCount (const Type* T); /* Get the element count of the array specified in T (which must be of ** array type). diff --git a/src/cc65/funcdesc.c b/src/cc65/funcdesc.c index 273263dec..4c959fb6c 100644 --- a/src/cc65/funcdesc.c +++ b/src/cc65/funcdesc.c @@ -60,6 +60,7 @@ FuncDesc* NewFuncDesc (void) F->ParamCount = 0; F->ParamSize = 0; F->LastParam = 0; + F->FuncDef = 0; F->WrappedCall = 0; F->WrappedCallData = 0; diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h index a04ffb14a..1cfb2bd09 100644 --- a/src/cc65/funcdesc.h +++ b/src/cc65/funcdesc.h @@ -68,6 +68,7 @@ struct FuncDesc { unsigned ParamCount; /* Number of parameters */ unsigned ParamSize; /* Size of the parameters */ struct SymEntry* LastParam; /* Pointer to last parameter */ + struct FuncDesc* FuncDef; /* Descriptor used in definition */ struct SymEntry* WrappedCall; /* Pointer to the WrappedCall */ unsigned char WrappedCallData;/* The WrappedCall's user data */ }; diff --git a/src/cc65/function.c b/src/cc65/function.c index fc113b29a..290916cd2 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -382,6 +382,9 @@ void NewFunc (SymEntry* Func, FuncDesc* D) SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ + /* Remember this function descriptor used for definition */ + GetFuncDesc (Func->Type)->FuncDef = D; + /* Allocate the function activation record for the function */ CurrentFunc = NewFunction (Func, D); From 85e8a6cb9fde50cfc54854fb8161fcb0af722d6c Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 19 Aug 2020 09:29:30 +0200 Subject: [PATCH 240/806] Clarify docs that bss is zero-initialized Addresses comment raised in #1202. --- doc/cc65.sgml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index a81358510..e7b8f260d 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -1043,8 +1043,8 @@ parameter with the #pragma bss-name ([push,] <name>)

This pragma changes the name used for the BSS segment (the BSS segment - is used to store uninitialized data). The argument is a string enclosed - in double quotes. + is used to store variables with static storage duration and no explicit + initializer). The argument is a string enclosed in double quotes. Note: The default linker configuration file does only map the standard segments. If you use other segments, you have to create a new linker @@ -1052,7 +1052,8 @@ parameter with the Date: Wed, 19 Aug 2020 14:50:12 +0200 Subject: [PATCH 241/806] rename bdiff.c to isequal.c, make it handle different line-endings as equal --- test/asm/Makefile | 4 ++-- test/bdiff.c | 28 ------------------------ test/dasm/Makefile | 4 ++-- test/isequal.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ test/misc/Makefile | 4 ++-- test/ref/Makefile | 4 ++-- 6 files changed, 62 insertions(+), 36 deletions(-) delete mode 100644 test/bdiff.c create mode 100644 test/isequal.c diff --git a/test/asm/Makefile b/test/asm/Makefile index a0825574c..94a925376 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -24,7 +24,7 @@ CL65 := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) WORKDIR = ../../testwrk/asm -DIFF = $(WORKDIR)/bdiff$(EXE) +DIFF = $(WORKDIR)/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -44,7 +44,7 @@ all: $(OPCODE_BINS) $(CPUDETECT_BINS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(DIFF): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define OPCODE_template diff --git a/test/bdiff.c b/test/bdiff.c deleted file mode 100644 index 797ba4302..000000000 --- a/test/bdiff.c +++ /dev/null @@ -1,28 +0,0 @@ - -// minimal tool to compare two binaries - -#include -#include - -int main(int argc, char *argv[]) -{ - FILE *f1, *f2; - if (argc < 3) { - return EXIT_FAILURE; - } - f1 = fopen(argv[1], "rb"); - f2 = fopen(argv[2], "rb"); - if ((f1 == NULL) || (f2 == NULL)) { - return EXIT_FAILURE; - } - for(;;) { - if (feof(f1) && feof(f2)) { - return EXIT_SUCCESS; - } else if (feof(f1) || feof(f2)) { - return EXIT_FAILURE; - } - if (fgetc(f1) != fgetc(f2)) { - return EXIT_FAILURE; - } - } -} diff --git a/test/dasm/Makefile b/test/dasm/Makefile index d70711491..faa6b7fa0 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -25,7 +25,7 @@ DA65 := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) WORKDIR = ../../testwrk/dasm -DIFF = $(WORKDIR)/bdiff$(EXE) +DIFF = $(WORKDIR)/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -44,7 +44,7 @@ all: $(BINS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(DIFF): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define DISASS_template diff --git a/test/isequal.c b/test/isequal.c new file mode 100644 index 000000000..b3806c7e4 --- /dev/null +++ b/test/isequal.c @@ -0,0 +1,54 @@ + +// minimal tool to compare two text files + +#include +#include + +/* get the next character from FILE and convert commonly used line-endings all + into the same value (0x0a, as used on *nix systems) + + recognized values/pairs: + + 0x0a (LF) Linux, macOS + 0x0d, 0x0a (CR, LF) Windows, MSDOS, OS/2 + 0x0d (CR) classic MacOS +*/ + +int getnext(FILE *f) +{ + int c = fgetc(f); + if (c == 0x0d) { + if (!feof(f)) { + int n = fgetc(f); + if (n != 0x0a) { + ungetc(n, f); + } + clearerr(f); /* clears EOF when we did not push back */ + } + return 0x0a; + } + return c; +} + +int main(int argc, char *argv[]) +{ + FILE *f1, *f2; + if (argc < 3) { + return EXIT_FAILURE; + } + f1 = fopen(argv[1], "rb"); + f2 = fopen(argv[2], "rb"); + if ((f1 == NULL) || (f2 == NULL)) { + return EXIT_FAILURE; + } + for(;;) { + if (feof(f1) && feof(f2)) { + return EXIT_SUCCESS; + } else if (feof(f1) || feof(f2)) { + return EXIT_FAILURE; + } + if (getnext(f1) != getnext(f2)) { + return EXIT_FAILURE; + } + } +} diff --git a/test/misc/Makefile b/test/misc/Makefile index 1d98e2d62..55c19704e 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -37,7 +37,7 @@ WORKDIR = ..$S..$Stestwrk$Smisc OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sbdiff$(EXE) +DIFF = $(WORKDIR)$Sisequal$(EXE) CC = gcc CFLAGS = -O2 @@ -58,7 +58,7 @@ all: $(TESTS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../bdiff.c | $(WORKDIR) +$(DIFF): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define PRG_template diff --git a/test/ref/Makefile b/test/ref/Makefile index a94f65dd8..f88821f64 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -35,7 +35,7 @@ WORKDIR = ..$S..$Stestwrk$Sref OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sbdiff$(EXE) +DIFF = $(WORKDIR)$Sisequal$(EXE) CC = gcc CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow @@ -57,7 +57,7 @@ $(WORKDIR)/%.ref: %.c | $(WORKDIR) $(CC) $(CFLAGS) -o $(WORKDIR)/$*.host $< $(NULLERR) $(WORKDIR)$S$*.host > $@ -$(DIFF): ../bdiff.c | $(WORKDIR) +$(DIFF): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< # "yaccdbg.c" includes "yacc.c". From e6b8f4d71581883083d0bb34e67141ad0b2e53c5 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 19 Aug 2020 22:25:18 +0200 Subject: [PATCH 242/806] move/fix bug264.c as suggested in issue #1122 --- test/err/bug264.c | 62 +++++++++++++++++++++ test/{misc/bug264.c => val/return-struct.c} | 0 2 files changed, 62 insertions(+) create mode 100644 test/err/bug264.c rename test/{misc/bug264.c => val/return-struct.c} (100%) diff --git a/test/err/bug264.c b/test/err/bug264.c new file mode 100644 index 000000000..6898f3790 --- /dev/null +++ b/test/err/bug264.c @@ -0,0 +1,62 @@ +/* bug #264 - cc65 fails to warn about a function returning struct */ + +#include +#include +#include + +typedef uint32_t u32; +typedef uint16_t u16; + +/* this struct is too large, we can only handle max 4 bytes right now */ +typedef struct { + u32 quot; + u32 rem; +} udiv_t; + +udiv_t div3(u32 in) { + + udiv_t u; + u32 q = 0; + + while (in >= 300) { + in -= 300; + q += 100; + } + + while (in >= 30) { + in -= 30; + q += 10; + } + + while (in >= 3) { + in -= 3; + ++q; + } + + u.quot = q; + u.rem = in; + + return u; /* error */ +} + +int res = 0; + +int main(void) { + + u32 i; + div_t d; + udiv_t u; + + for (i = 1024; i; i--) { + d = div((u16)i, 3); + u = div3(i); + + if (d.quot != u.quot || d.rem != u.rem) { + printf("Mismatch at %u/3, div %u %u, div3 %u %u\n", i, + d.quot, d.rem, u.quot, u.rem); + res++; + } + } + + return res; +} diff --git a/test/misc/bug264.c b/test/val/return-struct.c similarity index 100% rename from test/misc/bug264.c rename to test/val/return-struct.c From 794adcc512e8f80eda9de0e852a0443a9c08fc5d Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 19 Aug 2020 22:33:10 +0200 Subject: [PATCH 243/806] remove unneeded rule --- test/misc/Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index 55c19704e..b21384b68 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -81,12 +81,6 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/pptest2.$1.$2.prg) $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# this should fail to compile, because cc65 does not support returning structs -$(WORKDIR)/bug264.$1.$2.prg: bug264.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiles but should give an error." - $(if $(QUIET),echo misc/bug264.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # this should fail to compile, because there are errors in the code # instead, the compiler crashes $(WORKDIR)/bug1113.$1.$2.prg: bug1113.c | $(WORKDIR) From 17bbba7327f3dddbe555c44df964c6ce0f587f53 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:03 +0800 Subject: [PATCH 244/806] Added integer boolean type string. No longer set the "expression tested" flag with constant results in comparison. --- src/cc65/datatype.c | 1 + src/cc65/datatype.h | 1 + src/cc65/expr.c | 10 +++++----- src/cc65/exprdesc.c | 14 ++++++++++++++ src/cc65/exprdesc.h | 3 +++ 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index ad008cfd3..f4f978a3c 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -66,6 +66,7 @@ Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; Type type_ulong[] = { TYPE(T_ULONG), TYPE(T_END) }; +Type type_bool[] = { TYPE(T_INT), TYPE(T_END) }; Type type_void[] = { TYPE(T_VOID), TYPE(T_END) }; Type type_size_t[] = { TYPE(T_SIZE_T), TYPE(T_END) }; Type type_float[] = { TYPE(T_FLOAT), TYPE(T_END) }; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 8e9ee4c33..5c3e71da5 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -196,6 +196,7 @@ extern Type type_int[]; extern Type type_uint[]; extern Type type_long[]; extern Type type_ulong[]; +extern Type type_bool[]; extern Type type_void[]; extern Type type_size_t[]; extern Type type_float[]; diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 12a0c0b57..3d4a5eb1b 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -2595,13 +2595,13 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* The result is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); + + /* Condition codes are set */ + ED_TestDone (Expr); } - /* Result type is always int */ - Expr->Type = type_int; - -Done: /* Condition codes are set */ - ED_TestDone (Expr); + /* Result type is always boolean */ +Done: Expr->Type = type_bool; } } diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 5ff848fb7..d0ee2789a 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -229,6 +229,20 @@ ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) +/* Replace Expr with a constant boolean expression with the given value */ +{ + Expr->Sym = 0; + Expr->Type = type_bool; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Name = 0; + Expr->IVal = Value; + Expr->FVal = FP_D_Make (0.0); + return Expr; +} + + + ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) /* Finalize the result of LoadExpr to be an rvalue in the primary register */ { diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 1541c2e4e..0a0f970a6 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -411,6 +411,9 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type); ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value); /* Replace Expr with an constant integer with the given value */ +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value); +/* Replace Expr with a constant boolean expression with the given value */ + ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr); /* Finalize the result of LoadExpr to be an rvalue in the primary register */ From b15ab348ba51100933d1b0b726f88e61f3f44caa Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 26 Aug 2020 08:23:05 +0800 Subject: [PATCH 245/806] Moved bug250.c from test/misc as it is to be fixed. --- test/misc/Makefile | 6 ------ test/misc/bug250.c | 13 ------------- 2 files changed, 19 deletions(-) delete mode 100644 test/misc/bug250.c diff --git a/test/misc/Makefile b/test/misc/Makefile index b21384b68..e48d86e30 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -63,12 +63,6 @@ $(DIFF): ../isequal.c | $(WORKDIR) define PRG_template -# should compile, but gives an error -$(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug250.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but gives an error $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/misc/bug250.c b/test/misc/bug250.c deleted file mode 100644 index 60f3c633d..000000000 --- a/test/misc/bug250.c +++ /dev/null @@ -1,13 +0,0 @@ -/* bug #250 - Array size compile-time optimization stops halfway */ - -#include - -#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) -unsigned char c[2*4]; -unsigned char b[2*LZO_MAX(8,sizeof(int))]; // this will not compile - -int main(void) -{ - /* FIXME: add some runtime check */ - return EXIT_SUCCESS; -} From 3ea3887c77d8a6bc0bc784b0fa5bc2917b988be4 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:06 +0800 Subject: [PATCH 246/806] Fixed warnings on const comparison. Warnings on expressions with no effects. Fixed const ternary. Fixed ternary with struct/union types as the true/false-branch expressions. --- src/cc65/asmstmt.c | 8 ++ src/cc65/assignment.c | 1 + src/cc65/declare.c | 11 +- src/cc65/expr.c | 263 ++++++++++++++++++++++++++++------------ src/cc65/exprdesc.c | 27 ++++- src/cc65/exprdesc.h | 131 +++++++++++++++++--- src/cc65/goto.c | 2 + src/cc65/loadexpr.c | 6 +- src/cc65/locals.c | 15 ++- src/cc65/preproc.c | 1 + src/cc65/shiftexpr.c | 5 +- src/cc65/staticassert.c | 2 + src/cc65/stdfunc.c | 5 + src/cc65/stmt.c | 36 +++--- src/cc65/swstmt.c | 5 +- src/cc65/testexpr.c | 8 +- src/cc65/typeconv.c | 5 + 17 files changed, 399 insertions(+), 132 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index ae4308522..20de6033e 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -132,6 +132,8 @@ static void ParseByteArg (StrBuf* T, unsigned Arg) ExprDesc Expr; char Buf [16]; + ED_Init (&Expr); + /* We expect an argument separated by a comma */ ConsumeComma (); @@ -166,6 +168,8 @@ static void ParseWordArg (StrBuf* T, unsigned Arg) ExprDesc Expr; char Buf [16]; + ED_Init (&Expr); + /* We expect an argument separated by a comma */ ConsumeComma (); @@ -200,6 +204,8 @@ static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) ExprDesc Expr; char Buf [16]; + ED_Init (&Expr); + /* We expect an argument separated by a comma */ ConsumeComma (); @@ -309,6 +315,8 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) ExprDesc Expr; char Buf [64]; + ED_Init (&Expr); + /* We expect an argument separated by a comma */ ConsumeComma (); diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 6acab3fe8..71df0c4f2 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -139,6 +139,7 @@ void Assignment (ExprDesc* Expr) ExprDesc Expr2; Type* ltype = Expr->Type; + ED_Init (&Expr2); /* We must have an lvalue for an assignment */ if (ED_IsRVal (Expr)) { diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ccd4e9004..7e5f24b24 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -591,6 +591,7 @@ static SymEntry* ParseEnumDecl (const char* Name) if (CurTok.Tok == TOK_ASSIGN) { ExprDesc Expr; + ED_Init (&Expr); NextToken (); ConstAbsIntExpr (hie1, &Expr); EnumVal = Expr.IVal; @@ -720,6 +721,7 @@ static int ParseFieldWidth (Declaration* Decl) */ { ExprDesc Expr; + ED_Init (&Expr); if (CurTok.Tok != TOK_COLON) { /* No bit-field declaration */ @@ -1818,6 +1820,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { ExprDesc Expr; + ED_Init (&Expr); ConstAbsIntExpr (hie1, &Expr); if (Expr.IVal <= 0) { if (D->Ident[0] != '\0') { @@ -2184,6 +2187,7 @@ static unsigned ParseScalarInit (Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { ExprDesc ED; + ED_Init (&ED); /* Parse initialization */ ParseScalarInitInternal (T, &ED); @@ -2205,6 +2209,8 @@ static unsigned ParsePointerInit (Type* T) /* Expression */ ExprDesc ED; + ED_Init (&ED); + ConstExpr (hie1, &ED); TypeConversion (&ED, T); @@ -2420,6 +2426,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** handling. */ ExprDesc ED; + ED_Init (&ED); unsigned Val; unsigned Shift; @@ -2541,7 +2548,6 @@ static unsigned ParseVoidInit (Type* T) ** Return the number of bytes initialized. */ { - ExprDesc Expr; unsigned Size; /* Opening brace */ @@ -2550,6 +2556,9 @@ static unsigned ParseVoidInit (Type* T) /* Allow an arbitrary list of values */ Size = 0; do { + ExprDesc Expr; + ED_Init (&Expr); + ConstExpr (hie1, &Expr); switch (GetUnderlyingTypeCode (&Expr.Type[0])) { diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 3d4a5eb1b..62136e122 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -311,13 +311,15 @@ void PushAddr (const ExprDesc* Expr) -static void WarnConstCompareResult (void) +static void WarnConstCompareResult (const ExprDesc* Expr) /* If the result of a comparison is constant, this is suspicious when not in ** preprocessor mode. */ { - if (!Preprocessing && IS_Get (&WarnConstComparison) != 0) { - Warning ("Result of comparison is constant"); + if (!Preprocessing && + !ED_NeedsConst (Expr) && + IS_Get (&WarnConstComparison) != 0) { + Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); } } @@ -387,6 +389,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) unsigned Flags; ExprDesc Expr; + ED_Init (&Expr); /* Count arguments */ ++PushedCount; @@ -727,23 +730,20 @@ static void Primary (ExprDesc* E) { SymEntry* Sym; - /* Initialize fields in the expression stucture */ - ED_Init (E); - /* Character and integer constants. */ if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { - E->IVal = CurTok.IVal; - E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; + E->IVal = CurTok.IVal; + E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; NextToken (); return; } /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { - E->FVal = CurTok.FVal; - E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; + E->FVal = CurTok.FVal; + E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; NextToken (); return; } @@ -777,6 +777,8 @@ static void Primary (ExprDesc* E) return; } + unsigned Flags = E->Flags & E_MASK_KEEP_MAKE; + switch (CurTok.Tok) { case TOK_BOOL_AND: @@ -812,8 +814,8 @@ static void Primary (ExprDesc* E) /* Cannot use type symbols */ Error ("Variable identifier expected"); /* Assume an int type to make E valid */ - E->Flags = E_LOC_STACK | E_RTYPE_LVAL; - E->Type = type_int; + E->Flags |= E_LOC_STACK | E_RTYPE_LVAL; + E->Type = type_int; return; } @@ -924,7 +926,7 @@ static void Primary (ExprDesc* E) case TOK_ASM: /* ASM statement */ AsmStatement (); - E->Flags = E_RTYPE_RVAL; + E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED; E->Type = type_void; break; @@ -985,6 +987,8 @@ static void Primary (ExprDesc* E) ED_MakeConstAbsInt (E, 1); break; } + + E->Flags |= Flags; } @@ -1000,6 +1004,8 @@ static void ArrayRef (ExprDesc* Expr) Type* ElementType; Type* tptr1; + ED_Init (&Subscript); + Subscript.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Skip the bracket */ NextToken (); @@ -2029,7 +2035,6 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ int* UsedGen) /* Helper function */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; const GenDesc* Gen; @@ -2044,6 +2049,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ *UsedGen = 0; while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Tell the caller that we handled it's ops */ *UsedGen = 1; @@ -2248,7 +2257,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ void (*hienext) (ExprDesc*)) /* Helper function for the compare operators */ { - ExprDesc Expr2; CodeMark Mark0; CodeMark Mark1; CodeMark Mark2; @@ -2263,6 +2271,10 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Remember the generator function */ void (*GenFunc) (unsigned, unsigned long) = Gen->Func; @@ -2362,11 +2374,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Check for const operands */ if (ED_IsConstAbs (Expr) && rconst) { - /* If the result is constant, this is suspicious when not in - ** preprocessor mode. - */ - WarnConstCompareResult (); - /* Both operands are constant, remove the generated code */ RemoveCode (&Mark1); @@ -2403,6 +2410,11 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } + /* If the result is constant, this is suspicious when not in + ** preprocessor mode. + */ + WarnConstCompareResult (Expr); + } else { /* Determine the signedness of the operands */ @@ -2461,7 +2473,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_EQ: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, 0); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2469,7 +2481,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_NE: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, 1); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2477,7 +2489,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LT: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2485,7 +2497,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LE: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2493,7 +2505,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GE: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2501,7 +2513,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GT: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2635,6 +2647,9 @@ static void parseadd (ExprDesc* Expr) Type* lhst; /* Type of left hand side */ Type* rhst; /* Type of right hand side */ + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Skip the PLUS token */ NextToken (); @@ -2869,6 +2884,8 @@ static void parsesub (ExprDesc* Expr) CodeMark Mark2; /* Another position in the queue */ int rscale; /* Scale factor for the result */ + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* lhs cannot be function or pointer to function */ if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) { @@ -3130,11 +3147,12 @@ static void hieAndPP (ExprDesc* Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - ConstAbsIntExpr (hie2, Expr); while (CurTok.Tok == TOK_BOOL_AND) { + ExprDesc Expr2; + ED_Init (&Expr2); + /* Skip the && */ NextToken (); @@ -3153,11 +3171,12 @@ static void hieOrPP (ExprDesc *Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - ConstAbsIntExpr (hieAndPP, Expr); while (CurTok.Tok == TOK_BOOL_OR) { + ExprDesc Expr2; + ED_Init (&Expr2); + /* Skip the && */ NextToken (); @@ -3175,8 +3194,6 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) /* Process "exp && exp" */ { int FalseLab; - ExprDesc Expr2; - ExprWithCheck (hie2, Expr); if (CurTok.Tok == TOK_BOOL_AND) { @@ -3186,10 +3203,8 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) /* Get a label that we will use for false expressions */ FalseLab = GetLocalLabel (); - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); - } + /* Set the test flag */ + ED_RequireTest (Expr); /* Load the value */ LoadExpr (CF_FORCECHAR, Expr); @@ -3200,14 +3215,15 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) /* Parse more boolean and's */ while (CurTok.Tok == TOK_BOOL_AND) { + ExprDesc Expr2; + ED_Init (&Expr2); + /* Skip the && */ NextToken (); /* Get rhs */ hie2 (&Expr2); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); - } + ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); /* Do short circuit evaluation */ @@ -3233,7 +3249,6 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) static void hieOr (ExprDesc *Expr) /* Process "exp || exp". */ { - ExprDesc Expr2; int BoolOp = 0; /* Did we have a boolean op? */ int AndOp; /* Did we have a && operation? */ unsigned TrueLab; /* Jump to this label if true */ @@ -3248,10 +3263,8 @@ static void hieOr (ExprDesc *Expr) /* Any boolean or's? */ if (CurTok.Tok == TOK_BOOL_OR) { - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); - } + /* Set the test flag */ + ED_RequireTest (Expr); /* Get first expr */ LoadExpr (CF_FORCECHAR, Expr); @@ -3269,15 +3282,16 @@ static void hieOr (ExprDesc *Expr) /* while there's more expr */ while (CurTok.Tok == TOK_BOOL_OR) { + ExprDesc Expr2; + ED_Init (&Expr2); + /* skip the || */ NextToken (); /* Get a subexpr */ AndOp = 0; hieAnd (&Expr2, TrueLab, &AndOp); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); - } + ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); /* If there is more to come, add shortcut boolean eval. */ @@ -3308,6 +3322,7 @@ static void hieQuest (ExprDesc* Expr) { int FalseLab; int TrueLab; + CodeMark SkippedBranch; CodeMark TrueCodeEnd; ExprDesc Expr2; /* Expression 2 */ ExprDesc Expr3; /* Expression 3 */ @@ -3315,6 +3330,10 @@ static void hieQuest (ExprDesc* Expr) int Expr3IsNULL; /* Expression 3 is a NULL pointer */ Type* ResultType; /* Type of result */ + ED_Init (&Expr2); + Expr2.Flags = Expr->Flags & E_MASK_KEEP_RESULT; + ED_Init (&Expr3); + Expr3.Flags = Expr->Flags & E_MASK_KEEP_RESULT; /* Call the lower level eval routine */ if (Preprocessing) { @@ -3325,14 +3344,20 @@ static void hieQuest (ExprDesc* Expr) /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { + + int ConstantCond = ED_IsConstAbsInt (Expr); NextToken (); - if (!ED_IsTested (Expr)) { + + if (!ConstantCond) { /* Condition codes not set, request a test */ - ED_MarkForTest (Expr); + ED_RequireTest (Expr); + LoadExpr (CF_NONE, Expr); + FalseLab = GetLocalLabel (); + g_falsejump (CF_NONE, FalseLab); + } else if (Expr->IVal == 0) { + /* Remember the current code position */ + GetCodePos (&SkippedBranch); } - LoadExpr (CF_NONE, Expr); - FalseLab = GetLocalLabel (); - g_falsejump (CF_NONE, FalseLab); /* Parse second expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. @@ -3340,22 +3365,37 @@ static void hieQuest (ExprDesc* Expr) ExprWithCheck (hie1, &Expr2); Expr2IsNULL = ED_IsNullPtr (&Expr2); if (!IsTypeVoid (Expr2.Type)) { - /* Load it into the primary */ - LoadExpr (CF_NONE, &Expr2); - ED_FinalizeRValLoad (&Expr2); + if (!ConstantCond || !ED_IsConst (&Expr2)) { + /* Load it into the primary */ + LoadExpr (CF_NONE, &Expr2); + ED_FinalizeRValLoad (&Expr2); + } Expr2.Type = PtrConversion (Expr2.Type); } - /* Remember the current code position */ - GetCodePos (&TrueCodeEnd); + if (!ConstantCond) { + /* Remember the current code position */ + GetCodePos (&TrueCodeEnd); - /* Jump around the evaluation of the third expression */ - TrueLab = GetLocalLabel (); - ConsumeColon (); - g_jump (TrueLab); + /* Jump around the evaluation of the third expression */ + TrueLab = GetLocalLabel (); - /* Jump here if the first expression was false */ - g_defcodelabel (FalseLab); + ConsumeColon (); + + g_jump (TrueLab); + + /* Jump here if the first expression was false */ + g_defcodelabel (FalseLab); + } else { + if (Expr->IVal == 0) { + /* Remove the load code of Expr2 */ + RemoveCode (&SkippedBranch); + } else { + /* Remember the current code position */ + GetCodePos (&SkippedBranch); + } + ConsumeColon(); + } /* Parse third expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. @@ -3363,12 +3403,19 @@ static void hieQuest (ExprDesc* Expr) ExprWithCheck (hie1, &Expr3); Expr3IsNULL = ED_IsNullPtr (&Expr3); if (!IsTypeVoid (Expr3.Type)) { - /* Load it into the primary */ - LoadExpr (CF_NONE, &Expr3); - ED_FinalizeRValLoad (&Expr3); + if (!ConstantCond || !ED_IsConst (&Expr3)) { + /* Load it into the primary */ + LoadExpr (CF_NONE, &Expr3); + ED_FinalizeRValLoad (&Expr3); + } Expr3.Type = PtrConversion (Expr3.Type); } + if (ConstantCond && Expr->IVal != 0) { + /* Remove the load code of Expr3 */ + RemoveCode (&SkippedBranch); + } + /* Check if any conversions are needed, if so, do them. ** Conversion rules for ?: expression are: ** - if both expressions are int expressions, default promotion @@ -3401,9 +3448,11 @@ static void hieQuest (ExprDesc* Expr) TypeConversion (&Expr2, ResultType); GetCodePos (&CvtCodeEnd); - /* If we had conversion code, move it to the right place */ - if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) { - MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd); + if (!ConstantCond) { + /* If we had conversion code, move it to the right place */ + if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) { + MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd); + } } } else if (IsClassPtr (Expr2.Type) && IsClassPtr (Expr3.Type)) { @@ -3423,16 +3472,30 @@ static void hieQuest (ExprDesc* Expr) /* Result type is void */ ResultType = Expr3.Type; } else { - TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, - "Incompatible types in ternary '%s' with '%s'"); - ResultType = Expr2.Type; /* Doesn't matter here */ + if (IsClassStruct (Expr2.Type) && IsClassStruct (Expr3.Type) && + TypeCmp (Expr2.Type, Expr3.Type) == TC_IDENTICAL) { + /* Result type is struct/union */ + ResultType = Expr2.Type; + } else { + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, + "Incompatible types in ternary '%s' with '%s'"); + ResultType = Expr2.Type; /* Doesn't matter here */ + } } - /* Define the final label */ - g_defcodelabel (TrueLab); + if (!ConstantCond) { + /* Define the final label */ + g_defcodelabel (TrueLab); + ED_FinalizeRValLoad (Expr); + } else { + if (Expr->IVal != 0) { + *Expr = Expr2; + } else { + *Expr = Expr3; + } + } /* Setup the target expression */ - ED_FinalizeRValLoad (Expr); Expr->Type = ResultType; } } @@ -3442,7 +3505,6 @@ static void hieQuest (ExprDesc* Expr) static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) /* Process "op=" operators. */ { - ExprDesc Expr2; unsigned flags; CodeMark Mark; int MustScale; @@ -3483,6 +3545,10 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) GetCodePos (&Mark); g_push (flags, 0); + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Evaluate the rhs */ MarkedExprWithCheck (hie1, &Expr2); @@ -3563,6 +3629,8 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) unsigned rflags; int MustScale; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* We're currently only able to handle some addressing modes */ if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { @@ -3756,8 +3824,33 @@ void hie1 (ExprDesc* Expr) void hie0 (ExprDesc *Expr) /* Parse comma operator. */ { + unsigned Flags = Expr->Flags & E_MASK_KEEP_MAKE; + unsigned PrevErrorCount = ErrorCount; + CodeMark Start, End; + + /* Remember the current code position */ + GetCodePos (&Start); + hie1 (Expr); while (CurTok.Tok == TOK_COMMA) { + /* If the expression didn't generate code or isn't cast to type void, + ** emit a warning. + */ + GetCodePos (&End); + if (!ED_MayHaveNoEffect (Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + + PrevErrorCount = ErrorCount; + /* Remember the current code position */ + GetCodePos (&Start); + + /* Reset the expression */ + ED_Init (Expr); + Expr->Flags = Flags; NextToken (); hie1 (Expr); } @@ -3791,8 +3884,18 @@ int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr) void Expression0 (ExprDesc* Expr) /* Evaluate an expression via hie0 and put the result into the primary register */ { + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + /* Only check further after the expression is evaluated */ ExprWithCheck (hie0, Expr); - LoadExpr (CF_NONE, Expr); + + if ((Expr->Flags & Flags & E_MASK_EVAL) != (Flags & E_MASK_EVAL)) { + Internal ("Expression flags tampered: %08X", Flags); + } + + if (ED_YetToLoad (Expr)) { + LoadExpr (CF_NONE, Expr); + } } @@ -3804,6 +3907,7 @@ void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) ** result from this input error. */ { + Expr->Flags |= E_EVAL_CONST; ExprWithCheck (Func, Expr); if (!ED_IsConst (Expr)) { Error ("Constant expression expected"); @@ -3838,6 +3942,7 @@ void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) ** errors that result from this input error. */ { + Expr->Flags |= E_EVAL_CONST; ExprWithCheck (Func, Expr); if (!ED_IsConstAbsInt (Expr)) { Error ("Constant integer expression expected"); diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index d0ee2789a..c3f4c63f7 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -58,7 +58,7 @@ ExprDesc* ED_Init (ExprDesc* Expr) { Expr->Sym = 0; Expr->Type = 0; - Expr->Flags = 0; + Expr->Flags = E_NEED_EAX; Expr->Name = 0; Expr->IVal = 0; Expr->FVal = FP_D_Make (0.0); @@ -113,6 +113,24 @@ int ED_IsIndExpr (const ExprDesc* Expr) +int ED_YetToLoad (const ExprDesc* Expr) +/* Check if the expression needs to be loaded somehow. */ +{ + return ED_NeedsPrimary (Expr) || + ED_YetToTest (Expr) || + (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)); +} + + + +void ED_MarkForUneval (ExprDesc* Expr) +/* Mark the expression as not to be evaluated */ +{ + Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL; +} + + + void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) /* Set the code range for this expression */ { @@ -206,7 +224,7 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) { Expr->Sym = 0; Expr->Type = Type; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; Expr->FVal = FP_D_Make (0.0); @@ -220,7 +238,7 @@ ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) { Expr->Sym = 0; Expr->Type = type_int; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; Expr->FVal = FP_D_Make (0.0); @@ -412,7 +430,8 @@ int ED_IsBool (const ExprDesc* Expr) /* Either ints, floats, or pointers can be used in a boolean context */ return IsClassInt (Expr->Type) || IsClassFloat (Expr->Type) || - IsClassPtr (Expr->Type); + IsClassPtr (Expr->Type) || + IsClassFunc (Expr->Type); } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 0a0f970a6..68db8b2fe 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -94,7 +94,7 @@ enum { ** E_LOC_ -- dereference -> E_LOC_EXPR (pointed-to-value, must load) ** + E_ADDRESS_OF -- dereference -> (lvalue reference) */ - E_MASK_LOC = 0x00FF, + E_MASK_LOC = 0x01FF, E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ @@ -104,29 +104,58 @@ enum { E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ E_LOC_EXPR = 0x0040, /* A location that the primary register points to */ E_LOC_LITERAL = 0x0080, /* Literal in the literal pool */ + E_LOC_CODE = 0x0100, /* C code label location (&&Label) */ - /* Constant location of some sort (only if rval) */ + /* Immutable location addresses (immutable bases and offsets) */ E_LOC_CONST = E_LOC_NONE | E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | - E_LOC_REGISTER | E_LOC_LITERAL, + E_LOC_REGISTER | E_LOC_LITERAL | E_LOC_CODE, + + /* Not-so-immutable location addresses (stack offsets can change) */ + E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, + + /* Expression type modifiers */ + E_BITFIELD = 0x0200, /* Expression is a bit-field */ + E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */ /* lvalue/rvalue in C language's sense */ - E_MASK_RTYPE = 0x0100, + E_MASK_RTYPE = 0x0800, E_RTYPE_RVAL = 0x0000, - E_RTYPE_LVAL = 0x0100, + E_RTYPE_LVAL = 0x0800, - /* Bit-field? */ - E_BITFIELD = 0x0200, + /* Expression status */ + E_LOADED = 0x1000, /* Expression is loaded in primary */ + E_CC_SET = 0x2000, /* Condition codes are set */ + E_HAVE_MARKS = 0x4000, /* Code marks are valid */ - /* Test */ - E_NEED_TEST = 0x0400, /* Expression needs a test to set cc */ - E_CC_SET = 0x0800, /* Condition codes are set */ + /* Optimization hints */ + E_MASK_NEED = 0x030000, + E_NEED_EAX = 0x000000, /* Expression needs to be loaded in Primary */ + E_NEED_NONE = 0x010000, /* Expression value is unused */ + E_NEED_TEST = 0x020000, /* Expression needs a test to set cc */ - E_HAVE_MARKS = 0x1000, /* Code marks are valid */ + /* Expression evaluation requirements. + ** Usage: (Flags & E_EVAL_) == E_EVAL_ + */ + E_MASK_EVAL = 0xFC0000, + E_EVAL_NONE = 0x000000, /* No requirements */ + E_EVAL_CONST = 0x040000, /* Result must be immutable */ + E_EVAL_COMPILE_TIME = 0x0C0000, /* Result must be known at compile time */ + E_EVAL_PURE = 0x100000, /* Evaluation must have no side effects */ + E_EVAL_STATIC = 0x340000, /* Evaluation must generate no code */ + E_EVAL_MAYBE_UNUSED = 0x400000, /* Result may be unused */ + E_EVAL_UNEVAL = 0xC00000, /* Expression is unevaluated */ - E_LOADED = 0x4000, /* Expression is loaded in primary */ + /* Expression must be static and have result known at compile time */ + E_EVAL_C_CONST = E_EVAL_COMPILE_TIME | E_EVAL_STATIC, - E_ADDRESS_OF = 0x8000, /* Expression is the address of the lvalue */ + /* Flags to keep in subexpressions */ + E_MASK_KEEP_SUBEXPR = E_MASK_EVAL, + /* Flags to keep in ternary subexpressions */ + E_MASK_KEEP_RESULT = E_MASK_NEED | E_MASK_EVAL, + + /* Flags to keep when using the ED_Make functions */ + E_MASK_KEEP_MAKE = E_HAVE_MARKS | E_MASK_KEEP_RESULT, }; /* Forward */ @@ -292,13 +321,33 @@ void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); /* Make this expression a bit field expression */ #if defined(HAVE_INLINE) -INLINE void ED_MarkForTest (ExprDesc* Expr) +INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ { Expr->Flags |= E_NEED_TEST; } #else -# define ED_MarkForTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_GetNeeds (const ExprDesc* Expr) +/* Get flags about what the expression needs. */ +{ + return (Expr->Flags & E_MASK_NEED); +} +#else +# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsPrimary (const ExprDesc* Expr) +/* Check if the expression needs to be in Primary. */ +{ + return (Expr->Flags & E_MASK_NEED) == E_NEED_EAX; +} +#else +# define ED_NeedsPrimary(Expr) (((Expr)->Flags & E_MASK_NEED) == E_NEED_EAX) #endif #if defined(HAVE_INLINE) @@ -311,15 +360,25 @@ INLINE int ED_NeedsTest (const ExprDesc* Expr) # define ED_NeedsTest(Expr) (((Expr)->Flags & E_NEED_TEST) != 0) #endif +#if defined(HAVE_INLINE) +INLINE int ED_YetToTest (const ExprDesc* Expr) +/* Check if the expression needs to be tested but not yet. */ +{ + return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST; +} +#else +# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST) +#endif + #if defined(HAVE_INLINE) INLINE void ED_TestDone (ExprDesc* Expr) /* Mark the expression as tested and condition codes set. */ { - Expr->Flags = (Expr->Flags & ~E_NEED_TEST) | E_CC_SET; + Expr->Flags |= E_CC_SET; } #else # define ED_TestDone(Expr) \ - do { (Expr)->Flags = ((Expr)->Flags & ~E_NEED_TEST) | E_CC_SET; } while (0) + do { (Expr)->Flags |= E_CC_SET; } while (0) #endif #if defined(HAVE_INLINE) @@ -354,6 +413,42 @@ INLINE int ED_IsLoaded (const ExprDesc* Expr) # define ED_IsLoaded(Expr) (((Expr)->Flags & E_LOADED) != 0) #endif +int ED_YetToLoad (const ExprDesc* Expr); +/* Check if the expression is yet to be loaded somehow. */ + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsConst (const ExprDesc* Expr) +/* Check if the expression need be immutable */ +{ + return (Expr->Flags & E_EVAL_CONST) == E_EVAL_CONST; +} +#else +# define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_CONST) == E_EVAL_CONST) +#endif + +void ED_MarkForUneval (ExprDesc* Expr); +/* Mark the expression as not to be evaluated */ + +#if defined(HAVE_INLINE) +INLINE int ED_MayBeUneval (const ExprDesc* Expr) +/* Check if the expression may be uevaluated */ +{ + return (Expr->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL; +} +#else +# define ED_MayBeUneval(Expr) (((Expr)->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr) +/* Check if the expression may be present without effects */ +{ + return (Expr->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED; +} +#else +# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED) +#endif + #if defined(HAVE_INLINE) INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -522,7 +617,7 @@ int ED_IsNullPtr (const ExprDesc* Expr); int ED_IsBool (const ExprDesc* Expr); /* Return true of the expression can be treated as a boolean, that is, it can -** be an operand to a compare operation. +** be an operand to a compare operation with 0/NULL. */ void PrintExprDesc (FILE* F, ExprDesc* Expr); diff --git a/src/cc65/goto.c b/src/cc65/goto.c index f1fb725d2..06364068f 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -80,6 +80,8 @@ void GotoStatement (void) CodeEntry *E; unsigned char val; + ED_Init (&desc); + NextToken (); /* arr[foo], we only support simple foo for now */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index f3a1a6add..5c5434393 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -145,7 +145,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) Flags |= TypeOf (Expr->Type); } - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { /* If we're only testing, we do not need to promote char to int. ** CF_FORCECHAR does nothing if the type is not CF_CHAR. */ @@ -234,7 +234,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ CHECK (Expr->BitOffs < CHAR_BITS); - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); } else { g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), @@ -252,7 +252,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) LoadAddress (Flags, Expr); /* Are we testing this value? */ - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { /* Yes, force a test */ g_test (Flags); ED_TestDone (Expr); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index a21a09e8e..87c8e6cb9 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -122,8 +122,6 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -152,6 +150,9 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Parse the expression */ hie1 (&Expr); @@ -203,8 +204,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -242,6 +241,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate previously reserved local space */ F_AllocLocalSpace (CurrentFunc); @@ -303,8 +305,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Allow assignments */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -328,6 +328,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate space for the variable */ AllocStorage (DataLabel, g_usebss, Size); diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 1c4837d94..518570ff5 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1043,6 +1043,7 @@ static int DoIf (int Skip) /* Process #if directive */ { ExprDesc Expr; + ED_Init (&Expr); /* We're about to abuse the compiler expression parser to evaluate the ** #if expression. Save the current tokens to come back here later. diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index 9fe9d1188..0c69e96e5 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -61,7 +61,6 @@ void ShiftExpr (struct ExprDesc* Expr) /* Parse the << and >> operators. */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; token_t Tok; /* The operator token */ @@ -78,6 +77,10 @@ void ShiftExpr (struct ExprDesc* Expr) while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* All operators that call this function expect an int on the lhs */ if (!IsClassInt (Expr->Type)) { Error ("Integer expression expected"); diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 68f0a41bc..b963947b6 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -55,6 +55,8 @@ void ParseStaticAssert () ExprDesc Expr; int failed; + ED_Init (&Expr); + /* Skip the _Static_assert token itself */ CHECK (CurTok.Tok == TOK_STATIC_ASSERT); NextToken (); diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 94e56a625..8335177bb 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -152,6 +152,9 @@ static void ParseArg (ArgDesc* Arg, Type* Type) /* Remember the required argument type */ Arg->ArgType = Type; + /* Init expression */ + ED_Init (&Arg->Expr); + /* Read the expression we're going to pass to the function */ MarkedExprWithCheck (hie1, &Arg->Expr); @@ -1180,6 +1183,8 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) long ECount; unsigned L; + ED_Init (&Arg); + /* Setup the argument type string */ ArgType[1].C = T_CHAR | T_QUAL_CONST; diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 036cc2d89..ba9ae389f 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -312,6 +312,7 @@ static void ReturnStatement (void) ExprDesc Expr; const Type* ReturnType; + ED_Init (&Expr); NextToken (); if (CurTok.Tok != TOK_SEMI) { @@ -424,8 +425,6 @@ static void ContinueStatement (void) static void ForStatement (void) /* Handle a 'for' statement */ { - ExprDesc lval1; - ExprDesc lval3; int HaveIncExpr; CodeMark IncExprStart; CodeMark IncExprEnd; @@ -450,6 +449,10 @@ static void ForStatement (void) /* Parse the initializer expression */ if (CurTok.Tok != TOK_SEMI) { + /* The value of the expression is unused */ + ExprDesc lval1; + ED_Init (&lval1); + lval1.Flags = E_NEED_NONE; Expression0 (&lval1); } ConsumeSemi (); @@ -475,6 +478,10 @@ static void ForStatement (void) /* Parse the increment expression */ HaveIncExpr = (CurTok.Tok != TOK_RPAREN); if (HaveIncExpr) { + /* The value of the expression is unused */ + ExprDesc lval3; + ED_Init (&lval3); + lval3.Flags = E_NEED_NONE; Expression0 (&lval3); } @@ -580,8 +587,10 @@ int Statement (int* PendingToken) { ExprDesc Expr; int GotBreak; + unsigned PrevErrorCount; CodeMark Start, End; - unsigned PrevErrorCount = ErrorCount; + + ED_Init (&Expr); /* Assume no pending token */ if (PendingToken) { @@ -666,25 +675,24 @@ int Statement (int* PendingToken) break; default: - /* Remember the current code position */ + /* Remember the current error count and code position */ + PrevErrorCount = ErrorCount; GetCodePos (&Start); + /* Actual statement */ - ExprWithCheck (hie0, &Expr); - /* Load the result only if it is an lvalue and the type is - ** marked as volatile. Otherwise the load is useless. - */ - if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) { - LoadExpr (CF_NONE, &Expr); - } + Expr.Flags |= E_NEED_NONE; + Expression0 (&Expr); + /* If the statement didn't generate code, and is not of type ** void, emit a warning. */ GetCodePos (&End); - if (CodeRangeIsEmpty (&Start, &End) && - !IsTypeVoid (Expr.Type) && + if (!ED_YetToLoad (&Expr) && + !ED_MayHaveNoEffect (&Expr) && + CodeRangeIsEmpty (&Start, &End) && IS_Get (&WarnNoEffect) && PrevErrorCount == ErrorCount) { - Warning ("Statement has no effect"); + Warning ("Expression result unused"); } CheckSemi (PendingToken); } diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 8cab93bba..524a31ddf 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -97,7 +97,6 @@ void SwitchStatement (void) SwitchCtrl* OldSwitch; /* Pointer to old switch control data */ SwitchCtrl SwitchData; /* New switch data */ - /* Eat the "switch" token */ NextToken (); @@ -105,6 +104,8 @@ void SwitchStatement (void) ** integer type. */ ConsumeLParen (); + + ED_Init (&SwitchExpr); Expression0 (&SwitchExpr); if (!IsClassInt (SwitchExpr.Type)) { Error ("Switch quantity is not an integer"); @@ -211,11 +212,11 @@ void CaseLabel (void) long Val; /* Case label value */ unsigned CodeLabel; /* Code label for this case */ - /* Skip the "case" token */ NextToken (); /* Read the selector expression */ + ED_Init (&CaseExpr); ConstAbsIntExpr (hie1, &CaseExpr); Val = CaseExpr.IVal; diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index 5bb7bf7e4..b924eaa40 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -58,6 +58,8 @@ unsigned Test (unsigned Label, int Invert) ExprDesc Expr; unsigned Result; + ED_Init (&Expr); + /* Read a boolean expression */ BoolExpr (hie0, &Expr); @@ -80,10 +82,8 @@ unsigned Test (unsigned Label, int Invert) /* Result is unknown */ Result = TESTEXPR_UNKNOWN; - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (&Expr)) { - ED_MarkForTest (&Expr); - } + /* Set the test flag */ + ED_RequireTest (&Expr); /* Load the value into the primary register */ LoadExpr (CF_FORCECHAR, &Expr); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 0f67db107..930aef8d4 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -371,6 +371,11 @@ void TypeCast (ExprDesc* Expr) GetBasicTypeName (NewType)); } + /* If the new type is void, the cast expression can have no effects */ + if (IsTypeVoid (NewType)) { + Expr->Flags |= E_EVAL_MAYBE_UNUSED; + } + /* The result is always an rvalue */ ED_MarkExprAsRVal (Expr); } From 1abb9da2b23b4de4b2e138818c1db7f7c537be96 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 26 Aug 2020 09:40:32 +0800 Subject: [PATCH 247/806] Moved bug250.c to test/val as it is fixed. --- test/val/bug250.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/val/bug250.c diff --git a/test/val/bug250.c b/test/val/bug250.c new file mode 100644 index 000000000..60f3c633d --- /dev/null +++ b/test/val/bug250.c @@ -0,0 +1,13 @@ +/* bug #250 - Array size compile-time optimization stops halfway */ + +#include + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +unsigned char c[2*4]; +unsigned char b[2*LZO_MAX(8,sizeof(int))]; // this will not compile + +int main(void) +{ + /* FIXME: add some runtime check */ + return EXIT_SUCCESS; +} From 725511131a40737c33fe7731194f7481ebbbc69d Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:11 +0800 Subject: [PATCH 248/806] Fixed constant expression checks with no-code requirement. Used return-by-value initialization for ExprDesc. --- src/cc65/asmstmt.c | 19 +++------- src/cc65/declare.c | 38 +++++++------------- src/cc65/expr.c | 80 ++++++++++++++++++++++------------------- src/cc65/expr.h | 20 +++++------ src/cc65/preproc.c | 5 +-- src/cc65/preproc.h | 2 +- src/cc65/staticassert.c | 4 +-- src/cc65/swstmt.c | 3 +- 8 files changed, 73 insertions(+), 98 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 20de6033e..148d62d9c 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -129,16 +129,13 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) static void ParseByteArg (StrBuf* T, unsigned Arg) /* Parse the %b format specifier */ { - ExprDesc Expr; char Buf [16]; - ED_Init (&Expr); - /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -165,16 +162,13 @@ static void ParseByteArg (StrBuf* T, unsigned Arg) static void ParseWordArg (StrBuf* T, unsigned Arg) /* Parse the %w format specifier */ { - ExprDesc Expr; char Buf [16]; - ED_Init (&Expr); - /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -201,16 +195,13 @@ static void ParseWordArg (StrBuf* T, unsigned Arg) static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) /* Parse the %l format specifier */ { - ExprDesc Expr; char Buf [16]; - ED_Init (&Expr); - /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); /* Convert into a hex number */ xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF); @@ -315,8 +306,6 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) ExprDesc Expr; char Buf [64]; - ED_Init (&Expr); - /* We expect an argument separated by a comma */ ConsumeComma (); @@ -336,7 +325,7 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) break; default: - ConstAbsIntExpr (hie1, &Expr); + Expr = StaticConstAbsIntExpr (hie1); xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal); SB_AppendStr (T, Buf); break; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 7e5f24b24..8d24b168e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -590,10 +590,8 @@ static SymEntry* ParseEnumDecl (const char* Name) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - ED_Init (&Expr); NextToken (); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); EnumVal = Expr.IVal; MemberType = Expr.Type; IsSigned = IsSignSigned (MemberType); @@ -720,9 +718,6 @@ static int ParseFieldWidth (Declaration* Decl) ** otherwise the width of the field. */ { - ExprDesc Expr; - ED_Init (&Expr); - if (CurTok.Tok != TOK_COLON) { /* No bit-field declaration */ return -1; @@ -746,7 +741,7 @@ static int ParseFieldWidth (Declaration* Decl) /* Read the width */ NextToken (); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); if (Expr.IVal < 0) { Error ("Negative width in bit-field"); @@ -1819,9 +1814,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { - ExprDesc Expr; - ED_Init (&Expr); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = StaticConstAbsIntExpr (hie1); if (Expr.IVal <= 0) { if (D->Ident[0] != '\0') { Error ("Size of array '%s' is invalid", D->Ident); @@ -2158,7 +2151,7 @@ static void OutputBitFieldData (StructInitData* SI) -static void ParseScalarInitInternal (Type* T, ExprDesc* ED) +static ExprDesc ParseScalarInitInternal (Type* T) /* Parse initializaton for scalar data types. This function will not output the ** data but return it in ED. */ @@ -2174,11 +2167,13 @@ static void ParseScalarInitInternal (Type* T, ExprDesc* ED) } /* Get the expression and convert it to the target type */ - ConstExpr (hie1, ED); - TypeConversion (ED, T); + ExprDesc ED = StaticConstExpr (hie1); + TypeConversion (&ED, T); /* Close eventually opening braces */ ClosingCurlyBraces (BraceCount); + + return ED; } @@ -2186,11 +2181,8 @@ static void ParseScalarInitInternal (Type* T, ExprDesc* ED) static unsigned ParseScalarInit (Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { - ExprDesc ED; - ED_Init (&ED); - /* Parse initialization */ - ParseScalarInitInternal (T, &ED); + ExprDesc ED = ParseScalarInitInternal (T); /* Output the data */ DefineData (&ED); @@ -2208,10 +2200,7 @@ static unsigned ParsePointerInit (Type* T) unsigned BraceCount = OpeningCurlyBraces (0); /* Expression */ - ExprDesc ED; - ED_Init (&ED); - - ConstExpr (hie1, &ED); + ExprDesc ED = StaticConstExpr (hie1); TypeConversion (&ED, T); /* Output the data */ @@ -2438,7 +2427,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ParseScalarInitInternal (Entry->Type, &ED); + ED = ParseScalarInitInternal (Entry->Type); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2556,10 +2545,7 @@ static unsigned ParseVoidInit (Type* T) /* Allow an arbitrary list of values */ Size = 0; do { - ExprDesc Expr; - ED_Init (&Expr); - - ConstExpr (hie1, &Expr); + ExprDesc Expr = StaticConstExpr (hie1); switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 62136e122..dfc8007e4 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3147,17 +3147,14 @@ static void hieAndPP (ExprDesc* Expr) ** called recursively from the preprocessor. */ { - ConstAbsIntExpr (hie2, Expr); + *Expr = StaticConstAbsIntExpr (hie2); while (CurTok.Tok == TOK_BOOL_AND) { - ExprDesc Expr2; - ED_Init (&Expr2); - /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hie2, &Expr2); + ExprDesc Expr2 = StaticConstAbsIntExpr (hie2); /* Combine the two */ Expr->IVal = (Expr->IVal && Expr2.IVal); @@ -3171,17 +3168,14 @@ static void hieOrPP (ExprDesc *Expr) ** called recursively from the preprocessor. */ { - ConstAbsIntExpr (hieAndPP, Expr); + *Expr = StaticConstAbsIntExpr (hieAndPP); while (CurTok.Tok == TOK_BOOL_OR) { - ExprDesc Expr2; - ED_Init (&Expr2); - /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hieAndPP, &Expr2); + ExprDesc Expr2 = StaticConstAbsIntExpr (hieAndPP); /* Combine the two */ Expr->IVal = (Expr->IVal || Expr2.IVal); @@ -3900,24 +3894,6 @@ void Expression0 (ExprDesc* Expr) -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. -*/ -{ - Expr->Flags |= E_EVAL_CONST; - ExprWithCheck (Func, Expr); - if (!ED_IsConst (Expr)) { - Error ("Constant expression expected"); - /* To avoid any compiler errors, make the expression a valid const */ - ED_MakeConstAbsInt (Expr, 1); - } -} - - - void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) /* Will evaluate an expression via the given function. If the result is not ** something that may be evaluated in a boolean context, a diagnostic will be @@ -3927,26 +3903,56 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) { ExprWithCheck (Func, Expr); if (!ED_IsBool (Expr)) { - Error ("Boolean expression expected"); + Error ("Scalar expression expected"); /* To avoid any compiler errors, make the expression a valid int */ - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstBool (Expr, 1); } } -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the +ExprDesc StaticConstExpr (void (*Func) (ExprDesc*)) +/* Will evaluate an expression via the given function. If the result is not a +** static constant expression, a diagnostic will be printed, and the value is +** replaced by a constant one to make sure there are no internal errors that +** result from this input error. +*/ +{ + ExprDesc Expr; + ED_Init (&Expr); + + Expr.Flags |= E_EVAL_C_CONST; + MarkedExprWithCheck (Func, &Expr); + if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { + Error ("Constant expression expected"); + /* To avoid any compiler errors, make the expression a valid const */ + ED_MakeConstAbsInt (&Expr, 1); + } + + /* Return by value */ + return Expr; +} + + + +ExprDesc StaticConstAbsIntExpr (void (*Func) (ExprDesc*)) +/* Will evaluate an expression via the given function. If the result is not a +** static constant numeric integer value, a diagnostic will be printed, and the ** value is replaced by a constant one to make sure there are no internal ** errors that result from this input error. */ { - Expr->Flags |= E_EVAL_CONST; - ExprWithCheck (Func, Expr); - if (!ED_IsConstAbsInt (Expr)) { + ExprDesc Expr; + ED_Init (&Expr); + + Expr.Flags |= E_EVAL_C_CONST; + MarkedExprWithCheck (Func, &Expr); + if (!ED_IsConstAbsInt (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) { Error ("Constant integer expression expected"); /* To avoid any compiler errors, make the expression a valid const */ - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstAbsInt (&Expr, 1); } + + /* Return by value */ + return Expr; } diff --git a/src/cc65/expr.h b/src/cc65/expr.h index a6c183cbd..8f7eb6f09 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -54,13 +54,6 @@ int evalexpr (unsigned flags, void (*Func) (ExprDesc*), ExprDesc* Expr); void Expression0 (ExprDesc* Expr); /* Evaluate an expression via hie0 and put the result into the primary register */ -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. -*/ - void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Will evaluate an expression via the given function. If the result is not ** something that may be evaluated in a boolean context, a diagnostic will be @@ -68,9 +61,16 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); ** are no internal errors that result from this input error. */ -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the +ExprDesc StaticConstExpr (void (*Func) (ExprDesc*)); +/* Get an expression evaluated via the given function. If the result is not a +** static constant expression, a diagnostic will be printed, and the value is +** replaced by a constant one to make sure there are no internal errors that +** result from this input error. +*/ + +ExprDesc StaticConstAbsIntExpr (void (*Func) (ExprDesc*)); +/* Get an expression evaluate via the given function. If the result is not a +** static constant numeric integer value, a diagnostic will be printed, and the ** value is replaced by a constant one to make sure there are no internal ** errors that result from this input error. */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 518570ff5..2d2f316d7 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1042,9 +1042,6 @@ static void DoError (void) static int DoIf (int Skip) /* Process #if directive */ { - ExprDesc Expr; - ED_Init (&Expr); - /* We're about to abuse the compiler expression parser to evaluate the ** #if expression. Save the current tokens to come back here later. ** NOTE: Yes, this is a hack, but it saves a complete separate expression @@ -1079,7 +1076,7 @@ static int DoIf (int Skip) NextToken (); /* Call the expression parser */ - ConstExpr (hie1, &Expr); + ExprDesc Expr = StaticConstExpr (hie1); /* End preprocessing mode */ Preprocessing = 0; diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 8d7b3c374..464b02337 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -44,7 +44,7 @@ -/* Set when the preprocessor calls ConstExpr() recursively */ +/* Set when the preprocessor calls StaticConstExpr() recursively */ extern unsigned char Preprocessing; diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index b963947b6..3372c59a1 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -55,8 +55,6 @@ void ParseStaticAssert () ExprDesc Expr; int failed; - ED_Init (&Expr); - /* Skip the _Static_assert token itself */ CHECK (CurTok.Tok == TOK_STATIC_ASSERT); NextToken (); @@ -67,7 +65,7 @@ void ParseStaticAssert () } /* Parse assertion condition */ - ConstAbsIntExpr (hie1, &Expr); + Expr = StaticConstAbsIntExpr (hie1); failed = !Expr.IVal; /* If there is a comma, we also have an error message. The message is optional because we diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 524a31ddf..559e168c3 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -216,8 +216,7 @@ void CaseLabel (void) NextToken (); /* Read the selector expression */ - ED_Init (&CaseExpr); - ConstAbsIntExpr (hie1, &CaseExpr); + CaseExpr = StaticConstAbsIntExpr (hie1); Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ From 85ea06687b7ba638841aeb83a0d22ae9112d5425 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:15 +0800 Subject: [PATCH 249/806] Fixed logical AND and logical OR. --- src/cc65/expr.c | 253 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 196 insertions(+), 57 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index dfc8007e4..b27a1c6fc 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3184,58 +3184,135 @@ static void hieOrPP (ExprDesc *Expr) -static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) -/* Process "exp && exp" */ +static int hieAnd (ExprDesc* Expr, unsigned TrueLab, int* UsedTrueLab) +/* Process "exp && exp". This should only be called within hieOr. +** Return true if logic AND does occur. +*/ { - int FalseLab; + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int HasFalseJump = 0, HasTrueJump = 0; + CodeMark Start; + + /* Get a label that we will use for false expressions */ + int FalseLab = GetLocalLabel (); + + /* Get lhs */ + GetCodePos (&Start); ExprWithCheck (hie2, Expr); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } + if (CurTok.Tok == TOK_BOOL_AND) { - /* Tell our caller that we're evaluating a boolean */ - *BoolOp = 1; + ExprDesc Expr2; - /* Get a label that we will use for false expressions */ - FalseLab = GetLocalLabel (); + /* Check type */ + if (!ED_IsBool (Expr)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (Expr, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (!ED_IsConstAbs (Expr)) { + /* Set the test flag */ + ED_RequireTest (Expr); - /* Set the test flag */ - ED_RequireTest (Expr); + /* Load the value */ + LoadExpr (CF_FORCECHAR, Expr); - /* Load the value */ - LoadExpr (CF_FORCECHAR, Expr); + /* Remember that the jump is used */ + HasFalseJump = 1; - /* Generate the jump */ - g_falsejump (CF_NONE, FalseLab); + /* Generate the jump */ + g_falsejump (CF_NONE, FalseLab); + } else if (Expr->IVal == 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } + } /* Parse more boolean and's */ while (CurTok.Tok == TOK_BOOL_AND) { - ExprDesc Expr2; ED_Init (&Expr2); + Expr2.Flags = Flags; /* Skip the && */ NextToken (); /* Get rhs */ + GetCodePos (&Start); hie2 (&Expr2); - ED_RequireTest (&Expr2); - LoadExpr (CF_FORCECHAR, &Expr2); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } - /* Do short circuit evaluation */ - if (CurTok.Tok == TOK_BOOL_AND) { - g_falsejump (CF_NONE, FalseLab); - } else { - /* Last expression - will evaluate to true */ - g_truejump (CF_NONE, TrueLab); + /* Check type */ + if (!ED_IsBool (&Expr2)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (&Expr2, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (!ED_IsConstAbs (&Expr2)) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + /* Do short circuit evaluation */ + if (CurTok.Tok == TOK_BOOL_AND) { + HasFalseJump = 1; + g_falsejump (CF_NONE, FalseLab); + } else { + /* We need the true label for the last expression */ + HasTrueJump = 1; + } + } else if (Expr2.IVal == 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The value of the expression will be false */ + ED_MakeConstBool (Expr, 0); + } } } - /* Define the false jump label here */ - g_defcodelabel (FalseLab); + /* Last expression */ + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (HasFalseJump || HasTrueJump) { + /* In either case we need the true label anyways */ + HasTrueJump = 1; + if (!ED_IsConstAbs (&Expr2)) { + /* Will branch to true and fall to false */ + g_truejump (CF_NONE, TrueLab); + } else { + /* Will jump away */ + g_jump (TrueLab); + } + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* No need to test as the result will be jumped to */ + ED_TestDone (Expr); + } + } - /* The result is an rvalue in primary */ - ED_FinalizeRValLoad (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + if (HasFalseJump) { + /* Define the false jump label here */ + g_defcodelabel (FalseLab); + } + + /* Convert to bool */ + if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } + + /* Tell our caller that we're used the true label */ + if (HasTrueJump) { + *UsedTrueLab = 1; + } + + /* Tell our caller that we're evaluating a boolean */ + return 1; } + + return 0; } @@ -3243,69 +3320,131 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) static void hieOr (ExprDesc *Expr) /* Process "exp || exp". */ { - int BoolOp = 0; /* Did we have a boolean op? */ - int AndOp; /* Did we have a && operation? */ + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int AndOp; /* Did we have a && operation? */ unsigned TrueLab; /* Jump to this label if true */ unsigned DoneLab; + int HasTrueJump = 0; + int HasNewTrueJump; + CodeMark Start; - /* Get a label */ + /* Get a label that we will use for true expressions */ TrueLab = GetLocalLabel (); /* Call the next level parser */ - hieAnd (Expr, TrueLab, &BoolOp); + GetCodePos (&Start); + AndOp = hieAnd (Expr, TrueLab, &HasNewTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } else { + /* Remember the jump */ + HasTrueJump = HasNewTrueJump; + } /* Any boolean or's? */ if (CurTok.Tok == TOK_BOOL_OR) { - /* Set the test flag */ - ED_RequireTest (Expr); + /* Check type */ + if (!ED_IsBool (Expr)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (Expr, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + + if (!ED_IsConstAbs (Expr)) { + /* Test the lhs if we haven't had && operators. If we had them, the + ** jump is already in place and there's no need to do the test. + */ + if (!AndOp) { + /* Set the test flag */ + ED_RequireTest (Expr); - /* Get first expr */ - LoadExpr (CF_FORCECHAR, Expr); + /* Get first expr */ + LoadExpr (CF_FORCECHAR, Expr); - /* For each expression jump to TrueLab if true. Beware: If we - ** had && operators, the jump is already in place! - */ - if (!BoolOp) { - g_truejump (CF_NONE, TrueLab); + /* Remember that the jump is used */ + HasTrueJump = 1; + + /* Jump to TrueLab if true */ + g_truejump (CF_NONE, TrueLab); + } + } else if (Expr->IVal != 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } } - /* Remember that we had a boolean op */ - BoolOp = 1; - /* while there's more expr */ while (CurTok.Tok == TOK_BOOL_OR) { ExprDesc Expr2; ED_Init (&Expr2); + Expr2.Flags = Flags; /* skip the || */ NextToken (); - /* Get a subexpr */ - AndOp = 0; - hieAnd (&Expr2, TrueLab, &AndOp); - ED_RequireTest (&Expr2); - LoadExpr (CF_FORCECHAR, &Expr2); + /* Get rhs subexpression */ + GetCodePos (&Start); + AndOp = hieAnd (&Expr2, TrueLab, &HasNewTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } else { + /* Remember the jump */ + HasTrueJump = HasTrueJump || HasNewTrueJump; + } - /* If there is more to come, add shortcut boolean eval. */ - g_truejump (CF_NONE, TrueLab); + /* Check type */ + if (!ED_IsBool (&Expr2)) { + Error ("Scalar expression expected"); + ED_MakeConstBool (&Expr2, 0); + } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + + if (!ED_IsConstAbs (&Expr2)) { + /* If there is more to come, add shortcut boolean eval */ + if (!AndOp) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + HasTrueJump = 1; + g_truejump (CF_NONE, TrueLab); + } + } else if (Expr2.IVal != 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The result is always true */ + ED_MakeConstBool (Expr, 1); + } + } } - /* The result is an rvalue in primary */ - ED_FinalizeRValLoad (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + /* Convert to bool */ + if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } } - /* If we really had boolean ops, generate the end sequence */ - if (BoolOp) { + /* If we really had boolean ops, generate the end sequence if necessary */ + if (HasTrueJump) { + /* False case needs to jump over true case */ DoneLab = GetLocalLabel (); - g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ - g_falsejump (CF_NONE, DoneLab); + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + /* Load false only if the result is not true */ + g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ + g_falsejump (CF_NONE, DoneLab); + } + + /* Load the true value */ g_defcodelabel (TrueLab); g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ g_defcodelabel (DoneLab); + + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* Condition codes are set */ + ED_TestDone (Expr); } } From 75dc2349882c563e7646715bf2933ac50cf6753f Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 21 Aug 2020 15:15:19 -0400 Subject: [PATCH 250/806] Guarded the static_assert macro with a C standards test. --- include/assert.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/assert.h b/include/assert.h index 3f8b46ac0..a4dcd5d7f 100644 --- a/include/assert.h +++ b/include/assert.h @@ -46,16 +46,14 @@ extern void __fastcall__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif -/* -** TODO: Guard with #if __STDC_VERSION__ >= 201112L or similar when there +/* TODO: Guard with #if __CC65_STD__ >= __CC65_STD_C11__ if there ** is a C11 mode. */ -#define static_assert _Static_assert +#if __CC65_STD__ > __CC65_STD_C99__ +# define static_assert _Static_assert +#endif /* End of assert.h */ #endif - - - From ea7336591cc807d63351a223896a3d9d84ae7d9a Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 251/806] Removed special-casing code for scaling up. Now it uses the normal multiplying code. --- src/cc65/codegen.c | 53 +++++++++------------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 6b6b292b0..a2dbf12b4 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1496,54 +1496,14 @@ void g_scale (unsigned flags, long val) ** pointer points to. */ { - int p2; - /* Value may not be zero */ if (val == 0) { Internal ("Data type has no size"); } else if (val > 0) { - /* Scale up */ - if ((p2 = PowerOf2 (val)) > 0 && p2 <= 4) { - - /* Factor is 2, 4, 8 and 16, use special function */ - switch (flags & CF_TYPEMASK) { - - case CF_CHAR: - if (flags & CF_FORCECHAR) { - while (p2--) { - AddCodeLine ("asl a"); - } - break; - } - /* FALLTHROUGH */ - - case CF_INT: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shlax%d", p2); - } else { - AddCodeLine ("jsr aslax%d", p2); - } - break; - - case CF_LONG: - if (flags & CF_UNSIGNED) { - AddCodeLine ("jsr shleax%d", p2); - } else { - AddCodeLine ("jsr asleax%d", p2); - } - break; - - default: - typeerror (flags); - - } - - } else if (val != 1) { - - /* Use a multiplication instead */ + /* Use a multiplication instead */ + if (val != 1) { g_mul (flags | CF_CONST, val); - } } else { @@ -3224,7 +3184,6 @@ void g_asl (unsigned flags, unsigned long val) "tosaslax", "tosshlax", "tosasleax", "tosshleax" }; - /* If the right hand side is const, the lhs is not on stack but still ** in the primary register. */ @@ -3233,6 +3192,14 @@ void g_asl (unsigned flags, unsigned long val) switch (flags & CF_TYPEMASK) { case CF_CHAR: + if ((flags & CF_FORCECHAR) != 0 && val <= 4) { + while (val--) { + AddCodeLine ("asl a"); + } + return; + } + /* FALLTHROUGH */ + case CF_INT: val &= 0x0F; if (val >= 8) { From f59c2a08d998cc1dcfb2442a79d076b113ae9ad4 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 2 Jul 2020 10:30:02 +0800 Subject: [PATCH 252/806] Added support for changing multiplications with certain negative multipliers into bit-shifts. Only enable certain kinds of signed mul replacements with shift according to the code size factor settings. --- src/cc65/codegen.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index a2dbf12b4..6cd8f5c43 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2565,13 +2565,26 @@ void g_mul (unsigned flags, unsigned long val) "tosmulax", "tosumulax", "tosmuleax", "tosumuleax" }; - int p2; - /* Do strength reduction if the value is constant and a power of two */ - if (flags & CF_CONST && (p2 = PowerOf2 (val)) >= 0) { - /* Generate a shift instead */ - g_asl (flags, p2); - return; + if (flags & CF_CONST) { + + /* Deal with negative values if it's signed multiplication */ + int Negation = (flags & CF_UNSIGNED) == 0 && (signed long)val < 0; + int p2 = PowerOf2 (Negation ? (unsigned long)-(signed long)val : val); + + /* Check if we can use shift instead of multiplication */ + if (p2 == 0 || (p2 > 0 && IS_Get (&CodeSizeFactor) >= (Negation ? 100 : 0))) { + /* Generate a shift instead */ + g_asl (flags, p2); + + /* Negate the result if val is negative */ + if (Negation) { + g_neg (flags); + } + + /* Done */ + return; + } } /* If the right hand side is const, the lhs is not on stack but still From 63256fd15d0b54258a9df515bc62fca140eac201 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 22:47:08 +0800 Subject: [PATCH 253/806] Changed negation of signed long stored in unsigned long to unsigned subtraction. --- src/cc65/codegen.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 6cd8f5c43..48cf02319 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2569,8 +2569,8 @@ void g_mul (unsigned flags, unsigned long val) if (flags & CF_CONST) { /* Deal with negative values if it's signed multiplication */ - int Negation = (flags & CF_UNSIGNED) == 0 && (signed long)val < 0; - int p2 = PowerOf2 (Negation ? (unsigned long)-(signed long)val : val); + int Negation = (flags & CF_UNSIGNED) == 0 && (long)val < 0; + int p2 = PowerOf2 (Negation ? 0UL - val : val); /* Check if we can use shift instead of multiplication */ if (p2 == 0 || (p2 > 0 && IS_Get (&CodeSizeFactor) >= (Negation ? 100 : 0))) { @@ -2690,7 +2690,7 @@ void g_div (unsigned flags, unsigned long val) /* Deal with negative values as well as different sizes */ int Negation = (flags & CF_UNSIGNED) == 0 && (long)val < 0; - unsigned long NegatedVal = (unsigned long)-(long)val; + unsigned long NegatedVal = 0UL - val; int p2 = PowerOf2 (Negation ? NegatedVal : val); /* Generate a shift instead */ From 0536f4f9bdf9ce5bf7dd6d5dbfeaafda3c228f12 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:27 +0800 Subject: [PATCH 254/806] Minor fixes on constant expression checking and comments. --- src/cc65/expr.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 84d709eb9..12a0c0b57 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -557,7 +557,7 @@ static void FunctionCall (ExprDesc* Expr) ** For fastcall functions we do also need to place a copy of the ** pointer on stack, since we cannot use a/x. */ - PtrOnStack = IsFastcall || !ED_IsConst (Expr); + PtrOnStack = IsFastcall || !ED_IsConstAddr (Expr); if (PtrOnStack) { /* Not a global or local variable, or a fastcall function. Load @@ -1083,12 +1083,13 @@ static void ArrayRef (ExprDesc* Expr) ED_FinalizeRValLoad (&Subscript); } - /* Check if the subscript is constant absolute value */ + /* Make the address of the array element from the base and subscript */ if (ED_IsConstAbs (&Subscript) && ED_CodeRangeIsEmpty (&Subscript)) { - /* The array subscript is a numeric constant. If we had pushed the - ** array base address onto the stack before, we can remove this value, - ** since we can generate expression+offset. + /* The array subscript is a constant. Since we can have the element + ** address directly as base+offset, we can remove the array address + ** push onto the stack before if loading subscript doesn't tamper that + ** address in the primary. */ if (!ConstBaseAddr) { RemoveCode (&Mark2); @@ -1127,7 +1128,7 @@ static void ArrayRef (ExprDesc* Expr) } else { - /* Scale the rhs value according to the element type */ + /* Scale the lhs value according to the element type */ g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType)); /* Add the subscript. Since arrays are indexed by integers, @@ -1247,13 +1248,13 @@ static void ArrayRef (ExprDesc* Expr) } } - /* The pointer is an rvalue in the primary */ + /* The address of the element is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); } - /* The result is usually an lvalue expression of element type referenced in - ** the primary, unless it's an array which is a rare case. We can just + /* The final result is usually an lvalue expression of element type + ** referenced in the primary, unless it is once again an array. We can just ** assume the usual case first, and change it later if necessary. */ ED_IndExpr (Expr); From f289ea6c148341fc4d09fa46a55435668901c8a0 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 Aug 2020 11:10:17 +0800 Subject: [PATCH 255/806] Avoided generating unnecessary true-case labels in logic AND/OR. --- src/cc65/expr.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index b27a1c6fc..416d383d0 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3184,7 +3184,7 @@ static void hieOrPP (ExprDesc *Expr) -static int hieAnd (ExprDesc* Expr, unsigned TrueLab, int* UsedTrueLab) +static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Process "exp && exp". This should only be called within hieOr. ** Return true if logic AND does occur. */ @@ -3275,14 +3275,17 @@ static int hieAnd (ExprDesc* Expr, unsigned TrueLab, int* UsedTrueLab) /* Last expression */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { if (HasFalseJump || HasTrueJump) { - /* In either case we need the true label anyways */ - HasTrueJump = 1; + if (*TrueLabAllocated == 0) { + /* Get a label that we will use for true expressions */ + *TrueLab = GetLocalLabel (); + *TrueLabAllocated = 1; + } if (!ED_IsConstAbs (&Expr2)) { /* Will branch to true and fall to false */ - g_truejump (CF_NONE, TrueLab); + g_truejump (CF_NONE, *TrueLab); } else { /* Will jump away */ - g_jump (TrueLab); + g_jump (*TrueLab); } /* The result is an rvalue in primary */ ED_FinalizeRValLoad (Expr); @@ -3303,11 +3306,6 @@ static int hieAnd (ExprDesc* Expr, unsigned TrueLab, int* UsedTrueLab) Expr->Type = type_bool; } - /* Tell our caller that we're used the true label */ - if (HasTrueJump) { - *UsedTrueLab = 1; - } - /* Tell our caller that we're evaluating a boolean */ return 1; } @@ -3325,20 +3323,13 @@ static void hieOr (ExprDesc *Expr) unsigned TrueLab; /* Jump to this label if true */ unsigned DoneLab; int HasTrueJump = 0; - int HasNewTrueJump; CodeMark Start; - /* Get a label that we will use for true expressions */ - TrueLab = GetLocalLabel (); - /* Call the next level parser */ GetCodePos (&Start); - AndOp = hieAnd (Expr, TrueLab, &HasNewTrueJump); + AndOp = hieAnd (Expr, &TrueLab, &HasTrueJump); if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { RemoveCode (&Start); - } else { - /* Remember the jump */ - HasTrueJump = HasNewTrueJump; } /* Any boolean or's? */ @@ -3361,8 +3352,11 @@ static void hieOr (ExprDesc *Expr) /* Get first expr */ LoadExpr (CF_FORCECHAR, Expr); - /* Remember that the jump is used */ - HasTrueJump = 1; + if (HasTrueJump == 0) { + /* Get a label that we will use for true expressions */ + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } /* Jump to TrueLab if true */ g_truejump (CF_NONE, TrueLab); @@ -3385,12 +3379,9 @@ static void hieOr (ExprDesc *Expr) /* Get rhs subexpression */ GetCodePos (&Start); - AndOp = hieAnd (&Expr2, TrueLab, &HasNewTrueJump); + AndOp = hieAnd (&Expr2, &TrueLab, &HasTrueJump); if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { RemoveCode (&Start); - } else { - /* Remember the jump */ - HasTrueJump = HasTrueJump || HasNewTrueJump; } /* Check type */ @@ -3405,7 +3396,10 @@ static void hieOr (ExprDesc *Expr) ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); - HasTrueJump = 1; + if (HasTrueJump == 0) { + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } g_truejump (CF_NONE, TrueLab); } } else if (Expr2.IVal != 0) { From 0486d28abc155c32253b5fab0a00e5e35f81e053 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 07:52:58 +0800 Subject: [PATCH 256/806] Fixed Issue #327. --- src/cc65/typeconv.c | 4 ++-- test/{todo => val}/bug327.c | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename test/{todo => val}/bug327.c (100%) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 7e2787529..c9163eb51 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -124,7 +124,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) LoadExpr (CF_NONE, Expr); /* Emit typecast code */ - g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); + g_typecast (TypeOf (NewType), TypeOf (OldType)); /* Value is now in primary and an rvalue */ ED_FinalizeRValLoad (Expr); @@ -175,7 +175,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) LoadExpr (CF_NONE, Expr); /* Emit typecast code. */ - g_typecast (TypeOf (NewType), TypeOf (OldType) | CF_FORCECHAR); + g_typecast (TypeOf (NewType), TypeOf (OldType)); /* Value is now an rvalue in the primary */ ED_FinalizeRValLoad (Expr); diff --git a/test/todo/bug327.c b/test/val/bug327.c similarity index 100% rename from test/todo/bug327.c rename to test/val/bug327.c From 23621f3299f213117e0b14358ea3bf66d69b980b Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 22 Aug 2020 10:48:25 +0200 Subject: [PATCH 257/806] Add test case for #1209 Change err/ tests to use cl65 and .prg instead of cc65 and .s since this test only fails at the link stage. --- test/err/Makefile | 8 ++--- test/err/bug1209-ind-goto-rev.c | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/err/bug1209-ind-goto-rev.c diff --git a/test/err/Makefile b/test/err/Makefile index 1273bbb2c..6fa53a1db 100644 --- a/test/err/Makefile +++ b/test/err/Makefile @@ -23,23 +23,23 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) WORKDIR = ../../testwrk/err .PHONY: all clean SOURCES := $(wildcard *.c) -TESTS = $(patsubst %.c,$(WORKDIR)/%.s,$(SOURCES)) +TESTS = $(patsubst %.c,$(WORKDIR)/%.prg,$(SOURCES)) all: $(TESTS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(WORKDIR)/%.s: %.c | $(WORKDIR) +$(WORKDIR)/%.prg: %.c | $(WORKDIR) $(if $(QUIET),echo err/$*.s) - $(NOT) $(CC65) -o $@ $< $(NULLERR) + $(NOT) $(CL65) -o $@ $< $(NULLERR) clean: @$(call RMDIR,$(WORKDIR)) diff --git a/test/err/bug1209-ind-goto-rev.c b/test/err/bug1209-ind-goto-rev.c new file mode 100644 index 000000000..39d8639bd --- /dev/null +++ b/test/err/bug1209-ind-goto-rev.c @@ -0,0 +1,53 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of indirect goto with the label before the goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include +#include + +/* When operating correctly, this returns 0. */ +int f (void) +{ + static void *x[1]; + /* Define the label before referencing it with indirect label syntax. */ + L: if (x[0] != 0) return 0; + x[0] = &&L; + goto *x[0]; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL: %d failures\n", failures); + } + + return failures; +} From 911a79796dded8777953d8da63b98d04a3117b37 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 258/806] Fixed checks and diagnostics on type-casting. --- src/cc65/typeconv.c | 92 ++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index c9163eb51..3af092d42 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -207,27 +207,30 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) printf ("\n"); PrintRawType (stdout, NewType); #endif + /* First, do some type checking */ int HasWarning = 0; int HasError = 0; const char* Msg = 0; const Type* OldType = Expr->Type; - /* First, do some type checking */ - if (IsTypeVoid (NewType) || IsTypeVoid (Expr->Type)) { - /* If one of the sides are of type void, output a more apropriate - ** error message. - */ - Error ("Illegal type"); + /* If one of the sides is of type void, it is an error */ + if (IsTypeVoid (NewType) || IsTypeVoid (OldType)) { + HasError = 1; } - /* If Expr is a function, convert it to pointer to function */ - if (IsTypeFunc (Expr->Type)) { - Expr->Type = PointerTo (Expr->Type); + /* If both types are strictly compatible, no conversion is needed */ + if (TypeCmp (NewType, OldType) >= TC_STRICT_COMPATIBLE) { + /* We're already done */ + return; } - /* If both types are equal, no conversion is needed */ - if (TypeCmp (Expr->Type, NewType) >= TC_EQUAL) { + /* If Expr is an array or a function, convert it to a pointer */ + Expr->Type = PtrConversion (Expr->Type); + + /* If we have changed the type, check again for strictly compatibility */ + if (Expr->Type != OldType && + TypeCmp (NewType, Expr->Type) >= TC_STRICT_COMPATIBLE) { /* We're already done */ return; } @@ -237,10 +240,6 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Handle conversions to int type */ if (IsClassPtr (Expr->Type)) { - /* Pointer -> int conversion. Convert array to pointer */ - if (IsTypeArray (Expr->Type)) { - Expr->Type = ArrayToPtr (Expr->Type); - } Warning ("Converting pointer to integer without a cast"); } else if (!IsClassInt (Expr->Type) && !IsClassFloat (Expr->Type)) { HasError = 1; @@ -254,11 +253,6 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Handle conversions to pointer type */ if (IsClassPtr (Expr->Type)) { - /* Convert array to pointer */ - if (IsTypeArray (Expr->Type)) { - Expr->Type = ArrayToPtr (Expr->Type); - } - /* Pointer to pointer assignment is valid, if: ** - both point to the same types, or ** - the rhs pointer is a void pointer, or @@ -271,11 +265,15 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) case TC_INCOMPATIBLE: HasWarning = 1; Msg = "Incompatible pointer assignment to '%s' from '%s'"; + /* Use the pointer type in the diagnostic */ + OldType = Expr->Type; break; case TC_QUAL_DIFF: HasWarning = 1; Msg = "Pointer assignment to '%s' from '%s' discards qualifiers"; + /* Use the pointer type in the diagnostic */ + OldType = Expr->Type; break; default: @@ -309,8 +307,21 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) TypeCompatibilityDiagnostic (NewType, OldType, 0, Msg); } - /* Do the actual conversion */ - DoConversion (Expr, NewType); + /* Both types must be complete */ + if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { + /* Do the actual conversion */ + DoConversion (Expr, NewType); + } else { + /* We should have already generated error elsewhere so that we + ** could just silently fail here to avoid excess errors, but to + ** be safe, we must ensure that we do have errors. + */ + if (IsIncompleteESUType (NewType)) { + Error ("Conversion to incomplete type '%s'", GetFullTypeName (NewType)); + } else { + Error ("Conversion from incomplete type '%s'", GetFullTypeName (Expr->Type)); + } + } } } @@ -333,19 +344,30 @@ void TypeCast (ExprDesc* Expr) /* Read the expression we have to cast */ hie10 (Expr); - /* Only allow casts to arithmetic or pointer types, or just changing the - ** qualifiers. - */ - if (TypeCmp (NewType, Expr->Type) >= TC_QUAL_DIFF) { - /* The expression has always the new type */ - ReplaceType (Expr, NewType); - } else if (IsCastType (NewType)) { - /* Convert functions and arrays to "pointer to" object */ - Expr->Type = PtrConversion (Expr->Type); - /* Convert the value */ - DoConversion (Expr, NewType); + /* Only allow casts to arithmetic, pointer or void types */ + if (IsCastType (NewType)) { + if (!IsIncompleteESUType (NewType)) { + /* Convert functions and arrays to "pointer to" object */ + Expr->Type = PtrConversion (Expr->Type); + + if (TypeCmp (NewType, Expr->Type) >= TC_QUAL_DIFF) { + /* If the new type only differs in qualifiers, just use it to + ** replace the old one. + */ + ReplaceType (Expr, NewType); + } else if (IsCastType (Expr->Type)) { + /* Convert the value. The rsult has always the new type */ + DoConversion (Expr, NewType); + } else { + TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, + "Cast to incompatible type '%s' from '%s'"); + } + } else { + Error ("Cast to incomplete type '%s'", + GetFullTypeName (NewType)); + } } else { - Error ("Arithmetic or pointer type expected but '%s' is used", - GetFullTypeName (NewType)); + Error ("Arithmetic or pointer type expected but %s is used", + GetBasicTypeName (NewType)); } } From 63fa9a5a429385fceba1d97f8dfdbf92accb21fd Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 Aug 2020 01:30:52 +0800 Subject: [PATCH 259/806] Fixed usage of "lvalue-cast" in _scanf implementation. --- libsrc/common/_scanf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsrc/common/_scanf.c b/libsrc/common/_scanf.c index c18f1e289..ac89e3cb3 100644 --- a/libsrc/common/_scanf.c +++ b/libsrc/common/_scanf.c @@ -183,7 +183,7 @@ static void PushBack (void) asm ("jsr pushax"); /* Copy D into the zero-page. */ - (const struct scanfdata*) __AX__ = D_; + __AX__ = (unsigned) D_; asm ("sta ptr1"); asm ("stx ptr1+1"); @@ -272,7 +272,7 @@ static void __fastcall__ Error (unsigned char /* Code */) /* Does a longjmp using the given code */ { asm ("pha"); - (char*) __AX__ = JumpBuf; + __AX__ = (unsigned) JumpBuf; asm ("jsr pushax"); asm ("pla"); asm ("ldx #>$0000"); @@ -389,7 +389,7 @@ static void AssignInt (void) if (NoAssign == false) { /* Get the next argument pointer */ - (void*) __AX__ = va_arg (ap, void*); + __AX__ = (unsigned) va_arg (ap, void*); /* Put the argument pointer into the zero-page. */ asm ("sta ptr1"); @@ -468,7 +468,7 @@ static char GetFormat (void) /* Pick up the next character from the format string. */ { /* return (F = *format++); */ - (const char*) __AX__ = format; + __AX__ = (unsigned) format; asm ("sta regsave"); asm ("stx regsave+1"); ++format; From d1995fc81aee6047a05418f89d4ee57a73fcb09c Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 260/806] Fixed rvalue-ness of cast results. --- src/cc65/typeconv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 3af092d42..0f67db107 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -370,4 +370,7 @@ void TypeCast (ExprDesc* Expr) Error ("Arithmetic or pointer type expected but %s is used", GetBasicTypeName (NewType)); } + + /* The result is always an rvalue */ + ED_MarkExprAsRVal (Expr); } From c3a6b399456937093eda9994f19b7f722731528d Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:56:11 +0800 Subject: [PATCH 261/806] Made struct assignment less hackish. --- src/cc65/assignment.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 0151a9000..6acab3fe8 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -95,10 +95,13 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Store it into the location referred in the primary */ Store (LExpr, stype); + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (LExpr); + } else { - /* The only way this can happen is in chained assignments */ - if (!ED_IsLocPrimary (RExpr)) { + /* The rhs cannot happen to be loaded in the primary as it is too big */ + if (!ED_IsLocExpr (RExpr)) { ED_AddrExpr (RExpr); LoadExpr (CF_NONE, RExpr); } @@ -112,16 +115,19 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Call the memcpy function */ g_call (CF_FIXARGC, Func_memcpy, 4); + /* The result is an rvalue referenced in the primary */ + ED_FinalizeRValLoad (LExpr); + /* Restore the indirection level of lhs */ ED_IndExpr (LExpr); - - /* Clear the tested flag set during loading. This is not neccessary - ** currently (and probably ever) as a struct/union cannot be converted - ** to a boolean in C, but there is no harm to be future-proof. - */ - ED_MarkAsUntested (LExpr); } + /* Clear the tested flag set during loading. This is not neccessary + ** currently (and probably ever) as a struct/union cannot be converted + ** to a boolean in C, but there is no harm to be future-proof. + */ + ED_MarkAsUntested (LExpr); + return 1; } @@ -247,6 +253,9 @@ void Assignment (ExprDesc* Expr) /* Restore the expression type */ Expr->Type = ltype; + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); + } else { /* Get the address on stack if needed */ @@ -264,8 +273,8 @@ void Assignment (ExprDesc* Expr) /* Generate a store instruction */ Store (Expr, 0); - } + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); - /* Value might be still in primary and not an lvalue */ - ED_FinalizeRValLoad (Expr); + } } From 4b7cd491e3631e7e827f7d416ef38d17ac5c341f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 24 Aug 2020 16:33:50 +0200 Subject: [PATCH 262/806] Move #1209 test from err/ to misc/ misc/ is the correct place for tests that should compile but do not. Revert err/Makefile changes from #1210. --- test/err/Makefile | 8 ++++---- test/misc/Makefile | 6 ++++++ test/{err => misc}/bug1209-ind-goto-rev.c | 0 3 files changed, 10 insertions(+), 4 deletions(-) rename test/{err => misc}/bug1209-ind-goto-rev.c (100%) diff --git a/test/err/Makefile b/test/err/Makefile index 6fa53a1db..1273bbb2c 100644 --- a/test/err/Makefile +++ b/test/err/Makefile @@ -23,23 +23,23 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) WORKDIR = ../../testwrk/err .PHONY: all clean SOURCES := $(wildcard *.c) -TESTS = $(patsubst %.c,$(WORKDIR)/%.prg,$(SOURCES)) +TESTS = $(patsubst %.c,$(WORKDIR)/%.s,$(SOURCES)) all: $(TESTS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(WORKDIR)/%.prg: %.c | $(WORKDIR) +$(WORKDIR)/%.s: %.c | $(WORKDIR) $(if $(QUIET),echo err/$*.s) - $(NOT) $(CL65) -o $@ $< $(NULLERR) + $(NOT) $(CC65) -o $@ $< $(NULLERR) clean: @$(call RMDIR,$(WORKDIR)) diff --git a/test/misc/Makefile b/test/misc/Makefile index b21384b68..80cbcdf9e 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -75,6 +75,12 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(if $(QUIET),echo misc/bug760.$1.$2.prg) $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev.$1.$2.prg) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but gives an error $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/err/bug1209-ind-goto-rev.c b/test/misc/bug1209-ind-goto-rev.c similarity index 100% rename from test/err/bug1209-ind-goto-rev.c rename to test/misc/bug1209-ind-goto-rev.c From d38e5858f01f67e3ad096135312de8a9e7814a74 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 24 Aug 2020 10:01:58 +0200 Subject: [PATCH 263/806] Add tests for #1211 CL_MoveRefs: Add CHECK (E->JumpTo != NULL) to make failure clearer. --- src/cc65/codelab.c | 1 + test/err/bug1211-ice-move-refs-1.c | 52 ++++++++++++++++++++++++++++++ test/err/bug1211-ice-move-refs-2.c | 51 +++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 test/err/bug1211-ice-move-refs-1.c create mode 100644 test/err/bug1211-ice-move-refs-2.c diff --git a/src/cc65/codelab.c b/src/cc65/codelab.c index f36520835..ff26645dc 100644 --- a/src/cc65/codelab.c +++ b/src/cc65/codelab.c @@ -112,6 +112,7 @@ void CL_MoveRefs (CodeLabel* OldLabel, CodeLabel* NewLabel) CodeEntry* E = CL_GetRef (OldLabel, Count); /* Change the reference to the new label */ + CHECK (E->JumpTo != NULL); CHECK (E->JumpTo == OldLabel); CL_AddRef (NewLabel, E); diff --git a/test/err/bug1211-ice-move-refs-1.c b/test/err/bug1211-ice-move-refs-1.c new file mode 100644 index 000000000..f28d1fcbc --- /dev/null +++ b/test/err/bug1211-ice-move-refs-1.c @@ -0,0 +1,52 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of indirect goto with label merge ICE. + https://github.com/cc65/cc65/issues/1211 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include + +/* When operating correctly, f(0) = 31 and f(1) = 41. */ +int f (int x) +{ + static const void *const labels[] = {&&L0, &&L1}; + goto *labels[x]; +L0: if (labels[0] != labels[1]) return 31; + else return 13; +L1: return 41; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f (0) != 31) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/err/bug1211-ice-move-refs-2.c b/test/err/bug1211-ice-move-refs-2.c new file mode 100644 index 000000000..504433f45 --- /dev/null +++ b/test/err/bug1211-ice-move-refs-2.c @@ -0,0 +1,51 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of indirect goto with label merge ICE. + https://github.com/cc65/cc65/issues/1211 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include + +/* When operating correctly, this returns 0. */ +int f (void) +{ + static const void *const x[2] = {&&L0, &&L1}; + goto *x[0]; +L0: +L1: return 0; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} From 344aea06693130a41f348a56f3e85337b15bb96a Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 23 Aug 2020 21:50:54 +0200 Subject: [PATCH 264/806] Add additional test cases for #1209 These test cases don't use dynamic labels. https://github.com/cc65/cc65/issues/1209#issuecomment-678738971 Also update the original test case for consistency: * Change failure message to just "FAIL", as there is only one failure * Outdent label definitions * Clarify description --- test/misc/Makefile | 12 +++++++ test/misc/bug1209-ind-goto-rev-2.c | 54 ++++++++++++++++++++++++++++++ test/misc/bug1209-ind-goto-rev-3.c | 52 ++++++++++++++++++++++++++++ test/misc/bug1209-ind-goto-rev.c | 6 ++-- 4 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 test/misc/bug1209-ind-goto-rev-2.c create mode 100644 test/misc/bug1209-ind-goto-rev-3.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 80cbcdf9e..b28fc4d06 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -81,6 +81,18 @@ $(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1209-ind-goto-rev.$1.$2.prg) $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev-2.$1.$2.prg: bug1209-ind-goto-rev-2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev-2.$1.$2.prg) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but gives an error +$(WORKDIR)/bug1209-ind-goto-rev-3.$1.$2.prg: bug1209-ind-goto-rev-3.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1209-ind-goto-rev-3.$1.$2.prg) + $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but gives an error $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/misc/bug1209-ind-goto-rev-2.c b/test/misc/bug1209-ind-goto-rev-2.c new file mode 100644 index 000000000..8918e5878 --- /dev/null +++ b/test/misc/bug1209-ind-goto-rev-2.c @@ -0,0 +1,54 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of indirect goto without dynamic labels and order label def, label ref, goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include +#include + +/* When operating correctly, this returns 0. */ +static unsigned char y = 0; +int f (void) { +L: if (y) return 0; + { + static const void *const x[1] = {&&L}; + y = 1; + goto *x[0]; + } +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/misc/bug1209-ind-goto-rev-3.c b/test/misc/bug1209-ind-goto-rev-3.c new file mode 100644 index 000000000..4c3268c9a --- /dev/null +++ b/test/misc/bug1209-ind-goto-rev-3.c @@ -0,0 +1,52 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Test of indirect goto without dynamic labels and order label ref, label def, goto. + https://github.com/cc65/cc65/issues/1209 + This should compile and should be moved to tests/val/ when the bug is fixed. +*/ + +#include +#include + +/* When operating correctly, this returns 0. */ +static unsigned char y = 0; +int f (void) { + static const void *const x[1] = {&&L}; +L: if (y) return 0; + y = 1; + goto *x[0]; +} + +static unsigned char failures = 0; + +int main (void) +{ + if (f () != 0) failures++; + + if (failures == 0) { + printf ("PASS\n"); + } else { + printf ("FAIL\n"); + } + + return failures; +} diff --git a/test/misc/bug1209-ind-goto-rev.c b/test/misc/bug1209-ind-goto-rev.c index 39d8639bd..ab8213a30 100644 --- a/test/misc/bug1209-ind-goto-rev.c +++ b/test/misc/bug1209-ind-goto-rev.c @@ -19,7 +19,7 @@ */ /* - Tests of indirect goto with the label before the goto. + Test of indirect goto with dynamic labels and order label def, label ref, goto. https://github.com/cc65/cc65/issues/1209 This should compile and should be moved to tests/val/ when the bug is fixed. */ @@ -32,7 +32,7 @@ int f (void) { static void *x[1]; /* Define the label before referencing it with indirect label syntax. */ - L: if (x[0] != 0) return 0; +L: if (x[0] != 0) return 0; x[0] = &&L; goto *x[0]; } @@ -46,7 +46,7 @@ int main (void) if (failures == 0) { printf ("PASS\n"); } else { - printf ("FAIL: %d failures\n", failures); + printf ("FAIL\n"); } return failures; From d68925c6a8708e0bf2d4ca825bac8ded2f0af903 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 25 Aug 2020 22:34:25 +0800 Subject: [PATCH 265/806] Bug #1113 was fixed long ago. --- test/{misc => err}/bug1113.c | 0 test/misc/Makefile | 7 ------- 2 files changed, 7 deletions(-) rename test/{misc => err}/bug1113.c (100%) diff --git a/test/misc/bug1113.c b/test/err/bug1113.c similarity index 100% rename from test/misc/bug1113.c rename to test/err/bug1113.c diff --git a/test/misc/Makefile b/test/misc/Makefile index b28fc4d06..f4beec452 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -99,13 +99,6 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/pptest2.$1.$2.prg) $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# this should fail to compile, because there are errors in the code -# instead, the compiler crashes -$(WORKDIR)/bug1113.$1.$2.prg: bug1113.c | $(WORKDIR) - @echo "FIXME: " $$@ "compiler crashes but should give an error." - $(if $(QUIET),echo misc/bug1113.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) From b6a4715a38932c928db839bd511bb957c74788ed Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 26 Aug 2020 08:45:01 +0800 Subject: [PATCH 266/806] Added test/ref/pr1220 for testing constant AND/OR code generation. --- test/ref/pr1220.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 test/ref/pr1220.c diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c new file mode 100644 index 000000000..c594b5ac2 --- /dev/null +++ b/test/ref/pr1220.c @@ -0,0 +1,344 @@ +/* PR #1220 - test constant AND/OR */ + +#include + +#define CONTEXT_A(x) do {\ + s = 0, flags = 0, t = (x),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +#define CONTEXT_B(x) do {\ + s = 0, flags = 0,\ + (x ? printf("%3d %2X: 1\n", s, flags) : printf("%3d %2X: 0\n", s, flags));\ + } while (0) + +int s, t; +void *p; +unsigned flags; + +int f(x) +{ + flags |= 1U << s; + ++s; + return x; +} + +#define _A f(a) +#define _B f(b) +#define _C f(c) +#define _D f(d) +#define _T (f(0), -1) +#define _F (f(-1), 0) + +void f0() +{ + printf("f0()\n"); + + CONTEXT_A(_T && _T && _T); + CONTEXT_A(_F && _F && _F); + + CONTEXT_A(_T || _T || _T); + CONTEXT_A(_F || _F || _F); + + CONTEXT_A(_T && _T || _T && _T); + CONTEXT_A(_F && _F || _F && _F); + CONTEXT_A(_T && _F || _T && _F); + CONTEXT_A(_F && _T || _F && _T); + + CONTEXT_A((_T && _T) || (_T && _T)); + CONTEXT_A((_F && _F) || (_F && _F)); + CONTEXT_A((_T && _F) || (_T && _F)); + CONTEXT_A((_F && _T) || (_F && _T)); + + CONTEXT_A((_T || _T) && (_T || _T)); + CONTEXT_A((_F || _F) && (_F || _F)); + CONTEXT_A((_T || _F) && (_T || _F)); + CONTEXT_A((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1(int a, int b, int c) +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A && _B && _C); + + CONTEXT_A(_T && _B && _C); + CONTEXT_A(_A && _T && _C); + CONTEXT_A(_A && _B && _T); + + CONTEXT_A(_F && _B && _C); + CONTEXT_A(_A && _F && _C); + CONTEXT_A(_A && _B && _F); + + CONTEXT_A(_T && _T && _C); + CONTEXT_A(_A && _T && _T); + CONTEXT_A(_T && _B && _T); + + printf("\n"); +} + +void f2(int a, int b, int c) +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A || _B || _C); + + CONTEXT_A(_T || _B || _C); + CONTEXT_A(_A || _T || _C); + CONTEXT_A(_A || _B || _T); + + CONTEXT_A(_F || _B || _C); + CONTEXT_A(_A || _F || _C); + CONTEXT_A(_A || _B || _F); + + CONTEXT_A(_F || _F || _C); + CONTEXT_A(_A || _F || _F); + CONTEXT_A(_F || _B || _F); + + printf("\n"); +} + +void f3(int a, int b, int c, int d) +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A(_A && _B || _C && _D); + CONTEXT_A(_T && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _T); + + CONTEXT_A(_T && _B || _C && _D); + CONTEXT_A(_A && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _D); + CONTEXT_A(_A && _B || _C && _T); + + CONTEXT_A(_F && _B || _C && _D); + CONTEXT_A(_A && _F || _C && _D); + CONTEXT_A(_A && _B || _F && _D); + CONTEXT_A(_A && _B || _C && _F); + + printf("\n"); +} + +void f4(int a, int b, int c, int d) +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A && _B) || (_C && _D)); + CONTEXT_A((_T && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _T)); + + CONTEXT_A((_T && _B) || (_C && _D)); + CONTEXT_A((_A && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _D)); + CONTEXT_A((_A && _B) || (_C && _T)); + + CONTEXT_A((_F && _B) || (_C && _D)); + CONTEXT_A((_A && _F) || (_C && _D)); + CONTEXT_A((_A && _B) || (_F && _D)); + CONTEXT_A((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5(int a, int b, int c, int d) +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A || _B) && (_C || _D)); + CONTEXT_A((_F || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _F)); + + CONTEXT_A((_T || _B) && (_C || _D)); + CONTEXT_A((_A || _T) && (_C || _D)); + CONTEXT_A((_A || _B) && (_T || _D)); + CONTEXT_A((_A || _B) && (_C || _T)); + + CONTEXT_A((_F || _B) && (_C || _D)); + CONTEXT_A((_A || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _D)); + CONTEXT_A((_A || _B) && (_C || _F)); + + printf("\n"); +} + +void f0_B() +{ + printf("f0()\n"); + + CONTEXT_B(_T && _T && _T); + CONTEXT_B(_F && _F && _F); + + CONTEXT_B(_T || _T || _T); + CONTEXT_B(_F || _F || _F); + + CONTEXT_B(_T && _T || _T && _T); + CONTEXT_B(_F && _F || _F && _F); + CONTEXT_B(_T && _F || _T && _F); + CONTEXT_B(_F && _T || _F && _T); + + CONTEXT_B((_T && _T) || (_T && _T)); + CONTEXT_B((_F && _F) || (_F && _F)); + CONTEXT_B((_T && _F) || (_T && _F)); + CONTEXT_B((_F && _T) || (_F && _T)); + + CONTEXT_B((_T || _T) && (_T || _T)); + CONTEXT_B((_F || _F) && (_F || _F)); + CONTEXT_B((_T || _F) && (_T || _F)); + CONTEXT_B((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1_B(int a, int b, int c) +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A && _B && _C); + + CONTEXT_B(_T && _B && _C); + CONTEXT_B(_A && _T && _C); + CONTEXT_B(_A && _B && _T); + + CONTEXT_B(_F && _B && _C); + CONTEXT_B(_A && _F && _C); + CONTEXT_B(_A && _B && _F); + + CONTEXT_B(_T && _T && _C); + CONTEXT_B(_A && _T && _T); + CONTEXT_B(_T && _B && _T); + + printf("\n"); +} + +void f2_B(int a, int b, int c) +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A || _B || _C); + + CONTEXT_B(_T || _B || _C); + CONTEXT_B(_A || _T || _C); + CONTEXT_B(_A || _B || _T); + + CONTEXT_B(_F || _B || _C); + CONTEXT_B(_A || _F || _C); + CONTEXT_B(_A || _B || _F); + + CONTEXT_B(_F || _F || _C); + CONTEXT_B(_A || _F || _F); + CONTEXT_B(_F || _B || _F); + + printf("\n"); +} + +void f3_B(int a, int b, int c, int d) +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B(_A && _B || _C && _D); + CONTEXT_B(_T && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _T); + + CONTEXT_B(_T && _B || _C && _D); + CONTEXT_B(_A && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _D); + CONTEXT_B(_A && _B || _C && _T); + + CONTEXT_B(_F && _B || _C && _D); + CONTEXT_B(_A && _F || _C && _D); + CONTEXT_B(_A && _B || _F && _D); + CONTEXT_B(_A && _B || _C && _F); + + printf("\n"); +} + +void f4_B(int a, int b, int c, int d) +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A && _B) || (_C && _D)); + CONTEXT_B((_T && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _T)); + + CONTEXT_B((_T && _B) || (_C && _D)); + CONTEXT_B((_A && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _D)); + CONTEXT_B((_A && _B) || (_C && _T)); + + CONTEXT_B((_F && _B) || (_C && _D)); + CONTEXT_B((_A && _F) || (_C && _D)); + CONTEXT_B((_A && _B) || (_F && _D)); + CONTEXT_B((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5_B(int a, int b, int c, int d) +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A || _B) && (_C || _D)); + CONTEXT_B((_F || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _F)); + + CONTEXT_B((_T || _B) && (_C || _D)); + CONTEXT_B((_A || _T) && (_C || _D)); + CONTEXT_B((_A || _B) && (_T || _D)); + CONTEXT_B((_A || _B) && (_C || _T)); + + CONTEXT_B((_F || _B) && (_C || _D)); + CONTEXT_B((_A || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _D)); + CONTEXT_B((_A || _B) && (_C || _F)); + + printf("\n"); +} + +int main() +{ + f0(); + + f1(0, 0, 0); + f2(0, 0, 0); + f3(0, 0, 0, 0); + f4(0, 0, 0, 0); + f5(0, 0, 0, 0); + + f1(1, 1, 1); + f2(1, 1, 1); + f3(1, 1, 1, 1); + f4(1, 1, 1, 1); + f5(1, 1, 1, 1); + + f3(1, 0, 1, 0); + f4(1, 0, 1, 0); + f5(1, 0, 1, 0); + f3(0, 1, 0, 1); + f4(0, 1, 0, 1); + f5(0, 1, 0, 1); + + f0_B(); + + f1_B(0, 0, 0); + f2_B(0, 0, 0); + f3_B(0, 0, 0, 0); + f4_B(0, 0, 0, 0); + f5_B(0, 0, 0, 0); + + f1_B(1, 1, 1); + f2_B(1, 1, 1); + f3_B(1, 1, 1, 1); + f4_B(1, 1, 1, 1); + f5_B(1, 1, 1, 1); + + f3_B(1, 0, 1, 0); + f4_B(1, 0, 1, 0); + f5_B(1, 0, 1, 0); + f3_B(0, 1, 0, 1); + f4_B(0, 1, 0, 1); + f5_B(0, 1, 0, 1); + + return 0; +} From c216b3534b0a419b8fcad55e0c94dca2c7705d0c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 13:36:43 +0200 Subject: [PATCH 267/806] fix compilation, fixes issue #1184 --- testcode/lib/scanf-test.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/testcode/lib/scanf-test.c b/testcode/lib/scanf-test.c index 9fce01b77..f17b62294 100644 --- a/testcode/lib/scanf-test.c +++ b/testcode/lib/scanf-test.c @@ -14,9 +14,7 @@ /* Define USE_STDIO, when you want to use the stdio functions. ** Do not define it, when you want to use the conio functions. */ -/* #define USE_STDIO -*/ #include #include @@ -35,22 +33,23 @@ #define ARRAYSIZE(a) (sizeof (a) / sizeof (a)[0]) +typedef enum { + INT, + CHAR +} TYPE; + +typedef union { + int nvalue; + const char *svalue; +} VALUE; + static const struct { const char *input, *format; int rvalue; - enum TYPE { - INT, - CHAR - } type1; - union { - int nvalue; - const char *svalue; - } v1; - enum TYPE type2; - union { - int nvalue; - const char *svalue; - } v2; + TYPE type1; + VALUE v1; + TYPE type2; + VALUE v2; } test_data[] = { /* Input sequences for character specifiers must be less than 80 characters ** long. These format strings are allowwed a maximum of two assignment From e5a1755133b679c2cd67de9c64f0a05b7aa83b8b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 15:08:17 +0200 Subject: [PATCH 268/806] added some ifdefs to make testcode compile for apple2 and atari targets --- testcode/lib/dir-test.c | 16 +++++++++++++--- testcode/lib/heaptest.c | 7 ++++--- testcode/lib/mouse-test.c | 2 +- testcode/lib/strdup-test.c | 6 ++++-- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/testcode/lib/dir-test.c b/testcode/lib/dir-test.c index 9102fc74d..61e42247e 100644 --- a/testcode/lib/dir-test.c +++ b/testcode/lib/dir-test.c @@ -19,9 +19,13 @@ #include #include #include -#include #include +#if defined(__CBM__) +#include +#elif defined(__APPLE2__) +#include +#endif int main(void) { @@ -51,9 +55,13 @@ int main(void) printf("contents of \"%s\":\n", name); while ((E = readdir (D)) != 0) { printf ("dirent.d_name[] : \"%s\"\n", E->d_name); +#if !defined(__ATARI__) printf ("dirent.d_blocks : %10u\n", E->d_blocks); +#endif printf ("dirent.d_type : %10d\n", E->d_type); +#if !defined(__APPLE2__) && !defined(__ATARI__) printf ("telldir() : %10lu\n", telldir (D)); +#endif printf ("---\n"); if (!go) { switch (cgetc ()) { @@ -63,14 +71,16 @@ int main(void) case 'q': goto done; - +#if !defined(__APPLE2__) && !defined(__ATARI__) case 'r': seekdir (D, E->d_off); break; - +#endif +#if !defined(__ATARI__) case 's': rewinddir (D); break; +#endif } } diff --git a/testcode/lib/heaptest.c b/testcode/lib/heaptest.c index d776e2f0c..560694bee 100644 --- a/testcode/lib/heaptest.c +++ b/testcode/lib/heaptest.c @@ -214,10 +214,10 @@ int main (void) /* Show info at start */ ShowInfo (); - +#if !defined(__APPLE2__) /* Remember the time */ T = clock (); - +#endif /* Do the tests */ Test1 (); Test2 (); @@ -226,10 +226,11 @@ int main (void) Test5 (); Test6 (); +#if !defined(__APPLE2__) /* Calculate the time and print it */ T = clock () - T; printf ("Time needed: %lu ticks\n", T); - +#endif /* Done */ return EXIT_SUCCESS; } diff --git a/testcode/lib/mouse-test.c b/testcode/lib/mouse-test.c index ea8311d19..64482b937 100644 --- a/testcode/lib/mouse-test.c +++ b/testcode/lib/mouse-test.c @@ -190,7 +190,7 @@ int main (void) #endif /* Set dark-on-light colors. Clear the screen. */ -#ifdef __CBM__ +#if defined(__CBM__) && !defined(__VIC20__) (void) bordercolor (COLOR_GRAY2); (void) bgcolor (COLOR_WHITE); (void) textcolor (COLOR_GRAY1); diff --git a/testcode/lib/strdup-test.c b/testcode/lib/strdup-test.c index e30841c3e..2fcc9816f 100644 --- a/testcode/lib/strdup-test.c +++ b/testcode/lib/strdup-test.c @@ -84,19 +84,21 @@ int main (void) /* Show info at start */ ShowInfo (); - +#if !defined(__APPLE2__) /* Remember the time */ T = clock (); - +#endif /* Do the tests */ FillArray (); ShowInfo (); FreeArray (); ShowInfo (); +#if !defined(__APPLE2__) /* Calculate the time and print it */ T = clock () - T; printf ("Time needed: %lu ticks\n", T); +#endif /* Done */ return EXIT_SUCCESS; From 83cc1151121d775cef368fffa3c15bf4b13bbf03 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 15:51:20 +0200 Subject: [PATCH 269/806] re-add define for pad bits hw address, which was accidently removed in some refactor commit --- include/gamate.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/gamate.h b/include/gamate.h index 0af21623d..8b9790e39 100644 --- a/include/gamate.h +++ b/include/gamate.h @@ -170,6 +170,8 @@ /* No support for dynamically loadable drivers */ #define DYN_DRV 0 +#define JOY_DATA 0x4400 /* hw register to read the pad bits from */ + /* Masks for joy_read */ #define JOY_UP_MASK 0x01 #define JOY_DOWN_MASK 0x02 From 4c912a0fbe3ff74be793c09d7eee9ae92fe9bbad Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 15:59:52 +0200 Subject: [PATCH 270/806] make gamate testcode compile again --- testcode/lib/gamate/ctest.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testcode/lib/gamate/ctest.c b/testcode/lib/gamate/ctest.c index 793770cee..bff3f5986 100644 --- a/testcode/lib/gamate/ctest.c +++ b/testcode/lib/gamate/ctest.c @@ -25,19 +25,19 @@ int main(int argc, char *argv[]) gotoxy(0,2);cprintf("%04x %02x %02x %02x", n, x, y, *((unsigned char*)JOY_DATA)); switch((*((unsigned char*)JOY_DATA))) { - case 0xff ^ JOY_DATA_UP: + case 0xff ^ JOY_UP_MASK: ++y; if (y == 0xc8) y = 0; break; - case 0xff ^ JOY_DATA_DOWN: + case 0xff ^ JOY_DOWN_MASK: --y; if (y == 0xff) y = 0xc7; break; - case 0xff ^ JOY_DATA_LEFT: + case 0xff ^ JOY_LEFT_MASK: ++x; break; - case 0xff ^ JOY_DATA_RIGHT: + case 0xff ^ JOY_RIGHT_MASK: --x; break; - case 0xff ^ JOY_DATA_FIRE_A: + case 0xff ^ JOY_BTN_A_MASK: break; } From c658acbf850e32508ea0e14f0f62d675a009c7d9 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Wed, 26 Aug 2020 20:37:28 +0200 Subject: [PATCH 271/806] Avoid cl65 in tests. cl65 creates intermediate files based on the source file name in the source file directory. Calling cl65 in parallel with the same source file causes those intermediate files to get overwritten. Fixes #1080 --- test/asm/Makefile | 27 ++++++++++++-------------- test/dasm/Makefile | 11 ++++------- test/misc/Makefile | 48 ++++++++++++++++++++++------------------------ test/ref/Makefile | 19 +++++++++--------- test/todo/Makefile | 11 ++++++----- test/val/Makefile | 16 ++++++---------- 6 files changed, 61 insertions(+), 71 deletions(-) diff --git a/test/asm/Makefile b/test/asm/Makefile index 94a925376..b157bb672 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -8,23 +8,22 @@ ifdef CMD_EXE EXE = .exe MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else EXE = MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET .SILENT: endif -CL65 := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) +CA65 := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) WORKDIR = ../../testwrk/asm -DIFF = $(WORKDIR)/isequal$(EXE) +ISEQUAL = $(WORKDIR)/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -44,29 +43,28 @@ all: $(OPCODE_BINS) $(CPUDETECT_BINS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../isequal.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define OPCODE_template -$(WORKDIR)/$1-opcodes.bin: $1-opcodes.s $(DIFF) +$(WORKDIR)/$1-opcodes.bin: $1-opcodes.s $(ISEQUAL) $(if $(QUIET),echo asm/$1-opcodes.bin) - $(CL65) --cpu $1 -t none -l $(WORKDIR)/$1-opcodes.lst -o $$@ $$< - $(DIFF) $$@ $1-opcodes.ref + $(CA65) --cpu $1 -t none -l $(WORKDIR)/$1-opcodes.lst -o $(WORKDIR)/$1-opcodes.o $$< + $(LD65) -t none -o $$@ $(WORKDIR)/$1-opcodes.o none.lib + $(ISEQUAL) $$@ $1-opcodes.ref endef # OPCODE_template $(foreach cpu,$(OPCODE_CPUS),$(eval $(call OPCODE_template,$(cpu)))) -# cpudetect.o is written by multiple rules -.NOTPARALLEL: - define CPUDETECT_template -$(WORKDIR)/$1-cpudetect.bin: cpudetect.s $(DIFF) +$(WORKDIR)/$1-cpudetect.bin: cpudetect.s $(ISEQUAL) $(if $(QUIET),echo asm/$1-cpudetect.bin) - $(CL65) --cpu $1 -t none -l $(WORKDIR)/$1-cpudetect.lst -o $$@ $$< - $(DIFF) $$@ $1-cpudetect.ref + $(CA65) --cpu $1 -t none -l $(WORKDIR)/$1-cpudetect.lst -o $(WORKDIR)/$1-cpudetect.o $$< + $(LD65) -t none -o $$@ $(WORKDIR)/$1-cpudetect.o none.lib + $(ISEQUAL) $$@ $1-cpudetect.ref endef # CPUDETECT_template @@ -74,4 +72,3 @@ $(foreach cpu,$(CPUDETECT_CPUS),$(eval $(call CPUDETECT_template,$(cpu)))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(OPCODE_REFS:.ref=.o) cpudetect.o) diff --git a/test/dasm/Makefile b/test/dasm/Makefile index faa6b7fa0..dc0b57c76 100644 --- a/test/dasm/Makefile +++ b/test/dasm/Makefile @@ -8,12 +8,10 @@ ifdef CMD_EXE EXE = .exe MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else EXE = MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -25,7 +23,7 @@ DA65 := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) WORKDIR = ../../testwrk/dasm -DIFF = $(WORKDIR)/isequal$(EXE) +ISEQUAL = $(WORKDIR)/isequal$(EXE) CC = gcc CFLAGS = -O2 @@ -44,7 +42,7 @@ all: $(BINS) $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../isequal.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define DISASS_template @@ -55,10 +53,10 @@ $(WORKDIR)/$1-disass.bin: $1-disass.s | $(WORKDIR) $(WORKDIR)/$1-reass.s: $(WORKDIR)/$1-disass.bin $(DA65) --cpu $1 $(START) -o $$@ $$< -$(WORKDIR)/$1-reass.bin: $(WORKDIR)/$1-reass.s $(DIFF) +$(WORKDIR)/$1-reass.bin: $(WORKDIR)/$1-reass.s $(ISEQUAL) $(if $(QUIET),echo dasm/$1-reass.bin) $(CL65) --cpu $1 -t none $(START) -o $$@ $$< - $(DIFF) $$@ $(WORKDIR)/$1-disass.bin + $(ISEQUAL) $$@ $(WORKDIR)/$1-disass.bin endef # DISASS_template @@ -66,4 +64,3 @@ $(foreach cpu,$(CPUS),$(eval $(call DISASS_template,$(cpu)))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.s=.o)) diff --git a/test/misc/Makefile b/test/misc/Makefile index f4beec452..993a46bd4 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -11,7 +11,6 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NOT = ! @@ -19,7 +18,6 @@ else NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -30,14 +28,16 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) WORKDIR = ..$S..$Stestwrk$Smisc OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sisequal$(EXE) +ISEQUAL = $(WORKDIR)$Sisequal$(EXE) CC = gcc CFLAGS = -O2 @@ -50,15 +50,10 @@ TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02. all: $(TESTS) -# The same input file is processed with different cl65 args, -# but cl65 uses the input file name to make the temp file name, -# and they stomp each other. -.NOTPARALLEL: - $(WORKDIR): $(call MKDIR,$(WORKDIR)) -$(DIFF): ../isequal.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< define PRG_template @@ -67,61 +62,65 @@ define PRG_template $(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug250.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug760.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev-2.$1.$2.prg: bug1209-ind-goto-rev-2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev-2.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev-3.$1.$2.prg: bug1209-ind-goto-rev-3.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev-3.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/pptest2.$1.$2.prg) - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) # these need reference data that can't be generated by a host-compiled program, # in a useful way -$(WORKDIR)/limits.$1.$2.prg: limits.c $(DIFF) | $(WORKDIR) +$(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/limits.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/limits.$1.$2.out - $(DIFF) $(WORKDIR)/limits.$1.$2.out limits.ref + $(ISEQUAL) $(WORKDIR)/limits.$1.$2.out limits.ref -$(WORKDIR)/goto.$1.$2.prg: goto.c $(DIFF) | $(WORKDIR) +$(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR) $(if $(QUIET),echo misc/goto.$1.$2.prg) - $(CL65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out - $(DIFF) $(WORKDIR)/goto.$1.$2.out goto.ref + $(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out + $(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref # the rest are tests that fail currently for one reason or another $(WORKDIR)/sitest.$1.$2.prg: sitest.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." - $(NOT) $(CL65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -131,4 +130,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/ref/Makefile b/test/ref/Makefile index f88821f64..5faf9fb19 100644 --- a/test/ref/Makefile +++ b/test/ref/Makefile @@ -11,14 +11,12 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / EXE = NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -28,14 +26,16 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) WORKDIR = ..$S..$Stestwrk$Sref OPTIONS = g O Os Osi Osir Osr Oi Oir Or -DIFF = $(WORKDIR)$Sisequal$(EXE) +ISEQUAL = $(WORKDIR)$Sisequal$(EXE) CC = gcc CFLAGS = -O2 -Wall -W -Wextra -funsigned-char -fwrapv -fno-strict-overflow @@ -57,7 +57,7 @@ $(WORKDIR)/%.ref: %.c | $(WORKDIR) $(CC) $(CFLAGS) -o $(WORKDIR)/$*.host $< $(NULLERR) $(WORKDIR)$S$*.host > $@ -$(DIFF): ../isequal.c | $(WORKDIR) +$(ISEQUAL): ../isequal.c | $(WORKDIR) $(CC) $(CFLAGS) -o $@ $< # "yaccdbg.c" includes "yacc.c". @@ -68,11 +68,13 @@ $(WORKDIR)/yaccdbg.%.prg: yacc.c define PRG_template -$(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(DIFF) +$(WORKDIR)/%.$1.$2.prg: %.c $(WORKDIR)/%.ref $(ISEQUAL) $(if $(QUIET),echo ref/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ > $(WORKDIR)/$$*.$1.$2.out - $(DIFF) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref + $(ISEQUAL) $(WORKDIR)/$$*.$1.$2.out $(WORKDIR)/$$*.ref endef # PRG_template @@ -81,4 +83,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/todo/Makefile b/test/todo/Makefile index ab5eb598c..17561f8f4 100644 --- a/test/todo/Makefile +++ b/test/todo/Makefile @@ -10,14 +10,12 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NOT = ! NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -28,7 +26,9 @@ endif SIM65FLAGS = -x 200000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) WORKDIR = ../../testwrk/val @@ -50,7 +50,9 @@ define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) $(if $(QUIET),echo val/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -60,4 +62,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) diff --git a/test/val/Makefile b/test/val/Makefile index be63cd8da..1a9fa9a45 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -9,13 +9,11 @@ ifdef CMD_EXE NULLDEV = nul: MKDIR = mkdir $(subst /,\,$1) RMDIR = -rmdir /s /q $(subst /,\,$1) - DEL = del /f $(subst /,\,$1) else S = / NULLDEV = /dev/null MKDIR = mkdir -p $1 RMDIR = $(RM) -r $1 - DEL = $(RM) $1 endif ifdef QUIET @@ -26,7 +24,9 @@ endif SIM65FLAGS = -x 5000000000 -CL65 := $(if $(wildcard ../../bin/cl65*),..$S..$Sbin$Scl65,cl65) +CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) +CA65 := $(if $(wildcard ../../bin/ca65*),..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../bin/ld65*),..$S..$Sbin$Sld65,ld65) SIM65 := $(if $(wildcard ../../bin/sim65*),..$S..$Sbin$Ssim65,sim65) WORKDIR = ../../testwrk/val @@ -41,11 +41,6 @@ TESTS += $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).65c02. all: $(TESTS) -# The same input file is processed with different cl65 args, -# but cl65 uses the input file name to make the temp file name, -# and they stomp each other. -.NOTPARALLEL: - $(WORKDIR): $(call MKDIR,$(WORKDIR)) @@ -53,7 +48,9 @@ define PRG_template $(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR) $(if $(QUIET),echo val/$$*.$1.$2.prg) - $(CL65) -t sim$2 $$(CC65FLAGS) -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 $$(CC65FLAGS) -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) endef # PRG_template @@ -63,4 +60,3 @@ $(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02))) clean: @$(call RMDIR,$(WORKDIR)) - @$(call DEL,$(SOURCES:.c=.o)) From c6adf4364fe0ce299a9ef415c3653231045560ad Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 20:53:45 +0200 Subject: [PATCH 272/806] make atari testcode compile again, added makefile --- testcode/lib/atari/Makefile | 32 ++++++++++++++++++++++++++++++++ testcode/lib/atari/multi.xex | Bin 157 -> 0 bytes testcode/lib/atari/scrcode.s | 5 +++++ testcode/lib/atari/sys.c | 2 ++ 4 files changed, 39 insertions(+) create mode 100644 testcode/lib/atari/Makefile delete mode 100644 testcode/lib/atari/multi.xex diff --git a/testcode/lib/atari/Makefile b/testcode/lib/atari/Makefile new file mode 100644 index 000000000..e9ec853b8 --- /dev/null +++ b/testcode/lib/atari/Makefile @@ -0,0 +1,32 @@ + +all: charmapping.xex defdev.xex displaylist.xex mem.xex multi.xex ostype.xex \ + scrcode.com sys.xex + +charmapping.xex: charmapping.c + cl65 -t atari -o charmapping.xex charmapping.c +defdev.xex: defdev.c + cl65 -t atari -o defdev.xex defdev.c +displaylist.xex: displaylist.c + cl65 -t atari -o displaylist.xex displaylist.c +mem.xex: mem.c ../getsp.s + cl65 -t atari -o mem.xex mem.c ../getsp.s +multi.xex: multi-xex.s multi-xex.cfg + cl65 -t atari -C multi-xex.cfg multi-xex.s -o multi.xex +ostype.xex: ostype.c + cl65 -t atari -o ostype.xex ostype.c +scrcode.com: scrcode.s + ca65 -t atari -o scrcode.o scrcode.s + ld65 -C atari-asm.cfg -o scrcode.com scrcode.o +sys.xex: sys.c + cl65 -t atari -o sys.xex sys.c + +clean: + $(RM) charmapping.xex + $(RM) defdev.xex + $(RM) displaylist.xex + $(RM) mem.xex + $(RM) multi.xex + $(RM) ostype.xex + $(RM) scrcode.o + $(RM) scrcode.com + $(RM) sys.xex diff --git a/testcode/lib/atari/multi.xex b/testcode/lib/atari/multi.xex deleted file mode 100644 index 7da39ad47af630bef4edab9f21e06b7162e98e88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmezWzkzKz+aiXQbLP4*uVkC+%DnRbTo2~Cp3Eyb=Q=U_ggx=ePfW?oOV%(0lQ&>8 UWCEI?$E4@Op!b03Arpfh0RH4B!2kdN diff --git a/testcode/lib/atari/scrcode.s b/testcode/lib/atari/scrcode.s index cd4290781..ba778579b 100644 --- a/testcode/lib/atari/scrcode.s +++ b/testcode/lib/atari/scrcode.s @@ -43,15 +43,20 @@ key: lda CH dispdata: scrcode "fooBa", 'r', $66, 3+4 disp_len = * - dispdata +.export __AUTOSTART__: absolute = 1 .segment "AUTOSTRT" .word $02E0 .word $02E1 .word __CODE_LOAD__+1 +.export __EXEHDR__: absolute = 1 .segment "EXEHDR" .word $FFFF + +.segment "MAINHDR" + .word __CODE_LOAD__ .word __BSS_LOAD__ - 1 diff --git a/testcode/lib/atari/sys.c b/testcode/lib/atari/sys.c index 9ec7aa631..59debd758 100644 --- a/testcode/lib/atari/sys.c +++ b/testcode/lib/atari/sys.c @@ -10,6 +10,8 @@ #include <6502.h> #include +#define IOCB (OS.iocb[0]) + static struct regs regs; static struct __iocb *iocb = &IOCB; /* use IOCB #0 */ From b0d3b19a6a1c1e56589f1453ccb99dcde63257e2 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Wed, 26 Aug 2020 20:58:44 +0200 Subject: [PATCH 273/806] The bug1209 test fails at link stage. --- test/misc/Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index 993a46bd4..1f7a788ba 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -74,19 +74,25 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev-2.$1.$2.prg: bug1209-ind-goto-rev-2.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev-2.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) # should compile, but gives an error $(WORKDIR)/bug1209-ind-goto-rev-3.$1.$2.prg: bug1209-ind-goto-rev-3.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1209-ind-goto-rev-3.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(NOT) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) # should compile, but gives an error $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) From 8649859bc544d9ebb1c4f8215f00cd303a23438b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 21:28:19 +0200 Subject: [PATCH 274/806] added/updated Makefiles, preparing for CI (later) --- testcode/lib/accelerator/Makefile | 31 +++++++++++++++++++- testcode/lib/apple2/Makefile | 33 +++++++++++++++++++++- testcode/lib/atari/Makefile | 47 +++++++++++++++++++++++++------ testcode/lib/atari5200/Makefile | 36 +++++++++++++++++++++++ testcode/lib/gamate/Makefile | 41 +++++++++++++++++++++++---- testcode/lib/pce/Makefile | 29 +++++++++++++++++++ 6 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 testcode/lib/atari5200/Makefile diff --git a/testcode/lib/accelerator/Makefile b/testcode/lib/accelerator/Makefile index 6b90a9556..f4f651535 100644 --- a/testcode/lib/accelerator/Makefile +++ b/testcode/lib/accelerator/Makefile @@ -1,4 +1,30 @@ -CL ?= cl65 +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif all: c64-scpu-test.prg c128-scpu-test.prg c64dtv-test.prg \ c64-c128-test.prg c128-test.prg chameleon-test.prg \ @@ -27,3 +53,6 @@ c65-test.prg: c65-test.c turbomaster-test.prg: turbomaster-test.c $(CL) -t c64 turbomaster-test.c -o turbomaster-test.prg + +clean: + $(RM) *.prg diff --git a/testcode/lib/apple2/Makefile b/testcode/lib/apple2/Makefile index 9d551aa62..3ea2463f1 100644 --- a/testcode/lib/apple2/Makefile +++ b/testcode/lib/apple2/Makefile @@ -1,7 +1,33 @@ # For this one see https://applecommander.github.io/ AC ?= ac.jar -CL ?= cl65 +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif all: hgr.dsk dhgr.dsk @@ -35,3 +61,8 @@ dhgr.dsk: dhgrshow dhgrshow: dhgrshow.c $(CL) -Oirs -t apple2enh --start-addr 0x4000 -m dhgrshow.map $^ + +clean: + $(RM) hgr.dsk dhgr.dsk + $(RM) hgrshow hgrshow.map + $(RM) hgrtest hgrtest.map diff --git a/testcode/lib/atari/Makefile b/testcode/lib/atari/Makefile index e9ec853b8..a34a5f2d9 100644 --- a/testcode/lib/atari/Makefile +++ b/testcode/lib/atari/Makefile @@ -1,24 +1,53 @@ +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + all: charmapping.xex defdev.xex displaylist.xex mem.xex multi.xex ostype.xex \ scrcode.com sys.xex charmapping.xex: charmapping.c - cl65 -t atari -o charmapping.xex charmapping.c + $(CL) -t atari -o charmapping.xex charmapping.c defdev.xex: defdev.c - cl65 -t atari -o defdev.xex defdev.c + $(CL) -t atari -o defdev.xex defdev.c displaylist.xex: displaylist.c - cl65 -t atari -o displaylist.xex displaylist.c + $(CL) -t atari -o displaylist.xex displaylist.c mem.xex: mem.c ../getsp.s - cl65 -t atari -o mem.xex mem.c ../getsp.s + $(CL) -t atari -o mem.xex mem.c ../getsp.s multi.xex: multi-xex.s multi-xex.cfg - cl65 -t atari -C multi-xex.cfg multi-xex.s -o multi.xex + $(CL) -t atari -C multi-xex.cfg multi-xex.s -o multi.xex ostype.xex: ostype.c - cl65 -t atari -o ostype.xex ostype.c + $(CL) -t atari -o ostype.xex ostype.c scrcode.com: scrcode.s - ca65 -t atari -o scrcode.o scrcode.s - ld65 -C atari-asm.cfg -o scrcode.com scrcode.o +# ca65 -t atari -o scrcode.o scrcode.s +# ld65 -C atari-asm.cfg -o scrcode.com scrcode.o + $(CL) -t atari -C atari-asm.cfg -o scrcode.com scrcode.s sys.xex: sys.c - cl65 -t atari -o sys.xex sys.c + $(CL) -t atari -o sys.xex sys.c clean: $(RM) charmapping.xex diff --git a/testcode/lib/atari5200/Makefile b/testcode/lib/atari5200/Makefile new file mode 100644 index 000000000..990ced689 --- /dev/null +++ b/testcode/lib/atari5200/Makefile @@ -0,0 +1,36 @@ + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + +all: hello + +hello: hello.c + $(CL) -t atari5200 -o hello hello.c + +clean: + $(RM) hello diff --git a/testcode/lib/gamate/Makefile b/testcode/lib/gamate/Makefile index 2cf98d189..d4f1b9673 100644 --- a/testcode/lib/gamate/Makefile +++ b/testcode/lib/gamate/Makefile @@ -1,14 +1,42 @@ +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + all: audiotest.bin lcdtest.bin ctest.bin audiotest.bin: audiotest.s - ../../../bin/cl65 -l audiotest.lst -t gamate -o audiotest.bin audiotest.s + $(CL) -l audiotest.lst -t gamate -o audiotest.bin audiotest.s lcdtest.bin: lcdtest.s - ../../../bin/cl65 -l lcdtest.lst -t gamate -o lcdtest.bin lcdtest.s + $(CL) -l lcdtest.lst -t gamate -o lcdtest.bin lcdtest.s ctest.bin: ctest.c - ../../../bin/cl65 -l ctest.lst -t gamate -o ctest.bin ctest.c + $(CL) -l ctest.lst -t gamate -o ctest.bin ctest.c nachtm.bin: nachtm.c - ../../../bin/cl65 -Os -l nachtm.lst -t gamate -o nachtm.bin nachtm.c + $(CL) -Os -l nachtm.lst -t gamate -o nachtm.bin nachtm.c gamate-fixcart nachtm.bin test1: lcdtest.bin @@ -21,5 +49,6 @@ testn: nachtm.bin cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/nachtm.bin clean: - rm -f lcdtest.o audiotest.o ctest.o - rm -f lcdtest.bin audiotest.bin ctest.bin nachtm.bin + $(RM) lcdtest.o audiotest.o ctest.o + $(RM) lcdtest.bin audiotest.bin ctest.bin nachtm.bin + $(RM) audiotest.lst lcdtest.lst ctest.lst diff --git a/testcode/lib/pce/Makefile b/testcode/lib/pce/Makefile index a4a495c9a..0c41778cc 100644 --- a/testcode/lib/pce/Makefile +++ b/testcode/lib/pce/Makefile @@ -1,3 +1,32 @@ + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + .PHONY: all clean test # Size of cartridge to generate. From f5b1b69376f978223dfabe535f23a5ec03bb3dc2 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 275/806] Forbid struct/union fields of incomplete types. --- src/cc65/declare.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index ccd4e9004..6c1dbd751 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -911,8 +911,15 @@ static SymEntry* ParseUnionDecl (const char* Name) } } + /* Check for incomplete type */ + if (IsIncompleteESUType (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } + /* Handle sizes */ - FieldSize = CheckedSizeOf (Decl.Type); + FieldSize = SizeOf (Decl.Type); if (FieldSize > UnionSize) { UnionSize = FieldSize; } @@ -1095,6 +1102,13 @@ static SymEntry* ParseStructDecl (const char* Name) } } + /* Check for incomplete type */ + if (IsIncompleteESUType (Decl.Type)) { + Error ("Field '%s' has incomplete type '%s'", + Decl.Ident, + GetFullTypeName (Decl.Type)); + } + /* Add a field entry to the table */ if (FieldWidth > 0) { /* Full bytes have already been added to the StructSize, @@ -1119,7 +1133,7 @@ static SymEntry* ParseStructDecl (const char* Name) AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); } if (!FlexibleMember) { - StructSize += CheckedSizeOf (Decl.Type); + StructSize += SizeOf (Decl.Type); } } From 1957dc7a5c36bab15fd2021ce52b5b7e4f2f7479 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 276/806] Disallowed arrays of incomplete types. Fixed diagnostics on incomplete local arrays. --- src/cc65/compile.c | 21 +++------------ src/cc65/declare.c | 67 ++++++++++++++++++++++++++++++++++++---------- src/cc65/locals.c | 18 ++++++++++--- 3 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 82dc7ec63..3296968f6 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -140,19 +140,10 @@ static void Parse (void) comma = 0; while (1) { - Declaration Decl; + Declaration Decl; /* Read the next declaration */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); - if (Decl.Ident[0] == '\0') { - NextToken (); - break; - } - - if ((Decl.StorageClass & SC_FICTITIOUS) == SC_FICTITIOUS) { - /* Failed parsing */ - goto SkipOneDecl; - } /* Check if we must reserve storage for the variable. We do this, ** @@ -163,8 +154,9 @@ static void Parse (void) ** ** This means that "extern int i;" will not get storage allocated. */ - if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && - (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) { + if ((Decl.StorageClass & SC_FUNC) != SC_FUNC && + (Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF && + (Decl.StorageClass & SC_FICTITIOUS) != SC_FICTITIOUS) { if ((Spec.Flags & DS_DEF_STORAGE) != 0 || (Decl.StorageClass & (SC_EXTERN|SC_STATIC)) == SC_STATIC || ((Decl.StorageClass & SC_EXTERN) != 0 && @@ -296,7 +288,6 @@ static void Parse (void) } -SkipOneDecl: /* Check for end of declaration list */ if (CurTok.Tok == TOK_COMMA) { NextToken (); @@ -452,10 +443,6 @@ void Compile (const char* FileName) } Sym = GetSymType (GetElementType (Entry->Type)); - if (Size == 0 && Sym != 0 && SymIsDef (Sym)) { - /* Array of 0-size elements */ - Warning ("Array '%s[]' has 0-sized elements", Entry->Name); - } } /* For non-ESU types, Size != 0 */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 6c1dbd751..0bfbcedd2 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -457,6 +457,37 @@ static unsigned ParseOneStorageClass (void) +static void CheckArrayElementType (Type* DataType) +/* Check if data type consists of arrays of incomplete element types */ +{ + Type* T = DataType; + + while (T->C != T_END) { + if (IsTypeArray (T)) { + ++T; + if (IsIncompleteESUType (T)) { + /* We cannot have an array of incomplete elements */ + Error ("Array of incomplete element type '%s'", GetFullTypeName (T)); + } else if (SizeOf (T) == 0) { + /* If the array is multi-dimensional, try to get the true + ** element type. + */ + if (IsTypeArray (T)) { + continue; + } + /* We could support certain 0-size element types as an extension */ + if (!IsTypeVoid (T) || IS_Get (&Standard) != STD_CC65) { + Error ("Array of 0-size element type '%s'", GetFullTypeName (T)); + } + } + } else { + ++T; + } + } +} + + + static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) /* Parse a storage class */ { @@ -956,16 +987,16 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { FieldTab = GetSymTab (); LeaveStructLevel (); - /* Empty union is not supported now */ - if (UnionSize == 0) { - Error ("Empty union type '%s' is not supported", Name); - } - /* Return a fictitious symbol if errors occurred during parsing */ if (PrevErrorCount != ErrorCount) { Flags |= SC_FICTITIOUS; } + /* Empty union is not supported now */ + if (UnionSize == 0) { + Error ("Empty union type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab); } @@ -1160,16 +1191,16 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { FieldTab = GetSymTab (); LeaveStructLevel (); - /* Empty struct is not supported now */ - if (StructSize == 0) { - Error ("Empty struct type '%s' is not supported", Name); - } - /* Return a fictitious symbol if errors occurred during parsing */ if (PrevErrorCount != ErrorCount) { Flags |= SC_FICTITIOUS; } + /* Empty struct is not supported now */ + if (StructSize == 0) { + Error ("Empty struct type '%s' is not supported", Name); + } + /* Make a real entry from the forward decl and return it */ return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab); } @@ -1922,6 +1953,9 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Do several fixes on qualifiers */ FixQualifiers (D->Type); + /* Check if the data type consists of any arrays of forbidden types */ + CheckArrayElementType (D->Type); + /* If we have a function, add a special storage class */ if (IsTypeFunc (D->Type)) { D->StorageClass |= SC_FUNC; @@ -1993,10 +2027,15 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) Error ("Invalid size in declaration (0x%06X)", Size); } } + } - if (PrevErrorCount != ErrorCount) { - /* Don't give storage if the declaration is not parsed correctly */ - D->StorageClass |= SC_DECL | SC_FICTITIOUS; + if (PrevErrorCount != ErrorCount) { + /* Make the declaration fictitious if is is not parsed correctly */ + D->StorageClass |= SC_DECL | SC_FICTITIOUS; + + if (Mode == DM_NEED_IDENT && D->Ident[0] == '\0') { + /* Use a fictitious name for the identifier if it is missing */ + AnonName (D->Ident, "global"); } } } @@ -2242,7 +2281,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Get the array data */ Type* ElementType = GetElementType (T); - unsigned ElementSize = CheckedSizeOf (ElementType); + unsigned ElementSize = SizeOf (ElementType); long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ diff --git a/src/cc65/locals.c b/src/cc65/locals.c index a21a09e8e..4323943a1 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -172,7 +172,11 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } @@ -357,7 +361,11 @@ static void ParseAutoDecl (Declaration* Decl) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } @@ -411,7 +419,11 @@ static void ParseStaticDecl (Declaration* Decl) /* Cannot allocate a variable of zero size */ if (Size == 0) { - Error ("Variable '%s' has unknown size", Decl->Ident); + if (IsTypeArray (Decl->Type)) { + Error ("Array '%s' has unknown size", Decl->Ident); + } else { + Error ("Variable '%s' has unknown size", Decl->Ident); + } } } From 43cb092a68004768baa28cdb41831c1d2d1a52e7 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 Aug 2020 05:56:33 +0800 Subject: [PATCH 277/806] Fixed CHECK failures on certain usage of incomplete enums. --- src/cc65/datatype.c | 14 +++++++++++--- src/cc65/loadexpr.c | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index ad008cfd3..9efb142ee 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -686,7 +686,10 @@ const Type* GetUnderlyingType (const Type* Type) Internal ("Enum tag type error in GetUnderlyingTypeCode"); } - return ((SymEntry*)Type->A.P)->V.E.Type; + /* If incomplete enum type is used, just return its raw type */ + if (((SymEntry*)Type->A.P)->V.E.Type != 0) { + return ((SymEntry*)Type->A.P)->V.E.Type; + } } return Type; @@ -1246,9 +1249,14 @@ Type* IntPromotion (Type* T) ** to unsigned int. */ return IsSignUnsigned (T) ? type_uint : type_int; - } else { - /* Otherwise, the type is not smaller than int, so leave it alone. */ + } else if (!IsIncompleteESUType (T)) { + /* The type is a complete type not smaller than int, so leave it alone. */ return T; + } else { + /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. + ** Assume int to avoid further errors. + */ + return type_int; } } diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index f3a1a6add..07f88acbc 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -142,6 +142,10 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) BitFieldFullWidthFlags |= CF_UNSIGNED; } } else if ((Flags & CF_TYPEMASK) == 0) { + /* If Expr is an incomplete ESY type, bail out */ + if (IsIncompleteESUType (Expr->Type)) { + return; + } Flags |= TypeOf (Expr->Type); } From 8d225c32b194f1bb3e2c1606ec55b41b3eca6c76 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 Aug 2020 05:57:12 +0800 Subject: [PATCH 278/806] Fixed checks on assignment to incomplete types. --- src/cc65/assignment.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 6acab3fe8..633083669 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -156,6 +156,11 @@ void Assignment (ExprDesc* Expr) Error ("Assignment to const"); } + /* Check for assignment to incomplete type */ + if (IsIncompleteESUType (ltype)) { + Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); + } + /* Skip the '=' token */ NextToken (); From bb9c2032224a9abd034f8fcb17699048ac351e00 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 07:53:28 +0800 Subject: [PATCH 279/806] Fixed integer promotion of unary operations. --- src/cc65/expr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 12a0c0b57..9f7902284 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1856,8 +1856,8 @@ static void UnaryOp (ExprDesc* Expr) /* Value is not constant */ LoadExpr (CF_NONE, Expr); - /* Get the type of the expression */ - Flags = TypeOf (Expr->Type); + /* Adjust the type of the value */ + Flags = g_typeadjust (TypeOf (Expr->Type), TypeOf (type_int) | CF_CONST); /* Handle the operation */ switch (Tok) { @@ -1870,6 +1870,9 @@ static void UnaryOp (ExprDesc* Expr) /* The result is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); } + + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); } From bf5384a7124de8b44847f57ae7e209e85de5cc4d Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 22:46:45 +0200 Subject: [PATCH 280/806] some more refactoring of Makefiles, preparing for CI --- testcode/assembler/Makefile | 37 ++++++++++++++++++++++ testcode/disasm/Makefile | 58 ++++++++++++++++++++++++++++++++++ testcode/disasm/sample-unix.mk | 28 ---------------- 3 files changed, 95 insertions(+), 28 deletions(-) create mode 100644 testcode/assembler/Makefile create mode 100644 testcode/disasm/Makefile delete mode 100644 testcode/disasm/sample-unix.mk diff --git a/testcode/assembler/Makefile b/testcode/assembler/Makefile new file mode 100644 index 000000000..6cb3fe1f2 --- /dev/null +++ b/testcode/assembler/Makefile @@ -0,0 +1,37 @@ + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) +endif + +all: paramcount.o + +paramcount.o: paramcount.s + $(AS) -o paramcount.o -l paramcount.lst paramcount.s + +clean: + $(RM) paramcount.o + $(RM) paramcount.lst diff --git a/testcode/disasm/Makefile b/testcode/disasm/Makefile new file mode 100644 index 000000000..05e5b8373 --- /dev/null +++ b/testcode/disasm/Makefile @@ -0,0 +1,58 @@ +# Sample makefile using a preprocessor against info files +# and the --sync-lines option + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + DA = $(CC65_HOME)/bin/da65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) + DA := $(if $(wildcard ../../../bin/da65*),../../../bin/da65,da65) +endif + +CPP = env LANG=C cpp +CPPFLAGS = # -DTEST_ERROR + +ASMS = fixed.s bank0.s bank1.s +DAIS = fixed.dai bank0.dai bank1.dai + +.SUFFIXES: .da .dai .s +.PHONY: all clean maintainer-clean +.SECONDARY: $(DAIS) + +.da.dai: + $(CPP) -o $@ $(CPPFLAGS) $< + +.dai.s: + $(DA) --sync-lines -o $@ -i $< image.bin + +all: $(ASMS) + +clean: + $(RM) $(ASMS) + +maintainer-clean: clean + $(RM) $(DAIS) + +$(DAIS): fixed.da diff --git a/testcode/disasm/sample-unix.mk b/testcode/disasm/sample-unix.mk deleted file mode 100644 index 0ef64a5e5..000000000 --- a/testcode/disasm/sample-unix.mk +++ /dev/null @@ -1,28 +0,0 @@ -# Sample makefile using a preprocessor against info files -# and the --sync-lines option - -CPP = env LANG=C cpp -CPPFLAGS = # -DTEST_ERROR - -ASMS = fixed.s bank0.s bank1.s -DAIS = fixed.dai bank0.dai bank1.dai - -.SUFFIXES: .da .dai .s -.PHONY: all clean maintainer-clean -.SECONDARY: $(DAIS) - -.da.dai: - $(CPP) -o $@ $(CPPFLAGS) $< - -.dai.s: - da65 --sync-lines -o $@ -i $< image.bin - -all: $(ASMS) - -clean: - rm -f $(ASMS) - -maintainer-clean: clean - rm -f $(DAIS) - -$(DAIS): fixed.da From 39a3de3119bda406c8a51625b35f20851a02060a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 22:47:47 +0200 Subject: [PATCH 281/806] made geoslib testcode compile again, added the required linker config and a Makefile --- testcode/grc/Makefile | 43 ++++++++++++++++ testcode/grc/geos-cbm-overlay.cfg | 84 +++++++++++++++++++++++++++++++ testcode/grc/vlir0.s | 8 +-- testcode/grc/vlir1.s | 8 +-- testcode/grc/vlir2.s | 8 +-- 5 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 testcode/grc/Makefile create mode 100644 testcode/grc/geos-cbm-overlay.cfg diff --git a/testcode/grc/Makefile b/testcode/grc/Makefile new file mode 100644 index 000000000..4df2f17bc --- /dev/null +++ b/testcode/grc/Makefile @@ -0,0 +1,43 @@ + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + GRC = $(CC65_HOME)/bin/grc65 +else + AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) + CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) + CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) + LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) + GRC := $(if $(wildcard ../../../bin/grc65*),../../../bin/grc65,grc65) +endif + +all: test.s vlir.cvt + +test.s: test.grc + $(GRC) -s test.s test.grc + +vlir.cvt: vlir.grc + $(GRC) -s vlir.s test.grc + $(CL) -t geos -C geos-cbm-overlay.cfg -o vlir.cvt vlir.s vlir0.s vlir1.s vlir2.s + +clean: + $(RM) test.s test.h + $(RM) vlir.s vlir.cvt diff --git a/testcode/grc/geos-cbm-overlay.cfg b/testcode/grc/geos-cbm-overlay.cfg new file mode 100644 index 000000000..ddf1d1e1f --- /dev/null +++ b/testcode/grc/geos-cbm-overlay.cfg @@ -0,0 +1,84 @@ +FEATURES { + STARTADDRESS: default = $0400; +} +SYMBOLS { + __BACKBUFSIZE__: type = weak, value = $2000; + __HIMEM__: type = weak, value = $8000 - __BACKBUFSIZE__; + __OVERLAYSIZE__: type = weak, value = $1000; + __OVERLAYADDR__: type = weak, value = __HIMEM__ - __OVERLAYSIZE__; + __STACKSIZE__: type = weak, value = $0400; # 1k stack + __STACKADDR__: type = weak, value = __OVERLAYADDR__ - __STACKSIZE__; +} +MEMORY { + CVT: file = %O, start = $0, size = $40000; + ZP: define = yes, start = $58, size = $1A + $06; + VLIR0: define = yes, start = %S, size = __STACKADDR__ - %S; + VLIR1: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR2: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR3: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR4: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR5: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR6: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR7: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR8: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR9: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR10: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR11: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR12: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR13: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR14: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR15: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR16: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR17: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR18: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; + VLIR19: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; +} +SEGMENTS { + ZEROPAGE: type = zp, load = ZP; + EXTZP: type = zp, load = ZP, optional = yes; + DIRENTRY: type = ro, load = CVT, align = $FE; + FILEINFO: type = ro, load = CVT, align = $FE; + RECORDS: type = ro, load = CVT, align = $FE, optional = yes; + STARTUP: type = ro, run = VLIR0, load = CVT, align_load = $FE, define = yes; + LOWCODE: type = ro, run = VLIR0, load = CVT, optional = yes; + ONCE: type = ro, run = VLIR0, load = CVT, optional = yes; + CODE: type = ro, run = VLIR0, load = CVT; + RODATA: type = ro, run = VLIR0, load = CVT; + DATA: type = rw, run = VLIR0, load = CVT; + INIT: type = bss, load = VLIR0, optional = yes; + BSS: type = bss, load = VLIR0, define = yes; + OVERLAY1: type = ro, run = VLIR1, load = CVT, align_load = $FE, optional = yes; + OVERLAY2: type = ro, run = VLIR2, load = CVT, align_load = $FE, optional = yes; + OVERLAY3: type = ro, run = VLIR3, load = CVT, align_load = $FE, optional = yes; + OVERLAY4: type = ro, run = VLIR4, load = CVT, align_load = $FE, optional = yes; + OVERLAY5: type = ro, run = VLIR5, load = CVT, align_load = $FE, optional = yes; + OVERLAY6: type = ro, run = VLIR6, load = CVT, align_load = $FE, optional = yes; + OVERLAY7: type = ro, run = VLIR7, load = CVT, align_load = $FE, optional = yes; + OVERLAY8: type = ro, run = VLIR8, load = CVT, align_load = $FE, optional = yes; + OVERLAY9: type = ro, run = VLIR9, load = CVT, align_load = $FE, optional = yes; + OVERLAY10: type = ro, run = VLIR10, load = CVT, align_load = $FE, optional = yes; + OVERLAY11: type = ro, run = VLIR11, load = CVT, align_load = $FE, optional = yes; + OVERLAY12: type = ro, run = VLIR12, load = CVT, align_load = $FE, optional = yes; + OVERLAY13: type = ro, run = VLIR13, load = CVT, align_load = $FE, optional = yes; + OVERLAY14: type = ro, run = VLIR14, load = CVT, align_load = $FE, optional = yes; + OVERLAY15: type = ro, run = VLIR15, load = CVT, align_load = $FE, optional = yes; + OVERLAY16: type = ro, run = VLIR16, load = CVT, align_load = $FE, optional = yes; + OVERLAY17: type = ro, run = VLIR17, load = CVT, align_load = $FE, optional = yes; + OVERLAY18: type = ro, run = VLIR18, load = CVT, align_load = $FE, optional = yes; + OVERLAY19: type = ro, run = VLIR19, load = CVT, align_load = $FE, optional = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/testcode/grc/vlir0.s b/testcode/grc/vlir0.s index 2e9a3ffd9..a54d406df 100644 --- a/testcode/grc/vlir0.s +++ b/testcode/grc/vlir0.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../libsrc/geos-common/const.inc" + .include "../../libsrc/geos-cbm/jumptab.inc" + .include "../../libsrc/geos-cbm/geossym.inc" + .include "../../libsrc/geos-common/geosmac.inc" ; import load addresses for all VLIR chains ; these labels are defined upon linking with ld65 diff --git a/testcode/grc/vlir1.s b/testcode/grc/vlir1.s index eae34565e..6ee3cca37 100644 --- a/testcode/grc/vlir1.s +++ b/testcode/grc/vlir1.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../libsrc/geos-common/const.inc" + .include "../../libsrc/geos-cbm/jumptab.inc" + .include "../../libsrc/geos-cbm/geossym.inc" + .include "../../libsrc/geos-common/geosmac.inc" ; export names of functions that will be used in the main program diff --git a/testcode/grc/vlir2.s b/testcode/grc/vlir2.s index 9d180c847..4c02983ff 100644 --- a/testcode/grc/vlir2.s +++ b/testcode/grc/vlir2.s @@ -5,10 +5,10 @@ ; similar to vlir1.s except the fact that this is chain #2 - .include "../../libsrc/geos/inc/const.inc" - .include "../../libsrc/geos/inc/jumptab.inc" - .include "../../libsrc/geos/inc/geossym.inc" - .include "../../libsrc/geos/inc/geosmac.inc" + .include "../../libsrc/geos-common/const.inc" + .include "../../libsrc/geos-cbm/jumptab.inc" + .include "../../libsrc/geos-cbm/geossym.inc" + .include "../../libsrc/geos-common/geosmac.inc" .export OVERLAY2_Function1 .export OVERLAY2_Function2 From c1a514c0f8c6a41b0b971cd772ce724f9dfc888f Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 26 Aug 2020 23:20:28 +0200 Subject: [PATCH 282/806] added test related to issue #1201 --- test/val/bug1201.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/val/bug1201.c diff --git a/test/val/bug1201.c b/test/val/bug1201.c new file mode 100644 index 000000000..6d194fbee --- /dev/null +++ b/test/val/bug1201.c @@ -0,0 +1,25 @@ + +/* bug #1201 - The unary operators +, - and ~ should do integer promote on the result types. */ + +char a; +short b; +int c; +long d; +enum E { + Z +} e; +struct S { + int a : 1; +} f; + +_Static_assert(sizeof(+a) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+b) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+c) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+d) == sizeof(long), "Result type should be long"); +_Static_assert(sizeof(+e) == sizeof(int), "Result type should be int"); +_Static_assert(sizeof(+f.a) == sizeof(int), "Result type should be int"); + +int main(void) +{ + return 0; +} From 41dbd31b02b00c3b4120b7dfafedc85b6c879dca Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 00:04:34 +0200 Subject: [PATCH 283/806] fix makefile, remove the unneeded .cfg again. oops :) --- testcode/grc/Makefile | 17 +++++-- testcode/grc/geos-cbm-overlay.cfg | 84 ------------------------------- 2 files changed, 13 insertions(+), 88 deletions(-) delete mode 100644 testcode/grc/geos-cbm-overlay.cfg diff --git a/testcode/grc/Makefile b/testcode/grc/Makefile index 4df2f17bc..793506890 100644 --- a/testcode/grc/Makefile +++ b/testcode/grc/Makefile @@ -34,10 +34,19 @@ all: test.s vlir.cvt test.s: test.grc $(GRC) -s test.s test.grc -vlir.cvt: vlir.grc - $(GRC) -s vlir.s test.grc - $(CL) -t geos -C geos-cbm-overlay.cfg -o vlir.cvt vlir.s vlir0.s vlir1.s vlir2.s +vlir.cvt: vlir.grc vlir0.s vlir1.s vlir2.s +# using seperate calls here for demonstration purposes: + $(GRC) -t geos-cbm -s vlir.s vlir.grc + $(AS) -t geos-cbm vlir.s + $(AS) -t geos-cbm vlir0.s + $(AS) -t geos-cbm vlir1.s + $(AS) -t geos-cbm vlir2.s + $(LD) -t geos-cbm -o vlir.cvt vlir.o vlir0.o vlir1.o vlir2.o geos-cbm.lib + +# you can also do the above in one command: +# $(CL) -t geos-cbm -o vlir.cvt vlir.grc vlir0.s vlir1.s vlir2.s clean: $(RM) test.s test.h - $(RM) vlir.s vlir.cvt + $(RM) vlir.s vlir.cvt vlir.c vlir.h + $(RM) *.o diff --git a/testcode/grc/geos-cbm-overlay.cfg b/testcode/grc/geos-cbm-overlay.cfg deleted file mode 100644 index ddf1d1e1f..000000000 --- a/testcode/grc/geos-cbm-overlay.cfg +++ /dev/null @@ -1,84 +0,0 @@ -FEATURES { - STARTADDRESS: default = $0400; -} -SYMBOLS { - __BACKBUFSIZE__: type = weak, value = $2000; - __HIMEM__: type = weak, value = $8000 - __BACKBUFSIZE__; - __OVERLAYSIZE__: type = weak, value = $1000; - __OVERLAYADDR__: type = weak, value = __HIMEM__ - __OVERLAYSIZE__; - __STACKSIZE__: type = weak, value = $0400; # 1k stack - __STACKADDR__: type = weak, value = __OVERLAYADDR__ - __STACKSIZE__; -} -MEMORY { - CVT: file = %O, start = $0, size = $40000; - ZP: define = yes, start = $58, size = $1A + $06; - VLIR0: define = yes, start = %S, size = __STACKADDR__ - %S; - VLIR1: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR2: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR3: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR4: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR5: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR6: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR7: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR8: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR9: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR10: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR11: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR12: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR13: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR14: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR15: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR16: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR17: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR18: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; - VLIR19: define = yes, start = __OVERLAYADDR__, size = __OVERLAYSIZE__; -} -SEGMENTS { - ZEROPAGE: type = zp, load = ZP; - EXTZP: type = zp, load = ZP, optional = yes; - DIRENTRY: type = ro, load = CVT, align = $FE; - FILEINFO: type = ro, load = CVT, align = $FE; - RECORDS: type = ro, load = CVT, align = $FE, optional = yes; - STARTUP: type = ro, run = VLIR0, load = CVT, align_load = $FE, define = yes; - LOWCODE: type = ro, run = VLIR0, load = CVT, optional = yes; - ONCE: type = ro, run = VLIR0, load = CVT, optional = yes; - CODE: type = ro, run = VLIR0, load = CVT; - RODATA: type = ro, run = VLIR0, load = CVT; - DATA: type = rw, run = VLIR0, load = CVT; - INIT: type = bss, load = VLIR0, optional = yes; - BSS: type = bss, load = VLIR0, define = yes; - OVERLAY1: type = ro, run = VLIR1, load = CVT, align_load = $FE, optional = yes; - OVERLAY2: type = ro, run = VLIR2, load = CVT, align_load = $FE, optional = yes; - OVERLAY3: type = ro, run = VLIR3, load = CVT, align_load = $FE, optional = yes; - OVERLAY4: type = ro, run = VLIR4, load = CVT, align_load = $FE, optional = yes; - OVERLAY5: type = ro, run = VLIR5, load = CVT, align_load = $FE, optional = yes; - OVERLAY6: type = ro, run = VLIR6, load = CVT, align_load = $FE, optional = yes; - OVERLAY7: type = ro, run = VLIR7, load = CVT, align_load = $FE, optional = yes; - OVERLAY8: type = ro, run = VLIR8, load = CVT, align_load = $FE, optional = yes; - OVERLAY9: type = ro, run = VLIR9, load = CVT, align_load = $FE, optional = yes; - OVERLAY10: type = ro, run = VLIR10, load = CVT, align_load = $FE, optional = yes; - OVERLAY11: type = ro, run = VLIR11, load = CVT, align_load = $FE, optional = yes; - OVERLAY12: type = ro, run = VLIR12, load = CVT, align_load = $FE, optional = yes; - OVERLAY13: type = ro, run = VLIR13, load = CVT, align_load = $FE, optional = yes; - OVERLAY14: type = ro, run = VLIR14, load = CVT, align_load = $FE, optional = yes; - OVERLAY15: type = ro, run = VLIR15, load = CVT, align_load = $FE, optional = yes; - OVERLAY16: type = ro, run = VLIR16, load = CVT, align_load = $FE, optional = yes; - OVERLAY17: type = ro, run = VLIR17, load = CVT, align_load = $FE, optional = yes; - OVERLAY18: type = ro, run = VLIR18, load = CVT, align_load = $FE, optional = yes; - OVERLAY19: type = ro, run = VLIR19, load = CVT, align_load = $FE, optional = yes; -} -FEATURES { - CONDES: type = constructor, - label = __CONSTRUCTOR_TABLE__, - count = __CONSTRUCTOR_COUNT__, - segment = ONCE; - CONDES: type = destructor, - label = __DESTRUCTOR_TABLE__, - count = __DESTRUCTOR_COUNT__, - segment = RODATA; - CONDES: type = interruptor, - label = __INTERRUPTOR_TABLE__, - count = __INTERRUPTOR_COUNT__, - segment = RODATA, - import = __CALLIRQ__; -} From 7a453d1f901da8d0686d30b13d6bd672e471a39c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 00:08:22 +0200 Subject: [PATCH 284/806] add a "disk" target to build the disk images as suggested by Oliver --- testcode/lib/apple2/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testcode/lib/apple2/Makefile b/testcode/lib/apple2/Makefile index 3ea2463f1..0020b4ec5 100644 --- a/testcode/lib/apple2/Makefile +++ b/testcode/lib/apple2/Makefile @@ -29,7 +29,9 @@ else LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) endif -all: hgr.dsk dhgr.dsk +all: hgrshow hgrtest dhgrshow + +disk: hgr.dsk dhgr.dsk hgr.dsk: hgrshow hgrtest cp prodos.dsk $@ @@ -66,3 +68,4 @@ clean: $(RM) hgr.dsk dhgr.dsk $(RM) hgrshow hgrshow.map $(RM) hgrtest hgrtest.map + $(RM) dhgrshow dhgrshow.map From f34644186f0dd55b647acc9171e1b1d9b4f42bd7 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 01:19:33 +0200 Subject: [PATCH 285/806] build a bogus "ROM image" that can be disassembled, fix the makefile, remove generated files --- testcode/disasm/Makefile | 17 +++++++++-------- testcode/disasm/bank0.dai | 33 --------------------------------- testcode/disasm/bank1.dai | 33 --------------------------------- testcode/disasm/fixed.da | 2 +- testcode/disasm/fixed.dai | 25 ------------------------- testcode/disasm/image.cfg | 15 +++++++++++++++ 6 files changed, 25 insertions(+), 100 deletions(-) delete mode 100644 testcode/disasm/bank0.dai delete mode 100644 testcode/disasm/bank1.dai delete mode 100644 testcode/disasm/fixed.dai create mode 100644 testcode/disasm/image.cfg diff --git a/testcode/disasm/Makefile b/testcode/disasm/Makefile index 05e5b8373..61b37e314 100644 --- a/testcode/disasm/Makefile +++ b/testcode/disasm/Makefile @@ -31,15 +31,17 @@ else DA := $(if $(wildcard ../../../bin/da65*),../../../bin/da65,da65) endif -CPP = env LANG=C cpp -CPPFLAGS = # -DTEST_ERROR +CPP = cpp +#CPPFLAGS = -DTEST_ERROR ASMS = fixed.s bank0.s bank1.s DAIS = fixed.dai bank0.dai bank1.dai .SUFFIXES: .da .dai .s -.PHONY: all clean maintainer-clean -.SECONDARY: $(DAIS) + +all: image.bin $(ASMS) + +$(DAIS): fixed.da .da.dai: $(CPP) -o $@ $(CPPFLAGS) $< @@ -47,12 +49,11 @@ DAIS = fixed.dai bank0.dai bank1.dai .dai.s: $(DA) --sync-lines -o $@ -i $< image.bin -all: $(ASMS) +image.bin: image.s image.cfg + $(CL) -t none -C image.cfg -o image.bin image.s clean: $(RM) $(ASMS) - -maintainer-clean: clean $(RM) $(DAIS) + $(RM) image.bin -$(DAIS): fixed.da diff --git a/testcode/disasm/bank0.dai b/testcode/disasm/bank0.dai deleted file mode 100644 index 2d865e77c..000000000 --- a/testcode/disasm/bank0.dai +++ /dev/null @@ -1,33 +0,0 @@ -# 1 "bank0.da" -# 1 "" -# 1 "" -# 1 "bank0.da" - - - - -global { - inputoffs $00010; - inputsize $4000; - startaddr $8000; - cpu "6502"; -}; - -# 1 "fixed.da" 1 -# 18 "fixed.da" -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; -# 13 "bank0.da" 2 - -label { addr $8000; name "Bank0ProcA"; }; -label { addr $8123; name "Bank0ProcB"; }; -range { start $A000; end $BFFF; name "Bank0Data"; type ByteTable; }; diff --git a/testcode/disasm/bank1.dai b/testcode/disasm/bank1.dai deleted file mode 100644 index 2b5b68532..000000000 --- a/testcode/disasm/bank1.dai +++ /dev/null @@ -1,33 +0,0 @@ -# 1 "bank1.da" -# 1 "" -# 1 "" -# 1 "bank1.da" - - - - -global { - inputoffs $04010; - inputsize $4000; - startaddr $8000; - cpu "6502"; -}; - -# 1 "fixed.da" 1 -# 18 "fixed.da" -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; -# 13 "bank1.da" 2 - -range { start $8000; end $AFFF; name "Bank1Data"; type ByteTable; }; -label { addr $B000; name "Bank1ProcA"; }; -label { addr $B123; name "Bank1ProcB"; }; diff --git a/testcode/disasm/fixed.da b/testcode/disasm/fixed.da index e8aa03427..f8ad4ba27 100644 --- a/testcode/disasm/fixed.da +++ b/testcode/disasm/fixed.da @@ -7,7 +7,7 @@ #ifndef TARGET_BANK #define TARGET_BANK -1 global { - inputoffs $1C010; + inputoffs $0C010; inputsize $4000; startaddr $C000; cpu "6502"; diff --git a/testcode/disasm/fixed.dai b/testcode/disasm/fixed.dai deleted file mode 100644 index d73155cf0..000000000 --- a/testcode/disasm/fixed.dai +++ /dev/null @@ -1,25 +0,0 @@ -# 1 "fixed.da" -# 1 "" -# 1 "" -# 1 "fixed.da" -# 9 "fixed.da" -global { - inputoffs $1C010; - inputsize $4000; - startaddr $C000; - cpu "6502"; -}; - - - -label { addr $00; name "VariableA"; }; -label { addr $01; name "VariableB"; }; -label { addr $0100; name "Stack"; size $0100; }; - - - - - -label { addr $C000; name "CommonProcA"; }; -label { addr $C123; name "CommonProcB"; }; -range { start $E123; end $FFFF; name "CommonData"; type ByteTable; }; diff --git a/testcode/disasm/image.cfg b/testcode/disasm/image.cfg new file mode 100644 index 000000000..2225c722f --- /dev/null +++ b/testcode/disasm/image.cfg @@ -0,0 +1,15 @@ + +MEMORY { + HEADER: file = %O, start = $0000, size = $0010, fill = yes; + BANK00: file = %O, start = $8000, size = $4000, fill = yes; + BANK01: file = %O, start = $8000, size = $4000, fill = yes; + BANK02: file = %O, start = $8000, size = $4000, fill = yes; + FIXED: file = %O, start = $c000, size = $4000, fill = yes; +} +SEGMENTS { + HDR: load = HEADER, type = rw, optional = yes, define = yes; + BANK0: load = BANK00, type = rw, optional = yes, define = yes; + BANK1: load = BANK01, type = rw, optional = yes, define = yes; + BANK2: load = BANK02, type = rw, optional = yes, define = yes; + FIX: load = FIXED, type = rw, optional = yes, define = yes; +} From 4008ab556cd725cc7d24f5b443d3224b2d8957eb Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 01:34:18 +0200 Subject: [PATCH 286/806] added a Makefile --- samples/tutorial/Makefile | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 samples/tutorial/Makefile diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile new file mode 100644 index 000000000..8ed91697f --- /dev/null +++ b/samples/tutorial/Makefile @@ -0,0 +1,40 @@ + +# Run 'make SYS='; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../bin/ca65*),../bin/ca65,ca65) + CC := $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65) + CL := $(if $(wildcard ../bin/cl65*),../bin/cl65,cl65) + LD := $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65) +endif + +all: hello + +hello: hello.c text.s + $(CL) -t $(SYS) -o hello hello.c text.s + +clean: + $(RM) hello From 5e2d2a54f631c3d3855c927d11dd8f39506fb986 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 14:25:33 +0200 Subject: [PATCH 287/806] added makefile for the GEOS samples. geosconio.c and rmvprot.c do not compile right now, someone with more GEOS knowledge should look at them --- samples/geos/Makefile | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 samples/geos/Makefile diff --git a/samples/geos/Makefile b/samples/geos/Makefile new file mode 100644 index 000000000..32a28e118 --- /dev/null +++ b/samples/geos/Makefile @@ -0,0 +1,82 @@ + +# Run 'make SYS='; or, set a SYS env. +# var. to build for another target system. +SYS ?= geos-cbm + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../bin/ca65*),../bin/ca65,ca65) + CC := $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65) + CL := $(if $(wildcard ../bin/cl65*),../bin/cl65,cl65) + LD := $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65) + SP := $(if $(wildcard ../bin/sp65*),../bin/sp65,sp65) +endif + +# omitted: dialog.c grphstr.c inittab.c menu.c + +# TODO: geosconio.cvt rmvprot.cvt +all: bitmap-demo.cvt filesel.cvt geosver.cvt getid.cvt hello1.cvt hello2.cvt \ + overlay-demo.cvt vector-demo.cvt yesno.cvt + +bitmap.c: logo.pcx + $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap + +bitmap-demo.cvt: bitmap.c bitmap-demores.grc bitmap-demo.c + $(CL) -t $(SYS) -O -o bitmap-demo.cvt -m bitmap-demo.map bitmap-demores.grc bitmap-demo.c + +filesel.cvt: fileselres.grc filesel.c + $(CL) -t $(SYS) -O -o filesel.cvt -m filesel.map fileselres.grc filesel.c + +geosconio.cvt: geosconiores.grc geosconio.c + $(CL) -t $(SYS) -O -o geosconio.cvt -m geosconio.map geosconiores.grc geosconio.c + +geosver.cvt: geosverres.grc geosver.c + $(CL) -t $(SYS) -O -o geover.cvt -m geosver.map geosverres.grc geosver.c + +getid.cvt: getidres.grc getid.c + $(CL) -t $(SYS) -O -o getid.cvt -m getid.map getidres.grc getid.c + +hello1.cvt: hello1res.grc hello1.c + $(CL) -t $(SYS) -O -o hello1.cvt -m hello1.map hello1res.grc hello1.c + +hello2.cvt: hello2res.grc hello2.c + $(CL) -t $(SYS) -O -o hello2.cvt -m hello2.map hello2res.grc hello2.c + +overlay-demo.cvt: overlay-demores.grc overlay-demo.c + $(CL) -t $(SYS) -O -o overlay-demo.cvt -m overlay-demo.map overlay-demores.grc overlay-demo.c + +rmvprot.cvt: rmvprotres.grc rmvprot.c + $(CL) -t $(SYS) -O -o rmvprot.cvt -m rmvprot.map rmvprotres.grc rmvprot.c + +vector-demo.cvt: vector-demores.grc vector-demo.c + $(CL) -t $(SYS) -O -o vector-demo.cvt -m vector-demo.map vector-demores.grc vector-demo.c + +yesno.cvt: yesnores.grc yesno.c + $(CL) -t $(SYS) -O -o yesno.cvt -m yesno.map yesnores.grc yesno.c + + +clean: + $(RM) bitmap.c + $(RM) *.cvt + $(RM) *.map From 5158ee2092442ad458ccf2a522b8a8fd8b6f4099 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 27 Aug 2020 14:50:00 +0200 Subject: [PATCH 288/806] preliminary makefile to build all programs in the testcode directory --- testcode/lib/Makefile | 395 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 testcode/lib/Makefile diff --git a/testcode/lib/Makefile b/testcode/lib/Makefile new file mode 100644 index 000000000..33f725f6a --- /dev/null +++ b/testcode/lib/Makefile @@ -0,0 +1,395 @@ +# +# Makefile for cc65 testcode +# +# This Makefile requires GNU make +# + +# Run 'make SYS='; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to define a variable +# containing a single space character. +SPACE := +SPACE += + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 +else + AS := $(if $(wildcard ../bin/ca65*),../bin/ca65,ca65) + CC := $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65) + CL := $(if $(wildcard ../bin/cl65*),../bin/cl65,cl65) + LD := $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65) +endif + +ifneq ($(filter disk testcode.%,$(MAKECMDGOALS)),) + ifdef CC65_HOME + TARGET_PATH = $(CC65_HOME)/target + else + TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path)) + endif + + # If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make + # has very limited support for paths containing spaces. $(wildcard) is the only function + # that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped + # spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a + # return value consisting of 12 plain words :-(( + # + # Fortunately we can work around that behaviour here because we know that the files we + # are looking for have known extensions. So we can $(filter) the in our example above 12 + # words for file extensions so we come up with 4 path fragments. Then we remove those + # path fragments with $(notdir) from the file names. + # + # So far so good. But here we want to process files from different paths in a single + # recipe further down below and therefore want to prepend the paths to the files with + # $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is). + # Therefore, we need to replace the spaces with some other character temporarily in order + # to have $(foreach) generate one invocation per file. We use the character '?' for that + # purpose here, just because it is known to not be part of file names. + # + # Inside the recipe generated per file we then replace the '?' again with a space. As we + # want to be compatible with cmd.exe for execution we're not using an escaped space but + # rather double-quote the whole path. + # + # Note: The "strange" $(wildcard) further down below just serves the purpose to unescape + # spaces for cmd.exe. This could have as well been done with another $(subst). + + SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH)) + + EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*) + MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*) + TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*) + + EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD)))) + MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU)))) + TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI)))) + + # This one comes with the VICE emulator. + # See http://vice-emu.sourceforge.net/ + C1541 ?= c1541 + + # For this one, see https://applecommander.github.io/ + AC ?= ac.jar + + # For this one, see https://www.horus.com/~hias/atari/ + DIR2ATR ?= dir2atr +endif + +DISK_c64 = testcode.d64 +DISK_apple2 = testcode.dsk +DISK_apple2enh = testcode.dsk +DISK_atari = testcode.atr +DISK_atarixl = testcode.atr + +# -------------------------------------------------------------------------- +# System-dependent settings +# For convenience, these groups and lines are sorted alphabetically, first +# by target-machine group, then by mission, then by program and sub-target. + +# -------------------------------------------------------------------------- +# Generic rules + +.PHONY: all mostlyclean clean zip testcode disk + +%: %.c +%: %.s + +.c.o: + $(CC) $(CFLAGS) -Ors --codesize 500 -T -g -t $(SYS) $< + $(AS) $(<:.c=.s) + +.s.o: + $(AS) $(ASFLAGS) -t $(SYS) $< + +.PRECIOUS: %.o + +.o: +ifeq ($(SYS),vic20) + $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib +else + $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -t $(SYS) -m $@.map $^ $(SYS).lib +endif + +# -------------------------------------------------------------------------- +# Lists of executables + +# omitted: seek +EXELIST_c64 = \ + arg-test \ + clock \ + clock-test \ + conio \ + cpeek-test \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +# omitted: seek clock-test mouse-test ser-test +EXELIST_vic20 = \ + arg-test \ + clock \ + conio \ + cpeek-test \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +# omitted: cpeek-test, clock +EXELIST_apple2 = \ + arg-test \ + clock-test \ + conio \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + seek \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +EXELIST_apple2enh = $(EXELIST_apple2) + +# omitted: cpeek-test +EXELIST_atari = \ + arg-test \ + clock-test \ + clock \ + conio \ + cprintf \ + cursor \ + deb \ + dir-test \ + div-test \ + em-test \ + exec-test1 \ + exec-test2 \ + fileio-test \ + ft \ + getopt-test \ + heaptest \ + joy-test \ + moddiv-test \ + mouse-test \ + mul-test \ + posixio-test \ + rename-test \ + scanf-test \ + seek \ + ser-test \ + strdup-test \ + stroserror-test \ + strqtok-test \ + tinyshell \ + uname-test + +EXELIST_atarixl = $(EXELIST_atari) + +# none of the testcode can work on the 2600 +# EXELIST_atari2600 = + +# none of the testcode can work on supervision +# EXELIST_supervision = + +# Unlisted targets will try to build everything. +# That lets us learn what they cannot build, and what settings +# we need to use for programs that can be built and run. +ifndef EXELIST_$(SYS) +EXELIST_$(SYS) := ${patsubst %.c,%,$(wildcard *.c)} +endif + +# -------------------------------------------------------------------------- +# Rules to make the binaries and the disk + +testcode: $(EXELIST_$(SYS)) + +disk: $(DISK_$(SYS)) + +all: testcode + make -C accelerator + make -C apple2 + make -C atari + make -C atari5200 + make -C cbm SYS=$(SYS) + make -C gamate + make -C pce + +# -------------------------------------------------------------------------- +# some programs link against getsp.o + +mouse-test: mouse-test.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib + +ifneq ($(SYS),vic20) +ft: ft.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib + +tinyshell: tinyshell.o getsp.o + $(LD) $(LDFLAGS) -t $(SYS) -o $@ $^ $(SYS).lib +endif + +# some programs need more memory on the vic20 + +ifeq ($(SYS),vic20) +ft: ft.o getsp.o + $(LD) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib + +tinyshell: tinyshell.o getsp.o + $(LD) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib +endif + +# -------------------------------------------------------------------------- +# Rule to make a CBM disk with all testcode. Needs the c1541 program that comes +# with the VICE emulator. + +define D64_WRITE_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)) >$(NULLDEV) + +endef # D64_WRITE_recipe + +testcode.d64: testcode + @$(C1541) -format testcode,AA d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_recipe)) + +# -------------------------------------------------------------------------- +# Rule to make an Apple II disk with all testcode. Needs the AppleCommander +# program, available at https://applecommander.github.io/, and a template disk +# named 'prodos.dsk'. + +define DSK_WRITE_BIN_recipe + +$(if $(findstring BF00,$(LDFLAGS_$(notdir $(file))_$(SYS))), \ + java -jar $(AC) -p $@ $(notdir $(file)).system sys <"$(wildcard $(TARGET_PATH)/$(SYS)/util/loader.system)") +java -jar $(AC) -as $@ $(notdir $(file)) <"$(file)" + +endef # DSK_WRITE_BIN_recipe + +define DSK_WRITE_REL_recipe + +java -jar $(AC) -p $@ $(notdir $(file)) rel 0 <"$(subst ?,$(SPACE),$(file))" + +endef # DSK_WRITE_REL_recipe + +testcode.dsk: testcode + cp prodos.dsk $@ + $(foreach file,$(EXELIST_$(SYS)),$(DSK_WRITE_BIN_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(DSK_WRITE_REL_recipe)) + +# -------------------------------------------------------------------------- +# Rule to make an Atari disk with all testcode. Needs the dir2atr program +# available at http://www.horus.com/~hias/atari/ and the MyDos4534 variant +# of dos.sys and dup.sys. + +define ATR_WRITE_recipe + +cp "$(subst ?,$(SPACE),$(file))" atr/$(notdir $(file)) + +endef # ATR_WRITE_recipe + +testcode.atr: testcode + @mkdir atr + cp "dos.sys" atr/dos.sys + cp "dup.sys" atr/dup.sys + @$(foreach file,$(EXELIST_$(SYS)),$(ATR_WRITE_recipe)) +# @$(foreach file,$(EMD) $(MOU) $(TGI),$(ATR_WRITE_recipe)) + $(DIR2ATR) -d -b MyDos4534 3200 $@ atr + @$(RMDIR) atr + +# -------------------------------------------------------------------------- +# Clean-up rules + +mostlyclean: + @$(DEL) *.lbl *.map *.o 2>$(NULLDEV) +# we cant use .s since we have asm files in the directory that we want to keep + @$(DEL) ${patsubst %.c,%.s,$(wildcard *.c)} 2>$(NULLDEV) + +clean: mostlyclean + @$(DEL) $(EXELIST_$(SYS)) $(DISK_$(SYS)) 2>$(NULLDEV) + make -C accelerator clean + make -C apple2 clean + make -C atari clean + make -C atari5200 clean + make -C cbm SYS=$(SYS) clean + make -C gamate clean + make -C pce clean From f54e01781b6d0daa9ba51a642c5a2e4d0bb21d22 Mon Sep 17 00:00:00 2001 From: IrgendwerA8 Date: Sat, 29 Aug 2020 16:21:45 +0200 Subject: [PATCH 289/806] Tiny optimizations for multiplication. --- libsrc/runtime/lmul.s | 4 ++-- libsrc/runtime/umul8x16r24.s | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libsrc/runtime/lmul.s b/libsrc/runtime/lmul.s index 860d58cba..d3c34637c 100644 --- a/libsrc/runtime/lmul.s +++ b/libsrc/runtime/lmul.s @@ -63,7 +63,7 @@ L0: lsr tmp4 bcc L1 clc adc ptr3 - pha + tax lda ptr3+1 adc tmp2 sta tmp2 @@ -73,7 +73,7 @@ L0: lsr tmp4 lda ptr4+1 adc tmp4 sta tmp4 - pla + txa L1: dey bpl L0 lda ptr1 ; Load the low result word diff --git a/libsrc/runtime/umul8x16r24.s b/libsrc/runtime/umul8x16r24.s index c006082a4..54d730558 100644 --- a/libsrc/runtime/umul8x16r24.s +++ b/libsrc/runtime/umul8x16r24.s @@ -41,20 +41,19 @@ umul8x16r16m: .endif ldy #8 ; Number of bits - ldx ptr3 ; Get into register for speed lda ptr1 ror a ; Get next bit into carry @L0: bcc @L1 clc - pha - txa + tax + lda ptr3 adc ptr1+1 sta ptr1+1 lda ptr3+1 adc sreg sta sreg - pla + txa @L1: ror sreg ror ptr1+1 From 632da3f4eefc6675484fc547b34c525983ba9d34 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 290/806] Fixed tracking and checking flexible array members. --- src/cc65/declare.c | 45 ++++++++++++++++++++++++++++++++++----------- src/cc65/symentry.h | 11 +++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 0bfbcedd2..d7851bd44 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -963,12 +963,19 @@ static SymEntry* ParseUnionDecl (const char* Name) AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, SignednessSpecified); } else { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); if (IsAnonName (Decl.Ident)) { - Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++; AliasAnonStructFields (&Decl, Entry); - } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + } + + /* Check if the field itself has a flexible array member */ + if (IsClassStruct (Decl.Type)) { + SymEntry* Sym = GetSymType (Decl.Type); + if (Sym && SymHasFlexibleArrayMember (Sym)) { + Entry->Flags |= SC_HAVEFAM; + Flags |= SC_HAVEFAM; + } } } @@ -1107,6 +1114,8 @@ static SymEntry* ParseStructDecl (const char* Name) Error ("Flexible array member cannot be first struct field"); } FlexibleMember = 1; + Flags |= SC_HAVEFAM; + /* Assume zero for size calculations */ SetElementCount (Decl.Type, FLEXIBLE); } @@ -1156,13 +1165,21 @@ static SymEntry* ParseStructDecl (const char* Name) StructSize += BitOffs / CHAR_BITS; BitOffs %= CHAR_BITS; } else { + Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); if (IsAnonName (Decl.Ident)) { - Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); Entry->V.A.ANumber = StructTagEntry->V.S.ACount++; AliasAnonStructFields (&Decl, Entry); - } else { - AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); } + + /* Check if the field itself has a flexible array member */ + if (IsClassStruct (Decl.Type)) { + SymEntry* Sym = GetSymType (Decl.Type); + if (Sym && SymHasFlexibleArrayMember (Sym)) { + Entry->Flags |= SC_HAVEFAM; + Flags |= SC_HAVEFAM; + } + } + if (!FlexibleMember) { StructSize += SizeOf (Decl.Type); } @@ -2361,11 +2378,17 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Number of elements determined by initializer */ SetElementCount (T, Count); ElementCount = Count; - } else if (ElementCount == FLEXIBLE && AllowFlexibleMembers) { - /* In non ANSI mode, allow initialization of flexible array - ** members. - */ - ElementCount = Count; + } else if (ElementCount == FLEXIBLE) { + if (AllowFlexibleMembers) { + /* In non ANSI mode, allow initialization of flexible array + ** members. + */ + ElementCount = Count; + } else { + /* Forbid */ + Error ("Initializing flexible array member is forbidden"); + ElementCount = Count; + } } else if (Count < ElementCount) { g_zerobytes ((ElementCount - Count) * ElementSize); } else if (Count > ElementCount && HasCurly) { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 94fe66032..0224507ac 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -107,6 +107,7 @@ struct CodeEntry; #define SC_ALIAS 0x01000000U /* Alias of anonymous field */ #define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious */ +#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ @@ -272,6 +273,16 @@ INLINE int SymIsRegVar (const SymEntry* Sym) int SymIsOutputFunc (const SymEntry* Sym); /* Return true if this is a function that must be output */ +#if defined(HAVE_INLINE) +INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym) +/* Return true if the given entry has a flexible array member */ +{ + return ((Sym->Flags & SC_HAVEFAM) == SC_HAVEFAM); +} +#else +# define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM) +#endif + #if defined(HAVE_INLINE) INLINE const char* SymGetAsmName (const SymEntry* Sym) /* Return the assembler label name for the symbol (beware: may be NULL!) */ From 4e61ae5b3620a1003985d53e28d9dd160e51b915 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 291/806] Fixed function parameter type conversion. --- src/cc65/declare.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index d7851bd44..4e8446176 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1492,13 +1492,27 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, static Type* ParamTypeCvt (Type* T) -/* If T is an array, convert it to a pointer else do nothing. Return the -** resulting type. +/* If T is an array or a function, convert it to a pointer else do nothing. +** Return the resulting type. */ { + Type* Tmp = 0; + if (IsTypeArray (T)) { - T->C = T_PTR; + Tmp = ArrayToPtr (T); + } else if (IsTypeFunc (T)) { + Tmp = PointerTo (T); } + + if (Tmp != 0) { + /* Do several fixes on qualifiers */ + FixQualifiers (Tmp); + + /* Replace the type */ + TypeCopy (T, Tmp); + TypeFree (Tmp); + } + return T; } From 0a96ffc8786726ca6d492fd70ceaf5dfdae7b3a7 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 292/806] Fixed function parameter checking. Fixed function return type checking. --- src/cc65/codeinfo.c | 6 +- src/cc65/declare.c | 20 +----- src/cc65/expr.c | 112 ++++++++++++++++--------------- src/cc65/function.c | 156 ++++++++++++++++++++++++++++++++------------ src/cc65/function.h | 6 ++ src/cc65/stmt.c | 40 +++++++----- 6 files changed, 210 insertions(+), 130 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 3e1d58709..d7d85a12d 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -410,8 +410,10 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) (AutoCDecl ? IsQualFastcall (E->Type) : !IsQualCDecl (E->Type))) { - /* Will use registers depending on the last param. */ - switch (CheckedSizeOf (D->LastParam->Type)) { + /* Will use registers depending on the last param. If the last + ** param has incomplete type, just assume __EAX__. + */ + switch (SizeOf (D->LastParam->Type)) { case 1u: *Use = REG_A; break; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4e8446176..218ba6017 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1704,7 +1704,6 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) /* Parse the argument list of a function. */ { - unsigned Offs; SymEntry* Sym; SymEntry* WrappedCall; unsigned char WrappedCallData; @@ -1751,23 +1750,10 @@ static FuncDesc* ParseFuncDecl (void) */ F->LastParam = GetSymTab()->SymTail; - /* Assign offsets. If the function has a variable parameter list, - ** there's one additional byte (the arg size). + /* It is allowed to use incomplete types in function prototypes, so we + ** won't always get to know the parameter sizes here and may do that later. */ - Offs = (F->Flags & FD_VARIADIC)? 1 : 0; - Sym = F->LastParam; - while (Sym) { - unsigned Size = CheckedSizeOf (Sym->Type); - if (SymIsRegVar (Sym)) { - Sym->V.R.SaveOffs = Offs; - } else { - Sym->V.Offs = Offs; - } - Offs += Size; - F->ParamSize += Size; - Sym = Sym->PrevSym; - } - + /* Leave the lexical level remembering the symbol tables */ RememberFunctionLevel (F); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 9f7902284..89c7ff108 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -346,6 +346,9 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) int FrameOffs = 0; /* Offset into parameter frame */ int Ellipsis = 0; /* Function is variadic */ + /* Make sure the size of all parameters are known */ + int ParamComplete = F_CheckParamList (Func, 1); + /* As an optimization, we may allocate the complete parameter frame at ** once instead of pushing into each parameter as it comes. We may do that, ** if... @@ -359,7 +362,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) ** (instead of pushing) is enabled. ** */ - if (IS_Get (&CodeSizeFactor) >= 200) { + if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) { /* Calculate the number and size of the parameters */ FrameParams = Func->ParamCount; @@ -424,65 +427,68 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) /* Evaluate the argument expression */ hie1 (&Expr); - /* If we don't have a prototype, accept anything; otherwise, convert - ** the actual argument to the parameter type needed. - */ - Flags = CF_NONE; - if (!Ellipsis) { - - /* Convert the argument to the parameter type if needed */ - TypeConversion (&Expr, Param->Type); - - /* If we have a prototype, chars may be pushed as chars */ - Flags |= CF_FORCECHAR; - - } else { - - /* No prototype available. Convert array to "pointer to first - ** element", and function to "pointer to function". + /* Skip to the next parameter if there are any incomplete types */ + if (ParamComplete) { + /* If we don't have an argument spec., accept anything; otherwise, + ** convert the actual argument to the type needed. */ - Expr.Type = PtrConversion (Expr.Type); + Flags = CF_NONE; + if (!Ellipsis) { - } + /* Convert the argument to the parameter type if needed */ + TypeConversion (&Expr, Param->Type); - /* Handle struct/union specially */ - if (IsClassStruct (Expr.Type)) { - /* Use the replacement type */ - Flags |= TypeOf (GetStructReplacementType (Expr.Type)); - } else { - /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); - } + /* If we have a prototype, chars may be pushed as chars */ + Flags |= CF_FORCECHAR; - /* Load the value into the primary if it is not already there */ - LoadExpr (Flags, &Expr); - - /* If this is a fastcall function, don't push the last argument */ - if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { - unsigned ArgSize = sizeofarg (Flags); - - if (FrameSize > 0) { - /* We have the space already allocated, store in the frame. - ** Because of invalid type conversions (that have produced an - ** error before), we can end up here with a non-aligned stack - ** frame. Since no output will be generated anyway, handle - ** these cases gracefully instead of doing a CHECK. - */ - if (FrameSize >= ArgSize) { - FrameSize -= ArgSize; - } else { - FrameSize = 0; - } - FrameOffs -= ArgSize; - /* Store */ - g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); } else { - /* Push the argument */ - g_push (Flags, Expr.IVal); + + /* No prototype available. Convert array to "pointer to first + ** element", and function to "pointer to function". + */ + Expr.Type = PtrConversion (Expr.Type); + } - /* Calculate total parameter size */ - PushedSize += ArgSize; + /* Handle struct/union specially */ + if (IsClassStruct (Expr.Type)) { + /* Use the replacement type */ + Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + } else { + /* Use the type of the argument for the push */ + Flags |= TypeOf (Expr.Type); + } + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); + + /* If this is a fastcall function, don't push the last argument */ + if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { + unsigned ArgSize = sizeofarg (Flags); + + if (FrameSize > 0) { + /* We have the space already allocated, store in the frame. + ** Because of invalid type conversions (that have produced an + ** error before), we can end up here with a non-aligned stack + ** frame. Since no output will be generated anyway, handle + ** these cases gracefully instead of doing a CHECK. + */ + if (FrameSize >= ArgSize) { + FrameSize -= ArgSize; + } else { + FrameSize = 0; + } + FrameOffs -= ArgSize; + /* Store */ + g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); + } else { + /* Push the argument */ + g_push (Flags, Expr.IVal); + } + + /* Calculate total parameter size */ + PushedSize += ArgSize; + } } /* Check for end of argument list */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 290916cd2..451efc54d 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -105,6 +105,62 @@ static void FreeFunction (Function* F) +int F_CheckParamList (FuncDesc* D, int RequireAll) +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ +{ + unsigned I = 0; + unsigned Offs; + SymEntry* Param; + unsigned ParamSize = 0; + unsigned IncompleteCount = 0; + + /* Assign offsets. If the function has a variable parameter list, + ** there's one additional byte (the arg size). + */ + Offs = (D->Flags & FD_VARIADIC) ? 1 : 0; + Param = D->LastParam; + while (Param) { + unsigned Size = SizeOf (Param->Type); + if (RequireAll && IsIncompleteESUType (Param->Type)) { + if (D->Flags & FD_UNNAMED_PARAMS) { + Error ("Parameter %u has incomplete type '%s'", + D->ParamCount - I, + GetFullTypeName (Param->Type)); + } else { + Error ("Parameter '%s' has incomplete type '%s'", + Param->Name, + GetFullTypeName (Param->Type)); + } + ++IncompleteCount; + } + if (SymIsRegVar (Param)) { + Param->V.R.SaveOffs = Offs; + } else { + Param->V.Offs = Offs; + } + Offs += Size; + ParamSize += Size; + Param = Param->PrevSym; + ++I; + } + + /* If all parameters have complete types, set the total size description + ** and return true. + */ + if (IncompleteCount == 0) { + D->ParamSize = ParamSize; + return 1; + } + + /* Otherwise return false */ + return 0; +} + + + const char* F_GetFuncName (const Function* F) /* Return the name of the current function */ { @@ -378,9 +434,11 @@ static void F_EmitDebugInfo (void) 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 */ /* Remember this function descriptor used for definition */ GetFuncDesc (Func->Type)->FuncDef = D; @@ -391,6 +449,21 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Reenter the lexical level */ ReenterFunctionLevel (D); + /* Check return type */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* There are already diagnostics on returning arrays or functions */ + if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { + Error ("Function has incomplete return type '%s'", + GetFullTypeName (ReturnType)); + } + } + + /* Check and set the parameter sizes. All parameter must have complete + ** types now. + */ + ParamComplete = F_CheckParamList (D, 1); + /* Check if the function header contains unnamed parameters. These are ** only allowed in cc65 mode. */ @@ -429,7 +502,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* If cc65 extensions aren't enabled, don't allow a main function that ** doesn't return an int. */ - if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) { + if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) { Error ("'main' must always return an int"); } @@ -472,16 +545,11 @@ void NewFunc (SymEntry* Func, FuncDesc* D) unsigned Flags; /* Generate the push */ - if (IsTypeFunc (D->LastParam->Type)) { - /* Pointer to function */ - Flags = CF_PTR; + /* Handle struct/union specially */ + if (IsClassStruct (D->LastParam->Type)) { + Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { - /* Handle struct/union specially */ - if (IsClassStruct (D->LastParam->Type)) { - Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; - } else { - Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; - } + Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; } g_push (Flags, 0); } @@ -497,48 +565,50 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Setup the stack */ StackPtr = 0; - /* Walk through the parameter list and allocate register variable space - ** for parameters declared as register. Generate code to swap the contents - ** of the register bank with the save area on the stack. - */ - Param = D->SymTab->SymHead; - while (Param && (Param->Flags & SC_PARAM) != 0) { + /* Emit code to handle the parameters if all of them have complete types */ + if (ParamComplete) { + /* Walk through the parameter list and allocate register variable space + ** for parameters declared as register. Generate code to swap the contents + ** of the register bank with the save area on the stack. + */ + Param = D->SymTab->SymHead; + while (Param && (Param->Flags & SC_PARAM) != 0) { - /* Check if we need copy for struct/union type */ - RType = Param->Type; - if (IsClassStruct (RType)) { - RType = GetStructReplacementType (RType); + /* Check if we need copy for struct/union type */ + RType = Param->Type; + if (IsClassStruct (RType)) { + RType = GetStructReplacementType (RType); - /* If there is no replacement type, then it is just the address. - ** We don't currently support this case. - */ - if (RType == Param->Type) { - Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + /* If there is no replacement type, then it is just the address. + ** We don't currently support this case. + */ + if (RType == Param->Type) { + Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + } } - } + /* Check for a register variable */ + if (SymIsRegVar (Param)) { - /* Check for a register variable */ - if (SymIsRegVar (Param)) { + /* Allocate space */ + int Reg = F_AllocRegVar (CurrentFunc, RType); - /* Allocate space */ - int Reg = F_AllocRegVar (CurrentFunc, RType); + /* Could we allocate a register? */ + if (Reg < 0) { + /* No register available: Convert parameter to auto */ + CvtRegVarToAuto (Param); + } else { + /* Remember the register offset */ + Param->V.R.RegOffs = Reg; - /* Could we allocate a register? */ - if (Reg < 0) { - /* No register available: Convert parameter to auto */ - CvtRegVarToAuto (Param); - } else { - /* Remember the register offset */ - Param->V.R.RegOffs = Reg; - - /* Generate swap code */ - g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); + /* Generate swap code */ + g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); + } } - } - /* Next parameter */ - Param = Param->NextSym; + /* Next parameter */ + Param = Param->NextSym; + } } /* Need a starting curly brace */ diff --git a/src/cc65/function.h b/src/cc65/function.h index 8231a1970..e0b7ef0a2 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -81,6 +81,12 @@ extern Function* CurrentFunc; +int F_CheckParamList (FuncDesc* D, int RequireAll); +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ + const char* F_GetFuncName (const Function* F); /* Return the name of the current function */ diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 036cc2d89..0925c6d3d 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -318,28 +318,38 @@ static void ReturnStatement (void) /* Evaluate the return expression */ hie0 (&Expr); - /* If we return something in a void function, print an error and - ** ignore the value. Otherwise convert the value to the type of the - ** return. + /* If we return something in a function with void or incomplete return + ** type, print an error and ignore the value. Otherwise convert the + ** value to the type of the return. */ if (F_HasVoidReturn (CurrentFunc)) { - Error ("Returning a value in function with return type void"); + Error ("Returning a value in function with return type 'void'"); } else { - /* Convert the return value to the type of the function result */ - TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); - /* Load the value into the primary */ - if (IsClassStruct (Expr.Type)) { - /* Handle struct/union specially */ - ReturnType = GetStructReplacementType (Expr.Type); - if (ReturnType == Expr.Type) { - Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); + /* Check the return type first */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* Avoid excess errors */ + if (ErrorCount == 0) { + Error ("Returning a value in function with incomplete return type"); } - LoadExpr (TypeOf (ReturnType), &Expr); - } else { + /* Convert the return value to the type of the function result */ + TypeConversion (&Expr, ReturnType); + /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); + if (IsClassStruct (Expr.Type)) { + /* Handle struct/union specially */ + ReturnType = GetStructReplacementType (Expr.Type); + if (ReturnType == Expr.Type) { + Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); + } + LoadExpr (TypeOf (ReturnType), &Expr); + + } else { + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + } } } From 33a75e0a7315f5dc3a5cd1bf6db2b691e655da7b Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 293/806] Optimized parameter list checking. Fixed function type comparison between ANSI and K&R styles. --- src/cc65/declare.c | 1 + src/cc65/funcdesc.h | 3 ++- src/cc65/function.c | 10 ++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 218ba6017..8d3e0f458 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1753,6 +1753,7 @@ static FuncDesc* ParseFuncDecl (void) /* It is allowed to use incomplete types in function prototypes, so we ** won't always get to know the parameter sizes here and may do that later. */ + F->Flags |= FD_INCOMPLETE_PARAM; /* Leave the lexical level remembering the symbol tables */ RememberFunctionLevel (F); diff --git a/src/cc65/funcdesc.h b/src/cc65/funcdesc.h index 1cfb2bd09..423e7621f 100644 --- a/src/cc65/funcdesc.h +++ b/src/cc65/funcdesc.h @@ -49,13 +49,14 @@ #define FD_EMPTY 0x0001U /* Function with empty param list */ #define FD_VOID_PARAM 0x0002U /* Function with a void param list */ #define FD_VARIADIC 0x0004U /* Function with variable param list */ +#define FD_INCOMPLETE_PARAM 0x0008U /* Function with param of unknown size */ #define FD_OLDSTYLE 0x0010U /* Old style (K&R) function */ #define FD_OLDSTYLE_INTRET 0x0020U /* K&R func has implicit int return */ #define FD_UNNAMED_PARAMS 0x0040U /* Function has unnamed params */ #define FD_CALL_WRAPPER 0x0080U /* This function is used as a wrapper */ /* Bits that must be ignored when comparing funcs */ -#define FD_IGNORE (FD_OLDSTYLE | FD_OLDSTYLE_INTRET | FD_UNNAMED_PARAMS | FD_CALL_WRAPPER) +#define FD_IGNORE (FD_INCOMPLETE_PARAM | FD_OLDSTYLE | FD_OLDSTYLE_INTRET | FD_UNNAMED_PARAMS | FD_CALL_WRAPPER) diff --git a/src/cc65/function.c b/src/cc65/function.c index 451efc54d..9d4f8c2f9 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -117,6 +117,11 @@ int F_CheckParamList (FuncDesc* D, int RequireAll) unsigned ParamSize = 0; unsigned IncompleteCount = 0; + /* Don't bother to check unnecessarily */ + if ((D->Flags & FD_INCOMPLETE_PARAM) == 0) { + return 1; + } + /* Assign offsets. If the function has a variable parameter list, ** there's one additional byte (the arg size). */ @@ -147,11 +152,12 @@ int F_CheckParamList (FuncDesc* D, int RequireAll) ++I; } - /* If all parameters have complete types, set the total size description - ** and return true. + /* If all parameters have complete types, set the total size description, + ** clear the FD_INCOMPLETE_PARAM flag and return true. */ if (IncompleteCount == 0) { D->ParamSize = ParamSize; + D->Flags &= ~FD_INCOMPLETE_PARAM; return 1; } From df755df44dfc97188dfea755d4c01b06d6656b5a Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH 294/806] Warning about ESU types declared inside parameter list as they are invisble outside. --- src/cc65/declare.c | 50 +++++++++++++++++++++++++++++----------------- src/cc65/declare.h | 3 +++ src/cc65/symtab.c | 30 ++++++++++++++++++++++++++-- src/cc65/symtab.h | 4 ++-- 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8d3e0f458..6aff1960e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -515,7 +515,7 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) -static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags) +static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags) /* Handle an enum, struct or union forward decl */ { /* Try to find an enum/struct/union with the given name. If there is none, @@ -524,9 +524,9 @@ static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags) SymEntry* Entry = FindTagSym (Name); if (Entry == 0) { if ((Flags & SC_ESUTYPEMASK) != SC_ENUM) { - Entry = AddStructSym (Name, Flags, 0, 0); + Entry = AddStructSym (Name, Flags, 0, 0, DSFlags); } else { - Entry = AddEnumSym (Name, Flags, 0, 0); + Entry = AddEnumSym (Name, Flags, 0, 0, DSFlags); } } else if ((Entry->Flags & SC_TYPEMASK) != (Flags & SC_ESUTYPEMASK)) { /* Already defined, but not the same type class */ @@ -575,7 +575,7 @@ static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) -static SymEntry* ParseEnumDecl (const char* Name) +static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) /* Process an enum declaration */ { SymTable* FieldTab; @@ -593,11 +593,11 @@ static SymEntry* ParseEnumDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward definition */ - return ESUForwardDecl (Name, SC_ENUM); + return ESUForwardDecl (Name, SC_ENUM, DSFlags); } /* Add a forward declaration for the enum tag in the current lexical level */ - AddEnumSym (Name, 0, 0, 0); + AddEnumSym (Name, 0, 0, 0, DSFlags); /* Skip the opening curly brace */ NextToken (); @@ -740,7 +740,7 @@ static SymEntry* ParseEnumDecl (const char* Name) Flags |= SC_FICTITIOUS; } - return AddEnumSym (Name, Flags, MemberType, FieldTab); + return AddEnumSym (Name, Flags, MemberType, FieldTab, DSFlags); } @@ -870,7 +870,7 @@ static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) -static SymEntry* ParseUnionDecl (const char* Name) +static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) /* Parse a union declaration. */ { @@ -886,11 +886,11 @@ static SymEntry* ParseUnionDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration */ - return ESUForwardDecl (Name, SC_UNION); + return ESUForwardDecl (Name, SC_UNION, DSFlags); } /* Add a forward declaration for the union tag in the current lexical level */ - UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0); + UnionTagEntry = AddStructSym (Name, SC_UNION, 0, 0, DSFlags); UnionTagEntry->V.S.ACount = 0; @@ -1005,12 +1005,12 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { } /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab); + return AddStructSym (Name, SC_UNION | SC_DEF | Flags, UnionSize, FieldTab, DSFlags); } -static SymEntry* ParseStructDecl (const char* Name) +static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) /* Parse a struct declaration. */ { @@ -1027,11 +1027,11 @@ static SymEntry* ParseStructDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration */ - return ESUForwardDecl (Name, SC_STRUCT); + return ESUForwardDecl (Name, SC_STRUCT, DSFlags); } /* Add a forward declaration for the struct tag in the current lexical level */ - StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0); + StructTagEntry = AddStructSym (Name, SC_STRUCT, 0, 0, DSFlags); StructTagEntry->V.S.ACount = 0; @@ -1219,7 +1219,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { } /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab); + return AddStructSym (Name, SC_STRUCT | SC_DEF | Flags, StructSize, FieldTab, DSFlags); } @@ -1402,7 +1402,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Declare the union in the current scope */ - Entry = ParseUnionDecl (Ident); + Entry = ParseUnionDecl (Ident, &D->Flags); /* Encode the union entry into the type */ D->Type[0].C = T_UNION; SetESUSymEntry (D->Type, Entry); @@ -1421,7 +1421,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Declare the struct in the current scope */ - Entry = ParseStructDecl (Ident); + Entry = ParseStructDecl (Ident, &D->Flags); /* Encode the struct entry into the type */ D->Type[0].C = T_STRUCT; SetESUSymEntry (D->Type, Entry); @@ -1444,7 +1444,8 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - Entry = ParseEnumDecl (Ident); + Entry = ParseEnumDecl (Ident, &D->Flags); + /* Encode the enum entry into the type */ D->Type[0].C |= T_ENUM; SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; @@ -1583,6 +1584,13 @@ static void ParseOldStyleParamList (FuncDesc* F) /* Read the parameter */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); + + /* Warn about new local type declaration */ + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + Warning ("'%s' will be invisible out of this function", + GetFullTypeName (Spec.Type)); + } + if (Decl.Ident[0] != '\0') { /* We have a name given. Search for the symbol */ @@ -1650,6 +1658,12 @@ static void ParseAnsiParamList (FuncDesc* F) Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } + /* Warn about new local type declaration */ + if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) { + Warning ("'%s' will be invisible out of this function", + GetFullTypeName (Spec.Type)); + } + /* Allow parameters without a name, but remember if we had some to ** eventually print an error message later. */ diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 615f16a4a..3293a0dcb 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -57,6 +57,9 @@ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */ #define DS_DEF_TYPE 0x0002U /* Default type used */ #define DS_EXTRA_TYPE 0x0004U /* Extra type declared */ +#define DS_NEW_TYPE_DECL 0x0010U /* New type declared */ +#define DS_NEW_TYPE_DEF 0x0020U /* New type defined */ +#define DS_NEW_TYPE (DS_NEW_TYPE_DECL | DS_NEW_TYPE_DEF) /* Result of ParseDeclSpec */ typedef struct DeclSpec DeclSpec; diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 7e6a9f5bb..6a02fc0ce 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -686,7 +686,7 @@ static void AddSymEntry (SymTable* T, SymEntry* S) -SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab) +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags) /* Add an enum entry and return it */ { SymTable* CurTagTab = TagTab; @@ -719,6 +719,11 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab Entry->V.E.Type = Type; Entry->Flags &= ~SC_DECL; Entry->Flags |= SC_DEF; + + /* Remember this is the first definition of this type */ + if (DSFlags != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } } } @@ -741,6 +746,14 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab Entry->Flags |= SC_DEF; } + /* Remember this is the first definition of this type */ + if (CurTagTab != FailSafeTab && DSFlags != 0) { + if ((Entry->Flags & SC_DEF) != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } + *DSFlags |= DS_NEW_TYPE_DECL; + } + /* Add it to the current table */ AddSymEntry (CurTagTab, Entry); } @@ -751,7 +764,7 @@ SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTab -SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab) +SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags) /* Add a struct/union entry and return it */ { SymTable* CurTagTab = TagTab; @@ -791,6 +804,11 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl Entry->Flags = Flags; Entry->V.S.SymTab = Tab; Entry->V.S.Size = Size; + + /* Remember this is the first definition of this type */ + if (DSFlags != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } } } @@ -809,6 +827,14 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl Entry->V.S.SymTab = Tab; Entry->V.S.Size = Size; + /* Remember this is the first definition of this type */ + if (CurTagTab != FailSafeTab && DSFlags != 0) { + if ((Entry->Flags & SC_DEF) != 0) { + *DSFlags |= DS_NEW_TYPE_DEF; + } + *DSFlags |= DS_NEW_TYPE_DECL; + } + /* Add it to the current tag table */ AddSymEntry (CurTagTab, Entry); } diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 750e41b54..1231eb528 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -149,10 +149,10 @@ unsigned short FindSPAdjustment (const char* Name); -SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab); +SymEntry* AddEnumSym (const char* Name, unsigned Flags, const Type* Type, SymTable* Tab, unsigned* DSFlags); /* Add an enum entry and return it */ -SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab); +SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTable* Tab, unsigned* DSFlags); /* Add a struct/union entry and return it */ SymEntry* AddBitField (const char* Name, const Type* Type, unsigned Offs, From 492ee7fc455dbef2b5a3e52c37a20a5ecdbd3eee Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 30 Aug 2020 03:10:24 +0800 Subject: [PATCH 295/806] Improved test/ref/pr1220.c. --- test/ref/pr1220.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c index c594b5ac2..4f88ada73 100644 --- a/test/ref/pr1220.c +++ b/test/ref/pr1220.c @@ -1,24 +1,28 @@ -/* PR #1220 - test constant AND/OR */ +/* PR #1220 - test constant ternary, AND and OR */ #include +/* test AND/OR, results as integers */ #define CONTEXT_A(x) do {\ s = 0, flags = 0, t = (x),\ printf("%3d %2X: %d\n", s, flags, t);\ } while (0) +/* test AND/OR in ternary context */ #define CONTEXT_B(x) do {\ s = 0, flags = 0,\ (x ? printf("%3d %2X: 1\n", s, flags) : printf("%3d %2X: 0\n", s, flags));\ } while (0) int s, t; -void *p; unsigned flags; int f(x) +/* The call to this function should be and only be skipped strictly according to +** the short-circuit evaluation rule. +*/ { - flags |= 1U << s; + flags |= (x != 0) << s; ++s; return x; } @@ -31,6 +35,7 @@ int f(x) #define _F (f(-1), 0) void f0() +/* constant short-circuit */ { printf("f0()\n"); @@ -59,6 +64,7 @@ void f0() } void f1(int a, int b, int c) +/* AND */ { printf("f1(%d, %d, %d)\n", a, b, c); @@ -80,6 +86,7 @@ void f1(int a, int b, int c) } void f2(int a, int b, int c) +/* OR */ { printf("f2(%d, %d, %d)\n", a, b, c); @@ -101,6 +108,7 @@ void f2(int a, int b, int c) } void f3(int a, int b, int c, int d) +/* AND and OR */ { printf("f3(%d, %d, %d, %d)\n", a, b, c, d); @@ -122,6 +130,7 @@ void f3(int a, int b, int c, int d) } void f4(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ { printf("f4(%d, %d, %d, %d)\n", a, b, c, d); @@ -143,6 +152,7 @@ void f4(int a, int b, int c, int d) } void f5(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ { printf("f5(%d, %d, %d, %d)\n", a, b, c, d); @@ -164,6 +174,7 @@ void f5(int a, int b, int c, int d) } void f0_B() +/* constant short-circuit */ { printf("f0()\n"); @@ -192,6 +203,7 @@ void f0_B() } void f1_B(int a, int b, int c) +/* AND */ { printf("f1(%d, %d, %d)\n", a, b, c); @@ -213,6 +225,7 @@ void f1_B(int a, int b, int c) } void f2_B(int a, int b, int c) +/* OR */ { printf("f2(%d, %d, %d)\n", a, b, c); @@ -234,6 +247,7 @@ void f2_B(int a, int b, int c) } void f3_B(int a, int b, int c, int d) +/* AND and OR */ { printf("f3(%d, %d, %d, %d)\n", a, b, c, d); @@ -255,6 +269,7 @@ void f3_B(int a, int b, int c, int d) } void f4_B(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ { printf("f4(%d, %d, %d, %d)\n", a, b, c, d); @@ -276,6 +291,7 @@ void f4_B(int a, int b, int c, int d) } void f5_B(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ { printf("f5(%d, %d, %d, %d)\n", a, b, c, d); From 60c59f59a3957e819f1c4889b01142d9b7d3c981 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 30 Aug 2020 00:26:52 +0800 Subject: [PATCH 296/806] Renamed StaticConstExpr() and StaticConstAbsIntExpr() with clearer comments. --- src/cc65/asmstmt.c | 8 ++++---- src/cc65/declare.c | 12 ++++++------ src/cc65/expr.c | 28 ++++++++++++++-------------- src/cc65/expr.h | 18 +++++++++--------- src/cc65/preproc.c | 2 +- src/cc65/preproc.h | 2 +- src/cc65/staticassert.c | 2 +- src/cc65/swstmt.c | 2 +- 8 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 148d62d9c..9dd1f906a 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -135,7 +135,7 @@ static void ParseByteArg (StrBuf* T, unsigned Arg) ConsumeComma (); /* Evaluate the expression */ - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -168,7 +168,7 @@ static void ParseWordArg (StrBuf* T, unsigned Arg) ConsumeComma (); /* Evaluate the expression */ - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -201,7 +201,7 @@ static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) ConsumeComma (); /* Evaluate the expression */ - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Convert into a hex number */ xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF); @@ -325,7 +325,7 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) break; default: - Expr = StaticConstAbsIntExpr (hie1); + Expr = NoCodeConstAbsIntExpr (hie1); xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal); SB_AppendStr (T, Buf); break; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index d1ac0e43f..b184c31a6 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -622,7 +622,7 @@ static SymEntry* ParseEnumDecl (const char* Name) if (CurTok.Tok == TOK_ASSIGN) { NextToken (); - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); EnumVal = Expr.IVal; MemberType = Expr.Type; IsSigned = IsSignSigned (MemberType); @@ -772,7 +772,7 @@ static int ParseFieldWidth (Declaration* Decl) /* Read the width */ NextToken (); - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); if (Expr.IVal < 0) { Error ("Negative width in bit-field"); @@ -1859,7 +1859,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { - ExprDesc Expr = StaticConstAbsIntExpr (hie1); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); if (Expr.IVal <= 0) { if (D->Ident[0] != '\0') { Error ("Size of array '%s' is invalid", D->Ident); @@ -2220,7 +2220,7 @@ static ExprDesc ParseScalarInitInternal (Type* T) } /* Get the expression and convert it to the target type */ - ExprDesc ED = StaticConstExpr (hie1); + ExprDesc ED = NoCodeConstExpr (hie1); TypeConversion (&ED, T); /* Close eventually opening braces */ @@ -2253,7 +2253,7 @@ static unsigned ParsePointerInit (Type* T) unsigned BraceCount = OpeningCurlyBraces (0); /* Expression */ - ExprDesc ED = StaticConstExpr (hie1); + ExprDesc ED = NoCodeConstExpr (hie1); TypeConversion (&ED, T); /* Output the data */ @@ -2598,7 +2598,7 @@ static unsigned ParseVoidInit (Type* T) /* Allow an arbitrary list of values */ Size = 0; do { - ExprDesc Expr = StaticConstExpr (hie1); + ExprDesc Expr = NoCodeConstExpr (hie1); switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d1ceb27c5..e627b6606 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3150,14 +3150,14 @@ static void hieAndPP (ExprDesc* Expr) ** called recursively from the preprocessor. */ { - *Expr = StaticConstAbsIntExpr (hie2); + *Expr = NoCodeConstAbsIntExpr (hie2); while (CurTok.Tok == TOK_BOOL_AND) { /* Skip the && */ NextToken (); /* Get rhs */ - ExprDesc Expr2 = StaticConstAbsIntExpr (hie2); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2); /* Combine the two */ Expr->IVal = (Expr->IVal && Expr2.IVal); @@ -3171,14 +3171,14 @@ static void hieOrPP (ExprDesc *Expr) ** called recursively from the preprocessor. */ { - *Expr = StaticConstAbsIntExpr (hieAndPP); + *Expr = NoCodeConstAbsIntExpr (hieAndPP); while (CurTok.Tok == TOK_BOOL_OR) { /* Skip the && */ NextToken (); /* Get rhs */ - ExprDesc Expr2 = StaticConstAbsIntExpr (hieAndPP); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP); /* Combine the two */ Expr->IVal = (Expr->IVal || Expr2.IVal); @@ -4047,11 +4047,11 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -ExprDesc StaticConstExpr (void (*Func) (ExprDesc*)) -/* Will evaluate an expression via the given function. If the result is not a -** static constant expression, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. +ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)) +/* Get an expression evaluated via the given function. If the result is not a +** constant expression without runtime code generated, a diagnostic will be +** printed, and the value is replaced by a constant one to make sure there are +** no internal errors that result from this input error. */ { ExprDesc Expr; @@ -4071,11 +4071,11 @@ ExprDesc StaticConstExpr (void (*Func) (ExprDesc*)) -ExprDesc StaticConstAbsIntExpr (void (*Func) (ExprDesc*)) -/* Will evaluate an expression via the given function. If the result is not a -** static constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)) +/* Get an expression evaluated via the given function. If the result is not a +** constant numeric integer value without runtime code generated, a diagnostic +** will be printed, and the value is replaced by a constant one to make sure +** there are no internal errors that result from this input error. */ { ExprDesc Expr; diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 8f7eb6f09..806a376bb 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -61,18 +61,18 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); ** are no internal errors that result from this input error. */ -ExprDesc StaticConstExpr (void (*Func) (ExprDesc*)); +ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*)); /* Get an expression evaluated via the given function. If the result is not a -** static constant expression, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. +** constant expression without runtime code generated, a diagnostic will be +** printed, and the value is replaced by a constant one to make sure there are +** no internal errors that result from this input error. */ -ExprDesc StaticConstAbsIntExpr (void (*Func) (ExprDesc*)); -/* Get an expression evaluate via the given function. If the result is not a -** static constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*)); +/* Get an expression evaluated via the given function. If the result is not a +** constant numeric integer value without runtime code generated, a diagnostic +** will be printed, and the value is replaced by a constant one to make sure +** there are no internal errors that result from this input error. */ void hie10 (ExprDesc* lval); diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 2d2f316d7..cc160c1c3 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1076,7 +1076,7 @@ static int DoIf (int Skip) NextToken (); /* Call the expression parser */ - ExprDesc Expr = StaticConstExpr (hie1); + ExprDesc Expr = NoCodeConstExpr (hie1); /* End preprocessing mode */ Preprocessing = 0; diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 464b02337..1487179f4 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -44,7 +44,7 @@ -/* Set when the preprocessor calls StaticConstExpr() recursively */ +/* Set when the preprocessor calls NoCodeConstExpr() recursively */ extern unsigned char Preprocessing; diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 3372c59a1..1bf8dd4c5 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -65,7 +65,7 @@ void ParseStaticAssert () } /* Parse assertion condition */ - Expr = StaticConstAbsIntExpr (hie1); + Expr = NoCodeConstAbsIntExpr (hie1); failed = !Expr.IVal; /* If there is a comma, we also have an error message. The message is optional because we diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 559e168c3..3878f7b67 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -216,7 +216,7 @@ void CaseLabel (void) NextToken (); /* Read the selector expression */ - CaseExpr = StaticConstAbsIntExpr (hie1); + CaseExpr = NoCodeConstAbsIntExpr (hie1); Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ From d87846e1e1d1f083a1f01be41e4514a15cdd2dbd Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 30 Aug 2020 22:12:30 +0800 Subject: [PATCH 297/806] Improved comments according to PR reviews. --- src/cc65/exprdesc.h | 86 +++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 68db8b2fe..f04be6a8d 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -65,16 +65,16 @@ enum { ** - E_LOC_ refers to any other than E_LOC_NONE and E_LOC_PRIMARY. ** - E_LOC_EXPR can be regarded as a generalized E_LOC_. ** - E_LOC_NONE can be regarded as E_LOC_PRIMARY + E_ADDRESS_OF unless - ** remarked otherwise (see below). + ** remarked otherwise (see below). ** - An E_LOC_NONE value is not considered to be an "address". ** - ref-load doesn't change the location, while rval-load puts into the - ** primary register a "temporary" that is the straight integer rvalue or - ** a "delegate" to the real rvalue somewhere else. + ** primary register a "temporary" that is the straight integer rvalue or + ** a "delegate" to the real rvalue somewhere else. ** - ref-load doesn't change the rval/lval category of the expression, - ** while rval-load converts it to an rvalue if it wasn't. + ** while rval-load converts it to an rvalue if it wasn't. ** - In practice, ref-load is unimplemented, and can be simulated with - ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, - ** whilst val-load is done with LoadExpr + FinalizeRValLoad. + ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, + ** whilst val-load is done with LoadExpr + FinalizeRValLoad. ** ** E_LOC_NONE -- ref-load -> + E_LOADED (int rvalue) ** E_LOC_PRIMARY -- ref-load -> + E_LOADED (unchanged) @@ -110,7 +110,7 @@ enum { E_LOC_CONST = E_LOC_NONE | E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | E_LOC_REGISTER | E_LOC_LITERAL | E_LOC_CODE, - /* Not-so-immutable location addresses (stack offsets can change) */ + /* Not-so-immutable location addresses (stack offsets may change dynamically) */ E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, /* Expression type modifiers */ @@ -129,33 +129,67 @@ enum { /* Optimization hints */ E_MASK_NEED = 0x030000, - E_NEED_EAX = 0x000000, /* Expression needs to be loaded in Primary */ - E_NEED_NONE = 0x010000, /* Expression value is unused */ + E_NEED_EAX = 0x000000, /* Expression result needs to be loaded in Primary */ + E_NEED_NONE = 0x010000, /* Expression result is unused */ E_NEED_TEST = 0x020000, /* Expression needs a test to set cc */ /* Expression evaluation requirements. ** Usage: (Flags & E_EVAL_) == E_EVAL_ + ** + ** Remark: + ** - Expression result, that is the "final value" of the expression, is no + ** more than one of the effects of the whole expression. Effects other + ** than it are usually consided "side-effects" in this regard. + ** - The compiler front end cannot know things determined by the linker, + ** such as the actual address of an object with static storage. What it + ** can know is categorized as "compiler-known" here. + ** - The concept "immutable" here means that once something is determined + ** (not necessarily by the compiler), it will never change. This is not + ** the same meaning as the "constant" word in the C standard. + ** - The concept "compile-time" ( not to be confued with "compiler-known"), + ** or "static" (compared to "run-time" as in "_Static_assert" in C, not + ** to be confused with the "static" storage) here means that something + ** has no run-time behaviors, enforced by the fact that it generates no + ** target code (hence "no-code"). It is closely related to the concepts + ** above but not the same. + ** - An "unevaluated" expression is special and different from the above: + ** while it generates no code, cannot change its "value" (it actually has + ** no value), and must be completely handled by the compiler front-end, + ** it is unique in that it is not "evaluated" while the others are, and + ** the codegen routine of such an expression is usually separated from + ** the normally evaluated ones. Therefore it is treated differently from + ** the above and uses a separate flag that implies none of the above. + ** - The "maybe-unused" flag is to suppress the checking and warning on + ** expressions with no effects. It doesn't have any special meanings + ** beyond that, and is independent from the E_NEED_s. All + ** "unevaluated" expressions are flagged as "maybe-unused" just to + ** avoid unnecessary warnings. + ** + ** Relationship of some concepts: + ** - "no-code" implies "no-side-effects" + ** - "immutable" = "compiler-known" OR "no-code" + ** - "constant expression" in C = "compiler-known" AND "no-code", with minor differences */ - E_MASK_EVAL = 0xFC0000, - E_EVAL_NONE = 0x000000, /* No requirements */ - E_EVAL_CONST = 0x040000, /* Result must be immutable */ - E_EVAL_COMPILE_TIME = 0x0C0000, /* Result must be known at compile time */ - E_EVAL_PURE = 0x100000, /* Evaluation must have no side effects */ - E_EVAL_STATIC = 0x340000, /* Evaluation must generate no code */ - E_EVAL_MAYBE_UNUSED = 0x400000, /* Result may be unused */ - E_EVAL_UNEVAL = 0xC00000, /* Expression is unevaluated */ + E_MASK_EVAL = 0xFC0000, + E_EVAL_NONE = 0x000000, /* No requirements */ + E_EVAL_IMMUTABLE_RESULT = 0x040000, /* Expression result must be immutable */ + E_EVAL_COMPILER_KNOWN = 0x0C0000, /* Expression result must be known to the compiler */ + E_EVAL_NO_SIDE_EFFECTS = 0x100000, /* Evaluation must have no side effects */ + E_EVAL_NO_CODE = 0x340000, /* Evaluation must generate no code */ + E_EVAL_MAYBE_UNUSED = 0x400000, /* Expression result may be unused */ + E_EVAL_UNEVAL = 0xC00000, /* Expression is unevaluated */ - /* Expression must be static and have result known at compile time */ - E_EVAL_C_CONST = E_EVAL_COMPILE_TIME | E_EVAL_STATIC, + /* Expression result must be known to the compiler and generate no code to load */ + E_EVAL_C_CONST = E_EVAL_COMPILER_KNOWN | E_EVAL_NO_CODE, - /* Flags to keep in subexpressions */ - E_MASK_KEEP_SUBEXPR = E_MASK_EVAL, + /* Flags to keep in subexpressions of most operations other than ternary */ + E_MASK_KEEP_SUBEXPR = E_MASK_EVAL, - /* Flags to keep in ternary subexpressions */ - E_MASK_KEEP_RESULT = E_MASK_NEED | E_MASK_EVAL, + /* Flags to keep for the two result subexpressions of the ternary operation */ + E_MASK_KEEP_RESULT = E_MASK_NEED | E_MASK_EVAL, /* Flags to keep when using the ED_Make functions */ - E_MASK_KEEP_MAKE = E_HAVE_MARKS | E_MASK_KEEP_RESULT, + E_MASK_KEEP_MAKE = E_HAVE_MARKS | E_MASK_KEEP_RESULT, }; /* Forward */ @@ -420,10 +454,10 @@ int ED_YetToLoad (const ExprDesc* Expr); INLINE int ED_NeedsConst (const ExprDesc* Expr) /* Check if the expression need be immutable */ { - return (Expr->Flags & E_EVAL_CONST) == E_EVAL_CONST; + return (Expr->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT; } #else -# define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_CONST) == E_EVAL_CONST) +# define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT) #endif void ED_MarkForUneval (ExprDesc* Expr); From abcc2a8f1a06083dd8847dab5fd974cd50eba4d8 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:46:08 +0800 Subject: [PATCH 298/806] Disallowed void arrays of elements of variant sizes. --- src/cc65/declare.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 6aff1960e..e32c880cc 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2389,6 +2389,11 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } } + /* Size of 'void' elements are determined after initialization */ + if (ElementSize == 0) { + ElementSize = SizeOf (ElementType); + } + if (ElementCount == UNSPECIFIED) { /* Number of elements determined by initializer */ SetElementCount (T, Count); @@ -2695,7 +2700,11 @@ static unsigned ParseVoidInit (Type* T) ConsumeRCurly (); /* Number of bytes determined by initializer */ - T->A.U = Size; + if (T->A.U != 0 && T->A.U != Size) { + Error ("'void' array initialized with elements of variant sizes"); + } else { + T->A.U = Size; + } /* Return the number of bytes initialized */ return Size; From 8541f18340f01cece75fbffea3f3d8febd7cd7f9 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 26 Aug 2020 00:12:23 +0800 Subject: [PATCH 299/806] Improved diagnostic info on assignment to void types. --- src/cc65/datatype.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 9efb142ee..522b653c6 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -903,6 +903,7 @@ unsigned TypeOf (const Type* T) /* Address of ... */ return CF_INT | CF_UNSIGNED; + case T_VOID: case T_ENUM: /* Incomplete enum type */ Error ("Incomplete type '%s'", GetFullTypeName (T)); From f1161daee91890aeb4f028fe81637a4bf73079f3 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 30 Aug 2020 02:30:43 +0800 Subject: [PATCH 300/806] Recursively checking for incomplete/unknown-sized types. --- src/cc65/datatype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 522b653c6..c5fb784a2 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -979,7 +979,7 @@ int IsClassIncomplete (const Type* T) /* Return true if this is an object type lacking size info */ { if (IsTypeArray (T)) { - return GetElementCount (T) == UNSPECIFIED; + return GetElementCount (T) == UNSPECIFIED || IsClassIncomplete (T + 1); } return IsTypeVoid (T) || IsIncompleteESUType (T); } @@ -1072,7 +1072,7 @@ int HasUnknownSize (const Type* T) /* Return true if this is an incomplete ESU type or an array of unknown size */ { if (IsTypeArray (T)) { - return GetElementCount (T) == UNSPECIFIED; + return GetElementCount (T) == UNSPECIFIED || HasUnknownSize (T + 1); } return IsIncompleteESUType (T); } From 8b580e1191cad7d6da0ef97be60105d659514d6e Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 30 Aug 2020 02:31:09 +0800 Subject: [PATCH 301/806] Disabled struct/union fields of 'void' type. --- src/cc65/declare.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index e32c880cc..f65702e91 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -942,8 +942,8 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) } } - /* Check for incomplete type */ - if (IsIncompleteESUType (Decl.Type)) { + /* Check for incomplete types including 'void' */ + if (IsClassIncomplete (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); @@ -1142,8 +1142,8 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) } } - /* Check for incomplete type */ - if (IsIncompleteESUType (Decl.Type)) { + /* Check for incomplete types including 'void' */ + if (IsClassIncomplete (Decl.Type)) { Error ("Field '%s' has incomplete type '%s'", Decl.Ident, GetFullTypeName (Decl.Type)); From c0a873e0c8198a1c0111e766f15e028a527c01bb Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 27 Aug 2020 08:02:05 +0800 Subject: [PATCH 302/806] Reduced exess errors on wrong initializations with curly braces. --- src/cc65/expr.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 89c7ff108..75d695623 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -959,7 +959,16 @@ static void Primary (ExprDesc* E) /* Illegal primary. Be sure to skip the token to avoid endless ** error loops. */ - { + if (CurTok.Tok == TOK_LCURLY) { + /* Statement block */ + NextToken (); + Error ("Expression expected"); + hie0 (E); + if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + } + break; + } else { /* Let's see if this is a C99-style declaration */ DeclSpec Spec; InitDeclSpec (&Spec); From 74def4608aab0065d118e4a072e8aab9b6918cd6 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 02:30:12 +0800 Subject: [PATCH 303/806] The 'E_NEED_TEST' flag shouldn't be overwritten when loading the expression result. --- src/cc65/expr.c | 15 ++++++++++----- src/cc65/exprdesc.c | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index e627b6606..0b34ba237 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3460,11 +3460,6 @@ static void hieQuest (ExprDesc* Expr) int Expr3IsNULL; /* Expression 3 is a NULL pointer */ Type* ResultType; /* Type of result */ - ED_Init (&Expr2); - Expr2.Flags = Expr->Flags & E_MASK_KEEP_RESULT; - ED_Init (&Expr3); - Expr3.Flags = Expr->Flags & E_MASK_KEEP_RESULT; - /* Call the lower level eval routine */ if (Preprocessing) { ExprWithCheck (hieOrPP, Expr); @@ -3476,6 +3471,13 @@ static void hieQuest (ExprDesc* Expr) if (CurTok.Tok == TOK_QUEST) { int ConstantCond = ED_IsConstAbsInt (Expr); + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + ED_Init (&Expr2); + Expr2.Flags = Flags; + ED_Init (&Expr3); + Expr3.Flags = Flags; + NextToken (); if (!ConstantCond) { @@ -3616,7 +3618,10 @@ static void hieQuest (ExprDesc* Expr) if (!ConstantCond) { /* Define the final label */ g_defcodelabel (TrueLab); + /* Set up the result expression type */ ED_FinalizeRValLoad (Expr); + /* Restore the original evaluation flags */ + Expr->Flags = (Expr->Flags & ~E_MASK_KEEP_RESULT) | Flags; } else { if (Expr->IVal != 0) { *Expr = Expr2; diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index c3f4c63f7..ac19873e7 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -266,7 +266,7 @@ ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) { Expr->Sym = 0; Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF); - Expr->Flags &= ~(E_NEED_TEST | E_CC_SET); + Expr->Flags &= ~E_CC_SET; Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); Expr->Name = 0; Expr->IVal = 0; /* No offset */ From 9398e1cd33496e9fcc61b9db386f684b0da1946b Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 23 Aug 2020 01:35:06 +0800 Subject: [PATCH 304/806] Use a dedicated label pool for literals. --- src/cc65/asmlabel.c | 29 +++++++++++++++++++++++ src/cc65/asmlabel.h | 8 +++++++ src/cc65/codegen.c | 57 ++++++++++++++++++++++++++++++--------------- src/cc65/codegen.h | 9 ++++--- src/cc65/declare.c | 8 +++++-- src/cc65/exprdesc.c | 4 ++-- src/cc65/litpool.c | 8 +++---- src/cc65/loadexpr.c | 16 +++++++++---- 8 files changed, 105 insertions(+), 34 deletions(-) diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index 09aee3b92..1dda96fb0 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -98,3 +98,32 @@ int IsLocalLabelName (const char* Name) /* Local label name */ return 1; } + + + +unsigned GetPooledLiteralLabel (void) +/* Get an unused literal label. Will never return zero. */ +{ + /* Number to generate unique labels */ + static unsigned NextLabel = 0; + + /* Check for an overflow */ + if (NextLabel >= 0xFFFF) { + Internal ("Literal label overflow"); + } + + /* Return the next label */ + return ++NextLabel; +} + + + +const char* PooledLiteralLabelName (unsigned L) +/* Make a litral label name from the given label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ +{ + static char Buf[64]; + sprintf (Buf, "S%04X", L); + return Buf; +} diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index 4a76643a5..31b1f311a 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -56,6 +56,14 @@ const char* LocalLabelName (unsigned L); int IsLocalLabelName (const char* Name); /* Return true if Name is the name of a local label */ +unsigned GetPooledLiteralLabel (void); +/* Get an unused literal label. Will never return zero. */ + +const char* PooledLiteralLabelName (unsigned L); +/* Make a litral label name from the given label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ + /* End of asmlabel.h */ diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 48cf02319..b48d815d8 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -124,6 +124,16 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) } break; + case CF_LITERAL: + /* Literal */ + /* Static memory cell */ + if (Offs) { + xsprintf (Buf, sizeof (Buf), "%s%+ld", PooledLiteralLabelName (Label), Offs); + } else { + xsprintf (Buf, sizeof (Buf), "%s", PooledLiteralLabelName (Label)); + } + break; + case CF_ABSOLUTE: /* Absolute address */ xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned)((Label+Offs) & 0xFFFF)); @@ -355,24 +365,6 @@ void g_defdatalabel (unsigned label) -void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs) -/* Define label as a local alias for baselabel+offs */ -{ - /* We need an intermediate buffer here since LocalLabelName uses a - ** static buffer which changes with each call. - */ - StrBuf L = AUTO_STRBUF_INITIALIZER; - SB_AppendStr (&L, LocalLabelName (label)); - SB_Terminate (&L); - AddDataLine ("%s\t:=\t%s+%ld", - SB_GetConstBuf (&L), - LocalLabelName (baselabel), - offs); - SB_Done (&L); -} - - - /*****************************************************************************/ /* Functions handling global labels */ /*****************************************************************************/ @@ -388,6 +380,33 @@ void g_defgloblabel (const char* Name) +void g_defliterallabel (unsigned label) +/* Define a literal data label */ +{ + /* Literal labels are always data labels */ + AddDataLine ("%s:", PooledLiteralLabelName (label)); +} + + + +void g_aliasliterallabel (unsigned label, unsigned baselabel, long offs) +/* Define label as an alias for baselabel+offs */ +{ + /* We need an intermediate buffer here since LocalLabelName uses a + ** static buffer which changes with each call. + */ + StrBuf L = AUTO_STRBUF_INITIALIZER; + SB_AppendStr (&L, PooledLiteralLabelName (label)); + SB_Terminate (&L); + AddDataLine ("%s\t:=\t%s+%ld", + SB_GetConstBuf (&L), + PooledLiteralLabelName (baselabel), + offs); + SB_Done (&L); +} + + + void g_defexport (const char* Name, int ZP) /* Export the given label */ { @@ -2364,7 +2383,7 @@ void g_call (unsigned Flags, const char* Label, unsigned ArgSize) void g_callind (unsigned Flags, unsigned ArgSize, int Offs) /* Call subroutine indirect */ { - if ((Flags & CF_STACK) == 0) { + if ((Flags & CF_ADDRMASK) != CF_STACK) { /* Address is in a/x */ if ((Flags & CF_FIXARGC) == 0) { /* Pass arg count */ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index d946a9a10..3054a39b3 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -148,9 +148,6 @@ void g_defcodelabel (unsigned label); void g_defdatalabel (unsigned label); /* Define a local data label */ -void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs); -/* Define label as a local alias for baselabel+offs */ - /*****************************************************************************/ @@ -162,6 +159,12 @@ void g_aliasdatalabel (unsigned label, unsigned baselabel, long offs); void g_defgloblabel (const char* Name); /* Define a global label with the given name */ +void g_defliterallabel (unsigned label); +/* Define a literal data label */ + +void g_aliasliterallabel (unsigned label, unsigned baselabel, long offs); +/* Define label as an alias for baselabel+offs */ + void g_defexport (const char* Name, int ZP); /* Export the given label */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 6953afa3c..e81c761ed 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2185,11 +2185,15 @@ static void DefineData (ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + /* Static variable */ g_defdata (CF_STATIC, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal in the literal pool */ + g_defdata (CF_LITERAL, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable. Taking the address is usually not ** allowed. diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index ac19873e7..b9b5ba5fb 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -190,9 +190,9 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) case E_LOC_LITERAL: /* Literal in the literal pool */ if (Offs) { - SB_Printf (&Buf, "%s%+ld", LocalLabelName (Expr->Name), Offs); + SB_Printf (&Buf, "%s%+ld", PooledLiteralLabelName (Expr->Name), Offs); } else { - SB_Printf (&Buf, "%s", LocalLabelName (Expr->Name)); + SB_Printf (&Buf, "%s", PooledLiteralLabelName (Expr->Name)); } break; diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index eb31c7ecc..95228179d 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -99,7 +99,7 @@ static Literal* NewLiteral (const void* Buf, unsigned Len) Literal* L = xmalloc (sizeof (*L)); /* Initialize the fields */ - L->Label = GetLocalLabel (); + L->Label = GetPooledLiteralLabel (); L->RefCount = 0; L->Output = 0; SB_Init (&L->Data); @@ -130,7 +130,7 @@ static void OutputLiteral (Literal* L) TranslateLiteral (L); /* Define the label for the literal */ - g_defdatalabel (L->Label); + g_defliterallabel (L->Label); /* Output the literal data */ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)); @@ -423,12 +423,12 @@ static void OutputReadOnlyLiterals (Collection* Literals) if (C != 0) { /* This literal is part of a longer literal, merge them */ - g_aliasdatalabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L)); + g_aliasliterallabel (L->Label, C->Label, GetLiteralSize (C) - GetLiteralSize (L)); } else { /* Define the label for the literal */ - g_defdatalabel (L->Label); + g_defliterallabel (L->Label); /* Output the literal data */ g_defbytes (SB_GetConstBuf (&L->Data), SB_GetLen (&L->Data)); diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 17b0dd0fe..07acb3cbc 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -64,11 +64,15 @@ static void LoadAddress (unsigned Flags, ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static symbol or literal, load address */ + /* Static symbol, load address */ g_getimmed ((Flags | CF_STATIC) & ~CF_CONST, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal, load address */ + g_getimmed ((Flags | CF_LITERAL) & ~CF_CONST, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable. Taking the address is usually not ** allowed. @@ -183,11 +187,15 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) break; case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + /* Static variable */ g_getstatic (Flags | CF_STATIC, Expr->Name, Expr->IVal); break; + case E_LOC_LITERAL: + /* Literal in the literal pool */ + g_getstatic (Flags | CF_LITERAL, Expr->Name, Expr->IVal); + break; + case E_LOC_REGISTER: /* Register variable */ g_getstatic (Flags | CF_REGVAR, Expr->Name, Expr->IVal); From 6b64b4339579839ba919bfb1ba52a4e065d9a798 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 23 Aug 2020 01:35:11 +0800 Subject: [PATCH 305/806] Made local static data use a separated label pool from the code label pool. --- src/cc65/asmlabel.c | 29 +++++++++++++++++++++++++++++ src/cc65/asmlabel.h | 8 ++++++++ src/cc65/asmstmt.c | 2 +- src/cc65/codegen.c | 21 +++++++++++++++------ src/cc65/declare.c | 5 +++++ src/cc65/expr.c | 15 ++++++++++----- src/cc65/exprdesc.c | 20 +++++++++++++++++--- src/cc65/loadexpr.c | 10 ++++++++++ src/cc65/locals.c | 20 ++++++++++---------- src/cc65/symtab.c | 10 +++++----- 10 files changed, 110 insertions(+), 30 deletions(-) diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index 1dda96fb0..f561036ce 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -101,6 +101,35 @@ int IsLocalLabelName (const char* Name) +unsigned GetLocalDataLabel (void) +/* Get an unused local data label. Will never return zero. */ +{ + /* Number to generate unique labels */ + static unsigned NextLabel = 0; + + /* Check for an overflow */ + if (NextLabel >= 0xFFFF) { + Internal ("Local data label overflow"); + } + + /* Return the next label */ + return ++NextLabel; +} + + + +const char* LocalDataLabelName (unsigned L) +/* Make a label name from the given data label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ +{ + static char Buf[64]; + sprintf (Buf, "M%04X", L); + return Buf; +} + + + unsigned GetPooledLiteralLabel (void) /* Get an unused literal label. Will never return zero. */ { diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index 31b1f311a..a79071400 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -56,6 +56,14 @@ const char* LocalLabelName (unsigned L); int IsLocalLabelName (const char* Name); /* Return true if Name is the name of a local label */ +unsigned GetLocalDataLabel (void); +/* Get an unused local data label. Will never return zero. */ + +const char* LocalDataLabelName (unsigned L); +/* Make a label name from the given data label number. The label name will be +** created in static storage and overwritten when calling the function again. +*/ + unsigned GetPooledLiteralLabel (void); /* Get an unused literal label. Will never return zero. */ diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 9dd1f906a..bf5a0815b 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -286,7 +286,7 @@ static void ParseLabelArg (StrBuf* T, unsigned Arg attribute ((unused))) } else { - /* Add a new label symbol if we don't have one until now */ + /* Add a new C label symbol if we don't have one until now */ SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF); /* Append the label name to the buffer */ diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index b48d815d8..031e4e81a 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -107,11 +107,11 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) break; case CF_STATIC: - /* Static memory cell */ + /* Local static memory cell */ if (Offs) { - xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalLabelName (Label), Offs); + xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalDataLabelName (Label), Offs); } else { - xsprintf (Buf, sizeof (Buf), "%s", LocalLabelName (Label)); + xsprintf (Buf, sizeof (Buf), "%s", LocalDataLabelName (Label)); } break; @@ -144,6 +144,15 @@ static const char* GetLabelName (unsigned Flags, uintptr_t Label, long Offs) xsprintf (Buf, sizeof (Buf), "regbank+%u", (unsigned)((Label+Offs) & 0xFFFF)); break; + case CF_CODE: + /* Code label location */ + if (Offs) { + xsprintf (Buf, sizeof (Buf), "%s%+ld", LocalLabelName (Label), Offs); + } else { + xsprintf (Buf, sizeof (Buf), "%s", LocalLabelName (Label)); + } + break; + default: Internal ("Invalid address flags: %04X", Flags); } @@ -360,7 +369,7 @@ void g_defcodelabel (unsigned label) void g_defdatalabel (unsigned label) /* Define a local data label */ { - AddDataLine ("%s:", LocalLabelName (label)); + AddDataLine ("%s:", LocalDataLabelName (label)); } @@ -2453,11 +2462,11 @@ void g_lateadjustSP (unsigned label) /* Adjust stack based on non-immediate data */ { AddCodeLine ("pha"); - AddCodeLine ("lda %s", LocalLabelName (label)); + AddCodeLine ("lda %s", LocalDataLabelName (label)); AddCodeLine ("clc"); AddCodeLine ("adc sp"); AddCodeLine ("sta sp"); - AddCodeLine ("lda %s+1", LocalLabelName (label)); + AddCodeLine ("lda %s+1", LocalDataLabelName (label)); AddCodeLine ("adc sp+1"); AddCodeLine ("sta sp+1"); AddCodeLine ("pla"); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index e81c761ed..d6286e62d 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2204,6 +2204,11 @@ static void DefineData (ExprDesc* Expr) g_defdata (CF_REGVAR, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label location */ + g_defdata (CF_CODE, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: case E_LOC_PRIMARY: case E_LOC_EXPR: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d1dfa62ec..44b0f49be 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -92,6 +92,7 @@ static unsigned GlobalModeFlags (const ExprDesc* Expr) case E_LOC_PRIMARY: return CF_PRIMARY; case E_LOC_EXPR: return CF_EXPR; case E_LOC_LITERAL: return CF_LITERAL; + case E_LOC_CODE: return CF_CODE; default: Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags); /* NOTREACHED */ @@ -794,7 +795,7 @@ static void Primary (ExprDesc* E) NextToken (); Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND); /* output its label */ - E->Flags = E_RTYPE_RVAL | E_LOC_STATIC | E_ADDRESS_OF; + E->Flags = E_RTYPE_RVAL | E_LOC_CODE | E_ADDRESS_OF; E->Name = Entry->V.L.Label; E->Type = PointerTo (type_void); NextToken (); @@ -1545,7 +1546,8 @@ void Store (ExprDesc* Expr, const Type* StoreType) case E_LOC_STATIC: case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + case E_LOC_CODE: + /* Static variable, pooled literal or code label location */ g_putstatic (Flags, Expr->Name, Expr->IVal); break; @@ -1624,7 +1626,8 @@ static void PreInc (ExprDesc* Expr) case E_LOC_STATIC: case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + case E_LOC_CODE: + /* Static variable, pooled literal or code label location */ g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); break; @@ -1700,7 +1703,8 @@ static void PreDec (ExprDesc* Expr) case E_LOC_STATIC: case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + case E_LOC_CODE: + /* Static variable, pooled literal or code label location */ g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); break; @@ -3878,7 +3882,8 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) case E_LOC_STATIC: case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ + case E_LOC_CODE: + /* Static variable, pooled literal or code label location */ if (Gen->Tok == TOK_PLUS_ASSIGN) { g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); } else { diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index b9b5ba5fb..62738bed7 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -196,6 +196,15 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) } break; + case E_LOC_CODE: + /* Code label location */ + if (Offs) { + SB_Printf (&Buf, "%s%+ld", LocalLabelName (Expr->Name), Offs); + } else { + SB_Printf (&Buf, "%s", LocalLabelName (Expr->Name)); + } + break; + default: Internal ("Invalid location in ED_GetLabelName: 0x%04X", ED_GetLoc (Expr)); } @@ -503,9 +512,9 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_LOC_LITERAL; Sep = ','; } - if (Flags & E_RTYPE_LVAL) { - fprintf (F, "%cE_RTYPE_LVAL", Sep); - Flags &= ~E_RTYPE_LVAL; + if (Flags & E_LOC_CODE) { + fprintf (F, "%cE_LOC_CODE", Sep); + Flags &= ~E_LOC_CODE; Sep = ','; } if (Flags & E_BITFIELD) { @@ -523,6 +532,11 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_CC_SET; Sep = ','; } + if (Flags & E_RTYPE_LVAL) { + fprintf (F, "%cE_RTYPE_LVAL", Sep); + Flags &= ~E_RTYPE_LVAL; + Sep = ','; + } if (Flags & E_ADDRESS_OF) { fprintf (F, "%cE_ADDRESS_OF", Sep); Flags &= ~E_ADDRESS_OF; diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 07acb3cbc..95617f596 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -83,6 +83,11 @@ static void LoadAddress (unsigned Flags, ExprDesc* Expr) g_getimmed ((Flags | CF_REGVAR) & ~CF_CONST, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label, load address */ + g_getimmed ((Flags | CF_CODE) & ~CF_CONST, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: g_leasp (Expr->IVal); break; @@ -201,6 +206,11 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) g_getstatic (Flags | CF_REGVAR, Expr->Name, Expr->IVal); break; + case E_LOC_CODE: + /* Code label location */ + g_getstatic (Flags | CF_CODE, Expr->Name, Expr->IVal); + break; + case E_LOC_STACK: /* Value on the stack */ g_getlocal (Flags, Expr->IVal); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 255a23b6d..81d0cea09 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -64,31 +64,31 @@ static unsigned AllocLabel (void (*UseSeg) ()) -/* Switch to a segment, define a local label and return it */ +/* Switch to a segment, define a local data label and return it */ { - unsigned Label; + unsigned DataLabel; /* Switch to the segment */ UseSeg (); /* Define the variable label */ - Label = GetLocalLabel (); - g_defdatalabel (Label); + DataLabel = GetLocalDataLabel (); + g_defdatalabel (DataLabel); /* Return the label */ - return Label; + return DataLabel; } -static void AllocStorage (unsigned Label, void (*UseSeg) (), unsigned Size) -/* Reserve Size bytes of BSS storage prefixed by a local label. */ +static void AllocStorage (unsigned DataLabel, void (*UseSeg) (), unsigned Size) +/* Reserve Size bytes of BSS storage prefixed by a local data label. */ { /* Switch to the segment */ UseSeg (); /* Define the variable label */ - g_defdatalabel (Label); + g_defdatalabel (DataLabel); /* Reserve space for the data */ g_res (Size); @@ -301,7 +301,7 @@ static void ParseAutoDecl (Declaration* Decl) Decl->StorageClass = (Decl->StorageClass & ~SC_AUTO) | SC_STATIC; /* Generate a label, but don't define it */ - DataLabel = GetLocalLabel (); + DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ Sym = AddLocalSym (Decl->Ident, Decl->Type, Decl->StorageClass, DataLabel); @@ -380,7 +380,7 @@ static void ParseStaticDecl (Declaration* Decl) unsigned Size; /* Generate a label, but don't define it */ - unsigned DataLabel = GetLocalLabel (); + unsigned DataLabel = GetLocalDataLabel (); /* Add the symbol to the symbol table. */ SymEntry* Sym = AddLocalSym (Decl->Ident, Decl->Type, diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 6a02fc0ce..10dc0d3ff 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -931,7 +931,7 @@ DefOrRef* AddDefOrRef (SymEntry* E, unsigned Flags) DOR->Flags = Flags; DOR->StackPtr = StackPtr; DOR->Depth = CollCount (&CurrentFunc->LocalsBlockStack); - DOR->LateSP_Label = GetLocalLabel (); + DOR->LateSP_Label = GetLocalDataLabel (); return DOR; } @@ -953,7 +953,7 @@ unsigned short FindSPAdjustment (const char* Name) SymEntry* AddLabelSym (const char* Name, unsigned Flags) -/* Add a goto label to the label table */ +/* Add a C goto label to the label table */ { unsigned i; DefOrRef *DOR, *NewDOR; @@ -1012,7 +1012,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags) /* Optimizer will need the information about the value of SP adjustment ** later, so let's preserve it. */ - E = NewSymEntry (LocalLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); + E = NewSymEntry (LocalDataLabelName (DOR->LateSP_Label), SC_SPADJUSTMENT); E->V.SPAdjustment = StackPtr - DOR->StackPtr; AddSymEntry (SPAdjustTab, E); } @@ -1134,9 +1134,9 @@ SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs Entry->V.L.Label = Offs; SymSetAsmName (Entry); } else if ((Flags & SC_STATIC) == SC_STATIC) { - /* Generate the assembler name from the label number */ + /* Generate the assembler name from the data label number */ Entry->V.L.Label = Offs; - Entry->AsmName = xstrdup (LocalLabelName (Entry->V.L.Label)); + Entry->AsmName = xstrdup (LocalDataLabelName (Entry->V.L.Label)); } else { Internal ("Invalid flags in AddLocalSym: %04X", Flags); } From 28de3423eb205a2f4125c61e47c256c6c4010cff Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 23 Aug 2020 01:35:13 +0800 Subject: [PATCH 306/806] Merged some switch cases in code generation subroutines. --- src/cc65/expr.c | 73 ++++++++++++------------------------------------- 1 file changed, 17 insertions(+), 56 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 44b0f49be..38df16faf 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1540,19 +1540,13 @@ void Store (ExprDesc* Expr, const Type* StoreType) break; case E_LOC_GLOBAL: - /* Global variable */ - g_putstatic (Flags, Expr->Name, Expr->IVal); - break; - case E_LOC_STATIC: + case E_LOC_REGISTER: case E_LOC_LITERAL: case E_LOC_CODE: - /* Static variable, pooled literal or code label location */ - g_putstatic (Flags, Expr->Name, Expr->IVal); - break; - - case E_LOC_REGISTER: - /* Register variable */ + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ g_putstatic (Flags, Expr->Name, Expr->IVal); break; @@ -1620,19 +1614,13 @@ static void PreInc (ExprDesc* Expr) break; case E_LOC_GLOBAL: - /* Global variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - case E_LOC_STATIC: + case E_LOC_REGISTER: case E_LOC_LITERAL: case E_LOC_CODE: - /* Static variable, pooled literal or code label location */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_REGISTER: - /* Register variable */ + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); break; @@ -1697,19 +1685,13 @@ static void PreDec (ExprDesc* Expr) break; case E_LOC_GLOBAL: - /* Global variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - case E_LOC_STATIC: + case E_LOC_REGISTER: case E_LOC_LITERAL: case E_LOC_CODE: - /* Static variable, pooled literal or code label location */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_REGISTER: - /* Register variable */ + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); break; @@ -3863,36 +3845,15 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) switch (ED_GetLoc (Expr)) { case E_LOC_ABS: - /* Absolute numeric addressed variable */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - case E_LOC_GLOBAL: - /* Global variable */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - case E_LOC_STATIC: + case E_LOC_REGISTER: case E_LOC_LITERAL: case E_LOC_CODE: - /* Static variable, pooled literal or code label location */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_REGISTER: - /* Register variable */ + /* Absolute numeric addressed variable, global variable, local + ** static variable, register variable, pooled literal or code + ** label location. + */ if (Gen->Tok == TOK_PLUS_ASSIGN) { g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); } else { From 23795044498cce8f4b821c910f17db33445f99bd Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 20:31:54 +0800 Subject: [PATCH 307/806] Fixed AND/OR in certain cases where the 'E_NEED_TEST' flag set for usage only in subexpressions should be cleared. --- src/cc65/expr.c | 6 ++++++ src/cc65/exprdesc.h | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 38df16faf..4071728ac 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3223,6 +3223,9 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Load the value */ LoadExpr (CF_FORCECHAR, Expr); + /* Clear the test flag */ + ED_RequireNoTest (Expr); + /* Remember that the jump is used */ HasFalseJump = 1; @@ -3356,6 +3359,9 @@ static void hieOr (ExprDesc *Expr) /* Get first expr */ LoadExpr (CF_FORCECHAR, Expr); + /* Clear the test flag */ + ED_RequireNoTest (Expr); + if (HasTrueJump == 0) { /* Get a label that we will use for true expressions */ TrueLab = GetLocalLabel(); diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index f04be6a8d..c435b3c38 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -364,6 +364,16 @@ INLINE void ED_RequireTest (ExprDesc* Expr) # define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) #endif +#if defined(HAVE_INLINE) +INLINE void ED_RequireNoTest (ExprDesc* Expr) +/* Mark the expression not for a test. */ +{ + Expr->Flags &= ~E_NEED_TEST; +} +#else +# define ED_RequireNoTest(Expr) do { (Expr)->Flags &= ~E_NEED_TEST; } while (0) +#endif + #if defined(HAVE_INLINE) INLINE int ED_GetNeeds (const ExprDesc* Expr) /* Get flags about what the expression needs. */ From fb6bc275bc500aeb5705cf67fb2955633aec11a7 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 20:33:58 +0800 Subject: [PATCH 308/806] Fixed evaluation flags propagation to subexpressions in assignments and function calls. --- src/cc65/assignment.c | 3 ++- src/cc65/expr.c | 7 ++++--- src/cc65/stdfunc.c | 24 +++++++++++++----------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 1ada3af0b..09a10a642 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -136,10 +136,11 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) void Assignment (ExprDesc* Expr) /* Parse an assignment */ { - ExprDesc Expr2; Type* ltype = Expr->Type; + ExprDesc Expr2; ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* We must have an lvalue for an assignment */ if (ED_IsRVal (Expr)) { diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 4071728ac..b91fab8a8 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -332,7 +332,7 @@ static void WarnConstCompareResult (const ExprDesc* Expr) -static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) +static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Parse a function parameter list, and pass the arguments to the called ** function. Depending on several criteria, this may be done by just pushing ** into each parameter separately, or creating the parameter frame once, and @@ -391,9 +391,10 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) /* Parse the actual argument list */ while (CurTok.Tok != TOK_RPAREN) { - unsigned Flags; + unsigned Flags; /* Code generator flags, not expression flags */ ExprDesc Expr; ED_Init (&Expr); + Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; /* Count arguments */ ++PushedCount; @@ -609,7 +610,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Parse the parameter list */ - ParamSize = FunctionParamList (Func, IsFastcall); + ParamSize = FunctionParamList (Func, IsFastcall, Expr); /* We need the closing paren here */ ConsumeRParen (); diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 8335177bb..74579212e 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -141,7 +141,7 @@ static long ArrayElementCount (const ArgDesc* Arg) -static void ParseArg (ArgDesc* Arg, Type* Type) +static void ParseArg (ArgDesc* Arg, Type* Type, ExprDesc* Expr) /* Parse one argument but do not push it onto the stack. Make all fields in ** Arg valid. */ @@ -154,6 +154,7 @@ static void ParseArg (ArgDesc* Arg, Type* Type) /* Init expression */ ED_Init (&Arg->Expr); + Arg->Expr.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Read the expression we're going to pass to the function */ MarkedExprWithCheck (hie1, &Arg->Expr); @@ -221,14 +222,14 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) int Offs; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); ConsumeComma (); /* Argument #2 */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); g_push (Arg2.Flags, Arg2.Expr.IVal); GetCodePos (&Arg2.End); ParamSize += SizeOf (Arg2Type); @@ -239,7 +240,7 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg3, Arg3Type); + ParseArg (&Arg3, Arg3Type, Expr); if (Arg3.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg3.Expr); } @@ -562,7 +563,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) unsigned Label; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); @@ -571,7 +572,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Argument #2. This argument is special in that we will call another ** function if it is a constant zero. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) { /* Don't call memset, call bzero instead */ MemSet = 0; @@ -588,7 +589,7 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg3, Arg3Type); + ParseArg (&Arg3, Arg3Type, Expr); if (Arg3.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg3.Expr); } @@ -790,13 +791,13 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) Arg2Type[1].C = T_CHAR | T_QUAL_CONST; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); ParamSize += SizeOf (Arg1Type); ConsumeComma (); /* Argument #2. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); /* Since strcmp is a fastcall function, we must load the ** arg into the primary if it is not already there. This parameter is @@ -990,7 +991,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) Arg2Type[1].C = T_CHAR | T_QUAL_CONST; /* Argument #1 */ - ParseArg (&Arg1, Arg1Type); + ParseArg (&Arg1, Arg1Type, Expr); g_push (Arg1.Flags, Arg1.Expr.IVal); GetCodePos (&Arg1.End); ParamSize += SizeOf (Arg1Type); @@ -1001,7 +1002,7 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) ** also ignored for the calculation of the parameter size, since it is ** not passed via the stack. */ - ParseArg (&Arg2, Arg2Type); + ParseArg (&Arg2, Arg2Type, Expr); if (Arg2.Flags & CF_CONST) { LoadExpr (CF_NONE, &Arg2.Expr); } @@ -1184,6 +1185,7 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) unsigned L; ED_Init (&Arg); + Arg.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Setup the argument type string */ ArgType[1].C = T_CHAR | T_QUAL_CONST; From 2a3d996077453c3171d67223271bd3ed8aaaf926 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 20:41:36 +0800 Subject: [PATCH 309/806] Improved test case for PR #1220. --- test/ref/pr1220.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c index 4f88ada73..eb9f7c7f5 100644 --- a/test/ref/pr1220.c +++ b/test/ref/pr1220.c @@ -10,14 +10,14 @@ /* test AND/OR in ternary context */ #define CONTEXT_B(x) do {\ - s = 0, flags = 0,\ - (x ? printf("%3d %2X: 1\n", s, flags) : printf("%3d %2X: 0\n", s, flags));\ + s = 0, flags = 0, t = (x ? 42 : -42),\ + printf("%3d %2X: %d\n", s, flags, t);\ } while (0) int s, t; unsigned flags; -int f(x) +int f(int x) /* The call to this function should be and only be skipped strictly according to ** the short-circuit evaluation rule. */ @@ -31,8 +31,8 @@ int f(x) #define _B f(b) #define _C f(c) #define _D f(d) -#define _T (f(0), -1) -#define _F (f(-1), 0) +#define _T (f(0), 256) +#define _F (f(256), 0) void f0() /* constant short-circuit */ @@ -176,7 +176,7 @@ void f5(int a, int b, int c, int d) void f0_B() /* constant short-circuit */ { - printf("f0()\n"); + printf("f0_B()\n"); CONTEXT_B(_T && _T && _T); CONTEXT_B(_F && _F && _F); @@ -205,7 +205,7 @@ void f0_B() void f1_B(int a, int b, int c) /* AND */ { - printf("f1(%d, %d, %d)\n", a, b, c); + printf("f1_B(%d, %d, %d)\n", a, b, c); CONTEXT_B(_A && _B && _C); @@ -227,7 +227,7 @@ void f1_B(int a, int b, int c) void f2_B(int a, int b, int c) /* OR */ { - printf("f2(%d, %d, %d)\n", a, b, c); + printf("f2_B(%d, %d, %d)\n", a, b, c); CONTEXT_B(_A || _B || _C); @@ -249,7 +249,7 @@ void f2_B(int a, int b, int c) void f3_B(int a, int b, int c, int d) /* AND and OR */ { - printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + printf("f3_B(%d, %d, %d, %d)\n", a, b, c, d); CONTEXT_B(_A && _B || _C && _D); CONTEXT_B(_T && _T || _C && _D); @@ -271,7 +271,7 @@ void f3_B(int a, int b, int c, int d) void f4_B(int a, int b, int c, int d) /* AND as top-level expression inside OR context */ { - printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + printf("f4_B(%d, %d, %d, %d)\n", a, b, c, d); CONTEXT_B((_A && _B) || (_C && _D)); CONTEXT_B((_T && _T) || (_C && _D)); @@ -293,7 +293,7 @@ void f4_B(int a, int b, int c, int d) void f5_B(int a, int b, int c, int d) /* OR as top-level expression inside AND context */ { - printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + printf("f5_B(%d, %d, %d, %d)\n", a, b, c, d); CONTEXT_B((_A || _B) && (_C || _D)); CONTEXT_B((_F || _F) && (_C || _D)); From f45d2515eb9191f21bc21c8449e407ebfb3ee415 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 01:56:57 +0800 Subject: [PATCH 310/806] Fixed OptPush1 in case later code would rely on that pushax zeroes register Y. --- src/cc65/coptpush.c | 19 +++++++++++++++++-- src/cc65/coptpush.h | 6 +++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/cc65/coptpush.c b/src/cc65/coptpush.c index 5c3daffca..34d794c64 100644 --- a/src/cc65/coptpush.c +++ b/src/cc65/coptpush.c @@ -56,12 +56,14 @@ unsigned OptPush1 (CodeSeg* S) ** ** ldy #xx+2 ** jsr pushwysp +** ldy #$00 ; present if later code expects Y = 0 ** -** saving 3 bytes and several cycles. +** saving several cycles. */ { unsigned I; unsigned Changes = 0; + unsigned R; /* Walk over the entries */ I = 0; @@ -79,7 +81,7 @@ unsigned OptPush1 (CodeSeg* S) (L[1] = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (L[1]) && CE_IsCallTo (L[1], "pushax") && - !RegAXUsed (S, I+2)) { + ((R = (GetRegInfo (S, I+2, REG_AXY))) & REG_AX) == 0) { /* Insert new code behind the pushax */ const char* Arg; @@ -94,12 +96,25 @@ unsigned OptPush1 (CodeSeg* S) X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushwysp", 0, L[1]->LI); CS_InsertEntry (S, X, I+3); + /* pushax sets Y = 0 and following code might rely on this */ + if ((R & REG_Y) != 0) { + /* ldy #0 */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (0), 0, L[1]->LI); + CS_InsertEntry (S, X, I+4); + } + /* Delete the old code */ CS_DelEntries (S, I, 2); /* Remember, we had changes */ ++Changes; + /* Skip the handled lines */ + if ((R & REG_Y) == 0) { + ++I; + } else { + I += 2; + } } /* Next entry */ diff --git a/src/cc65/coptpush.h b/src/cc65/coptpush.h index 0d637e824..aa5548517 100644 --- a/src/cc65/coptpush.h +++ b/src/cc65/coptpush.h @@ -52,16 +52,16 @@ unsigned OptPush1 (CodeSeg* S); /* Given a sequence ** -** ldy #xx ** jsr ldaxysp ** jsr pushax ** -** If a/x are not used later, replace that by +** If a/x are not used later, and Y is known, replace that by ** ** ldy #xx+2 ** jsr pushwysp +** ldy #$00 ; present if later code expects Y = 0 ** -** saving 3 bytes and several cycles. +** saving several cycles. */ unsigned OptPush2 (CodeSeg* S); From ae340703f2fa517ee69a6cf7a73ac24b0de857a6 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 01:57:01 +0800 Subject: [PATCH 311/806] OptDupLoads shouldn't silently do optimizations. --- src/cc65/coptind.c | 17 +++++++++++++---- src/cc65/opcodes.h | 5 ++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 04deb0119..811adff04 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -1147,8 +1147,9 @@ unsigned OptDupLoads (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Assume we won't delete the entry */ + /* Assume we won't delete or replace the entry */ int Delete = 0; + opc_t NewOPC = OP65_INVALID; /* Get a pointer to the input registers of the insn */ const RegContents* In = &E->RI->In; @@ -1218,7 +1219,7 @@ unsigned OptDupLoads (CodeSeg* S) E->AM != AM65_ABSY && E->AM != AM65_ZPY) { /* Use the A register instead */ - CE_ReplaceOPC (E, OP65_STA); + NewOPC = OP65_STA; } break; @@ -1242,11 +1243,11 @@ unsigned OptDupLoads (CodeSeg* S) */ } else if (RegValIsKnown (In->RegY)) { if (In->RegY == In->RegA) { - CE_ReplaceOPC (E, OP65_STA); + NewOPC = OP65_STA; } else if (In->RegY == In->RegX && E->AM != AM65_ABSX && E->AM != AM65_ZPX) { - CE_ReplaceOPC (E, OP65_STX); + NewOPC = OP65_STX; } } break; @@ -1319,6 +1320,14 @@ unsigned OptDupLoads (CodeSeg* S) } else { + if (NewOPC != OP65_INVALID) { + /* Replace the opcode */ + CE_ReplaceOPC (E, NewOPC); + + /* Remember, we had changes */ + ++Changes; + } + /* Next entry */ ++I; diff --git a/src/cc65/opcodes.h b/src/cc65/opcodes.h index bdb0c28a2..24a289c26 100644 --- a/src/cc65/opcodes.h +++ b/src/cc65/opcodes.h @@ -128,7 +128,10 @@ typedef enum { OP65_TYA, /* Number of opcodes available */ - OP65_COUNT + OP65_COUNT, + + /* Invalid opcode */ + OP65_INVALID = OP65_COUNT, } opc_t; /* 65XX addressing modes */ From 761d00b7dc873b74bb7c99a2be716098ee1e0f0a Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 06:16:28 +0800 Subject: [PATCH 312/806] Ignore running removed old optimization steps. Neither list them in help info. --- src/cc65/codeopt.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 498aa0ff2..b3b0d064b 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -1029,7 +1029,9 @@ void ListOptSteps (FILE* F) fprintf (F, "any\n"); for (I = 0; I < OPTFUNC_COUNT; ++I) { - fprintf (F, "%s\n", OptFuncs[I]->Name); + if (OptFuncs[I]->Func != 0) { + fprintf (F, "%s\n", OptFuncs[I]->Name); + } } } @@ -1190,10 +1192,10 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max) { unsigned Changes, C; - /* Don't run the function if it is disabled or if it is prohibited by the + /* Don't run the function if it is removed, disabled or prohibited by the ** code size factor */ - if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) { + if (F->Func == 0 || F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) { return 0; } From 29d1999947f0fb89e08b1f696dd6f97746659d13 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 06:16:32 +0800 Subject: [PATCH 313/806] Rearranged the OptFunc's in alphabetic order. --- src/cc65/codeopt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index b3b0d064b..dfb42e5db 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -796,9 +796,6 @@ static OptFunc DOptLoad2 = { OptLoad2, "OptLoad2", 200, 0, static OptFunc DOptLoad3 = { OptLoad3, "OptLoad3", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX1 = { OptNegAX1, "OptNegAX1", 165, 0, 0, 0, 0, 0 }; static OptFunc DOptNegAX2 = { OptNegAX2, "OptNegAX2", 200, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; -static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPrecalc = { OptPrecalc, "OptPrecalc", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPtrLoad1 = { OptPtrLoad1, "OptPtrLoad1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPtrLoad2 = { OptPtrLoad2, "OptPtrLoad2", 100, 0, 0, 0, 0, 0 }; @@ -822,6 +819,9 @@ static OptFunc DOptPtrStore3 = { OptPtrStore3, "OptPtrStore3", 100, 0, static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, 0, 0, 0, 0 }; static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; static OptFunc DOptPushPop = { OptPushPop, "OptPushPop", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; +static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift1 = { OptShift1, "OptShift1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift2 = { OptShift2, "OptShift2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift3 = { OptShift3, "OptShift3", 17, 0, 0, 0, 0, 0 }; From 676b14429dcb1357602639ea7ed455fcc48c73df Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 06:16:36 +0800 Subject: [PATCH 314/806] Better opt chance for certain optimization steps e.g. OptPtrStore1 etc. --- src/cc65/codeopt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index dfb42e5db..69ac35005 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -1241,10 +1241,10 @@ static unsigned RunOptGroup1 (CodeSeg* S) Changes += RunOptFunc (S, &DOptGotoSPAdj, 1); Changes += RunOptFunc (S, &DOptStackPtrOps, 5); + Changes += RunOptFunc (S, &DOptAdd3, 1); /* Before OptPtrLoad5! */ Changes += RunOptFunc (S, &DOptPtrStore1, 1); Changes += RunOptFunc (S, &DOptPtrStore2, 1); Changes += RunOptFunc (S, &DOptPtrStore3, 1); - Changes += RunOptFunc (S, &DOptAdd3, 1); /* Before OptPtrLoad5! */ Changes += RunOptFunc (S, &DOptPtrLoad1, 1); Changes += RunOptFunc (S, &DOptPtrLoad2, 1); Changes += RunOptFunc (S, &DOptPtrLoad3, 1); @@ -1332,7 +1332,6 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptAdd6, 1); C += RunOptFunc (S, &DOptJumpCascades, 1); C += RunOptFunc (S, &DOptDeadJumps, 1); - C += RunOptFunc (S, &DOptRTS, 1); C += RunOptFunc (S, &DOptDeadCode, 1); C += RunOptFunc (S, &DOptBoolTrans, 1); C += RunOptFunc (S, &DOptJumpTarget1, 1); @@ -1487,11 +1486,16 @@ static unsigned RunOptGroup7 (CodeSeg* S) /* Adjust branch distances */ Changes += RunOptFunc (S, &DOptBranchDist, 3); - /* Replace conditional branches to RTS. If we had changes, we must run dead - ** code elimination again, since the change may have introduced dead code. - */ + /* Replace conditional branches to RTS */ C = RunOptFunc (S, &DOptRTSJumps2, 1); + + /* Replace JSR followed by RTS to JMP */ + C += RunOptFunc (S, &DOptRTS, 1); + Changes += C; + /* If we had changes, we must run dead code elimination again, + ** since the changes may have introduced dead code. + */ if (C) { Changes += RunOptFunc (S, &DOptDeadCode, 1); } From 44b719d957a9f02c44207a4f922012405fff0200 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Thu, 3 Sep 2020 09:16:51 +0200 Subject: [PATCH 315/806] Change line endings from CRLF to LF test/ref/pr1220.c was somehow added with CRLFs. Other files use just LF. --- test/ref/pr1220.c | 720 +++++++++++++++++++++++----------------------- 1 file changed, 360 insertions(+), 360 deletions(-) diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c index eb9f7c7f5..0d5c1d800 100644 --- a/test/ref/pr1220.c +++ b/test/ref/pr1220.c @@ -1,360 +1,360 @@ -/* PR #1220 - test constant ternary, AND and OR */ - -#include - -/* test AND/OR, results as integers */ -#define CONTEXT_A(x) do {\ - s = 0, flags = 0, t = (x),\ - printf("%3d %2X: %d\n", s, flags, t);\ - } while (0) - -/* test AND/OR in ternary context */ -#define CONTEXT_B(x) do {\ - s = 0, flags = 0, t = (x ? 42 : -42),\ - printf("%3d %2X: %d\n", s, flags, t);\ - } while (0) - -int s, t; -unsigned flags; - -int f(int x) -/* The call to this function should be and only be skipped strictly according to -** the short-circuit evaluation rule. -*/ -{ - flags |= (x != 0) << s; - ++s; - return x; -} - -#define _A f(a) -#define _B f(b) -#define _C f(c) -#define _D f(d) -#define _T (f(0), 256) -#define _F (f(256), 0) - -void f0() -/* constant short-circuit */ -{ - printf("f0()\n"); - - CONTEXT_A(_T && _T && _T); - CONTEXT_A(_F && _F && _F); - - CONTEXT_A(_T || _T || _T); - CONTEXT_A(_F || _F || _F); - - CONTEXT_A(_T && _T || _T && _T); - CONTEXT_A(_F && _F || _F && _F); - CONTEXT_A(_T && _F || _T && _F); - CONTEXT_A(_F && _T || _F && _T); - - CONTEXT_A((_T && _T) || (_T && _T)); - CONTEXT_A((_F && _F) || (_F && _F)); - CONTEXT_A((_T && _F) || (_T && _F)); - CONTEXT_A((_F && _T) || (_F && _T)); - - CONTEXT_A((_T || _T) && (_T || _T)); - CONTEXT_A((_F || _F) && (_F || _F)); - CONTEXT_A((_T || _F) && (_T || _F)); - CONTEXT_A((_F || _T) && (_F || _T)); - - printf("\n"); -} - -void f1(int a, int b, int c) -/* AND */ -{ - printf("f1(%d, %d, %d)\n", a, b, c); - - CONTEXT_A(_A && _B && _C); - - CONTEXT_A(_T && _B && _C); - CONTEXT_A(_A && _T && _C); - CONTEXT_A(_A && _B && _T); - - CONTEXT_A(_F && _B && _C); - CONTEXT_A(_A && _F && _C); - CONTEXT_A(_A && _B && _F); - - CONTEXT_A(_T && _T && _C); - CONTEXT_A(_A && _T && _T); - CONTEXT_A(_T && _B && _T); - - printf("\n"); -} - -void f2(int a, int b, int c) -/* OR */ -{ - printf("f2(%d, %d, %d)\n", a, b, c); - - CONTEXT_A(_A || _B || _C); - - CONTEXT_A(_T || _B || _C); - CONTEXT_A(_A || _T || _C); - CONTEXT_A(_A || _B || _T); - - CONTEXT_A(_F || _B || _C); - CONTEXT_A(_A || _F || _C); - CONTEXT_A(_A || _B || _F); - - CONTEXT_A(_F || _F || _C); - CONTEXT_A(_A || _F || _F); - CONTEXT_A(_F || _B || _F); - - printf("\n"); -} - -void f3(int a, int b, int c, int d) -/* AND and OR */ -{ - printf("f3(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_A(_A && _B || _C && _D); - CONTEXT_A(_T && _T || _C && _D); - CONTEXT_A(_A && _B || _T && _T); - - CONTEXT_A(_T && _B || _C && _D); - CONTEXT_A(_A && _T || _C && _D); - CONTEXT_A(_A && _B || _T && _D); - CONTEXT_A(_A && _B || _C && _T); - - CONTEXT_A(_F && _B || _C && _D); - CONTEXT_A(_A && _F || _C && _D); - CONTEXT_A(_A && _B || _F && _D); - CONTEXT_A(_A && _B || _C && _F); - - printf("\n"); -} - -void f4(int a, int b, int c, int d) -/* AND as top-level expression inside OR context */ -{ - printf("f4(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_A((_A && _B) || (_C && _D)); - CONTEXT_A((_T && _T) || (_C && _D)); - CONTEXT_A((_A && _B) || (_T && _T)); - - CONTEXT_A((_T && _B) || (_C && _D)); - CONTEXT_A((_A && _T) || (_C && _D)); - CONTEXT_A((_A && _B) || (_T && _D)); - CONTEXT_A((_A && _B) || (_C && _T)); - - CONTEXT_A((_F && _B) || (_C && _D)); - CONTEXT_A((_A && _F) || (_C && _D)); - CONTEXT_A((_A && _B) || (_F && _D)); - CONTEXT_A((_A && _B) || (_C && _F)); - - printf("\n"); -} - -void f5(int a, int b, int c, int d) -/* OR as top-level expression inside AND context */ -{ - printf("f5(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_A((_A || _B) && (_C || _D)); - CONTEXT_A((_F || _F) && (_C || _D)); - CONTEXT_A((_A || _B) && (_F || _F)); - - CONTEXT_A((_T || _B) && (_C || _D)); - CONTEXT_A((_A || _T) && (_C || _D)); - CONTEXT_A((_A || _B) && (_T || _D)); - CONTEXT_A((_A || _B) && (_C || _T)); - - CONTEXT_A((_F || _B) && (_C || _D)); - CONTEXT_A((_A || _F) && (_C || _D)); - CONTEXT_A((_A || _B) && (_F || _D)); - CONTEXT_A((_A || _B) && (_C || _F)); - - printf("\n"); -} - -void f0_B() -/* constant short-circuit */ -{ - printf("f0_B()\n"); - - CONTEXT_B(_T && _T && _T); - CONTEXT_B(_F && _F && _F); - - CONTEXT_B(_T || _T || _T); - CONTEXT_B(_F || _F || _F); - - CONTEXT_B(_T && _T || _T && _T); - CONTEXT_B(_F && _F || _F && _F); - CONTEXT_B(_T && _F || _T && _F); - CONTEXT_B(_F && _T || _F && _T); - - CONTEXT_B((_T && _T) || (_T && _T)); - CONTEXT_B((_F && _F) || (_F && _F)); - CONTEXT_B((_T && _F) || (_T && _F)); - CONTEXT_B((_F && _T) || (_F && _T)); - - CONTEXT_B((_T || _T) && (_T || _T)); - CONTEXT_B((_F || _F) && (_F || _F)); - CONTEXT_B((_T || _F) && (_T || _F)); - CONTEXT_B((_F || _T) && (_F || _T)); - - printf("\n"); -} - -void f1_B(int a, int b, int c) -/* AND */ -{ - printf("f1_B(%d, %d, %d)\n", a, b, c); - - CONTEXT_B(_A && _B && _C); - - CONTEXT_B(_T && _B && _C); - CONTEXT_B(_A && _T && _C); - CONTEXT_B(_A && _B && _T); - - CONTEXT_B(_F && _B && _C); - CONTEXT_B(_A && _F && _C); - CONTEXT_B(_A && _B && _F); - - CONTEXT_B(_T && _T && _C); - CONTEXT_B(_A && _T && _T); - CONTEXT_B(_T && _B && _T); - - printf("\n"); -} - -void f2_B(int a, int b, int c) -/* OR */ -{ - printf("f2_B(%d, %d, %d)\n", a, b, c); - - CONTEXT_B(_A || _B || _C); - - CONTEXT_B(_T || _B || _C); - CONTEXT_B(_A || _T || _C); - CONTEXT_B(_A || _B || _T); - - CONTEXT_B(_F || _B || _C); - CONTEXT_B(_A || _F || _C); - CONTEXT_B(_A || _B || _F); - - CONTEXT_B(_F || _F || _C); - CONTEXT_B(_A || _F || _F); - CONTEXT_B(_F || _B || _F); - - printf("\n"); -} - -void f3_B(int a, int b, int c, int d) -/* AND and OR */ -{ - printf("f3_B(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_B(_A && _B || _C && _D); - CONTEXT_B(_T && _T || _C && _D); - CONTEXT_B(_A && _B || _T && _T); - - CONTEXT_B(_T && _B || _C && _D); - CONTEXT_B(_A && _T || _C && _D); - CONTEXT_B(_A && _B || _T && _D); - CONTEXT_B(_A && _B || _C && _T); - - CONTEXT_B(_F && _B || _C && _D); - CONTEXT_B(_A && _F || _C && _D); - CONTEXT_B(_A && _B || _F && _D); - CONTEXT_B(_A && _B || _C && _F); - - printf("\n"); -} - -void f4_B(int a, int b, int c, int d) -/* AND as top-level expression inside OR context */ -{ - printf("f4_B(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_B((_A && _B) || (_C && _D)); - CONTEXT_B((_T && _T) || (_C && _D)); - CONTEXT_B((_A && _B) || (_T && _T)); - - CONTEXT_B((_T && _B) || (_C && _D)); - CONTEXT_B((_A && _T) || (_C && _D)); - CONTEXT_B((_A && _B) || (_T && _D)); - CONTEXT_B((_A && _B) || (_C && _T)); - - CONTEXT_B((_F && _B) || (_C && _D)); - CONTEXT_B((_A && _F) || (_C && _D)); - CONTEXT_B((_A && _B) || (_F && _D)); - CONTEXT_B((_A && _B) || (_C && _F)); - - printf("\n"); -} - -void f5_B(int a, int b, int c, int d) -/* OR as top-level expression inside AND context */ -{ - printf("f5_B(%d, %d, %d, %d)\n", a, b, c, d); - - CONTEXT_B((_A || _B) && (_C || _D)); - CONTEXT_B((_F || _F) && (_C || _D)); - CONTEXT_B((_A || _B) && (_F || _F)); - - CONTEXT_B((_T || _B) && (_C || _D)); - CONTEXT_B((_A || _T) && (_C || _D)); - CONTEXT_B((_A || _B) && (_T || _D)); - CONTEXT_B((_A || _B) && (_C || _T)); - - CONTEXT_B((_F || _B) && (_C || _D)); - CONTEXT_B((_A || _F) && (_C || _D)); - CONTEXT_B((_A || _B) && (_F || _D)); - CONTEXT_B((_A || _B) && (_C || _F)); - - printf("\n"); -} - -int main() -{ - f0(); - - f1(0, 0, 0); - f2(0, 0, 0); - f3(0, 0, 0, 0); - f4(0, 0, 0, 0); - f5(0, 0, 0, 0); - - f1(1, 1, 1); - f2(1, 1, 1); - f3(1, 1, 1, 1); - f4(1, 1, 1, 1); - f5(1, 1, 1, 1); - - f3(1, 0, 1, 0); - f4(1, 0, 1, 0); - f5(1, 0, 1, 0); - f3(0, 1, 0, 1); - f4(0, 1, 0, 1); - f5(0, 1, 0, 1); - - f0_B(); - - f1_B(0, 0, 0); - f2_B(0, 0, 0); - f3_B(0, 0, 0, 0); - f4_B(0, 0, 0, 0); - f5_B(0, 0, 0, 0); - - f1_B(1, 1, 1); - f2_B(1, 1, 1); - f3_B(1, 1, 1, 1); - f4_B(1, 1, 1, 1); - f5_B(1, 1, 1, 1); - - f3_B(1, 0, 1, 0); - f4_B(1, 0, 1, 0); - f5_B(1, 0, 1, 0); - f3_B(0, 1, 0, 1); - f4_B(0, 1, 0, 1); - f5_B(0, 1, 0, 1); - - return 0; -} +/* PR #1220 - test constant ternary, AND and OR */ + +#include + +/* test AND/OR, results as integers */ +#define CONTEXT_A(x) do {\ + s = 0, flags = 0, t = (x),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +/* test AND/OR in ternary context */ +#define CONTEXT_B(x) do {\ + s = 0, flags = 0, t = (x ? 42 : -42),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +int s, t; +unsigned flags; + +int f(int x) +/* The call to this function should be and only be skipped strictly according to +** the short-circuit evaluation rule. +*/ +{ + flags |= (x != 0) << s; + ++s; + return x; +} + +#define _A f(a) +#define _B f(b) +#define _C f(c) +#define _D f(d) +#define _T (f(0), 256) +#define _F (f(256), 0) + +void f0() +/* constant short-circuit */ +{ + printf("f0()\n"); + + CONTEXT_A(_T && _T && _T); + CONTEXT_A(_F && _F && _F); + + CONTEXT_A(_T || _T || _T); + CONTEXT_A(_F || _F || _F); + + CONTEXT_A(_T && _T || _T && _T); + CONTEXT_A(_F && _F || _F && _F); + CONTEXT_A(_T && _F || _T && _F); + CONTEXT_A(_F && _T || _F && _T); + + CONTEXT_A((_T && _T) || (_T && _T)); + CONTEXT_A((_F && _F) || (_F && _F)); + CONTEXT_A((_T && _F) || (_T && _F)); + CONTEXT_A((_F && _T) || (_F && _T)); + + CONTEXT_A((_T || _T) && (_T || _T)); + CONTEXT_A((_F || _F) && (_F || _F)); + CONTEXT_A((_T || _F) && (_T || _F)); + CONTEXT_A((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1(int a, int b, int c) +/* AND */ +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A && _B && _C); + + CONTEXT_A(_T && _B && _C); + CONTEXT_A(_A && _T && _C); + CONTEXT_A(_A && _B && _T); + + CONTEXT_A(_F && _B && _C); + CONTEXT_A(_A && _F && _C); + CONTEXT_A(_A && _B && _F); + + CONTEXT_A(_T && _T && _C); + CONTEXT_A(_A && _T && _T); + CONTEXT_A(_T && _B && _T); + + printf("\n"); +} + +void f2(int a, int b, int c) +/* OR */ +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A || _B || _C); + + CONTEXT_A(_T || _B || _C); + CONTEXT_A(_A || _T || _C); + CONTEXT_A(_A || _B || _T); + + CONTEXT_A(_F || _B || _C); + CONTEXT_A(_A || _F || _C); + CONTEXT_A(_A || _B || _F); + + CONTEXT_A(_F || _F || _C); + CONTEXT_A(_A || _F || _F); + CONTEXT_A(_F || _B || _F); + + printf("\n"); +} + +void f3(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A(_A && _B || _C && _D); + CONTEXT_A(_T && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _T); + + CONTEXT_A(_T && _B || _C && _D); + CONTEXT_A(_A && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _D); + CONTEXT_A(_A && _B || _C && _T); + + CONTEXT_A(_F && _B || _C && _D); + CONTEXT_A(_A && _F || _C && _D); + CONTEXT_A(_A && _B || _F && _D); + CONTEXT_A(_A && _B || _C && _F); + + printf("\n"); +} + +void f4(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A && _B) || (_C && _D)); + CONTEXT_A((_T && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _T)); + + CONTEXT_A((_T && _B) || (_C && _D)); + CONTEXT_A((_A && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _D)); + CONTEXT_A((_A && _B) || (_C && _T)); + + CONTEXT_A((_F && _B) || (_C && _D)); + CONTEXT_A((_A && _F) || (_C && _D)); + CONTEXT_A((_A && _B) || (_F && _D)); + CONTEXT_A((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A || _B) && (_C || _D)); + CONTEXT_A((_F || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _F)); + + CONTEXT_A((_T || _B) && (_C || _D)); + CONTEXT_A((_A || _T) && (_C || _D)); + CONTEXT_A((_A || _B) && (_T || _D)); + CONTEXT_A((_A || _B) && (_C || _T)); + + CONTEXT_A((_F || _B) && (_C || _D)); + CONTEXT_A((_A || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _D)); + CONTEXT_A((_A || _B) && (_C || _F)); + + printf("\n"); +} + +void f0_B() +/* constant short-circuit */ +{ + printf("f0_B()\n"); + + CONTEXT_B(_T && _T && _T); + CONTEXT_B(_F && _F && _F); + + CONTEXT_B(_T || _T || _T); + CONTEXT_B(_F || _F || _F); + + CONTEXT_B(_T && _T || _T && _T); + CONTEXT_B(_F && _F || _F && _F); + CONTEXT_B(_T && _F || _T && _F); + CONTEXT_B(_F && _T || _F && _T); + + CONTEXT_B((_T && _T) || (_T && _T)); + CONTEXT_B((_F && _F) || (_F && _F)); + CONTEXT_B((_T && _F) || (_T && _F)); + CONTEXT_B((_F && _T) || (_F && _T)); + + CONTEXT_B((_T || _T) && (_T || _T)); + CONTEXT_B((_F || _F) && (_F || _F)); + CONTEXT_B((_T || _F) && (_T || _F)); + CONTEXT_B((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1_B(int a, int b, int c) +/* AND */ +{ + printf("f1_B(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A && _B && _C); + + CONTEXT_B(_T && _B && _C); + CONTEXT_B(_A && _T && _C); + CONTEXT_B(_A && _B && _T); + + CONTEXT_B(_F && _B && _C); + CONTEXT_B(_A && _F && _C); + CONTEXT_B(_A && _B && _F); + + CONTEXT_B(_T && _T && _C); + CONTEXT_B(_A && _T && _T); + CONTEXT_B(_T && _B && _T); + + printf("\n"); +} + +void f2_B(int a, int b, int c) +/* OR */ +{ + printf("f2_B(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A || _B || _C); + + CONTEXT_B(_T || _B || _C); + CONTEXT_B(_A || _T || _C); + CONTEXT_B(_A || _B || _T); + + CONTEXT_B(_F || _B || _C); + CONTEXT_B(_A || _F || _C); + CONTEXT_B(_A || _B || _F); + + CONTEXT_B(_F || _F || _C); + CONTEXT_B(_A || _F || _F); + CONTEXT_B(_F || _B || _F); + + printf("\n"); +} + +void f3_B(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B(_A && _B || _C && _D); + CONTEXT_B(_T && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _T); + + CONTEXT_B(_T && _B || _C && _D); + CONTEXT_B(_A && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _D); + CONTEXT_B(_A && _B || _C && _T); + + CONTEXT_B(_F && _B || _C && _D); + CONTEXT_B(_A && _F || _C && _D); + CONTEXT_B(_A && _B || _F && _D); + CONTEXT_B(_A && _B || _C && _F); + + printf("\n"); +} + +void f4_B(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A && _B) || (_C && _D)); + CONTEXT_B((_T && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _T)); + + CONTEXT_B((_T && _B) || (_C && _D)); + CONTEXT_B((_A && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _D)); + CONTEXT_B((_A && _B) || (_C && _T)); + + CONTEXT_B((_F && _B) || (_C && _D)); + CONTEXT_B((_A && _F) || (_C && _D)); + CONTEXT_B((_A && _B) || (_F && _D)); + CONTEXT_B((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5_B(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5_B(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A || _B) && (_C || _D)); + CONTEXT_B((_F || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _F)); + + CONTEXT_B((_T || _B) && (_C || _D)); + CONTEXT_B((_A || _T) && (_C || _D)); + CONTEXT_B((_A || _B) && (_T || _D)); + CONTEXT_B((_A || _B) && (_C || _T)); + + CONTEXT_B((_F || _B) && (_C || _D)); + CONTEXT_B((_A || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _D)); + CONTEXT_B((_A || _B) && (_C || _F)); + + printf("\n"); +} + +int main() +{ + f0(); + + f1(0, 0, 0); + f2(0, 0, 0); + f3(0, 0, 0, 0); + f4(0, 0, 0, 0); + f5(0, 0, 0, 0); + + f1(1, 1, 1); + f2(1, 1, 1); + f3(1, 1, 1, 1); + f4(1, 1, 1, 1); + f5(1, 1, 1, 1); + + f3(1, 0, 1, 0); + f4(1, 0, 1, 0); + f5(1, 0, 1, 0); + f3(0, 1, 0, 1); + f4(0, 1, 0, 1); + f5(0, 1, 0, 1); + + f0_B(); + + f1_B(0, 0, 0); + f2_B(0, 0, 0); + f3_B(0, 0, 0, 0); + f4_B(0, 0, 0, 0); + f5_B(0, 0, 0, 0); + + f1_B(1, 1, 1); + f2_B(1, 1, 1); + f3_B(1, 1, 1, 1); + f4_B(1, 1, 1, 1); + f5_B(1, 1, 1, 1); + + f3_B(1, 0, 1, 0); + f4_B(1, 0, 1, 0); + f5_B(1, 0, 1, 0); + f3_B(0, 1, 0, 1); + f4_B(0, 1, 0, 1); + f5_B(0, 1, 0, 1); + + return 0; +} From bc5570b708a8ea99f3e50ccdfbbc52e3739bdf0f Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:17 +0800 Subject: [PATCH 316/806] Fixed logical-NOT in constant context. --- src/cc65/expr.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index b91fab8a8..7fb6e455f 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1908,14 +1908,19 @@ void hie10 (ExprDesc* Expr) case TOK_BOOL_NOT: NextToken (); - if (evalexpr (CF_NONE, hie10, Expr) == 0) { + BoolExpr (hie10, Expr); + if (ED_IsConstAbs (Expr)) { /* Constant expression */ Expr->IVal = !Expr->IVal; } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, Expr); g_bneg (TypeOf (Expr->Type)); ED_FinalizeRValLoad (Expr); ED_TestDone (Expr); /* bneg will set cc */ } + /* The result type is always boolean */ + Expr->Type = type_bool; break; case TOK_STAR: @@ -3981,29 +3986,6 @@ void hie0 (ExprDesc *Expr) -int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is a -** constant, 0 is returned and the value is put in the Expr struct. If the -** result is not constant, LoadExpr is called to bring the value into the -** primary register and 1 is returned. -*/ -{ - /* Evaluate */ - ExprWithCheck (Func, Expr); - - /* Check for a constant expression */ - if (ED_IsConstAbs (Expr)) { - /* Constant expression */ - return 0; - } else { - /* Not constant, load into the primary */ - LoadExpr (Flags, Expr); - return 1; - } -} - - - void Expression0 (ExprDesc* Expr) /* Evaluate an expression via hie0 and put the result into the primary register */ { From 8e0b2f083356f206b6f5529d0d60b32fff3a7a0a Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:20 +0800 Subject: [PATCH 317/806] Object addresses as non-NULL in boolean context. --- src/cc65/expr.c | 32 +++++++++++++++++++++----------- src/cc65/exprdesc.c | 8 ++++++++ src/cc65/exprdesc.h | 3 +++ src/cc65/testexpr.c | 5 +++++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 7fb6e455f..94109d559 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1912,6 +1912,9 @@ void hie10 (ExprDesc* Expr) if (ED_IsConstAbs (Expr)) { /* Constant expression */ Expr->IVal = !Expr->IVal; + } else if (ED_IsAddrExpr (Expr)) { + /* Address != NULL, so !Address == 0 */ + ED_MakeConstBool (Expr, 0); } else { /* Not constant, load into the primary */ LoadExpr (CF_NONE, Expr); @@ -3222,7 +3225,7 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) Error ("Scalar expression expected"); ED_MakeConstBool (Expr, 0); } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - if (!ED_IsConstAbs (Expr)) { + if (!ED_IsConstBool (Expr)) { /* Set the test flag */ ED_RequireTest (Expr); @@ -3237,7 +3240,7 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Generate the jump */ g_falsejump (CF_NONE, FalseLab); - } else if (Expr->IVal == 0) { + } else if (Expr->IVal == 0 && !ED_IsAddrExpr (Expr)) { /* Skip remaining */ Flags |= E_EVAL_UNEVAL; } @@ -3264,7 +3267,7 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) Error ("Scalar expression expected"); ED_MakeConstBool (&Expr2, 0); } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - if (!ED_IsConstAbs (&Expr2)) { + if (!ED_IsConstBool (&Expr2)) { ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); @@ -3276,7 +3279,7 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* We need the true label for the last expression */ HasTrueJump = 1; } - } else if (Expr2.IVal == 0) { + } else if (Expr2.IVal == 0 && !ED_IsAddrExpr (&Expr2)) { /* Skip remaining */ Flags |= E_EVAL_UNEVAL; /* The value of the expression will be false */ @@ -3313,7 +3316,8 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) } /* Convert to bool */ - if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) || + ED_IsAddrExpr (Expr)) { ED_MakeConstBool (Expr, 1); } else { Expr->Type = type_bool; @@ -3354,7 +3358,7 @@ static void hieOr (ExprDesc *Expr) ED_MakeConstBool (Expr, 0); } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - if (!ED_IsConstAbs (Expr)) { + if (!ED_IsConstBool (Expr)) { /* Test the lhs if we haven't had && operators. If we had them, the ** jump is already in place and there's no need to do the test. */ @@ -3377,7 +3381,7 @@ static void hieOr (ExprDesc *Expr) /* Jump to TrueLab if true */ g_truejump (CF_NONE, TrueLab); } - } else if (Expr->IVal != 0) { + } else if (Expr->IVal != 0 || ED_IsAddrExpr (Expr)) { /* Skip remaining */ Flags |= E_EVAL_UNEVAL; } @@ -3406,7 +3410,7 @@ static void hieOr (ExprDesc *Expr) ED_MakeConstBool (&Expr2, 0); } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - if (!ED_IsConstAbs (&Expr2)) { + if (!ED_IsConstBool (&Expr2)) { /* If there is more to come, add shortcut boolean eval */ if (!AndOp) { ED_RequireTest (&Expr2); @@ -3418,7 +3422,7 @@ static void hieOr (ExprDesc *Expr) } g_truejump (CF_NONE, TrueLab); } - } else if (Expr2.IVal != 0) { + } else if (Expr2.IVal != 0 || ED_IsAddrExpr (&Expr2)) { /* Skip remaining */ Flags |= E_EVAL_UNEVAL; /* The result is always true */ @@ -3429,7 +3433,8 @@ static void hieOr (ExprDesc *Expr) } /* Convert to bool */ - if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) || + ED_IsAddrExpr (Expr)) { ED_MakeConstBool (Expr, 1); } else { Expr->Type = type_bool; @@ -3483,7 +3488,7 @@ static void hieQuest (ExprDesc* Expr) /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { - int ConstantCond = ED_IsConstAbsInt (Expr); + int ConstantCond = ED_IsConstBool (Expr); unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; ED_Init (&Expr2); @@ -3493,6 +3498,11 @@ static void hieQuest (ExprDesc* Expr) NextToken (); + /* Convert non-integer constant boolean */ + if (ED_IsAddrExpr (Expr)) { + ED_MakeConstBool (Expr, 1); + } + if (!ConstantCond) { /* Condition codes not set, request a test */ ED_RequireTest (Expr); diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 62738bed7..e82d0fafc 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -389,6 +389,14 @@ int ED_IsConstAbsInt (const ExprDesc* Expr) +int ED_IsConstBool (const ExprDesc* Expr) +/* Return true if the expression can be constantly evaluated as a boolean. */ +{ + return ED_IsConstAbsInt (Expr) || ED_IsAddrExpr (Expr); +} + + + int ED_IsConst (const ExprDesc* Expr) /* Return true if the expression denotes a constant of some sort. This can be a ** numeric constant, the address of a global variable (maybe with offset) or diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index c435b3c38..463828db6 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -640,6 +640,9 @@ int ED_IsConstAbs (const ExprDesc* Expr); int ED_IsConstAbsInt (const ExprDesc* Expr); /* Return true if the expression is a constant (numeric) integer. */ +int ED_IsConstBool (const ExprDesc* Expr); +/* Return true if the expression can be constantly evaluated as a boolean. */ + int ED_IsConst (const ExprDesc* Expr); /* Return true if the expression denotes a constant of some sort. This can be a ** numeric constant, the address of a global variable (maybe with offset) or diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index b924eaa40..80fe4bc3d 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -77,6 +77,11 @@ unsigned Test (unsigned Label, int Invert) g_jump (Label); } + } else if (ED_IsAddrExpr (&Expr)) { + + /* Object addresses are non-NULL */ + Result = 1; + } else { /* Result is unknown */ From f849de6769ac4f887c35fc9561fd38c4e584f708 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 20 Aug 2020 07:52:22 +0800 Subject: [PATCH 318/806] Object addresses as non-NULL for comparison with NULL. --- src/cc65/expr.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 94109d559..a562dfa76 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -2428,6 +2428,33 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ */ WarnConstCompareResult (Expr); + } else if (ED_CodeRangeIsEmpty (&Expr2) && + ((ED_IsAddrExpr (Expr) && ED_IsNullPtr (&Expr2)) || + (ED_IsNullPtr (Expr) && (ED_IsAddrExpr (&Expr2))))) { + + /* Object addresses are inequal to null pointer */ + Expr->IVal = (Tok != TOK_EQ); + if (ED_IsNullPtr (&Expr2)) { + if (Tok == TOK_LT || Tok == TOK_LE) { + Expr->IVal = 0; + } + } else { + if (Tok == TOK_GT || Tok == TOK_GE) { + Expr->IVal = 0; + } + } + + /* Get rid of unwanted flags */ + ED_MakeConstBool (Expr, Expr->IVal); + + /* If the result is constant, this is suspicious when not in + ** preprocessor mode. + */ + WarnConstCompareResult (Expr); + + /* Both operands are static, remove the generated code */ + RemoveCode (&Mark1); + } else { /* Determine the signedness of the operands */ @@ -2485,7 +2512,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_EQ: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, 0); + ED_MakeConstBool (Expr, 0); WarnConstCompareResult (Expr); goto Done; } @@ -2493,7 +2520,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_NE: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstBool (Expr, 1); WarnConstCompareResult (Expr); goto Done; } @@ -2501,7 +2528,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LT: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax); + ED_MakeConstBool (Expr, Expr2.IVal > LeftMax); WarnConstCompareResult (Expr); goto Done; } @@ -2509,7 +2536,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LE: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax); + ED_MakeConstBool (Expr, Expr2.IVal >= LeftMax); WarnConstCompareResult (Expr); goto Done; } @@ -2517,7 +2544,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GE: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin); + ED_MakeConstBool (Expr, Expr2.IVal <= LeftMin); WarnConstCompareResult (Expr); goto Done; } @@ -2525,7 +2552,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GT: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { - ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin); + ED_MakeConstBool (Expr, Expr2.IVal < LeftMin); WarnConstCompareResult (Expr); goto Done; } From df07e23f2c523838723e5c69cdcf35ef88d4ad59 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 5 Sep 2020 11:15:17 +0800 Subject: [PATCH 319/806] Fixed bnegeax funcinfo on register usage. --- src/cc65/codeinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index d7d85a12d..a89f6d54c 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -102,7 +102,7 @@ static const FuncInfo FuncInfoTable[] = { { "asreax4", REG_EAX, REG_EAXY | REG_TMP1 }, { "bnega", REG_A, REG_AX }, { "bnegax", REG_AX, REG_AX }, - { "bnegeax", REG_EAX, REG_EAX }, + { "bnegeax", REG_EAX, REG_EAX | REG_TMP1 }, { "booleq", REG_NONE, REG_AX }, { "boolge", REG_NONE, REG_AX }, { "boolgt", REG_NONE, REG_AX }, From 903e84c24cb9887a6d5029f78c3daeba50b70cea Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 17:45:25 +0800 Subject: [PATCH 320/806] Std-functions are no longer inlined if they are unevaluated. --- src/cc65/expr.c | 2 +- src/cc65/exprdesc.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a562dfa76..d39c9ebb6 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -593,7 +593,7 @@ static void FunctionCall (ExprDesc* Expr) } /* Check for known standard functions and inline them */ - if (Expr->Name != 0) { + if (Expr->Name != 0 && !ED_IsUneval (Expr)) { int StdFunc = FindStdFunc ((const char*) Expr->Name); if (StdFunc >= 0) { /* Inline this function */ diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 463828db6..810d68965 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -474,13 +474,13 @@ void ED_MarkForUneval (ExprDesc* Expr); /* Mark the expression as not to be evaluated */ #if defined(HAVE_INLINE) -INLINE int ED_MayBeUneval (const ExprDesc* Expr) -/* Check if the expression may be uevaluated */ +INLINE int ED_IsUneval (const ExprDesc* Expr) +/* Check if the expression is not to be evaluated */ { return (Expr->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL; } #else -# define ED_MayBeUneval(Expr) (((Expr)->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) +# define ED_IsUneval(Expr) (((Expr)->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) #endif #if defined(HAVE_INLINE) From e2f950b15e323bb89b31b2a0259c684df3edf262 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 31 Aug 2020 17:45:36 +0800 Subject: [PATCH 321/806] Avoided referencing string literals with sizeof so that they are not output if unused elsewhere. --- src/cc65/expr.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index d39c9ebb6..e1b22ac50 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -923,7 +923,11 @@ static void Primary (ExprDesc* E) case TOK_SCONST: case TOK_WCSCONST: /* String literal */ - E->LVal = UseLiteral (CurTok.SVal); + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + E->LVal = UseLiteral (CurTok.SVal); + } else { + E->LVal = CurTok.SVal; + } E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; E->IVal = 0; @@ -1996,19 +2000,18 @@ void hie10 (ExprDesc* Expr) /* Remember the output queue pointer */ CodeMark Mark; GetCodePos (&Mark); - hie10 (Expr); - if (ED_IsBitField (Expr)) { + + /* The expression shall be unevaluated */ + ExprDesc Uneval; + ED_Init (&Uneval); + ED_MarkForUneval (&Uneval); + hie10 (&Uneval); + if (ED_IsBitField (&Uneval)) { Error ("Cannot apply 'sizeof' to bit-field"); Size = 0; } else { - /* If the expression is a literal string, release it, so it - ** won't be output as data if not used elsewhere. - */ - if (ED_IsLocLiteral (Expr)) { - ReleaseLiteral (Expr->LVal); - } /* Calculate the size */ - Size = ExprCheckedSizeOf (Expr->Type); + Size = ExprCheckedSizeOf (Uneval.Type); } /* Remove any generated code */ RemoveCode (&Mark); From 6e0fb630d75b517faa48eea4a01b7edf76ac5d59 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 5 Sep 2020 21:31:22 +0800 Subject: [PATCH 322/806] Fixed check for processor flags usage in case of PHP. --- src/cc65/codeent.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 7dd90833b..09ca0a9c5 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -476,6 +476,11 @@ int CE_UseLoadFlags (CodeEntry* E) } } + /* PHP will use all flags */ + if (E->OPC == OP65_PHP) { + return 1; + } + /* Anything else */ return 0; } From 1cde952cf52a4c606680d1aebe9d9d0eaa146c4d Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 7 Sep 2020 04:19:27 +0800 Subject: [PATCH 323/806] Fixed comparing an enum type with a non-enum type in DoCompare(). --- src/cc65/typecmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 24d9a385f..f179bff14 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -203,8 +203,8 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if ((IsTypeEnum (lhs) || IsTypeEnum (rhs))) { /* Compare the tag types */ - Sym1 = GetESUSymEntry (lhs); - Sym2 = GetESUSymEntry (rhs); + Sym1 = IsTypeEnum (lhs) ? GetESUSymEntry (lhs) : 0; + Sym2 = IsTypeEnum (rhs) ? GetESUSymEntry (rhs) : 0; if (Sym1 != Sym2) { if (Sym1 == 0 || Sym2 == 0) { From 6bb2d1d5d1d67e63af8e84619eaa261bfda5d952 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 7 Sep 2020 04:19:27 +0800 Subject: [PATCH 324/806] Fixed a comment on struct/union types in DoCompare(). --- src/cc65/typecmp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index f179bff14..1f4643e73 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -378,11 +378,13 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) if (Sym1 != Sym2) { /* Both must be in the same scope and have the same name to - ** be identical. This shouldn't happen in the current code - ** base, but we still do this to be future-proof. + ** be identical. */ if (Sym1->Owner != Sym2->Owner || strcmp (Sym1->Name, Sym2->Name) != 0) { + /* This shouldn't happen in the current code base, but + ** we still handle this case to be future-proof. + */ SetResult (Result, TC_INCOMPATIBLE); return; } From 23a8b2c303f7329b29c7d9eb0604b056f66c75a4 Mon Sep 17 00:00:00 2001 From: Tevo Date: Thu, 3 Sep 2020 00:06:31 -0300 Subject: [PATCH 325/806] Define integer size macros for lacking systems Define integer size macros for lacking systems --- src/common/inttypes.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/common/inttypes.h b/src/common/inttypes.h index 29ac778ef..a36543e63 100644 --- a/src/common/inttypes.h +++ b/src/common/inttypes.h @@ -57,6 +57,16 @@ typedef size_t uintptr_t; typedef ptrdiff_t intmax_t; typedef size_t uintmax_t; +#define INT8_MAX (0x7F) +#define INT16_MAX (0x7FFF) +#define INT32_MAX (0x7FFFFFFF) + +#define INT8_MIN (-INT8_MAX - 1) +#define INT16_MIN (-INT16_MAX - 1) + +#define UINT8_MAX (0xFF) +#define UINT16_MAX (0xFFFF) + #endif From 1e7a9e44afeefdcd3746ac0ff1ec304a0b9e785b Mon Sep 17 00:00:00 2001 From: Tevo Date: Sun, 6 Sep 2020 21:19:01 -0300 Subject: [PATCH 326/806] Update comment to reflect addition of integer boundary constants --- src/common/inttypes.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/inttypes.h b/src/common/inttypes.h index a36543e63..0d8caf32a 100644 --- a/src/common/inttypes.h +++ b/src/common/inttypes.h @@ -38,7 +38,8 @@ -/* If we have , include it; otherwise, adapt types from . +/* If we have , include it; otherwise, adapt types from +** and define integer boundary constants. ** gcc and msvc don't define __STDC_VERSION__ without special flags, so check ** for them explicitly. Undefined symbols are replaced by zero; so, checks for ** defined(__GNUC__) and defined(_MSC_VER) aren't necessary. From 9a0e4a35e1c1083f3c3c900c1d3888a048b30a96 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 5 Sep 2020 20:03:24 +0200 Subject: [PATCH 327/806] Fix enum bit-field ICE #1244 This previously resulted in an ICE: cc65: Check failed: (Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED, file 'cc65/symtab.c', line 874 This CHECK is in the code that deals with changing `int` bitfields to `unsigned int`. Work around this by treating enum bit-fields as having their signedness specified, so this type change code does not get called. Fixes #1244. --- src/cc65/declare.c | 6 ++ src/cc65/symtab.c | 1 + test/val/enum-bitfield.c | 158 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 test/val/enum-bitfield.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index d6286e62d..857021671 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1446,6 +1446,12 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, D->Type[0].C |= T_ENUM; SetESUSymEntry (D->Type, Entry); D->Type[1].C = T_END; + /* The signedness of enums is determined by the type, so say this is specified to avoid + ** the int -> unsigned int handling for plain int bit-fields in AddBitField. + */ + if (SignednessSpecified) { + *SignednessSpecified = 1; + } break; case TOK_IDENT: diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 10dc0d3ff..5546d450c 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -870,6 +870,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, if (!SignednessSpecified) { /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, ** since this is allowed for bit-fields and avoids sign-extension, so is much faster. + ** enums set SignednessSpecified to 1 to avoid this adjustment. */ CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED); Entry->Type->C &= ~T_MASK_SIGN; diff --git a/test/val/enum-bitfield.c b/test/val/enum-bitfield.c new file mode 100644 index 000000000..62e05f71f --- /dev/null +++ b/test/val/enum-bitfield.c @@ -0,0 +1,158 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of enum bit-fields; see https://github.com/cc65/cc65/issues/1244 +*/ + +#include + +static unsigned char failures = 0; + +/* Enum with underlying type unsigned int. */ +enum e10u { + E10U_200 = 200, + E10U_1000 = 1000, +}; + +static struct enum_bitfield_uint { + enum e10u x : 1; + enum e10u y : 8; + enum e10u z : 16; +} e10ubf = {0, E10U_200, E10U_1000}; + +static void test_enum_bitfield_uint(void) +{ + if (sizeof (struct enum_bitfield_uint) != 4) { + printf ("Got sizeof(struct enum_bitfield_uint) = %zu, expected 4.\n", + sizeof(struct enum_bitfield_uint)); + failures++; + } + + if (e10ubf.x != 0) { + printf ("Got e10ubf.x = %u, expected 0.\n", e10ubf.x); + failures++; + } + if (e10ubf.y != 200) { + printf ("Got e10ubf.y = %u, expected 200.\n", e10ubf.y); + failures++; + } + if (e10ubf.z != 1000) { + printf ("Got e10ubf.z = %u, expected 1000.\n", e10ubf.z); + failures++; + } + + e10ubf.x = 1; + e10ubf.y = 17; + e10ubf.z = 1023; + + if (e10ubf.x != 1) { + printf ("Got e10ubf.x = %u, expected 1.\n", e10ubf.x); + failures++; + } + + /* Check signedness, should be unsigned. */ + { + long v = e10ubf.x - 2; + if (v < 0) { + printf ("Got negative v = %ld, expected large positive.\n", v); + failures++; + } + } + + if (e10ubf.y != 17) { + printf ("Got e10ubf.y = %u, expected 17.\n", e10ubf.y); + failures++; + } + if (e10ubf.z != 1023) { + printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z); + failures++; + } +} + +/* Enum with underlying type signed int. */ +enum e11i { + E11I_M1 = -1, + E11I_100 = 100, + E11I_1000 = 1000, +}; + +static struct enum_bitfield_int { + enum e11i x : 2; + enum e11i y : 8; + enum e11i z : 16; +} e11ibf = {E11I_M1, E11I_100, E11I_1000}; + +static void test_enum_bitfield_int(void) +{ + if (sizeof (struct enum_bitfield_int) != 4) { + printf ("Got sizeof(struct enum_bitfield_int) = %zu, expected 4.\n", + sizeof(struct enum_bitfield_int)); + failures++; + } + + if (e11ibf.x != -1) { + printf ("Got e11ibf.x = %d, expected -1.\n", e11ibf.x); + failures++; + } + if (e11ibf.y != 100) { + printf ("Got e11ibf.y = %d, expected 100.\n", e11ibf.y); + failures++; + } + if (e11ibf.z != 1000) { + printf ("Got e11ibf.z = %d, expected 1000.\n", e11ibf.z); + failures++; + } + + e11ibf.x = 1; + e11ibf.y = 17; + e11ibf.z = 1023; + + if (e11ibf.x != 1) { + printf ("Got e11ibf.x = %d, expected 1.\n", e11ibf.x); + failures++; + } + + /* Check signedness, should be signed. */ + { + long v = e11ibf.x - 2; + if (v > 0) { + printf ("Got positive v = %ld, expected negative.\n", v); + failures++; + } + } + + if (e11ibf.y != 17) { + printf ("Got e11ibf.y = %d, expected 17.\n", e11ibf.y); + failures++; + } + if (e11ibf.z != 1023) { + printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z); + failures++; + } +} + +int main(void) +{ + test_enum_bitfield_uint(); + test_enum_bitfield_int(); + printf("failures: %u\n", failures); + return failures; +} From 64ef562fa76cb89487f67fe7568ba350132f4696 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 7 Sep 2020 05:11:48 +0800 Subject: [PATCH 328/806] Moved all optimization steps from codeopt.c and some optimization steps from coptind.c into new separate files. --- src/cc65.vcxproj | 4 + src/cc65/codeopt.c | 655 +------------------------ src/cc65/coptind.c | 1105 +++---------------------------------------- src/cc65/coptind.h | 87 +--- src/cc65/coptjmp.c | 1009 +++++++++++++++++++++++++++++++++++++++ src/cc65/coptjmp.h | 116 +++++ src/cc65/coptmisc.c | 735 ++++++++++++++++++++++++++++ src/cc65/coptmisc.h | 113 +++++ 8 files changed, 2046 insertions(+), 1778 deletions(-) create mode 100644 src/cc65/coptjmp.c create mode 100644 src/cc65/coptjmp.h create mode 100644 src/cc65/coptmisc.c create mode 100644 src/cc65/coptmisc.h diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index d566c8bc1..1015b389f 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -93,6 +93,8 @@ + + @@ -167,6 +169,8 @@ + + diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 69ac35005..4a7722830 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -48,7 +48,6 @@ #include "xsprintf.h" /* cc65 */ -#include "asmlabel.h" #include "codeent.h" #include "codeinfo.h" #include "codeopt.h" @@ -56,6 +55,8 @@ #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" +#include "coptjmp.h" +#include "coptmisc.h" #include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" @@ -69,660 +70,8 @@ #include "error.h" #include "global.h" #include "output.h" -#include "symtab.h" -/*****************************************************************************/ -/* Optimize loads */ -/*****************************************************************************/ - - - -static unsigned OptLoad1 (CodeSeg* S) -/* Search for a call to ldaxysp where X is not used later and replace it by -** a load of just the A register. -*/ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* E; - - /* Get next entry */ - E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (E, "ldaxysp") && - RegValIsKnown (E->RI->In.RegY) && - !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Reload the Y register */ - const char* Arg = MakeHexArg (E->RI->In.RegY - 1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Load from stack */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad2 (CodeSeg* S) -/* Replace calls to ldaxysp by inline code */ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[3]; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (L[0], "ldaxysp")) { - - CodeEntry* X; - - /* Followed by sta abs/stx abs? */ - if (CS_GetEntries (S, L+1, I+1, 2) && - L[1]->OPC == OP65_STA && - L[2]->OPC == OP65_STX && - (L[1]->Arg == 0 || - L[2]->Arg == 0 || - strcmp (L[1]->Arg, L[2]->Arg) != 0) && - !CS_RangeHasLabel (S, I+1, 2) && - !RegXUsed (S, I+3)) { - - /* A/X are stored into memory somewhere and X is not used - ** later - */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); - CS_InsertEntry (S, X, I+4); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+5); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+6); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I+7); - - /* Now remove the call to the subroutine and the sta/stx */ - CS_DelEntries (S, I, 3); - - } else { - - /* Standard replacement */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+1); - - /* tax */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+4); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - } - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad3 (CodeSeg* S) -/* Remove repeated loads from one and the same memory location */ -{ - unsigned Changes = 0; - CodeEntry* Load = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Forget a preceeding load if we have a label */ - if (Load && CE_HasLabel (E)) { - Load = 0; - } - - /* Check if this insn is a load */ - if (E->Info & OF_LOAD) { - - CodeEntry* N; - - /* If we had a preceeding load that is identical, remove this one. - ** If it is not identical, or we didn't have one, remember it. - */ - if (Load != 0 && - E->OPC == Load->OPC && - E->AM == Load->AM && - ((E->Arg == 0 && Load->Arg == 0) || - strcmp (E->Arg, Load->Arg) == 0) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_CBRA) == 0) { - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Next insn */ - continue; - - } else { - - Load = E; - - } - - } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { - /* Forget the first load on occurance of any insn we don't like */ - Load = 0; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Decouple operations */ -/*****************************************************************************/ - - - -static unsigned OptDecouple (CodeSeg* S) -/* Decouple operations, that is, do the following replacements: -** -** dex -> ldx #imm -** inx -> ldx #imm -** dey -> ldy #imm -** iny -> ldy #imm -** tax -> ldx #imm -** txa -> lda #imm -** tay -> ldy #imm -** tya -> lda #imm -** lda zp -> lda #imm -** ldx zp -> ldx #imm -** ldy zp -> ldy #imm -** -** Provided that the register values are known of course. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - const char* Arg; - - /* Get next entry and it's input register values */ - CodeEntry* E = CS_GetEntry (S, I); - const RegContents* In = &E->RI->In; - - /* Assume we have no replacement */ - CodeEntry* X = 0; - - /* Check the instruction */ - switch (E->OPC) { - - case OP65_DEA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA - 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX - 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY - 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA + 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX + 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY + 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_LDA: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDX: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDY: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_TAX: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TAY: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TXA: - if (E->RI->In.RegX >= 0) { - Arg = MakeHexArg (In->RegX); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TYA: - if (E->RI->In.RegY >= 0) { - Arg = MakeHexArg (In->RegY); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - default: - /* Avoid gcc warnings */ - break; - - } - - /* Insert the replacement if we have one */ - if (X) { - CS_InsertEntry (S, X, I+1); - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize stack pointer ops */ -/*****************************************************************************/ - - - -static unsigned IsDecSP (const CodeEntry* E) -/* Check if this is an insn that decrements the stack pointer. If so, return -** the decrement. If not, return zero. -** The function expects E to be a subroutine call. -*/ -{ - if (strncmp (E->Arg, "decsp", 5) == 0) { - if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { - return (E->Arg[5] - '0'); - } - } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { - return E->RI->In.RegY; - } - - /* If we come here, it's not a decsp op */ - return 0; -} - - - -static unsigned OptStackPtrOps (CodeSeg* S) -/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all -** known cases! -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - unsigned Dec1; - unsigned Dec2; - const CodeEntry* N; - - /* Get the next entry */ - const CodeEntry* E = CS_GetEntry (S, I); - - /* Check for decspn or subysp */ - if (E->OPC == OP65_JSR && - (Dec1 = IsDecSP (E)) > 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - (Dec2 = IsDecSP (N)) > 0 && - (Dec1 += Dec2) <= 255 && - !CE_HasLabel (N)) { - - CodeEntry* X; - char Buf[20]; - - /* We can combine the two */ - if (Dec1 <= 8) { - /* Insert a call to decsp */ - xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); - CS_InsertEntry (S, X, I+2); - } else { - /* Insert a call to subysp */ - const char* Arg = MakeHexArg (Dec1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); - CS_InsertEntry (S, X, I+2); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); - CS_InsertEntry (S, X, I+3); - } - - /* Delete the old code */ - CS_DelEntries (S, I, 2); - - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} - -static unsigned OptGotoSPAdj (CodeSeg* S) -/* Optimize SP adjustment for forward 'goto' */ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[10], *X; - unsigned short adjustment; - const char* Arg; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence generated by g_lateadjustSP */ - if (L[0]->OPC == OP65_PHA && - CS_GetEntries (S, L+1, I+1, 9) && - L[1]->OPC == OP65_LDA && - L[1]->AM == AM65_ABS && - L[2]->OPC == OP65_CLC && - L[3]->OPC == OP65_ADC && - strcmp (L[3]->Arg, "sp") == 0 && - L[6]->OPC == OP65_ADC && - strcmp (L[6]->Arg, "sp+1") == 0 && - L[9]->OPC == OP65_JMP) { - adjustment = FindSPAdjustment (L[1]->Arg); - - if (adjustment == 0) { - /* No SP adjustment needed, remove the whole sequence */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 8) { - /* If adjustment is in range [-8, 0) we use decsp* calls */ - char Buf[20]; - adjustment = 65536 - adjustment; - xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 255) { - /* For range [-255, -8) we have ldy #, jsr subysp */ - adjustment = 65536 - adjustment; - Arg = MakeHexArg (adjustment); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment > 255) { - /* For ranges [-32768, 255) and (255, 32767) the only modification - ** is to replace the absolute with immediate addressing - */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 1); - Arg = MakeHexArg (adjustment >> 8); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); - CS_InsertEntry (S, X, I + 6); - - /* Delete the old code */ - CS_DelEntry (S, I + 2); - CS_DelEntry (S, I + 6); - } - else if (adjustment > 8) { - /* For range (8, 255] we have ldy #, jsr addysp */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else { - /* If adjustment is in range (0, 8] we use incsp* calls */ - char Buf[20]; - xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - Changes++; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} /*****************************************************************************/ /* struct OptFunc */ diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 811adff04..b506a21ca 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -124,46 +124,6 @@ static int MemAccess (CodeSeg* S, unsigned From, unsigned To, const CodeEntry* N -static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) -/* Get the branch distance between the two entries and return it. The distance -** will be negative for backward jumps and positive for forward jumps. -*/ -{ - /* Get the index of the branch target */ - unsigned TI = CS_GetEntryIndex (S, To); - - /* Determine the branch distance */ - int Distance = 0; - if (TI >= From) { - /* Forward branch, do not count the current insn */ - unsigned J = From+1; - while (J < TI) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance += N->Size; - } - } else { - /* Backward branch */ - unsigned J = TI; - while (J < From) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance -= N->Size; - } - } - - /* Return the calculated distance */ - return Distance; -} - - - -static int IsShortDist (int Distance) -/* Return true if the given distance is a short branch distance */ -{ - return (Distance >= -125 && Distance <= 125); -} - - - static short ZPRegVal (unsigned short Use, const RegContents* RC) /* Return the contents of the given zeropage register */ { @@ -184,838 +144,6 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) -static short RegVal (unsigned short Use, const RegContents* RC) -/* Return the contents of the given register */ -{ - if ((Use & REG_A) != 0) { - return RC->RegA; - } else if ((Use & REG_X) != 0) { - return RC->RegX; - } else if ((Use & REG_Y) != 0) { - return RC->RegY; - } else { - return ZPRegVal (Use, RC); - } -} - - - -/*****************************************************************************/ -/* Replace jumps to RTS by RTS */ -/*****************************************************************************/ - - - -unsigned OptRTSJumps1 (CodeSeg* S) -/* Replace jumps to RTS by RTS */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an unconditional branch to a local target */ - if ((E->Info & OF_UBRA) != 0 && - E->JumpTo != 0 && - E->JumpTo->Owner->OPC == OP65_RTS) { - - /* Insert an RTS instruction */ - CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptRTSJumps2 (CodeSeg* S) -/* Replace long conditional jumps to RTS or to a final target */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S) - 1) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an conditional branch to a local target */ - if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ - (E->Info & OF_LBRA) != 0 && /* Long branch */ - E->JumpTo != 0) { /* Local label */ - - - /* Get the jump target and the next entry. There's always a next - ** entry, because we don't cover the last entry in the loop. - */ - CodeEntry* X = 0; - CodeEntry* T = E->JumpTo->Owner; - CodeEntry* N = CS_GetNextEntry (S, I); - - /* Check if it's a jump to an RTS insn */ - if (T->OPC == OP65_RTS) { - - /* It's a jump to RTS. Create a conditional branch around an - ** RTS insn. - */ - X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); - - } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { - - /* It's a jump to a label outside the function. Create a - ** conditional branch around a jump to the external label. - */ - X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); - - } - - /* If we have a replacement insn, insert it */ - if (X) { - - CodeLabel* LN; - opc_t NewBranch; - - /* Insert the new insn */ - CS_InsertEntry (S, X, I+1); - - /* Create a conditional branch with the inverse condition - ** around the replacement insn - */ - - /* Get the new branch opcode */ - NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); - - /* Get the label attached to N, create a new one if needed */ - LN = CS_GenLabel (S, N); - - /* Generate the branch */ - X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the long branch */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Remove dead jumps */ -/*****************************************************************************/ - - - -unsigned OptDeadJumps (CodeSeg* S) -/* Remove dead jumps (jumps to the next instruction) */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a branch, if it has a local target, and if the target - ** is the next instruction. - */ - if (E->AM == AM65_BRA && - E->JumpTo && - E->JumpTo->Owner == CS_GetNextEntry (S, I)) { - - /* Delete the dead jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Remove dead code */ -/*****************************************************************************/ - - - -unsigned OptDeadCode (CodeSeg* S) -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* LN; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an unconditional branch, and if the next entry has - ** no labels attached, or if the label is just used so that the insn - ** can jump to itself. - */ - if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ - (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ - (!CE_HasLabel (N) || /* Don't has a label */ - ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ - (LN = N->JumpTo) != 0 && /* Jumps to known label */ - LN->Owner == N && /* Attached to insn */ - CL_GetRefCount (LN) == 1))) { /* Only reference */ - - /* Delete the next entry */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump cascades */ -/*****************************************************************************/ - - - -unsigned OptJumpCascades (CodeSeg* S) -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* OldLabel; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check: - ** - if it's a branch, - ** - if it has a jump label, - ** - if this jump label is not attached to the instruction itself, - ** - if the target instruction is itself a branch, - ** - if either the first branch is unconditional or the target of - ** the second branch is internal to the function. - ** The latter condition will avoid conditional branches to targets - ** outside of the function (usually incspx), which won't simplify the - ** code, since conditional far branches are emulated by a short branch - ** around a jump. - */ - if ((E->Info & OF_BRA) != 0 && - (OldLabel = E->JumpTo) != 0 && - (N = OldLabel->Owner) != E && - (N->Info & OF_BRA) != 0 && - ((E->Info & OF_CBRA) == 0 || - N->JumpTo != 0)) { - - /* Check if we can use the final target label. That is the case, - ** if the target branch is an absolute branch; or, if it is a - ** conditional branch checking the same condition as the first one. - */ - if ((N->Info & OF_UBRA) != 0 || - ((E->Info & OF_CBRA) != 0 && - GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { - - /* This is a jump cascade and we may jump to the final target, - ** provided that the other insn does not jump to itself. If - ** this is the case, we can also jump to ourselves, otherwise - ** insert a jump to the new instruction and remove the old one. - */ - CodeEntry* X; - CodeLabel* LN = N->JumpTo; - - if (LN != 0 && LN->Owner == N) { - - /* We found a jump to a jump to itself. Replace our jump - ** by a jump to itself. - */ - CodeLabel* LE = CS_GenLabel (S, E); - X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); - - } else { - - /* Jump to the final jump target */ - X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); - - } - - /* Insert it behind E */ - CS_InsertEntry (S, X, I+1); - - /* Remove E */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Check if both are conditional branches, and the condition of - ** the second is the inverse of that of the first. In this case, - ** the second branch will never be taken, and we may jump directly - ** to the instruction behind this one. - */ - } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { - - CodeEntry* X; /* Instruction behind N */ - CodeLabel* LX; /* Label attached to X */ - - /* Get the branch conditions of both branches */ - bc_t BC1 = GetBranchCond (E->OPC); - bc_t BC2 = GetBranchCond (N->OPC); - - /* Check the branch conditions */ - if (BC1 != GetInverseCond (BC2)) { - /* Condition not met */ - goto NextEntry; - } - - /* We may jump behind this conditional branch. Get the - ** pointer to the next instruction - */ - if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { - /* N is the last entry, bail out */ - goto NextEntry; - } - - /* Get the label attached to X, create a new one if needed */ - LX = CS_GenLabel (S, X); - - /* Move the reference from E to the new label */ - CS_MoveLabelRef (S, E, LX); - - /* Remember, we had changes */ - ++Changes; - } - } - -NextEntry: - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jsr/rts */ -/*****************************************************************************/ - - - -unsigned OptRTS (CodeSeg* S) -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a subroutine call and if the following insn is RTS */ - if (E->OPC == OP65_JSR && - (N = CS_GetNextEntry (S, I)) != 0 && - N->OPC == OP65_RTS) { - - /* Change the jsr to a jmp and use the additional info for a jump */ - E->AM = AM65_BRA; - CE_ReplaceOPC (E, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump targets */ -/*****************************************************************************/ - - - -unsigned OptJumpTarget1 (CodeSeg* S) -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ -{ - unsigned Changes = 0; - CodeEntry* E1; /* Entry 1 */ - CodeEntry* E2; /* Entry 2 */ - CodeEntry* T1; /* Jump target entry 1 */ - CodeLabel* TL1; /* Target label 1 */ - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - E2 = CS_GetNextEntry (S, I); - - /* Check if we have a jump or branch without a label attached, and - ** a jump target, which is not attached to the jump itself - */ - if (E2 != 0 && - (E2->Info & OF_UBRA) != 0 && - !CE_HasLabel (E2) && - E2->JumpTo && - E2->JumpTo->Owner != E2) { - - /* Get the entry preceeding the branch target */ - T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); - if (T1 == 0) { - /* There is no such entry */ - goto NextEntry; - } - - /* The entry preceeding the branch target may not be the branch - ** insn. - */ - if (T1 == E2) { - goto NextEntry; - } - - /* Get the entry preceeding the jump */ - E1 = CS_GetEntry (S, I); - - /* Check if both preceeding instructions are identical */ - if (!CodeEntriesAreEqual (E1, T1)) { - /* Not equal, try next */ - goto NextEntry; - } - - /* Get the label for the instruction preceeding the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - TL1 = CS_GenLabel (S, T1); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E2, TL1); - - /* If the instruction preceeding the jump has labels attached, - ** move references to this label to the new label. - */ - if (CE_HasLabel (E1)) { - CS_MoveLabels (S, E1, T1); - } - - /* Remove the entry preceeding the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { -NextEntry: - /* Next entry */ - ++I; - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget2 (CodeSeg* S) -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* OP that may be skipped */ - opc_t OPC; - - /* Jump target insn, old and new */ - CodeEntry* T; - CodeEntry* N; - - /* New jump label */ - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a bcc insn */ - if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { - OPC = OP65_CLC; - } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { - OPC = OP65_SEC; - } else { - /* Not what we're looking for */ - goto NextEntry; - } - - /* Must have a jump target */ - if (E->JumpTo == 0) { - goto NextEntry; - } - - /* Get the owner insn of the jump target and check if it's the one, we - ** will skip if present. - */ - T = E->JumpTo->Owner; - if (T->OPC != OPC) { - goto NextEntry; - } - - /* Get the entry following the branch target */ - N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); - if (N == 0) { - /* There is no such entry */ - goto NextEntry; - } - - /* Get the label for the instruction following the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - L = CS_GenLabel (S, N); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E, L); - - /* Remember that we had changes */ - ++Changes; - -NextEntry: - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget3 (CodeSeg* S) -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a load insn with a label and the next insn is not - ** a conditional branch that needs the flags from the load. - */ - if ((E->Info & OF_LOAD) != 0 && - CE_IsConstImm (E) && - CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { - - unsigned J; - int K; - - /* New jump label */ - CodeLabel* LN = 0; - - /* Walk over all insn that jump here */ - for (J = 0; J < CE_GetLabelCount (E); ++J) { - - /* Get the label */ - CodeLabel* L = CE_GetLabel (E, J); - - /* Loop over all insn that reference this label. Since we may - ** eventually remove a reference in the loop, we must loop - ** from end down to start. - */ - for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { - - /* Get the entry that jumps here */ - CodeEntry* Jump = CL_GetRef (L, K); - - /* Get the register info from this insn */ - short Val = RegVal (E->Chg, &Jump->RI->Out2); - - /* Check if the outgoing value is the one thats's loaded */ - if (Val == (unsigned char) E->Num) { - - /* OK, skip the insn. First, generate a label for the - ** next insn after E. - */ - if (LN == 0) { - LN = CS_GenLabel (S, N); - } - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, Jump, LN); - - /* Remember that we had changes */ - ++Changes; - } - } - } - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize conditional branches */ -/*****************************************************************************/ - - - -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** -** - If an immediate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -** - If the conditional branch is always taken because of the register load, -** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a register load */ - if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ - E->AM == AM65_IMM && /* ..with immidiate addressing */ - (E->Flags & CEF_NUMARG) != 0 && /* ..and a numeric argument. */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ - !CE_HasLabel (N)) { /* ..and does not have a label */ - - /* Get the branch condition */ - bc_t BC = GetBranchCond (N->OPC); - - /* Check the argument against the branch condition */ - if ((BC == BC_EQ && E->Num != 0) || - (BC == BC_NE && E->Num == 0) || - (BC == BC_PL && (E->Num & 0x80) != 0) || - (BC == BC_MI && (E->Num & 0x80) == 0)) { - - /* Remove the conditional branch */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } else if ((BC == BC_EQ && E->Num == 0) || - (BC == BC_NE && E->Num != 0) || - (BC == BC_PL && (E->Num & 0x80) == 0) || - (BC == BC_MI && (E->Num & 0x80) != 0)) { - - /* The branch is always taken, replace it by a jump */ - CE_ReplaceOPC (N, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - } - - } - - if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ - (L = E->JumpTo) != 0 && /* ..referencing a local label */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ - !CE_HasLabel (N) && /* ..has no label attached */ - L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ - - /* Replace the jump by a conditional branch with the inverse branch - ** condition than the branch around it. - */ - CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); - - /* Remove the conditional branch */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptCondBranches2 (CodeSeg* S) -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry flag. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a rol insn with A in accu and a branch follows */ - if (E->OPC == OP65_ROL && - E->AM == AM65_ACC && - E->RI->In.RegA == 0 && - !CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !RegAUsed (S, I+1)) { - - /* Replace the branch condition */ - switch (GetBranchCond (N->OPC)) { - case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; - case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; - default: Internal ("Unknown branch condition in OptCondBranches2"); - } - - /* Delete the rol insn */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Remove unused loads and stores */ /*****************************************************************************/ @@ -1132,6 +260,70 @@ unsigned OptUnusedStores (CodeSeg* S) +unsigned OptLoad3 (CodeSeg* S) +/* Remove repeated loads from one and the same memory location */ +{ + unsigned Changes = 0; + CodeEntry* Load = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Forget a preceeding load if we have a label */ + if (Load && CE_HasLabel (E)) { + Load = 0; + } + + /* Check if this insn is a load */ + if (E->Info & OF_LOAD) { + + CodeEntry* N; + + /* If we had a preceeding load that is identical, remove this one. + ** If it is not identical, or we didn't have one, remember it. + */ + if (Load != 0 && + E->OPC == Load->OPC && + E->AM == Load->AM && + ((E->Arg == 0 && Load->Arg == 0) || + strcmp (E->Arg, Load->Arg) == 0) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_CBRA) == 0) { + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Next insn */ + continue; + + } else { + + Load = E; + + } + + } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { + /* Forget the first load on occurance of any insn we don't like */ + Load = 0; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptDupLoads (CodeSeg* S) /* Remove loads of registers where the value loaded is already in the register. */ { @@ -2138,172 +1330,3 @@ unsigned OptPrecalc (CodeSeg* S) /* Return the number of changes made */ return Changes; } - - - -/*****************************************************************************/ -/* Optimize branch types */ -/*****************************************************************************/ - - - -unsigned OptBranchDist (CodeSeg* S) -/* Change branches for the distance needed. */ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a conditional branch to a local label. */ - if (E->Info & OF_CBRA) { - - /* Is this a branch to a local symbol? */ - if (E->JumpTo != 0) { - - /* Check if the branch distance is short */ - int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); - - /* Make the branch short/long according to distance */ - if ((E->Info & OF_LBRA) == 0 && !IsShort) { - /* Short branch but long distance */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - } else if ((E->Info & OF_LBRA) != 0 && IsShort) { - /* Long branch but short distance */ - CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); - ++Changes; - } - - } else if ((E->Info & OF_LBRA) == 0) { - - /* Short branch to external symbol - make it long */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - - } - - } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && - (E->Info & OF_UBRA) != 0 && - E->JumpTo != 0 && - IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { - - /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ - CE_ReplaceOPC (E, OP65_BRA); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize indirect loads */ -/*****************************************************************************/ - - - -unsigned OptIndLoads1 (CodeSeg* S) -/* Change -** -** lda (zp),y -** -** into -** -** lda (zp,x) -** -** provided that x and y are both zero. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZP_INDY && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { - - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptIndLoads2 (CodeSeg* S) -/* Change -** -** lda (zp,x) -** -** into -** -** lda (zp),y -** -** provided that x and y are both zero. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZPX_IND && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { - - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 90e27d547..64acb10d8 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -49,69 +49,15 @@ -unsigned OptRTSJumps1 (CodeSeg* S); -/* Replace jumps to RTS by RTS */ - -unsigned OptRTSJumps2 (CodeSeg* S); -/* Replace long conditional jumps to RTS */ - -unsigned OptDeadJumps (CodeSeg* S); -/* Remove dead jumps (jumps to the next instruction) */ - -unsigned OptDeadCode (CodeSeg* S); -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ - -unsigned OptJumpCascades (CodeSeg* S); -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ - -unsigned OptRTS (CodeSeg* S); -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ - -unsigned OptJumpTarget1 (CodeSeg* S); -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ - -unsigned OptJumpTarget2 (CodeSeg* S); -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ - -unsigned OptJumpTarget3 (CodeSeg* S); -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ - -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -*/ - -unsigned OptCondBranches2 (CodeSeg* S); -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry. -*/ - unsigned OptUnusedLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is not used later. */ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ +unsigned OptLoad3 (CodeSeg* S); +/* Remove repeated loads from one and the same memory location */ + unsigned OptDupLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is already in the register. */ @@ -144,33 +90,6 @@ unsigned OptPrecalc (CodeSeg* S); ** known by a load of the final value. */ -unsigned OptBranchDist (CodeSeg* S); -/* Change branches for the distance needed. */ - -unsigned OptIndLoads1 (CodeSeg* S); -/* Change -** -** lda (zp),y -** -** into -** -** lda (zp,x) -** -** provided that x and y are both zero. -*/ - -unsigned OptIndLoads2 (CodeSeg* S); -/* Change -** -** lda (zp,x) -** -** into -** -** lda (zp),y -** -** provided that x and y are both zero. -*/ - /* End of coptind.h */ diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c new file mode 100644 index 000000000..693c7eb79 --- /dev/null +++ b/src/cc65/coptjmp.c @@ -0,0 +1,1009 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.c */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "cpu.h" + +/* cc65 */ +#include "codeent.h" +#include "coptjmp.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) +/* Get the branch distance between the two entries and return it. The distance +** will be negative for backward jumps and positive for forward jumps. +*/ +{ + /* Get the index of the branch target */ + unsigned TI = CS_GetEntryIndex (S, To); + + /* Determine the branch distance */ + int Distance = 0; + if (TI >= From) { + /* Forward branch, do not count the current insn */ + unsigned J = From+1; + while (J < TI) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance += N->Size; + } + } else { + /* Backward branch */ + unsigned J = TI; + while (J < From) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance -= N->Size; + } + } + + /* Return the calculated distance */ + return Distance; +} + + + +static int IsShortDist (int Distance) +/* Return true if the given distance is a short branch distance */ +{ + return (Distance >= -125 && Distance <= 125); +} + + + +static short ZPRegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given zeropage register */ +{ + if ((Use & REG_TMP1) != 0) { + return RC->Tmp1; + } else if ((Use & REG_PTR1_LO) != 0) { + return RC->Ptr1Lo; + } else if ((Use & REG_PTR1_HI) != 0) { + return RC->Ptr1Hi; + } else if ((Use & REG_SREG_LO) != 0) { + return RC->SRegLo; + } else if ((Use & REG_SREG_HI) != 0) { + return RC->SRegHi; + } else { + return UNKNOWN_REGVAL; + } +} + + + +static short RegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given register */ +{ + if ((Use & REG_A) != 0) { + return RC->RegA; + } else if ((Use & REG_X) != 0) { + return RC->RegX; + } else if ((Use & REG_Y) != 0) { + return RC->RegY; + } else { + return ZPRegVal (Use, RC); + } +} + + + +/*****************************************************************************/ +/* Optimize branch types */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S) +/* Change branches for the distance needed. */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a conditional branch to a local label. */ + if (E->Info & OF_CBRA) { + + /* Is this a branch to a local symbol? */ + if (E->JumpTo != 0) { + + /* Check if the branch distance is short */ + int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); + + /* Make the branch short/long according to distance */ + if ((E->Info & OF_LBRA) == 0 && !IsShort) { + /* Short branch but long distance */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + } else if ((E->Info & OF_LBRA) != 0 && IsShort) { + /* Long branch but short distance */ + CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); + ++Changes; + } + + } else if ((E->Info & OF_LBRA) == 0) { + + /* Short branch to external symbol - make it long */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + + } + + } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && + (E->Info & OF_UBRA) != 0 && + E->JumpTo != 0 && + IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { + + /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ + CE_ReplaceOPC (E, OP65_BRA); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Replace jumps to RTS by RTS */ +/*****************************************************************************/ + + + +unsigned OptRTSJumps1 (CodeSeg* S) +/* Replace jumps to RTS by RTS */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch to a local target */ + if ((E->Info & OF_UBRA) != 0 && + E->JumpTo != 0 && + E->JumpTo->Owner->OPC == OP65_RTS) { + + /* Insert an RTS instruction */ + CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptRTSJumps2 (CodeSeg* S) +/* Replace long conditional jumps to RTS or to a final target */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S) - 1) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an conditional branch to a local target */ + if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ + (E->Info & OF_LBRA) != 0 && /* Long branch */ + E->JumpTo != 0) { /* Local label */ + + + /* Get the jump target and the next entry. There's always a next + ** entry, because we don't cover the last entry in the loop. + */ + CodeEntry* X = 0; + CodeEntry* T = E->JumpTo->Owner; + CodeEntry* N = CS_GetNextEntry (S, I); + + /* Check if it's a jump to an RTS insn */ + if (T->OPC == OP65_RTS) { + + /* It's a jump to RTS. Create a conditional branch around an + ** RTS insn. + */ + X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); + + } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { + + /* It's a jump to a label outside the function. Create a + ** conditional branch around a jump to the external label. + */ + X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); + + } + + /* If we have a replacement insn, insert it */ + if (X) { + + CodeLabel* LN; + opc_t NewBranch; + + /* Insert the new insn */ + CS_InsertEntry (S, X, I+1); + + /* Create a conditional branch with the inverse condition + ** around the replacement insn + */ + + /* Get the new branch opcode */ + NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); + + /* Get the label attached to N, create a new one if needed */ + LN = CS_GenLabel (S, N); + + /* Generate the branch */ + X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the long branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove dead jumps */ +/*****************************************************************************/ + + + +unsigned OptDeadJumps (CodeSeg* S) +/* Remove dead jumps (jumps to the next instruction) */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a branch, if it has a local target, and if the target + ** is the next instruction. + */ + if (E->AM == AM65_BRA && + E->JumpTo && + E->JumpTo->Owner == CS_GetNextEntry (S, I)) { + + /* Delete the dead jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove dead code */ +/*****************************************************************************/ + + + +unsigned OptDeadCode (CodeSeg* S) +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* LN; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch, and if the next entry has + ** no labels attached, or if the label is just used so that the insn + ** can jump to itself. + */ + if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ + (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ + (!CE_HasLabel (N) || /* Don't has a label */ + ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ + (LN = N->JumpTo) != 0 && /* Jumps to known label */ + LN->Owner == N && /* Attached to insn */ + CL_GetRefCount (LN) == 1))) { /* Only reference */ + + /* Delete the next entry */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump cascades */ +/*****************************************************************************/ + + + +unsigned OptJumpCascades (CodeSeg* S) +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* OldLabel; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check: + ** - if it's a branch, + ** - if it has a jump label, + ** - if this jump label is not attached to the instruction itself, + ** - if the target instruction is itself a branch, + ** - if either the first branch is unconditional or the target of + ** the second branch is internal to the function. + ** The latter condition will avoid conditional branches to targets + ** outside of the function (usually incspx), which won't simplify the + ** code, since conditional far branches are emulated by a short branch + ** around a jump. + */ + if ((E->Info & OF_BRA) != 0 && + (OldLabel = E->JumpTo) != 0 && + (N = OldLabel->Owner) != E && + (N->Info & OF_BRA) != 0 && + ((E->Info & OF_CBRA) == 0 || + N->JumpTo != 0)) { + + /* Check if we can use the final target label. That is the case, + ** if the target branch is an absolute branch; or, if it is a + ** conditional branch checking the same condition as the first one. + */ + if ((N->Info & OF_UBRA) != 0 || + ((E->Info & OF_CBRA) != 0 && + GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { + + /* This is a jump cascade and we may jump to the final target, + ** provided that the other insn does not jump to itself. If + ** this is the case, we can also jump to ourselves, otherwise + ** insert a jump to the new instruction and remove the old one. + */ + CodeEntry* X; + CodeLabel* LN = N->JumpTo; + + if (LN != 0 && LN->Owner == N) { + + /* We found a jump to a jump to itself. Replace our jump + ** by a jump to itself. + */ + CodeLabel* LE = CS_GenLabel (S, E); + X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); + + } else { + + /* Jump to the final jump target */ + X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); + + } + + /* Insert it behind E */ + CS_InsertEntry (S, X, I+1); + + /* Remove E */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Check if both are conditional branches, and the condition of + ** the second is the inverse of that of the first. In this case, + ** the second branch will never be taken, and we may jump directly + ** to the instruction behind this one. + */ + } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { + + CodeEntry* X; /* Instruction behind N */ + CodeLabel* LX; /* Label attached to X */ + + /* Get the branch conditions of both branches */ + bc_t BC1 = GetBranchCond (E->OPC); + bc_t BC2 = GetBranchCond (N->OPC); + + /* Check the branch conditions */ + if (BC1 != GetInverseCond (BC2)) { + /* Condition not met */ + goto NextEntry; + } + + /* We may jump behind this conditional branch. Get the + ** pointer to the next instruction + */ + if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { + /* N is the last entry, bail out */ + goto NextEntry; + } + + /* Get the label attached to X, create a new one if needed */ + LX = CS_GenLabel (S, X); + + /* Move the reference from E to the new label */ + CS_MoveLabelRef (S, E, LX); + + /* Remember, we had changes */ + ++Changes; + } + } + +NextEntry: + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jsr/rts */ +/*****************************************************************************/ + + + +unsigned OptRTS (CodeSeg* S) +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a subroutine call and if the following insn is RTS */ + if (E->OPC == OP65_JSR && + (N = CS_GetNextEntry (S, I)) != 0 && + N->OPC == OP65_RTS) { + + /* Change the jsr to a jmp and use the additional info for a jump */ + E->AM = AM65_BRA; + CE_ReplaceOPC (E, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump targets */ +/*****************************************************************************/ + + + +unsigned OptJumpTarget1 (CodeSeg* S) +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ +{ + unsigned Changes = 0; + CodeEntry* E1; /* Entry 1 */ + CodeEntry* E2; /* Entry 2 */ + CodeEntry* T1; /* Jump target entry 1 */ + CodeLabel* TL1; /* Target label 1 */ + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + E2 = CS_GetNextEntry (S, I); + + /* Check if we have a jump or branch without a label attached, and + ** a jump target, which is not attached to the jump itself + */ + if (E2 != 0 && + (E2->Info & OF_UBRA) != 0 && + !CE_HasLabel (E2) && + E2->JumpTo && + E2->JumpTo->Owner != E2) { + + /* Get the entry preceeding the branch target */ + T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); + if (T1 == 0) { + /* There is no such entry */ + goto NextEntry; + } + + /* The entry preceeding the branch target may not be the branch + ** insn. + */ + if (T1 == E2) { + goto NextEntry; + } + + /* Get the entry preceeding the jump */ + E1 = CS_GetEntry (S, I); + + /* Check if both preceeding instructions are identical */ + if (!CodeEntriesAreEqual (E1, T1)) { + /* Not equal, try next */ + goto NextEntry; + } + + /* Get the label for the instruction preceeding the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + TL1 = CS_GenLabel (S, T1); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E2, TL1); + + /* If the instruction preceeding the jump has labels attached, + ** move references to this label to the new label. + */ + if (CE_HasLabel (E1)) { + CS_MoveLabels (S, E1, T1); + } + + /* Remove the entry preceeding the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { +NextEntry: + /* Next entry */ + ++I; + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget2 (CodeSeg* S) +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* OP that may be skipped */ + opc_t OPC; + + /* Jump target insn, old and new */ + CodeEntry* T; + CodeEntry* N; + + /* New jump label */ + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a bcc insn */ + if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { + OPC = OP65_CLC; + } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { + OPC = OP65_SEC; + } else { + /* Not what we're looking for */ + goto NextEntry; + } + + /* Must have a jump target */ + if (E->JumpTo == 0) { + goto NextEntry; + } + + /* Get the owner insn of the jump target and check if it's the one, we + ** will skip if present. + */ + T = E->JumpTo->Owner; + if (T->OPC != OPC) { + goto NextEntry; + } + + /* Get the entry following the branch target */ + N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); + if (N == 0) { + /* There is no such entry */ + goto NextEntry; + } + + /* Get the label for the instruction following the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + L = CS_GenLabel (S, N); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E, L); + + /* Remember that we had changes */ + ++Changes; + +NextEntry: + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget3 (CodeSeg* S) +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a load insn with a label and the next insn is not + ** a conditional branch that needs the flags from the load. + */ + if ((E->Info & OF_LOAD) != 0 && + CE_IsConstImm (E) && + CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + + unsigned J; + int K; + + /* New jump label */ + CodeLabel* LN = 0; + + /* Walk over all insn that jump here */ + for (J = 0; J < CE_GetLabelCount (E); ++J) { + + /* Get the label */ + CodeLabel* L = CE_GetLabel (E, J); + + /* Loop over all insn that reference this label. Since we may + ** eventually remove a reference in the loop, we must loop + ** from end down to start. + */ + for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { + + /* Get the entry that jumps here */ + CodeEntry* Jump = CL_GetRef (L, K); + + /* Get the register info from this insn */ + short Val = RegVal (E->Chg, &Jump->RI->Out2); + + /* Check if the outgoing value is the one thats's loaded */ + if (Val == (unsigned char) E->Num) { + + /* OK, skip the insn. First, generate a label for the + ** next insn after E. + */ + if (LN == 0) { + LN = CS_GenLabel (S, N); + } + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, Jump, LN); + + /* Remember that we had changes */ + ++Changes; + } + } + } + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize conditional branches */ +/*****************************************************************************/ + + + +unsigned OptCondBranches1 (CodeSeg* S) +/* Performs several optimization steps: +** +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. +** - If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the +** inverse condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a register load */ + if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ + E->AM == AM65_IMM && /* ..with immidiate addressing */ + CE_HasNumArg (E) && /* ..and a numeric argument. */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Get the branch condition */ + bc_t BC = GetBranchCond (N->OPC); + + /* Check the argument against the branch condition */ + if ((BC == BC_EQ && E->Num != 0) || + (BC == BC_NE && E->Num == 0) || + (BC == BC_PL && (E->Num & 0x80) != 0) || + (BC == BC_MI && (E->Num & 0x80) == 0)) { + + /* Remove the conditional branch */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } else if ((BC == BC_EQ && E->Num == 0) || + (BC == BC_NE && E->Num != 0) || + (BC == BC_PL && (E->Num & 0x80) == 0) || + (BC == BC_MI && (E->Num & 0x80) != 0)) { + + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (L = E->JumpTo) != 0 && /* ..referencing a local label */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ + !CE_HasLabel (N) && /* ..has no label attached */ + L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ + + /* Replace the jump by a conditional branch with the inverse branch + ** condition than the branch around it. + */ + CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); + + /* Remove the conditional branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranches2 (CodeSeg* S) +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry flag. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a rol insn with A in accu and a branch follows */ + if (E->OPC == OP65_ROL && + E->AM == AM65_ACC && + E->RI->In.RegA == 0 && + !CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !RegAUsed (S, I+1)) { + + /* Replace the branch condition */ + switch (GetBranchCond (N->OPC)) { + case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; + case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; + default: Internal ("Unknown branch condition in OptCondBranches2"); + } + + /* Delete the rol insn */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h new file mode 100644 index 000000000..4cb7a2792 --- /dev/null +++ b/src/cc65/coptjmp.h @@ -0,0 +1,116 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.h */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTJMP_H +#define COPTJMP_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S); +/* Change branches for the distance needed. */ + +unsigned OptRTSJumps1 (CodeSeg* S); +/* Replace jumps to RTS by RTS */ + +unsigned OptRTSJumps2 (CodeSeg* S); +/* Replace long conditional jumps to RTS */ + +unsigned OptDeadJumps (CodeSeg* S); +/* Remove dead jumps (jumps to the next instruction) */ + +unsigned OptDeadCode (CodeSeg* S); +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ + +unsigned OptJumpCascades (CodeSeg* S); +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ + +unsigned OptRTS (CodeSeg* S); +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ + +unsigned OptJumpTarget1 (CodeSeg* S); +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ + +unsigned OptJumpTarget2 (CodeSeg* S); +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ + +unsigned OptJumpTarget3 (CodeSeg* S); +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ + +unsigned OptCondBranches1 (CodeSeg* S); +/* If an immidiate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +*/ + +unsigned OptCondBranches2 (CodeSeg* S); +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry. +*/ + + + +/* End of coptjmp.h */ + +#endif diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c new file mode 100644 index 000000000..523fbf17c --- /dev/null +++ b/src/cc65/coptmisc.c @@ -0,0 +1,735 @@ +/*****************************************************************************/ +/* */ +/* codemisc.c */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +/* common */ +#include "chartype.h" +#include "xsprintf.h" + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptmisc.h" +#include "error.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Decouple operations */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S) +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + const char* Arg; + + /* Get next entry and it's input register values */ + CodeEntry* E = CS_GetEntry (S, I); + const RegContents* In = &E->RI->In; + + /* Assume we have no replacement */ + CodeEntry* X = 0; + + /* Check the instruction */ + switch (E->OPC) { + + case OP65_DEA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA - 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX - 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY - 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA + 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX + 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY + 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_LDA: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDX: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDY: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_TAX: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TAY: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TXA: + if (E->RI->In.RegX >= 0) { + Arg = MakeHexArg (In->RegX); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TYA: + if (E->RI->In.RegY >= 0) { + Arg = MakeHexArg (In->RegY); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + default: + /* Avoid gcc warnings */ + break; + + } + + /* Insert the replacement if we have one */ + if (X) { + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads1 (CodeSeg* S) +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZP_INDY && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads2 (CodeSeg* S) +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZPX_IND && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack pointer ops */ +/*****************************************************************************/ + + + +static unsigned IsDecSP (const CodeEntry* E) +/* Check if this is an insn that decrements the stack pointer. If so, return +** the decrement. If not, return zero. +** The function expects E to be a subroutine call. +*/ +{ + if (strncmp (E->Arg, "decsp", 5) == 0) { + if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { + return (E->Arg[5] - '0'); + } + } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { + return E->RI->In.RegY; + } + + /* If we come here, it's not a decsp op */ + return 0; +} + + + +unsigned OptStackPtrOps (CodeSeg* S) +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + unsigned Dec1; + unsigned Dec2; + const CodeEntry* N; + + /* Get the next entry */ + const CodeEntry* E = CS_GetEntry (S, I); + + /* Check for decspn or subysp */ + if (E->OPC == OP65_JSR && + (Dec1 = IsDecSP (E)) > 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + (Dec2 = IsDecSP (N)) > 0 && + (Dec1 += Dec2) <= 255 && + !CE_HasLabel (N)) { + + CodeEntry* X; + char Buf[20]; + + /* We can combine the two */ + if (Dec1 <= 8) { + /* Insert a call to decsp */ + xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); + CS_InsertEntry (S, X, I+2); + } else { + /* Insert a call to subysp */ + const char* Arg = MakeHexArg (Dec1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); + CS_InsertEntry (S, X, I+2); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); + CS_InsertEntry (S, X, I+3); + } + + /* Delete the old code */ + CS_DelEntries (S, I, 2); + + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptGotoSPAdj (CodeSeg* S) +/* Optimize SP adjustment for forward 'goto' */ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[10], *X; + unsigned short adjustment; + const char* Arg; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence generated by g_lateadjustSP */ + if (L[0]->OPC == OP65_PHA && + CS_GetEntries (S, L+1, I+1, 9) && + L[1]->OPC == OP65_LDA && + L[1]->AM == AM65_ABS && + L[2]->OPC == OP65_CLC && + L[3]->OPC == OP65_ADC && + strcmp (L[3]->Arg, "sp") == 0 && + L[6]->OPC == OP65_ADC && + strcmp (L[6]->Arg, "sp+1") == 0 && + L[9]->OPC == OP65_JMP) { + adjustment = FindSPAdjustment (L[1]->Arg); + + if (adjustment == 0) { + /* No SP adjustment needed, remove the whole sequence */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 8) { + /* If adjustment is in range [-8, 0) we use decsp* calls */ + char Buf[20]; + adjustment = 65536 - adjustment; + xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 255) { + /* For range [-255, -8) we have ldy #, jsr subysp */ + adjustment = 65536 - adjustment; + Arg = MakeHexArg (adjustment); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment > 255) { + /* For ranges [-32768, 255) and (255, 32767) the only modification + ** is to replace the absolute with immediate addressing + */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 1); + Arg = MakeHexArg (adjustment >> 8); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); + CS_InsertEntry (S, X, I + 6); + + /* Delete the old code */ + CS_DelEntry (S, I + 2); + CS_DelEntry (S, I + 6); + } + else if (adjustment > 8) { + /* For range (8, 255] we have ldy #, jsr addysp */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else { + /* If adjustment is in range (0, 8] we use incsp* calls */ + char Buf[20]; + xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + Changes++; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack load ops */ +/*****************************************************************************/ + + + +unsigned OptLoad1 (CodeSeg* S) +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (E, "ldaxysp") && + RegValIsKnown (E->RI->In.RegY) && + !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Reload the Y register */ + const char* Arg = MakeHexArg (E->RI->In.RegY - 1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Load from stack */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptLoad2 (CodeSeg* S) +/* Replace calls to ldaxysp by inline code */ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (L[0], "ldaxysp")) { + + CodeEntry* X; + + /* Followed by sta abs/stx abs? */ + if (CS_GetEntries (S, L+1, I+1, 2) && + L[1]->OPC == OP65_STA && + L[2]->OPC == OP65_STX && + (L[1]->Arg == 0 || + L[2]->Arg == 0 || + strcmp (L[1]->Arg, L[2]->Arg) != 0) && + !CS_RangeHasLabel (S, I+1, 2) && + !RegXUsed (S, I+3)) { + + /* A/X are stored into memory somewhere and X is not used + ** later + */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); + CS_InsertEntry (S, X, I+4); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+5); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+6); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I+7); + + /* Now remove the call to the subroutine and the sta/stx */ + CS_DelEntries (S, I, 3); + + } else { + + /* Standard replacement */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+1); + + /* tax */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+4); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + } + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h new file mode 100644 index 000000000..89242351c --- /dev/null +++ b/src/cc65/coptmisc.h @@ -0,0 +1,113 @@ +/*****************************************************************************/ +/* */ +/* codemisc.h */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTMISC_H +#define COPTMISC_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S); +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ + +unsigned OptIndLoads1 (CodeSeg* S); +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ + +unsigned OptIndLoads2 (CodeSeg* S); +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ + +unsigned OptStackPtrOps (CodeSeg* S); +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ + +unsigned OptGotoSPAdj (CodeSeg* S); +/* Optimize SP adjustment for forward 'goto' */ + +unsigned OptLoad1 (CodeSeg* S); +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ + +unsigned OptLoad2 (CodeSeg* S); +/* Replace calls to ldaxysp by inline code */ + + +/* End of coptmisc.h */ + +#endif From 57117fa687de59234d299d76f406f4e666844f1f Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 23 Jan 2020 06:39:37 +0800 Subject: [PATCH 329/806] Utility functions about compare conditions. --- src/cc65/codeinfo.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/codeinfo.h | 14 ++++++++++- src/cc65/coptcmp.c | 6 +---- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index a89f6d54c..6bc62757e 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -64,6 +64,13 @@ static const char CmpSuffixTab [][4] = { "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule" }; +/* Table with the bool transformers */ +static const char BoolTransformerTab [][8] = { + "booleq", "boolne", + "boolgt", "boolge", "boollt", "boolle", + "boolugt", "booluge", "boolult", "boolule" +}; + /* Table listing the function names and code info values for known internally ** used functions. This table should get auto-generated in the future. */ @@ -840,3 +847,55 @@ cmp_t FindTosCmpCond (const char* Name) return CMP_INV; } } + + + +const char* GetBoolTransformer (cmp_t Cond) +/* Get the bool transformer corresponding to the given compare condition */ +{ + if (Cond > CMP_INV && Cond < CMP_END) { + return BoolTransformerTab[Cond]; + } + + /* Not found */ + return 0; +} + + +cmp_t GetNegatedCond (cmp_t Cond) +/* Get the logically opposite compare condition */ +{ + switch (Cond) { + case CMP_EQ: return CMP_NE; + case CMP_NE: return CMP_EQ; + case CMP_GT: return CMP_LE; + case CMP_GE: return CMP_LT; + case CMP_LT: return CMP_GE; + case CMP_LE: return CMP_GT; + case CMP_UGT: return CMP_ULE; + case CMP_UGE: return CMP_ULT; + case CMP_ULT: return CMP_UGE; + case CMP_ULE: return CMP_UGT; + default: return CMP_INV; + } +} + + + +cmp_t GetRevertedCond (cmp_t Cond) +/* Get the compare condition in reverted order of operands */ +{ + switch (Cond) { + case CMP_EQ: return CMP_EQ; + case CMP_NE: return CMP_NE; + case CMP_GT: return CMP_LT; + case CMP_GE: return CMP_LE; + case CMP_LT: return CMP_GT; + case CMP_LE: return CMP_GE; + case CMP_UGT: return CMP_ULT; + case CMP_UGE: return CMP_ULE; + case CMP_ULT: return CMP_UGT; + case CMP_ULE: return CMP_UGE; + default: return CMP_INV; + } +} diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 38e196bcf..3dda3a6bc 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -115,7 +115,10 @@ typedef enum { CMP_UGT, CMP_UGE, CMP_ULT, - CMP_ULE + CMP_ULE, + + /* End of the enumeration */ + CMP_END } cmp_t; @@ -185,6 +188,15 @@ cmp_t FindTosCmpCond (const char* Name); ** Return the condition code or CMP_INV on failure. */ +const char* GetBoolTransformer (cmp_t Cond); +/* Get the bool transformer corresponding to the given compare condition */ + +cmp_t GetNegatedCond (cmp_t Cond); +/* Get the logically opposite compare condition */ + +cmp_t GetRevertedCond (cmp_t Cond); +/* Get the compare condition in reverted order of operands */ + /* End of codeinfo.h */ diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index dbc71bcd5..ca0ba39a8 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -448,11 +448,7 @@ unsigned OptCmp3 (CodeSeg* S) Delete = 1; break; - case CMP_UGT: - case CMP_UGE: - case CMP_ULT: - case CMP_ULE: - case CMP_INV: + default: /* Leave it alone */ break; } From 7553b60ef07a53c797974a73627cd6bebcc8e8fe Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 23 Jan 2020 06:53:52 +0800 Subject: [PATCH 330/806] Improved OptStackOps for optimizating further when operands have equal hi-bytes. --- src/cc65/coptstop.c | 515 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 500 insertions(+), 15 deletions(-) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index a4fc0dc65..c366d5dde 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -898,6 +898,67 @@ static int IsRegVar (StackOpData* D) +static RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) +/* Get RegInfo of the last load insn entry */ +{ + CodeEntry* E; + + if (Reg->LoadIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->LoadIndex)) != 0) { + return E->RI; + } + + return 0; +} + + + +static int SameRegAValue (StackOpData* D) +/* Check if Rhs Reg A == Lhs Reg A */ +{ + RegInfo* LRI = GetLastChangedRegInfo (D, &D->Lhs.A); + RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.A); + + /* RHS can have a -1 LoadIndex only if it is carried over from LHS */ + if (RRI == 0 || + (D->Rhs.A.LoadIndex >= 0 && + D->Rhs.A.LoadIndex == D->Lhs.A.LoadIndex) || + (LRI != 0 && + RegValIsKnown (LRI->Out.RegA) && + RegValIsKnown (RRI->Out.RegA) && + (LRI->Out.RegA & 0xFF) == (RRI->Out.RegA & 0xFF))) { + + return 1; + } + + return 0; + +} + + + +static int SameRegXValue (StackOpData* D) +/* Check if Rhs Reg X == Lhs Reg X */ +{ + RegInfo* LRI = GetLastChangedRegInfo (D, &D->Lhs.X); + RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.X); + + if (RRI == 0 || + (D->Rhs.X.LoadIndex >= 0 && + D->Rhs.X.LoadIndex == D->Lhs.X.LoadIndex) || + (LRI != 0 && + RegValIsKnown (LRI->Out.RegX) && + RegValIsKnown (RRI->Out.RegX) && + (LRI->Out.RegX & 0xFF) == (RRI->Out.RegX & 0xFF))) { + + return 1; + } + + return 0; + +} + + + /*****************************************************************************/ /* Actual optimization functions */ /*****************************************************************************/ @@ -1850,6 +1911,251 @@ static unsigned Opt_tosxorax (StackOpData* D) +/*****************************************************************************/ +/* Optimization functions when hi-bytes can be ignored */ +/*****************************************************************************/ + + + +static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) +/* Optimize the tos compare sequence with a bool transformer */ +{ + CodeEntry* X; + cmp_t Cond; + + D->IP = D->OpIndex + 1; + + if (!D->RhsMultiChg && + (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0 && + (D->Rhs.A.Flags & LI_DIRECT) != 0) { + + /* cmp */ + AddOpLow (D, OP65_CMP, &D->Rhs); + + /* Rhs low-byte load must be removed and hi-byte load may be removed */ + D->Rhs.X.Flags |= LI_REMOVE; + D->Rhs.A.Flags |= LI_REMOVE; + + } else if ((D->Lhs.A.Flags & LI_DIRECT) != 0) { + + /* If the lhs is direct (but not stack relative), encode compares with lhs + ** effectively reverting the order (which doesn't matter for ==). + */ + Cond = FindBoolCmpCond (BoolTransformer); + Cond = GetRevertedCond (Cond); + BoolTransformer = GetBoolTransformer (Cond); + + /* This shouldn't fail */ + CHECK (BoolTransformer); + + /* cmp */ + AddOpLow (D, OP65_CMP, &D->Lhs); + + /* Lhs load entries can be removed if not used later */ + D->Lhs.X.Flags |= LI_REMOVE; + D->Lhs.A.Flags |= LI_REMOVE; + + } else { + + /* We'll do reverse-compare */ + Cond = FindBoolCmpCond (BoolTransformer); + Cond = GetRevertedCond (Cond); + BoolTransformer = GetBoolTransformer (Cond); + + /* This shouldn't fail */ + CHECK (BoolTransformer); + + /* Save lhs into zeropage */ + AddStoreLhsA (D); + + /* cmp */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } + + /* Create a call to the boolean transformer function. This is needed for all + ** variants. + */ + X = NewCodeEntry (OP65_JSR, AM65_ABS, BoolTransformer, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* Remove the push and the call to the tosgeax function */ + RemoveRemainders (D); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_a_toseq (StackOpData* D) +/* Optimize the toseqax sequence */ +{ + return Opt_a_toscmpbool (D, "booleq"); +} + + + +static unsigned Opt_a_tosge (StackOpData* D) +/* Optimize the tosgeax sequence */ +{ + return Opt_a_toscmpbool (D, "boolge"); +} + + + +static unsigned Opt_a_tosgt (StackOpData* D) +/* Optimize the tosgtax sequence */ +{ + return Opt_a_toscmpbool (D, "boolgt"); +} + + + +static unsigned Opt_a_tosicmp (StackOpData* D) +/* Replace tosicmp with CMP */ +{ + CodeEntry* X; + RegInfo* RI; + const char* Arg; + + if (!SameRegAValue (D)) { + /* Because of SameRegAValue */ + CHECK (D->Rhs.A.LoadIndex >= 0); + + /* Store LHS in ZP and reload it before op */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex + 1); + X = NewCodeEntry (OP65_LDA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->OpIndex); + + D->IP = D->OpIndex + 1; + + if ((D->Rhs.A.Flags & LI_DIRECT) == 0) { + /* RHS src is not directly comparable */ + X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->Rhs.A.LoadIndex + 1); + + /* Cmp with stored RHS */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + if ((D->Rhs.A.Flags & LI_RELOAD_Y) == 0) { + /* Cmp directly with RHS src */ + X = NewCodeEntry (OP65_CMP, AM65_ZP, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } else { + /* ldy #offs */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (D->Rhs.A.Offs), 0, D->OpEntry->LI); + InsertEntry(D, X, D->IP++); + + /* cmp (sp),y */ + X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + InsertEntry(D, X, D->IP++); + } + + /* RHS may be removed */ + D->Rhs.A.Flags |= LI_REMOVE; + D->Rhs.X.Flags |= LI_REMOVE; + } + + /* Fix up the N/V flags: N = ~C, V = 0 */ + Arg = MakeHexArg (0); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + X = NewCodeEntry (OP65_SBC, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + Arg = MakeHexArg (0x01); + X = NewCodeEntry (OP65_ORA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* jeq L1 */ + CodeLabel* Label = CS_GenLabel (D->Code, CS_GetEntry (D->Code, D->IP)); + X = NewCodeEntry (OP65_JEQ, AM65_BRA, Label->Name, Label, X->LI); + InsertEntry (D, X, D->IP-3); + + } else { + /* Just clear A,Z,N and set C */ + if ((RI = GetLastChangedRegInfo (D, &D->Lhs.A)) != 0 && + RegValIsKnown (RI->Out.RegA) && + (RI->Out.RegA & 0xFF) == 0) { + Arg = MakeHexArg (0); + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 1); + } else { + Arg = MakeHexArg (0); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 1); + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->OpIndex + 2); + } + } + + /* Remove the push and the call to the operator function */ + RemoveRemainders (D); + + return 1; +} + + + +static unsigned Opt_a_tosle (StackOpData* D) +/* Optimize the tosleax sequence */ +{ + return Opt_a_toscmpbool (D, "boolle"); +} + + + +static unsigned Opt_a_toslt (StackOpData* D) +/* Optimize the tosltax sequence */ +{ + return Opt_a_toscmpbool (D, "boollt"); +} + + + +static unsigned Opt_a_tosne (StackOpData* D) +/* Optimize the toseqax sequence */ +{ + return Opt_a_toscmpbool (D, "boolne"); +} + + + +static unsigned Opt_a_tosuge (StackOpData* D) +/* Optimize the tosugeax sequence */ +{ + return Opt_a_toscmpbool (D, "booluge"); +} + + + +static unsigned Opt_a_tosugt (StackOpData* D) +/* Optimize the tosugtax sequence */ +{ + return Opt_a_toscmpbool (D, "boolugt"); +} + + + +static unsigned Opt_a_tosule (StackOpData* D) +/* Optimize the tosuleax sequence */ +{ + return Opt_a_toscmpbool (D, "boolule"); +} + + + +static unsigned Opt_a_tosult (StackOpData* D) +/* Optimize the tosultax sequence */ +{ + return Opt_a_toscmpbool (D, "boolult"); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -1878,7 +2184,22 @@ static const OptFuncDesc FuncTable[] = { { "tosultax", Opt_tosultax, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosxorax", Opt_tosxorax, REG_NONE, OP_NONE }, }; -#define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0])) + +static const OptFuncDesc FuncRegATable[] = { + { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, + { "tosgeax", Opt_a_tosge, REG_NONE, OP_NONE }, + { "tosgtax", Opt_a_tosgt, REG_NONE, OP_NONE }, + { "tosicmp", Opt_a_tosicmp, REG_NONE, OP_NONE }, + { "tosleax", Opt_a_tosle, REG_NONE, OP_NONE }, + { "tosltax", Opt_a_toslt, REG_NONE, OP_NONE }, + { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, + { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, + { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, + { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, + { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, +}; + +#define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) @@ -1890,12 +2211,12 @@ static int CmpFunc (const void* Key, const void* Func) -static const OptFuncDesc* FindFunc (const char* Name) +static const OptFuncDesc* FindFunc (const OptFuncDesc FuncTable[], size_t Count, const char* Name) /* Find the function with the given name. Return a pointer to the table entry ** or NULL if the function was not found. */ { - return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc); + return bsearch (Name, FuncTable, Count, sizeof(OptFuncDesc), CmpFunc); } @@ -1990,7 +2311,7 @@ static void ResetStackOpData (StackOpData* Data) static int PreCondOk (StackOpData* D) /* Check if the preconditions for a call to the optimizer subfunction are ** satisfied. As a side effect, this function will also choose the zero page -** register to use. +** register to use for temporary storage. */ { LoadInfo* Lhs; @@ -2136,6 +2457,160 @@ static int PreCondOk (StackOpData* D) +static int RegAPreCondOk (StackOpData* D) +/* Check if the preconditions for a call to the RegA-only optimizer subfunction +** are satisfied. As a side effect, this function will also choose the zero page +** register to use for temporary storage. +*/ +{ + LoadInfo* Lhs; + LoadInfo* Rhs; + LoadRegInfo* LhsLo; + LoadRegInfo* RhsLo; + short LhsLoVal, LhsHiVal; + short RhsLoVal, RhsHiVal; + int I; + int Passed = 0; + + /* Check the flags */ + unsigned UnusedRegs = D->OptFunc->UnusedRegs; + if (UnusedRegs != REG_NONE && + (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) { + /* Cannot optimize */ + return 0; + } + + Passed = 0; + LhsLoVal = D->PushEntry->RI->In.RegA; + LhsHiVal = D->PushEntry->RI->In.RegX; + RhsLoVal = D->OpEntry->RI->In.RegA; + RhsHiVal = D->OpEntry->RI->In.RegX; + /* Check normally first, then interchange A/X and check again if necessary */ + for (I = (D->OptFunc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + + do { + if (LhsHiVal != RhsHiVal) { + /* Cannot optimize */ + break; + } + if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 && + RegValIsUnknown (LhsLoVal)) { + /* Cannot optimize */ + break; + } + if ((D->OptFunc->Flags & OP_X_ZERO) != 0 && + LhsHiVal != 0) { + /* Cannot optimize */ + break; + } + Passed = 1; + } while (0); + + /* Suppress warning about unused assignment in GCC */ + (void)RhsLoVal; + + /* Interchange A/X */ + LhsLoVal = D->PushEntry->RI->In.RegX; + LhsHiVal = D->PushEntry->RI->In.RegA; + RhsLoVal = D->OpEntry->RI->In.RegX; + RhsHiVal = D->OpEntry->RI->In.RegA; + } + if (!Passed) { + /* Cannot optimize */ + return 0; + } + + Passed = 0; + Lhs = &D->Lhs; + Rhs = &D->Rhs; + /* Check normally first, then interchange LHS/RHS and check again if necessary */ + for (I = (D->OptFunc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + + do { + LhsLo = &Lhs->A; + RhsLo = &Rhs->A; + /* Currently we have only LHS/RHS checks with identical requirements for A/X, + ** so we don't need to check twice for now. + */ + + if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) { + if ((LhsLo->Flags & LI_LOAD_INSN) == 0) { + /* Cannot optimize */ + break; + } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) { + if ((LhsLo->Flags & LI_DIRECT) == 0) { + /* Cannot optimize */ + break; + } + } + } + if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) { + if ((RhsLo->Flags & LI_LOAD_INSN) == 0) { + /* Cannot optimize */ + break; + } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) { + if ((RhsLo->Flags & LI_DIRECT) == 0) { + /* Cannot optimize */ + break; + } + } + } + if ((D->OptFunc->Flags & OP_LHS_REMOVE) != 0) { + /* Check if the load entries cannot be removed */ + if ((LhsLo->LoadEntry != 0 && (LhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { + if ((D->OptFunc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + } + } + if ((D->OptFunc->Flags & OP_RHS_REMOVE) != 0) { + if ((RhsLo->LoadEntry != 0 && (RhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { + if ((D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + } + } + if (D->RhsMultiChg && (D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + /* Cannot optimize */ + break; + } + Passed = 1; + } while (0); + + /* Interchange LHS/RHS for next round */ + Lhs = &D->Rhs; + Rhs = &D->Lhs; + } + if (!Passed) { + /* Cannot optimize */ + return 0; + } + + /* Determine the zero page locations to use. We've tracked the used + ** ZP locations, so try to find some for us that are unused. + */ + if ((D->ZPUsage & REG_PTR1) == REG_NONE) { + D->ZPLo = "ptr1"; + D->ZPHi = "ptr1+1"; + } else if ((D->ZPUsage & REG_SREG) == REG_NONE) { + D->ZPLo = "sreg"; + D->ZPHi = "sreg+1"; + } else if ((D->ZPUsage & REG_PTR2) == REG_NONE) { + D->ZPLo = "ptr2"; + D->ZPHi = "ptr2+1"; + } else { + /* No registers available */ + return 0; + } + + /* Determine if we have a basic block */ + return CS_IsBasicBlock (D->Code, D->PushIndex, D->OpIndex); +} + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -2191,14 +2666,15 @@ static void ResetDontRemoveEntryFlags (StackOpData* D) unsigned OptStackOps (CodeSeg* S) /* Optimize operations that take operands via the stack */ { - unsigned Changes = 0; /* Number of changes in one run */ - StackOpData Data; - int I; - int OldEntryCount; /* Old number of entries */ - unsigned Used; /* What registers would be used */ - unsigned PushedRegs; /* Track if the same regs are used after the push */ - int RhsALoadIndex; /* Track if rhs is changed more than once */ - int RhsXLoadIndex; /* Track if rhs is changed more than once */ + unsigned Changes = 0; /* Number of changes in one run */ + StackOpData Data; + int I; + int OldEntryCount; /* Old number of entries */ + unsigned Used; /* What registers would be used */ + unsigned PushedRegs; /* Track if the same regs are used after the push */ + int RhsALoadIndex; /* Track if rhs is changed more than once */ + int RhsXLoadIndex; /* Track if rhs is changed more than once */ + int IsRegAOptFunc = 0; /* Whether to use the RegA-only optimizations */ enum { Initialize, @@ -2225,6 +2701,8 @@ unsigned OptStackOps (CodeSeg* S) ** ** Since we need a zero page register later, do also check the ** intermediate code for zero page use. + ** When hibytes of both oprands are equal, we may have more specialized + ** optimization for the op. */ I = 0; while (I < (int)CS_GetEntryCount (S)) { @@ -2300,7 +2778,14 @@ unsigned OptStackOps (CodeSeg* S) /* Subroutine call: Check if this is one of the functions, ** we're going to replace. */ - Data.OptFunc = FindFunc (E->Arg); + if (SameRegXValue (&Data)) { + Data.OptFunc = FindFunc (FuncRegATable, FUNC_COUNT (FuncRegATable), E->Arg); + IsRegAOptFunc = 1; + } + if (Data.OptFunc == 0) { + Data.OptFunc = FindFunc (FuncTable, FUNC_COUNT (FuncTable), E->Arg); + IsRegAOptFunc = 0; + } if (Data.OptFunc) { /* Disallow removing the loads if the registers are used */ if (Data.UsedRegs & REG_A) { @@ -2419,11 +2904,11 @@ unsigned OptStackOps (CodeSeg* S) SetDontRemoveEntryFlags (&Data); /* Check the preconditions. If they aren't ok, reset the insn - ** pointer to the pushax and start over. We will loose part of + ** pointer to the pushax and start over. We will lose part of ** load tracking but at least a/x has probably lost between ** pushax and here and will be tracked again when restarting. */ - if (!PreCondOk (&Data)) { + if (IsRegAOptFunc ? !RegAPreCondOk (&Data) : !PreCondOk (&Data)) { /* Unflag entries that can't be removed */ ResetDontRemoveEntryFlags (&Data); I = Data.PushIndex; From 41cee0eb44de6f98910ca98255244f1d4731352d Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 23 Jan 2020 06:54:34 +0800 Subject: [PATCH 331/806] Extended support for more addressing modes in tos* optimizations. --- src/cc65/coptstop.c | 185 ++++++++++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index c366d5dde..f4f34d163 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -59,12 +59,14 @@ typedef enum { LI_RELOAD_Y = 0x02, /* Reload index register Y */ LI_REMOVE = 0x04, /* Load may be removed */ LI_DONT_REMOVE = 0x08, /* Load may not be removed */ - LI_MAYBE_DIRECT = 0x10, /* Load src might be modified later */ + LI_CHECK_ARG = 0x10, /* Load src might be modified later */ LI_SRC_CHG = 0x20, /* Load src is possibly modified */ LI_LOAD_INSN = 0x40, /* Has a load insn */ + LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ LI_USED_BY_A = 0x100, /* Content used by RegA */ LI_USED_BY_X = 0x200, /* Content used by RegX */ LI_USED_BY_Y = 0x400, /* Content used by RegY */ + LI_SP = 0x800, /* Content on stack */ } LI_FLAGS; /* Structure that tells us how to load the lhs values */ @@ -73,6 +75,8 @@ struct LoadRegInfo { LI_FLAGS Flags; /* Tells us how to load */ int LoadIndex; /* Index of load insn, -1 if invalid */ CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ + int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ + CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ int XferIndex; /* Index of transfer insn */ CodeEntry* XferEntry; /* The actual transfer entry */ int Offs; /* Stack offset if data is on stack */ @@ -178,12 +182,14 @@ struct StackOpData { static void ClearLoadRegInfo (LoadRegInfo* RI) /* Clear a LoadRegInfo struct */ { - RI->Flags = LI_NONE; - RI->LoadIndex = -1; - RI->LoadEntry = 0; - RI->XferIndex = -1; - RI->XferEntry = 0; - RI->Offs = 0; + RI->Flags = LI_NONE; + RI->LoadIndex = -1; + RI->LoadEntry = 0; + RI->LoadYIndex = -1; + RI->LoadYEntry = 0; + RI->XferIndex = -1; + RI->XferEntry = 0; + RI->Offs = 0; } @@ -191,12 +197,14 @@ static void ClearLoadRegInfo (LoadRegInfo* RI) static void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) /* Copy a LoadRegInfo struct */ { - To->Flags = From->Flags; - To->LoadIndex = From->LoadIndex; - To->LoadEntry = From->LoadEntry; - To->XferIndex = From->XferIndex; - To->XferEntry = From->XferEntry; - To->Offs = From->Offs; + To->Flags = From->Flags; + To->LoadIndex = From->LoadIndex; + To->LoadEntry = From->LoadEntry; + To->LoadYIndex = From->LoadYIndex; + To->LoadYEntry = From->LoadYEntry; + To->XferIndex = From->XferIndex; + To->XferEntry = From->XferEntry; + To->Offs = From->Offs; } @@ -216,8 +224,18 @@ static void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S) RI->XferEntry = 0; } /* Load from src not modified before op can be treated as direct */ - if ((RI->Flags & (LI_MAYBE_DIRECT | LI_SRC_CHG)) == LI_MAYBE_DIRECT) { + if ((RI->Flags & LI_SRC_CHG) == 0 && + (RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { RI->Flags |= LI_DIRECT; + if ((RI->Flags & LI_CHECK_Y) != 0) { + RI->Flags |= LI_RELOAD_Y; + } + } + /* We cannot ldy src,y */ + if ((RI->Flags & LI_RELOAD_Y) != 0 && + RI->LoadYEntry != 0 && + (RI->LoadYEntry->Use & REG_Y) == REG_Y) { + RI->Flags &= ~LI_DIRECT; } } @@ -305,17 +323,27 @@ static int Affected (LoadRegInfo* RI, const CodeEntry* E) fncls_t fncls; unsigned short Use; unsigned short Chg; + unsigned short UseToCheck = 0; - if (RI->Flags & LI_MAYBE_DIRECT) { + if ((RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { return 0; } - CHECK (RI->LoadEntry != 0); + CHECK ((RI->Flags & LI_CHECK_ARG) == 0 || RI->LoadEntry != 0); + CHECK ((RI->Flags & LI_CHECK_Y) == 0 || RI->LoadYEntry != 0); + + if ((RI->Flags & LI_CHECK_ARG) != 0) { + UseToCheck |= RI->LoadEntry->Use; + } + + if ((RI->Flags & LI_CHECK_Y) != 0) { + UseToCheck |= RI->LoadYEntry->Use; + } if (E->OPC == OP65_JSR) { /* Try to know about the function */ fncls = GetFuncInfo (E->Arg, &Use, &Chg); - if ((RI->LoadEntry->Use & Chg & REG_ALL) == 0 && + if ((UseToCheck & Chg & REG_ALL) == 0 && fncls == FNCLS_BUILTIN) { /* Builtin functions are known to be harmless */ return 0; @@ -327,8 +355,15 @@ static int Affected (LoadRegInfo* RI, const CodeEntry* E) E->OPC == OP65_ROL || E->OPC == OP65_ROR || E->OPC == OP65_TRB || E->OPC == OP65_TSB || E->OPC == OP65_STA || E->OPC == OP65_STX || E->OPC == OP65_STY) { - if ((E->AM == AM65_ABS || E->AM == AM65_ZP) && - strcmp (RI->LoadEntry->Arg, E->Arg) != 0) { + if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { + if ((RI->Flags & LI_CHECK_ARG) != 0 && + strcmp (RI->LoadEntry->Arg, E->Arg) == 0) { + return 1; + } + if ((RI->Flags & LI_CHECK_Y) != 0 && + strcmp (RI->LoadYEntry->Arg, E->Arg) == 0) { + return 1; + } return 0; } /* We could've check further for more cases where the load target isn't modified, @@ -398,11 +433,11 @@ static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) RI->Flags |= LI_DIRECT; } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_MAYBE_DIRECT; - /* Watch for any change of the load target */ - RI->LoadEntry = CS_GetEntry (S, I); + RI->Flags |= LI_CHECK_ARG; + } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + /* These insns are replaceable only if they are not modified later */ + RI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; } else if (E->AM == AM65_ZP_INDY && - RegValIsKnown (E->RI->In.RegY) && strcmp (E->Arg, "sp") == 0) { /* A load from the stack with known offset is also ok, but in this ** case we must reload the index register later. Please note that @@ -410,16 +445,34 @@ static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) ** these locations may change between the push and the actual ** operation. */ - RI->Offs = (unsigned char) E->RI->In.RegY; - RI->Flags |= (LI_DIRECT | LI_RELOAD_Y); + RI->Flags |= LI_DIRECT | LI_CHECK_Y | LI_SP; /* Reg Y can be regarded as unused if this load is removed */ Used &= ~REG_Y; - LI->Y.Flags |= LI_USED_BY_A; + if (RI == &LI->A) { + LI->Y.Flags |= LI_USED_BY_A; + } else { + LI->Y.Flags |= LI_USED_BY_X; + } + } + + /* If the load offset has a known value, we can just remember and reload + ** it into the index register later. + */ + if ((RI->Flags & LI_CHECK_Y) != 0) { + if (RegValIsKnown (E->RI->In.RegY)) { + RI->Offs = (unsigned char)E->RI->In.RegY; + RI->Flags &= ~LI_CHECK_Y; + RI->Flags |= LI_RELOAD_Y; + } else { + /* We need to check if the src of Y is changed */ + RI->LoadYIndex = LI->Y.LoadIndex; + RI->LoadYEntry = CS_GetEntry (S, RI->LoadYIndex); + } } /* Watch for any change of the load target */ - if ((RI->Flags & LI_MAYBE_DIRECT) != 0) { + if ((RI->Flags & LI_CHECK_ARG) != 0) { RI->LoadEntry = CS_GetEntry (S, I); } @@ -462,23 +515,25 @@ static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) } /* Transfer the data */ - Tgt->LoadIndex = Src->LoadIndex; - Tgt->LoadEntry = Src->LoadEntry; - Tgt->XferIndex = I; - Tgt->Offs = Src->Offs; - Tgt->Flags = Src->Flags; + Tgt->LoadIndex = Src->LoadIndex; + Tgt->LoadEntry = Src->LoadEntry; + Tgt->LoadYIndex = Src->LoadYIndex; + Tgt->LoadYEntry = Src->LoadYEntry; + Tgt->XferIndex = I; + Tgt->Offs = Src->Offs; + Tgt->Flags = Src->Flags; } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) { /* Both registers set, Y changed */ LI->A.LoadIndex = I; LI->A.XferIndex = -1; - LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y); + LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; LI->X.LoadIndex = I; LI->X.XferIndex = -1; - LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y); + LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); LI->X.Offs = (unsigned char) E->RI->In.RegY; /* Reg Y can be regarded as unused if this load is removed */ @@ -658,10 +713,10 @@ static void AdjustStackOffset (StackOpData* D, unsigned Offs) /* If we have rhs load insns that load from stack, we'll have to adjust ** the offsets for these also. */ - if (D->Rhs.A.Flags & LI_RELOAD_Y) { + if ((D->Rhs.A.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { D->Rhs.A.Offs -= Offs; } - if (D->Rhs.X.Flags & LI_RELOAD_Y) { + if ((D->Rhs.X.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { D->Rhs.X.Offs -= Offs; } } @@ -727,13 +782,22 @@ static void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI) } else { - /* ldy #offs */ - const char* Arg = MakeHexArg (LI->A.Offs); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); + if ((LI->A.Flags & LI_CHECK_Y) == 0) { + /* ldy #offs */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->A.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->A.LoadYEntry->AM, LI->A.LoadYEntry->Arg, 0, D->OpEntry->LI); + } InsertEntry (D, X, D->IP++); - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + if (LI->A.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); + } InsertEntry (D, X, D->IP++); } @@ -781,13 +845,22 @@ static void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) } else { - /* ldy #const */ - const char* Arg = MakeHexArg (LI->X.Offs); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI); + if ((LI->A.Flags & LI_CHECK_Y) == 0) { + /* ldy #const */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->X.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->X.LoadYEntry->AM, LI->X.LoadYEntry->Arg, 0, D->OpEntry->LI); + } InsertEntry (D, X, D->IP++); - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + if (LI->X.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); + } InsertEntry (D, X, D->IP++); } @@ -2047,12 +2120,22 @@ static unsigned Opt_a_tosicmp (StackOpData* D) InsertEntry (D, X, D->IP++); } else { /* ldy #offs */ - X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (D->Rhs.A.Offs), 0, D->OpEntry->LI); - InsertEntry(D, X, D->IP++); + if ((D->Rhs.A.Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (D->Rhs.A.Offs), 0, D->OpEntry->LI); + } else { + X = NewCodeEntry (OP65_LDY, D->Rhs.A.LoadYEntry->AM, D->Rhs.A.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); - /* cmp (sp),y */ - X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); - InsertEntry(D, X, D->IP++); + /* cmp src,y OR cmp (sp),y*/ + if (D->Rhs.A.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OP65_CMP, D->Rhs.A.LoadEntry->AM, D->Rhs.A.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); } /* RHS may be removed */ From 4e4e4c2d2194c2c1f73d8c4503b4c6c56e1aa9a7 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 30 Aug 2020 20:47:25 +0200 Subject: [PATCH 332/806] Allow char bit-fields These are not required to be supported (only int, signed int, and unsigned int are required), but most compilers support it. https://port70.net/~nsz/c/c89/c89-draft.html#3.5.2.1 https://port70.net/~nsz/c/c89/c89-draft.html#A.6.5.8 For consistency with other integral types, plain `char` bit-fields are unsigned, regardless of the `--signed-chars` option. Fixes #1047 --- doc/cc65.sgml | 5 + src/cc65/declare.c | 6 +- src/cc65/symtab.c | 8 +- test/{err => val}/bug1047.c | 0 test/val/char-bitfield.c | 280 ++++++++++++++++++++++++++++++++++++ test/val/enum-bitfield.c | 107 ++++++++++++++ 6 files changed, 401 insertions(+), 5 deletions(-) rename test/{err => val}/bug1047.c (100%) create mode 100644 test/val/char-bitfield.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index e7b8f260d..e3d9694fe 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -807,6 +807,11 @@ This cc65 version has some extensions to the ISO C standard. cc65 supports bit-fields of any integral type that is int-sized or + smaller, and enumerated types with those types as their underlying + type. (Only Computed gotos, a GCC extension, has limited support. With it you can use fast jump tables from C. You can take the address of a label with a double ampersand, putting them in a static const array of type void *. diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 857021671..8d0a1097c 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -764,9 +764,9 @@ static int ParseFieldWidth (Declaration* Decl) /* TODO: This can be relaxed to be any integral type, but ** ParseStructInit currently only supports up to int. */ - if (SizeOf (Decl->Type) != SizeOf (type_uint)) { - /* Only int sized types may be used for bit-fields for now */ - Error ("cc65 currently only supports unsigned int bit-fields"); + if (SizeOf (Decl->Type) > SizeOf (type_uint)) { + /* Only int-sized or smaller types may be used for bit-fields for now */ + Error ("cc65 currently only supports char-sized and int-sized bit-fields"); return -1; } diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 5546d450c..56c868b2a 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -870,9 +870,13 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, if (!SignednessSpecified) { /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, ** since this is allowed for bit-fields and avoids sign-extension, so is much faster. - ** enums set SignednessSpecified to 1 to avoid this adjustment. + ** enums set SignednessSpecified to 1 to avoid this adjustment. Character types + ** actually distinguish 3 types of char; char may either be signed or unsigned, which + ** is controlled by `--signed-chars`. In bit-fields, however, we perform the same + ** `char -> unsigned char` adjustment that is performed with other integral types. */ - CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED); + CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || + IsTypeChar (Entry->Type)); Entry->Type->C &= ~T_MASK_SIGN; Entry->Type->C |= T_SIGN_UNSIGNED; } diff --git a/test/err/bug1047.c b/test/val/bug1047.c similarity index 100% rename from test/err/bug1047.c rename to test/val/bug1047.c diff --git a/test/val/char-bitfield.c b/test/val/char-bitfield.c new file mode 100644 index 000000000..0d611f127 --- /dev/null +++ b/test/val/char-bitfield.c @@ -0,0 +1,280 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of char bit-fields; see https://github.com/cc65/cc65/issues/1047 +*/ + +#include + +static unsigned char failures = 0; + +static struct four_bits { + unsigned char x : 4; +} fb = {1}; + +static void test_four_bits (void) +{ + if (sizeof (struct four_bits) != 1) { + printf ("Got sizeof (struct four_bits) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fb.x != 1) { + printf ("Got fb.x = %u, expected 1.\n", fb.x); + failures++; + } + + fb.x = 3; + + if (fb.x != 3) { + printf ("Got fb.x = %u, expected 3.\n", fb.x); + failures++; + } +} + +static struct four_bits_signed { + signed char x : 4; +} fbs = {1}; + +static void test_four_bits_signed (void) +{ + if (sizeof (struct four_bits_signed) != 1) { + printf ("Got sizeof (struct four_bits_signed) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fbs.x != 1) { + printf ("Got fbs.x = %d, expected 1.\n", fbs.x); + failures++; + } + + fbs.x = 3; + + if (fbs.x != 3) { + printf ("Got fbs.x = %d, expected 3.\n", fbs.x); + failures++; + } +} + +static struct four_bits_plain { + char x : 4; +} fbp = {1}; + +static void test_four_bits_plain (void) +{ + if (sizeof (struct four_bits_plain) != 1) { + printf ("Got sizeof (struct four_bits_plain) = %zu, expected 1.\n", + sizeof (struct four_bits)); + failures++; + } + + if (fbp.x != 1) { + printf ("Got fbp.x = %d, expected 1.\n", fbp.x); + failures++; + } + + fbp.x = 3; + + if (fbp.x != 3) { + printf ("Got fbp.x = %d, expected 3.\n", fbp.x); + failures++; + } +} + +/* + Logic is somewhat diferent for bit-fields that end a struct vs + having additional fields. +*/ + +static struct four_bits_with_char { + unsigned char x : 4; + unsigned char y; +} fbi = {1, 2}; + +static void test_four_bits_with_char (void) +{ + if (sizeof (struct four_bits_with_char) != 2) { + printf ("Got sizeof (struct four_bits_with_char) = %zu, expected 2.\n", + sizeof (struct four_bits_with_char)); + failures++; + } + + if (fbi.x != 1) { + printf ("Got fbi.x = %u, expected 1.\n", fbi.x); + failures++; + } + + if (fbi.y != 2) { + printf ("Got fbi.y = %u, expected 2.\n", fbi.y); + failures++; + } + + fbi.x = 3; + fbi.y = 17; + + if (fbi.x != 3) { + printf ("Got fbi.x = %u, expected 3.\n", fbi.x); + failures++; + } + + if (fbi.y != 17) { + printf ("Got fbi.y = %u, expected 17.\n", fbi.y); + failures++; + } +} + +static struct two_chars { + unsigned char x : 4; + unsigned char y : 4; +} o = {11, 7}; + +/* Tests that bit-fields can share allocation units. */ +static void test_two_chars (void) +{ + if (sizeof (struct two_chars) != 1) { + printf ("Got sizeof (struct two_chars) = %zu, expected 1.\n", + sizeof (struct two_chars)); + failures++; + } + + if (o.x != 11) { + printf ("Got o.x = %u, expected 11.\n", o.x); + failures++; + } + + if (o.y != 7) { + printf ("Got o.y = %u, expected 7.\n", o.y); + failures++; + } + + o.x = 3; + o.y = 4; + + if (o.x != 3) { + printf ("Got o.x = %u, expected 3.\n", o.x); + failures++; + } + + if (o.y != 4) { + printf ("Got o.y = %u, expected 4.\n", o.y); + failures++; + } +} + +static struct full_width { + unsigned char x : 8; +} fw = {255}; + +static void test_full_width (void) +{ + if (sizeof (struct full_width) != 1) { + printf ("Got sizeof (struct full_width) = %zu, expected 1.\n", + sizeof (struct full_width)); + failures++; + } + + if (fw.x != 255) { + printf ("Got fw.x = %u, expected 255.\n", fw.x); + failures++; + } + + fw.x = 42; + + if (fw.x != 42) { + printf ("Got fw.x = %u, expected 42.\n", fw.x); + failures++; + } +} + +static struct aligned_end { + unsigned char : 2; + unsigned char x : 6; + unsigned char : 3; + unsigned char y : 5; +} ae = {63, 17}; + +static void test_aligned_end (void) +{ + if (sizeof (struct aligned_end) != 2) { + printf ("Got sizeof (struct aligned_end) = %zu, expected 2.\n", + sizeof (struct aligned_end)); + failures++; + } + + if (ae.x != 63) { + printf ("Got ae.x = %u, expected 63.\n", ae.x); + failures++; + } + + if (ae.y != 17) { + printf ("Got ae.y = %u, expected 17.\n", ae.y); + failures++; + } + + ae.x = 42; + ae.y = 15; + + if (ae.x != 42) { + printf ("Got ae.x = %u, expected 42.\n", ae.x); + failures++; + } + + if (ae.y != 15) { + printf ("Got ae.y = %u, expected 15.\n", ae.y); + failures++; + } +} + +struct { signed char x : 1; } sc = {-1}; +struct { unsigned char x : 1; } uc = {1}; +struct { char x : 1; } pc = {1}; + +static void test_signedness (void) +{ + if (sc.x != -1) { + printf ("Got sc.x = %d, expected -1.\n", sc.x); + failures++; + } + + if (uc.x != 1) { + printf ("Got uc.x = %u, expected 1.\n", uc.x); + failures++; + } + + if (pc.x != 1) { + printf ("Got pc.x = %u, expected 1.\n", pc.x); + failures++; + } +} + +int main (void) +{ + test_four_bits (); + test_four_bits_with_char (); + test_two_chars (); + test_full_width (); + test_aligned_end (); + test_signedness (); + printf ("failures: %u\n", failures); + return failures; +} diff --git a/test/val/enum-bitfield.c b/test/val/enum-bitfield.c index 62e05f71f..a942091c2 100644 --- a/test/val/enum-bitfield.c +++ b/test/val/enum-bitfield.c @@ -149,10 +149,117 @@ static void test_enum_bitfield_int(void) } } +/* Enum with underlying type unsigned char. */ +enum e7uc { + E7UC_100 = 100, +}; + +static struct enum_bitfield_uchar { + enum e7uc x : 1; + enum e7uc y : 4; + enum e7uc z : 8; +} e7ucbf = {0, 10, E7UC_100}; + +static void test_enum_bitfield_uchar(void) +{ + if (sizeof (struct enum_bitfield_uchar) != 2) { + printf ("Got sizeof(struct enum_bitfield_uchar) = %zu, expected 2.\n", + sizeof(struct enum_bitfield_uchar)); + failures++; + } + + if (e7ucbf.x != 0) { + printf ("Got e7ucbf.x = %u, expected 0.\n", e7ucbf.x); + failures++; + } + if (e7ucbf.y != 10) { + printf ("Got e7ucbf.y = %u, expected 10.\n", e7ucbf.y); + failures++; + } + if (e7ucbf.z != 100) { + printf ("Got e7ucbf.z = %u, expected 100.\n", e7ucbf.z); + failures++; + } + + e7ucbf.x = -1; /* Will store 1. */ + e7ucbf.y = -1; /* Will store 15. */ + e7ucbf.z = 127; + + /* Both signed char and unsigned char are converted to int in arithmetic expressions, + ** so we write this test differently to enum_bitfield_int. + */ + if (e7ucbf.x != 1) { + printf ("Got e7ucbf.x = %u, expected 1.\n", e7ucbf.x); + failures++; + } + + if (e7ucbf.y != 15) { + printf ("Got e7ucbf.y = %u, expected 15.\n", e7ucbf.y); + failures++; + } + if (e7ucbf.z != 127) { + printf ("Got e7ucbf.z = %u, expected 127.\n", e7ucbf.z); + failures++; + } +} + +/* Enum with underlying type signed char. */ +enum e8sc { + E8SC_M1 = -1, + E8SC_100 = 100, +}; + +static struct enum_bitfield_char { + enum e8sc x : 1; + enum e8sc y : 4; + enum e8sc z : 8; +} e8scbf = {0, 5, E8SC_100}; + +static void test_enum_bitfield_char(void) +{ + if (sizeof (struct enum_bitfield_char) != 2) { + printf ("Got sizeof(struct enum_bitfield_char) = %zu, expected 2.\n", + sizeof(struct enum_bitfield_char)); + failures++; + } + + if (e8scbf.x != 0) { + printf ("Got e8scbf.x = %d, expected 0.\n", e8scbf.x); + failures++; + } + if (e8scbf.y != 5) { + printf ("Got e8scbf.y = %d, expected 10.\n", e8scbf.y); + failures++; + } + if (e8scbf.z != 100) { + printf ("Got e8scbf.z = %d, expected 100.\n", e8scbf.z); + failures++; + } + + e8scbf.x = -1; + e8scbf.y = -3; + e8scbf.z = 127; + + if (e8scbf.x != -1) { + printf ("Got e8scbf.x = %d, expected -1.\n", e8scbf.x); + failures++; + } + if (e8scbf.y != -3) { + printf ("Got e8scbf.y = %d, expected -3.\n", e8scbf.y); + failures++; + } + if (e8scbf.z != 127) { + printf ("Got e8scbf.z = %d, expected 127.\n", e8scbf.z); + failures++; + } +} + int main(void) { test_enum_bitfield_uint(); test_enum_bitfield_int(); + test_enum_bitfield_uchar(); + test_enum_bitfield_char(); printf("failures: %u\n", failures); return failures; } From ab7e9f84241c6f686999eb61069273c27dc0add5 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 8 Sep 2020 21:36:38 +0800 Subject: [PATCH 333/806] Hotfix for Issue #1250. --- src/cc65/expr.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index e1b22ac50..826e72b09 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1933,13 +1933,6 @@ void hie10 (ExprDesc* Expr) case TOK_STAR: NextToken (); ExprWithCheck (hie10, Expr); - if (ED_IsLVal (Expr) || !ED_IsLocQuasiConst (Expr)) { - /* Not a const, load the pointer into the primary and make it a - ** calculated value. - */ - LoadExpr (CF_NONE, Expr); - ED_FinalizeRValLoad (Expr); - } /* If the expression is already a pointer to function, the ** additional dereferencing operator must be ignored. A function @@ -1951,6 +1944,14 @@ void hie10 (ExprDesc* Expr) /* Expression not storable */ ED_MarkExprAsRVal (Expr); } else { + if (!ED_IsQuasiConstAddr (Expr)) { + /* Not a constant address, load the pointer into the primary + ** and make it a calculated value. + */ + LoadExpr (CF_NONE, Expr); + ED_FinalizeRValLoad (Expr); + } + if (IsClassPtr (Expr->Type)) { Expr->Type = Indirect (Expr->Type); } else { From ae6696fcb9beed9e2c5d8c2207e861ac1a0861db Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 8 Sep 2020 10:24:27 -0400 Subject: [PATCH 334/806] Removed some ambiguity from a statement. A limited number of bit-field types are required by the C standard, not by cc65. --- doc/cc65.sgml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index e3d9694fe..578217307 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -810,7 +810,7 @@ This cc65 version has some extensions to the ISO C standard. cc65 supports bit-fields of any integral type that is int-sized or smaller, and enumerated types with those types as their underlying type. (Only Computed gotos, a GCC extension, has limited support. With it you can use fast jump tables from C. You can take the address of a label with From 142b0bf9b35103361bac5a50d8c74c29335943b9 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 5 Sep 2020 11:26:00 +0800 Subject: [PATCH 335/806] Added utility functions to get names of comparison function/transformer subroutines. --- src/cc65/codeinfo.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/codeinfo.h | 14 +++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 6bc62757e..24e27ee10 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -850,6 +850,63 @@ cmp_t FindTosCmpCond (const char* Name) +const char* GetCmpSuffix (cmp_t Cond) +/* Return the compare suffix by the given a compare condition or 0 on failure */ +{ + /* Check for the correct subroutine name */ + if (Cond >= 0 && + Cond != CMP_INV && + (unsigned)Cond < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab[0])) { + return CmpSuffixTab[Cond]; + } else { + /* Not found */ + return 0; + } +} + + + +char* GetBoolCmpSuffix (char* Buf, cmp_t Cond) +/* Search for a boolean transformer subroutine (eg. booleq) by the given compare +** condition. +** Return the output buffer filled with the name of the correct subroutine or 0 +** on failure. +*/ +{ + /* Check for the correct boolean transformer subroutine name */ + const char* Suf = GetCmpSuffix (Cond); + + if (Suf != 0) { + sprintf (Buf, "bool%s", Suf); + return Buf; + } else { + /* Not found */ + return 0; + } +} + + + +char* GetTosCmpSuffix (char* Buf, cmp_t Cond) +/* Search for a TOS compare function (eg. tosgtax) by the given compare condition. +** Return the output buffer filled with the name of the correct function or 0 on +** failure. +*/ +{ + /* Check for the correct TOS function name */ + const char* Suf = GetCmpSuffix (Cond); + + if (Suf != 0) { + sprintf (Buf, "tos%sax", Suf); + return Buf; + } else { + /* Not found */ + return 0; + } +} + + + const char* GetBoolTransformer (cmp_t Cond) /* Get the bool transformer corresponding to the given compare condition */ { diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 3dda3a6bc..763b947bf 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -197,7 +197,21 @@ cmp_t GetNegatedCond (cmp_t Cond); cmp_t GetRevertedCond (cmp_t Cond); /* Get the compare condition in reverted order of operands */ +const char* GetCmpSuffix (cmp_t Cond); +/* Return the compare suffix by the given a compare condition or 0 on failure */ +char* GetBoolCmpSuffix (char* Buf, cmp_t Cond); +/* Search for a boolean transformer subroutine (eg. booleq) by the given compare +** condition. +** Return the output buffer filled with the name of the correct subroutine or 0 +** on failure. +*/ + +char* GetTosCmpSuffix (char* Buf, cmp_t Cond); +/* Search for a TOS compare function (eg. tosgtax) by the given compare condition. +** Return the output buffer filled with the name of the correct function or 0 on +** failure. +*/ /* End of codeinfo.h */ From fe3f267233fe7651ae1c4acbb626ec626bd1c7a4 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 5 Sep 2020 11:28:38 +0800 Subject: [PATCH 336/806] Added new runtime sub bcasta/bcastax/bcasteax opposing to bnega/bnegax/bnegeax. --- libsrc/runtime/bcast.s | 21 +++++++++++++++++++++ libsrc/runtime/lbcast.s | 20 ++++++++++++++++++++ src/cc65/codeent.c | 4 +++- src/cc65/codeinfo.c | 3 +++ src/cc65/coptstop.c | 1 + 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 libsrc/runtime/bcast.s create mode 100644 libsrc/runtime/lbcast.s diff --git a/libsrc/runtime/bcast.s b/libsrc/runtime/bcast.s new file mode 100644 index 000000000..bb37b6ec0 --- /dev/null +++ b/libsrc/runtime/bcast.s @@ -0,0 +1,21 @@ +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast +; + + .export bcasta, bcastax + +bcastax: + cpx #0 + bne L1 + +bcasta: + tax + beq L0 ; Zero already in X + +L1: ldx #0 + lda #1 + +L0: rts + diff --git a/libsrc/runtime/lbcast.s b/libsrc/runtime/lbcast.s new file mode 100644 index 000000000..3dee8d956 --- /dev/null +++ b/libsrc/runtime/lbcast.s @@ -0,0 +1,20 @@ +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast for longs +; + + .export bcasteax + .importzp sreg, tmp1 + +bcasteax: + stx tmp1 + ldx #0 ; High byte of result + ora tmp1 + ora sreg + ora sreg+1 + beq L0 + + lda #1 +L0: rts + diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 09ca0a9c5..0e3094962 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -851,7 +851,9 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if ((In->RegA & 0x0F) >= 8) { Out->RegA = 0; } - } else if (FindBoolCmpCond (E->Arg) != CMP_INV || + } else if (strcmp (E->Arg, "bcastax") == 0 || + strcmp (E->Arg, "bnegax") == 0 || + FindBoolCmpCond (E->Arg) != CMP_INV || FindTosCmpCond (E->Arg) != CMP_INV) { /* Result is boolean value, so X is zero on output */ Out->RegX = 0; diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 24e27ee10..f51189293 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -107,6 +107,9 @@ static const FuncInfo FuncInfoTable[] = { { "asreax2", REG_EAX, REG_EAX | REG_TMP1 }, { "asreax3", REG_EAX, REG_EAX | REG_TMP1 }, { "asreax4", REG_EAX, REG_EAXY | REG_TMP1 }, + { "bcasta", REG_A, REG_AX }, + { "bcastax", REG_AX, REG_AX }, + { "bcasteax", REG_EAX, REG_EAX | REG_TMP1 }, { "bnega", REG_A, REG_AX }, { "bnegax", REG_AX, REG_AX }, { "bnegeax", REG_EAX, REG_EAX | REG_TMP1 }, diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index f4f34d163..34a9b88d6 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -2328,6 +2328,7 @@ static int HarmlessCall (const char* Name) "asrax3", "asrax4", "asraxy", + "bcastax", "bnegax", "complax", "decax1", From 66c5faeb9a5ddef4b1622679a971e8f7a0ee1090 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 8 Sep 2020 23:40:58 +0800 Subject: [PATCH 337/806] Added processor flags usage tracking. Added ZNRegs for tracking what register(s) Z/N flags currently reflect. Added utility functions to check if the specified processor state is known to be a certain value. --- src/cc65/codeent.c | 698 ++++++++++++++++++++++++++++++++++++++------ src/cc65/codeent.h | 4 +- src/cc65/codeinfo.c | 532 ++++++++++++++++----------------- src/cc65/codeinfo.h | 27 +- src/cc65/codeseg.c | 6 + src/cc65/coptsize.c | 584 ++++++++++++++++++------------------ src/cc65/coptstop.c | 8 +- src/cc65/reginfo.c | 50 ++++ src/cc65/reginfo.h | 118 ++++++++ 9 files changed, 1371 insertions(+), 656 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 0e3094962..085130001 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -498,6 +498,228 @@ void CE_FreeRegInfo (CodeEntry* E) +static void DeduceZ (RegContents* C, short Val) +/* Auto-set Z flag */ +{ + if (RegValIsUnknown (Val)) { + C->PFlags |= UNKNOWN_PFVAL_Z; + } else { + C->PFlags &= ~UNKNOWN_PFVAL_Z; + if (Val == 0) { + C->PFlags |= PFVAL_Z; + } + } +} + + + +static void DeduceZN (RegContents* C, short Val) +/* Auto-set Z/N flags */ +{ + if (RegValIsUnknown (Val)) { + C->PFlags |= UNKNOWN_PFVAL_ZN; + } else { + C->PFlags &= ~UNKNOWN_PFVAL_ZN; + if (Val == 0) { + C->PFlags |= PFVAL_Z; + } else if (Val & PFVAL_N) { + C->PFlags |= PFVAL_N; + } + } +} + + + +static short KnownOpADCDeduceCZVN (RegContents* Out, RegContents* In, short Rhs) +/* Do the ADC and auto-set C/Z/V/N flags. +** Both operands and the C flag must be known. +*/ +{ + short SVal, UVal; + SVal = (signed char)(In->RegA & 0xFF) + (signed char)(Rhs & 0xFF); + UVal = (In->RegA & 0xFF) + (Rhs & 0xFF); + if (In->PFlags & PFVAL_C) { + ++SVal; + ++UVal; + } + + Out->PFlags &= ~UNKNOWN_PFVAL_CZVN; + if (UVal > 0xFF) { + Out->PFlags |= PFVAL_C; + } + + if (SVal < -128 || SVal > 127) { + Out->PFlags |= PFVAL_V; + } + + DeduceZN (Out, UVal); + + return UVal; +} + + + +static short KnownOpSBCDeduceCZVN (RegContents* Out, RegContents* In, short Rhs) +/* Do the SBC and auto-set C/Z/V/N flags. +** Both operands and the C flag must be known. +*/ +{ + short SVal, UVal; + SVal = (signed char)(In->RegA & 0xFF) - (signed char)(Rhs & 0xFF); + UVal = (In->RegA & 0xFF) - (Rhs & 0xFF); + if ((In->PFlags & PFVAL_C) == 0) { + --SVal; + --UVal; + } + + Out->PFlags &= ~UNKNOWN_PFVAL_CZVN; + if (UVal >= 0) { + Out->PFlags |= PFVAL_C; + } + + if (SVal < -128 || SVal > 127) { + Out->PFlags |= PFVAL_V; + } + + DeduceZN (Out, UVal); + + return UVal; +} + + + +static short KnownOpCmpDeduceCZN (RegContents* C, short Lhs, short Rhs) +/* Do the CMP and auto-set C/Z/N flags. +** Both operands must be known. + */ +{ + short Val = (Lhs & 0xFF) - (Rhs & 0xFF); + + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Val >= 0) { + C->PFlags |= PFVAL_C; + } + DeduceZN (C, Val); + + return Val; +} + + + +static short AnyOpASLDeduceCZN (RegContents* C, short Shiftee) +/* Do the ASL and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x80U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee << 1) & 0xFF; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpLSRDeduceCZN (RegContents* C, short Shiftee) +/* Do the LSR and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x01U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee >> 1) & 0xFF; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpROLDeduceCZN (RegContents* C, short PFlags, short Shiftee) +/* Do the ROL and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee) && PStatesAreKnown (PFlags, PSTATE_C)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x80U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee << 1) & 0xFF; + if (PFlags & PFVAL_C) { + Shiftee |= 0x01U; + } + } else { + Shiftee = UNKNOWN_REGVAL; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static short AnyOpRORDeduceCZN (RegContents* C, short PFlags, short Shiftee) +/* Do the ROR and auto-set C/Z/N flags */ +{ + if (RegValIsKnown (Shiftee) && PStatesAreKnown (PFlags, PSTATE_C)) { + C->PFlags &= ~UNKNOWN_PFVAL_C; + if (Shiftee & 0x01U) { + C->PFlags |= PFVAL_C; + } + Shiftee = (Shiftee >> 1) & 0xFF; + if (PFlags & PFVAL_C) { + Shiftee |= 0x80U; + } + } else { + Shiftee = UNKNOWN_REGVAL; + } + DeduceZN (C, Shiftee); + + return Shiftee; +} + + + +static void BranchDeduceOnProcessorFlag (RegContents* True, RegContents* False, unsigned short PTrueFlag) +/* Auto-set the corresponding C/Z/V/N flag output for both True/Flase code flows */ +{ + PTrueFlag &= 0xFF; + unsigned short Mask = ~(PTrueFlag * 0x0101U) & 0xFFFFU; + True->PFlags &= Mask; + True->PFlags |= PTrueFlag; + False->PFlags &= Mask; +} + + + +static int MightAffectKnownZP (CodeEntry* E, RegContents* In) +/* TODO: This is supposed to check if any builtin ZP could be affected. +** It simply returns TRUE in most cases for now. +*/ +{ + unsigned Index = 0; + + if (E->AM == AM65_ZP || + E->AM == AM65_ABS || + (E->AM == AM65_ZPX && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ZPY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF)) || + (E->AM == AM65_ABSX && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ABSY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF))) { + return 1; + } else if ((E->AM == AM65_ZP_IND) || + (E->AM == AM65_ZPX_IND && RegValIsKnown (In->RegX) && (Index = In->RegX & 0xFF)) || + (E->AM == AM65_ZP_INDY && RegValIsKnown (In->RegY) && (Index = In->RegY & 0xFF))) { + return 1; + } + return 1; +} + + + void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Generate register info for this instruction. If an old info exists, it is ** overwritten. @@ -506,9 +728,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Pointers to the register contents */ RegContents* In; RegContents* Out; + RegContents* BranchOut; /* Function register usage */ - unsigned short Use, Chg; + unsigned Use, Chg; + + /* Value in question */ + short Val = UNKNOWN_REGVAL; /* If we don't have a register info struct, allocate one. */ if (E->RI == 0) { @@ -525,18 +751,46 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) /* Get pointers to the register contents */ In = &E->RI->In; Out = &E->RI->Out; + BranchOut = &E->RI->Out2; /* Handle the different instructions */ switch (E->OPC) { case OP65_ADC: - /* We don't know the value of the carry, so the result is - ** always unknown. - */ Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_CZVN; + Out->ZNRegs = ZNREG_A; + if (PStatesAreKnown (In->PFlags, PSTATE_C) && + RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + Out->RegA = KnownOpADCDeduceCZVN (Out, In, (short) E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Tmp1); + break; + case REG_PTR1_LO: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Ptr1Lo); + break; + case REG_PTR1_HI: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->Ptr1Hi); + break; + case REG_SREG_LO: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->SRegLo); + break; + case REG_SREG_HI: + Out->RegA = KnownOpADCDeduceCZVN (Out, In, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_AND: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA & (short) E->Num; @@ -558,145 +812,289 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA & In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - Out->RegA = UNKNOWN_REGVAL; } } else if (CE_IsKnownImm (E, 0)) { /* A and $00 does always give zero */ Out->RegA = 0; } + DeduceZN (Out, Out->RegA); break; case OP65_ASL: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA << 1) & 0xFF; + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + Out->RegA = AnyOpASLDeduceCZN (Out, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 << 1) & 0xFF; + Out->Tmp1 = AnyOpASLDeduceCZN (Out, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo << 1) & 0xFF; + Out->Ptr1Lo = AnyOpASLDeduceCZN (Out, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi << 1) & 0xFF; + Out->Ptr1Hi = AnyOpASLDeduceCZN (Out, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo << 1) & 0xFF; + Out->SRegLo = AnyOpASLDeduceCZN (Out, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi << 1) & 0xFF; + Out->SRegHi = AnyOpASLDeduceCZN (Out, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_BCC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_C); break; case OP65_BCS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_C); break; case OP65_BEQ: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_Z); break; case OP65_BIT: + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Chg & REG_ZP, In)) { + case REG_TMP1: + Val = In->Tmp1; + break; + case REG_PTR1_LO: + Val = In->Ptr1Lo; + break; + case REG_PTR1_HI: + Val = In->Ptr1Hi; + break; + case REG_SREG_LO: + Val = In->SRegLo; + break; + case REG_SREG_HI: + Val = In->SRegHi; + break; + } + } + Out->PFlags &= ~UNKNOWN_PFVAL_V; + if (Val & PFVAL_V) { + Out->PFlags |= PFVAL_V; + } + DeduceZN (Out, Val); break; case OP65_BMI: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_N); break; case OP65_BNE: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_Z); break; case OP65_BPL: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_N); break; case OP65_BRA: break; case OP65_BRK: + Out->PFlags &= ~UNKNOWN_PFVAL_B; + Out->PFlags |= PFVAL_B; break; case OP65_BVC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_V); break; case OP65_BVS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_V); break; case OP65_CLC: + Out->PFlags &= ~UNKNOWN_PFVAL_C; break; case OP65_CLD: + Out->PFlags &= ~UNKNOWN_PFVAL_D; break; case OP65_CLI: + Out->PFlags &= ~UNKNOWN_PFVAL_I; break; case OP65_CLV: + Out->PFlags &= ~UNKNOWN_PFVAL_V; break; case OP65_CMP: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegA, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegA, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegA, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegA, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_CPX: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegX)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegX, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegX, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegX, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegX, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_CPY: + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (RegValIsKnown (In->RegY)) { + if (CE_IsConstImm (E)) { + KnownOpCmpDeduceCZN (Out, In->RegY, (short)E->Num); + } else if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Tmp1); + break; + case REG_PTR1_LO: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Ptr1Lo); + break; + case REG_PTR1_HI: + KnownOpCmpDeduceCZN (Out, In->RegY, In->Ptr1Hi); + break; + case REG_SREG_LO: + KnownOpCmpDeduceCZN (Out, In->RegY, In->SRegLo); + break; + case REG_SREG_HI: + KnownOpCmpDeduceCZN (Out, In->RegY, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_DEA: + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { Out->RegA = (In->RegA - 1) & 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_DEC: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA - 1) & 0xFF; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + if (RegValIsKnown (In->RegA)) { + Val = Out->RegA = (In->RegA - 1) & 0xFF; + } + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 - 1) & 0xFF; + Val = Out->Tmp1 = (In->Tmp1 - 1) & 0xFF; + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo - 1) & 0xFF; + Val = Out->Ptr1Lo = (In->Ptr1Lo - 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi - 1) & 0xFF; + Val = Out->Ptr1Hi = (In->Ptr1Hi - 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo - 1) & 0xFF; + Val = Out->SRegLo = (In->SRegLo - 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi - 1) & 0xFF; + Val = Out->SRegHi = (In->SRegHi - 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } + DeduceZN (Out, Val); break; case OP65_DEX: + Out->ZNRegs = ZNREG_X; if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX - 1) & 0xFF; } + DeduceZN (Out, Out->RegX); break; case OP65_DEY: + Out->ZNRegs = ZNREG_Y; if (RegValIsKnown (In->RegY)) { Out->RegY = (In->RegY - 1) & 0xFF; } + DeduceZN (Out, Out->RegY); break; case OP65_EOR: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA ^ (short) E->Num; @@ -718,82 +1116,104 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA ^ In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - Out->RegA = UNKNOWN_REGVAL; } } + DeduceZN (Out, Out->RegA); break; case OP65_INA: + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { Out->RegA = (In->RegA + 1) & 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_INC: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA + 1) & 0xFF; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + if (RegValIsKnown (In->RegA)) { + Val = Out->RegA = (In->RegA + 1) & 0xFF; + } + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 + 1) & 0xFF; + Val = Out->Tmp1 = (In->Tmp1 + 1) & 0xFF; + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo + 1) & 0xFF; + Val = Out->Ptr1Lo = (In->Ptr1Lo + 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi + 1) & 0xFF; + Val = Out->Ptr1Hi = (In->Ptr1Hi + 1) & 0xFF; + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo + 1) & 0xFF; + Val = Out->SRegLo = (In->SRegLo + 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi + 1) & 0xFF; + Val = Out->SRegHi = (In->SRegHi + 1) & 0xFF; + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } + DeduceZN (Out, Val); break; case OP65_INX: + Out->ZNRegs = ZNREG_X; if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX + 1) & 0xFF; } + DeduceZN (Out, Out->RegX); break; case OP65_INY: + Out->ZNRegs = ZNREG_Y; if (RegValIsKnown (In->RegY)) { Out->RegY = (In->RegY + 1) & 0xFF; } + DeduceZN (Out, Out->RegY); break; case OP65_JCC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_C); break; case OP65_JCS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_C); break; case OP65_JEQ: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_Z); break; case OP65_JMI: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_N); break; case OP65_JMP: break; case OP65_JNE: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_Z); break; case OP65_JPL: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_N); break; case OP65_JSR: + Out->ZNRegs = ZNREG_NONE; + /* Get the code info for the function */ GetFuncInfo (E->Arg, &Use, &Chg); if (Chg & REG_A) { @@ -820,6 +1240,9 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (Chg & REG_SREG_HI) { Out->SRegHi = UNKNOWN_REGVAL; } + /* FIXME: Quick hack to set flags on process status: */ + Out->PFlags |= ((Chg & PSTATE_ALL) >> PSTATE_BITS_SHIFT) * 0x0101U; + /* ## FIXME: Quick hack for some known functions: */ if (strcmp (E->Arg, "complax") == 0) { if (RegValIsKnown (In->RegA)) { @@ -861,12 +1284,16 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; case OP65_JVC: + BranchDeduceOnProcessorFlag (Out, BranchOut, PFVAL_V); break; case OP65_JVS: + BranchDeduceOnProcessorFlag (BranchOut, Out, PFVAL_V); break; case OP65_LDA: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (CE_IsConstImm (E)) { Out->RegA = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -887,16 +1314,15 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - /* A is now unknown */ - Out->RegA = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegA); break; case OP65_LDX: + Out->RegX = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_X; if (CE_IsConstImm (E)) { Out->RegX = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -917,16 +1343,15 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegX = In->SRegHi; break; default: - Out->RegX = UNKNOWN_REGVAL; break; } - } else { - /* X is now unknown */ - Out->RegX = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegX); break; case OP65_LDY: + Out->RegY = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_Y; if (CE_IsConstImm (E)) { Out->RegY = (unsigned char) E->Num; } else if (E->AM == AM65_ZP) { @@ -947,37 +1372,42 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegY = In->SRegHi; break; default: - Out->RegY = UNKNOWN_REGVAL; break; } - } else { - /* Y is now unknown */ - Out->RegY = UNKNOWN_REGVAL; } + DeduceZN (Out, Out->RegY); break; case OP65_LSR: - if (E->AM == AM65_ACC && RegValIsKnown (In->RegA)) { - Out->RegA = (In->RegA >> 1) & 0xFF; + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ACC) { + Out->RegA = AnyOpLSRDeduceCZN (Out, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = (In->Tmp1 >> 1) & 0xFF; + Out->Tmp1 = AnyOpLSRDeduceCZN (Out, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = (In->Ptr1Lo >> 1) & 0xFF; + Out->Ptr1Lo = AnyOpLSRDeduceCZN (Out, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = (In->Ptr1Hi >> 1) & 0xFF; + Out->Ptr1Hi = AnyOpLSRDeduceCZN (Out, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = (In->SRegLo >> 1) & 0xFF; + Out->SRegLo = AnyOpLSRDeduceCZN (Out, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = (In->SRegHi >> 1) & 0xFF; + Out->SRegHi = AnyOpLSRDeduceCZN (Out, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -987,6 +1417,8 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; case OP65_ORA: + Out->RegA = UNKNOWN_REGVAL; + Out->ZNRegs = ZNREG_A; if (RegValIsKnown (In->RegA)) { if (CE_IsConstImm (E)) { Out->RegA = In->RegA | (short) E->Num; @@ -1008,17 +1440,14 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = In->RegA | In->SRegHi; break; default: - Out->RegA = UNKNOWN_REGVAL; break; } - } else { - /* A is now unknown */ - Out->RegA = UNKNOWN_REGVAL; } } else if (CE_IsKnownImm (E, 0xFF)) { /* ORA with 0xFF does always give 0xFF */ Out->RegA = 0xFF; } + DeduceZN (Out, Out->RegA); break; case OP65_PHA: @@ -1035,93 +1464,150 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_PLA: Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_A; break; case OP65_PLP: + Out->PFlags = UNKNOWN_PFVAL_ALL; + Out->ZNRegs = ZNREG_NONE; break; case OP65_PLX: Out->RegX = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_X; break; case OP65_PLY: Out->RegY = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_Y; break; case OP65_ROL: - /* We don't know the value of the carry bit */ + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; if (E->AM == AM65_ACC) { - Out->RegA = UNKNOWN_REGVAL; + Out->RegA = AnyOpROLDeduceCZN (Out, In->PFlags, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = UNKNOWN_REGVAL; + Out->Tmp1 = AnyOpROLDeduceCZN (Out, In->PFlags, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = UNKNOWN_REGVAL; + Out->Ptr1Lo = AnyOpROLDeduceCZN (Out, In->PFlags, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = UNKNOWN_REGVAL; + Out->Ptr1Hi = AnyOpROLDeduceCZN (Out, In->PFlags, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = UNKNOWN_REGVAL; + Out->SRegLo = AnyOpROLDeduceCZN (Out, In->PFlags, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = UNKNOWN_REGVAL; + Out->SRegHi = AnyOpROLDeduceCZN (Out, In->PFlags, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_ROR: - /* We don't know the value of the carry bit */ + Out->PFlags |= UNKNOWN_PFVAL_CZN; + Out->ZNRegs = ZNREG_NONE; if (E->AM == AM65_ACC) { - Out->RegA = UNKNOWN_REGVAL; + Out->RegA = AnyOpRORDeduceCZN (Out, In->PFlags, In->RegA); + Out->ZNRegs = ZNREG_A; } else if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: - Out->Tmp1 = UNKNOWN_REGVAL; + Out->Tmp1 = AnyOpRORDeduceCZN (Out, In->PFlags, In->Tmp1); + Out->ZNRegs = ZNREG_TMP1; break; case REG_PTR1_LO: - Out->Ptr1Lo = UNKNOWN_REGVAL; + Out->Ptr1Lo = AnyOpRORDeduceCZN (Out, In->PFlags, In->Ptr1Lo); + Out->ZNRegs = ZNREG_PTR1_LO; break; case REG_PTR1_HI: - Out->Ptr1Hi = UNKNOWN_REGVAL; + Out->Ptr1Hi = AnyOpRORDeduceCZN (Out, In->PFlags, In->Ptr1Hi); + Out->ZNRegs = ZNREG_PTR1_HI; break; case REG_SREG_LO: - Out->SRegLo = UNKNOWN_REGVAL; + Out->SRegLo = AnyOpRORDeduceCZN (Out, In->PFlags, In->SRegLo); + Out->ZNRegs = ZNREG_SREG_LO; break; case REG_SREG_HI: - Out->SRegHi = UNKNOWN_REGVAL; + Out->SRegHi = AnyOpRORDeduceCZN (Out, In->PFlags, In->SRegHi); + Out->ZNRegs = ZNREG_SREG_HI; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } break; case OP65_RTI: + Out->PFlags |= UNKNOWN_PFVAL_ALL; break; case OP65_RTS: break; case OP65_SBC: - /* We don't know the value of the carry bit */ Out->RegA = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_CZVN; + Out->ZNRegs = ZNREG_A; + if (PStatesAreKnown (In->PFlags, PSTATE_C) && + RegValIsKnown (In->RegA)) { + if (CE_IsConstImm (E)) { + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, (short) E->Num); + } + else if (E->AM == AM65_ZP) { + switch (GetKnownReg(E->Use & REG_ZP, In)) { + case REG_TMP1: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Tmp1); + break; + case REG_PTR1_LO: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Ptr1Lo); + break; + case REG_PTR1_HI: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->Ptr1Hi); + break; + case REG_SREG_LO: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->SRegLo); + break; + case REG_SREG_HI: + Out->RegA = KnownOpSBCDeduceCZVN (Out, In, In->SRegHi); + break; + default: + break; + } + } + } break; case OP65_SEC: + Out->PFlags &= ~UNKNOWN_PFVAL_C; + Out->PFlags |= PFVAL_C; break; case OP65_SED: + Out->PFlags &= ~UNKNOWN_PFVAL_D; + Out->PFlags |= PFVAL_D; break; case OP65_SEI: + Out->PFlags &= ~UNKNOWN_PFVAL_I; + Out->PFlags |= PFVAL_I; break; case OP65_STA: @@ -1143,7 +1629,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegA; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1171,7 +1657,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegX; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1196,7 +1682,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = In->RegY; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1221,7 +1707,7 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->SRegHi = 0; break; } - } else if (E->AM == AM65_ZPX) { + } else if (MightAffectKnownZP (E, In)) { /* Invalidates all ZP registers */ RC_InvalidateZP (Out); } @@ -1229,37 +1715,44 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) case OP65_TAX: Out->RegX = In->RegA; + Out->ZNRegs = ZNREG_AX; + DeduceZN (Out, Out->RegX); break; case OP65_TAY: Out->RegY = In->RegA; + Out->ZNRegs = ZNREG_AY; + DeduceZN (Out, Out->RegY); break; case OP65_TRB: - if (E->AM == AM65_ZPX) { - /* Invalidates all ZP registers */ - RC_InvalidateZP (Out); - } else if (E->AM == AM65_ZP) { + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { if (RegValIsKnown (In->RegA)) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: + Val = Out->Tmp1 & In->RegA; Out->Tmp1 &= ~In->RegA; break; case REG_PTR1_LO: + Val = Out->Ptr1Lo & In->RegA; Out->Ptr1Lo &= ~In->RegA; break; case REG_PTR1_HI: + Val = Out->Ptr1Hi & In->RegA; Out->Ptr1Hi &= ~In->RegA; break; case REG_SREG_LO: + Val = Out->SRegLo & In->RegA; Out->SRegLo &= ~In->RegA; break; case REG_SREG_HI: + Val = Out->SRegHi & In->RegA; Out->SRegHi &= ~In->RegA; break; } } else { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: Out->Tmp1 = UNKNOWN_REGVAL; break; @@ -1277,34 +1770,41 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; } } + } else if (MightAffectKnownZP (E, In)) { + /* Invalidates all ZP registers */ + RC_InvalidateZP (Out); } + DeduceZ (Out, Val); break; case OP65_TSB: - if (E->AM == AM65_ZPX) { - /* Invalidates all ZP registers */ - RC_InvalidateZP (Out); - } else if (E->AM == AM65_ZP) { + Out->ZNRegs = ZNREG_NONE; + if (E->AM == AM65_ZP) { if (RegValIsKnown (In->RegA)) { switch (GetKnownReg (E->Chg & REG_ZP, In)) { case REG_TMP1: + Val = Out->Tmp1 & In->RegA; Out->Tmp1 |= In->RegA; break; case REG_PTR1_LO: + Val = Out->Ptr1Lo & In->RegA; Out->Ptr1Lo |= In->RegA; break; case REG_PTR1_HI: + Val = Out->Ptr1Hi & In->RegA; Out->Ptr1Hi |= In->RegA; break; case REG_SREG_LO: + Val = Out->SRegLo & In->RegA; Out->SRegLo |= In->RegA; break; case REG_SREG_HI: + Val = Out->SRegHi & In->RegA; Out->SRegHi |= In->RegA; break; } } else { - switch (GetKnownReg (E->Chg & REG_ZP, In)) { + switch (GetKnownReg (E->Chg & REG_ZP, 0)) { case REG_TMP1: Out->Tmp1 = UNKNOWN_REGVAL; break; @@ -1322,22 +1822,34 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) break; } } + } else if (MightAffectKnownZP (E, In)) { + /* Invalidates all ZP registers */ + RC_InvalidateZP (Out); } + DeduceZ (Out, Val); break; case OP65_TSX: Out->RegX = UNKNOWN_REGVAL; + Out->PFlags |= UNKNOWN_PFVAL_ZN; + Out->ZNRegs = ZNREG_X; break; case OP65_TXA: Out->RegA = In->RegX; + Out->ZNRegs = ZNREG_AX; + DeduceZN (Out, Out->RegA); break; case OP65_TXS: + Out->ZNRegs = ZNREG_X; + DeduceZN (Out, In->RegX); break; case OP65_TYA: Out->RegA = In->RegY; + Out->ZNRegs = ZNREG_AY; + DeduceZN (Out, Out->RegA); break; default: diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 96373a8f9..3d07670d7 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -73,8 +73,8 @@ struct CodeEntry { char* Arg; /* Argument as string */ unsigned long Num; /* Numeric argument */ unsigned short Info; /* Additional code info */ - unsigned short Use; /* Registers used */ - unsigned short Chg; /* Registers changed/destroyed */ + unsigned int Use; /* Registers used */ + unsigned int Chg; /* Registers changed/destroyed */ CodeLabel* JumpTo; /* Jump label */ Collection Labels; /* Labels for this instruction */ LineInfo* LI; /* Source line info for this insn */ diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index f51189293..892f4cd5f 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -77,8 +77,8 @@ static const char BoolTransformerTab [][8] = { typedef struct FuncInfo FuncInfo; struct FuncInfo { const char* Name; /* Function name */ - unsigned short Use; /* Register usage */ - unsigned short Chg; /* Changed/destroyed registers */ + unsigned Use; /* Register usage */ + unsigned Chg; /* Changed/destroyed registers */ }; /* Note for the shift functions: Shifts are done modulo 32, so all shift @@ -86,264 +86,264 @@ struct FuncInfo { ** anyway. */ static const FuncInfo FuncInfoTable[] = { - { "addeq0sp", REG_AX, REG_AXY }, - { "addeqysp", REG_AXY, REG_AXY }, - { "addysp", REG_Y, REG_NONE }, - { "aslax1", REG_AX, REG_AX | REG_TMP1 }, - { "aslax2", REG_AX, REG_AX | REG_TMP1 }, - { "aslax3", REG_AX, REG_AX | REG_TMP1 }, - { "aslax4", REG_AX, REG_AX | REG_TMP1 }, - { "aslaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "asleax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "asleax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "asrax1", REG_AX, REG_AX | REG_TMP1 }, - { "asrax2", REG_AX, REG_AX | REG_TMP1 }, - { "asrax3", REG_AX, REG_AX | REG_TMP1 }, - { "asrax4", REG_AX, REG_AX | REG_TMP1 }, - { "asraxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "asreax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "asreax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "bcasta", REG_A, REG_AX }, - { "bcastax", REG_AX, REG_AX }, - { "bcasteax", REG_EAX, REG_EAX | REG_TMP1 }, - { "bnega", REG_A, REG_AX }, - { "bnegax", REG_AX, REG_AX }, - { "bnegeax", REG_EAX, REG_EAX | REG_TMP1 }, - { "booleq", REG_NONE, REG_AX }, - { "boolge", REG_NONE, REG_AX }, - { "boolgt", REG_NONE, REG_AX }, - { "boolle", REG_NONE, REG_AX }, - { "boollt", REG_NONE, REG_AX }, - { "boolne", REG_NONE, REG_AX }, - { "booluge", REG_NONE, REG_AX }, - { "boolugt", REG_NONE, REG_AX }, - { "boolule", REG_NONE, REG_AX }, - { "boolult", REG_NONE, REG_AX }, - { "callax", REG_AX, REG_ALL }, - { "complax", REG_AX, REG_AX }, - { "decax1", REG_AX, REG_AX }, - { "decax2", REG_AX, REG_AX }, - { "decax3", REG_AX, REG_AX }, - { "decax4", REG_AX, REG_AX }, - { "decax5", REG_AX, REG_AX }, - { "decax6", REG_AX, REG_AX }, - { "decax7", REG_AX, REG_AX }, - { "decax8", REG_AX, REG_AX }, - { "decaxy", REG_AXY, REG_AX | REG_TMP1 }, - { "deceaxy", REG_EAXY, REG_EAX }, - { "decsp1", REG_NONE, REG_Y }, - { "decsp2", REG_NONE, REG_A }, - { "decsp3", REG_NONE, REG_A }, - { "decsp4", REG_NONE, REG_A }, - { "decsp5", REG_NONE, REG_A }, - { "decsp6", REG_NONE, REG_A }, - { "decsp7", REG_NONE, REG_A }, - { "decsp8", REG_NONE, REG_A }, - { "incax1", REG_AX, REG_AX }, - { "incax2", REG_AX, REG_AX }, - { "incax3", REG_AX, REG_AXY | REG_TMP1 }, - { "incax4", REG_AX, REG_AXY | REG_TMP1 }, - { "incax5", REG_AX, REG_AXY | REG_TMP1 }, - { "incax6", REG_AX, REG_AXY | REG_TMP1 }, - { "incax7", REG_AX, REG_AXY | REG_TMP1 }, - { "incax8", REG_AX, REG_AXY | REG_TMP1 }, - { "incaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "incsp1", REG_NONE, REG_NONE }, - { "incsp2", REG_NONE, REG_Y }, - { "incsp3", REG_NONE, REG_Y }, - { "incsp4", REG_NONE, REG_Y }, - { "incsp5", REG_NONE, REG_Y }, - { "incsp6", REG_NONE, REG_Y }, - { "incsp7", REG_NONE, REG_Y }, - { "incsp8", REG_NONE, REG_Y }, - { "laddeq", REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeq0sp", REG_EAX, REG_EAXY }, - { "laddeq1", REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeqa", REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "laddeqysp", REG_EAXY, REG_EAXY }, - { "ldaidx", REG_AXY, REG_AX | REG_PTR1 }, - { "ldauidx", REG_AXY, REG_AX | REG_PTR1 }, - { "ldax0sp", REG_NONE, REG_AXY }, - { "ldaxi", REG_AX, REG_AXY | REG_PTR1 }, - { "ldaxidx", REG_AXY, REG_AXY | REG_PTR1 }, - { "ldaxysp", REG_Y, REG_AXY }, - { "ldeax0sp", REG_NONE, REG_EAXY }, - { "ldeaxi", REG_AX, REG_EAXY | REG_PTR1 }, - { "ldeaxidx", REG_AXY, REG_EAXY | REG_PTR1 }, - { "ldeaxysp", REG_Y, REG_EAXY }, - { "leaa0sp", REG_A, REG_AX }, - { "leaaxsp", REG_AX, REG_AX }, - { "lsubeq", REG_EAXY|REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeq0sp", REG_EAX, REG_EAXY }, - { "lsubeq1", REG_Y | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeqa", REG_AY | REG_PTR1_LO, REG_EAXY | REG_PTR1_HI }, - { "lsubeqysp", REG_EAXY, REG_EAXY }, - { "mulax10", REG_AX, REG_AX | REG_PTR1 }, - { "mulax3", REG_AX, REG_AX | REG_PTR1 }, - { "mulax5", REG_AX, REG_AX | REG_PTR1 }, - { "mulax6", REG_AX, REG_AX | REG_PTR1 }, - { "mulax7", REG_AX, REG_AX | REG_PTR1 }, - { "mulax9", REG_AX, REG_AX | REG_PTR1 }, - { "negax", REG_AX, REG_AX }, - { "push0", REG_NONE, REG_AXY }, - { "push0ax", REG_AX, REG_Y | REG_SREG }, - { "push1", REG_NONE, REG_AXY }, - { "push2", REG_NONE, REG_AXY }, - { "push3", REG_NONE, REG_AXY }, - { "push4", REG_NONE, REG_AXY }, - { "push5", REG_NONE, REG_AXY }, - { "push6", REG_NONE, REG_AXY }, - { "push7", REG_NONE, REG_AXY }, - { "pusha", REG_A, REG_Y }, - { "pusha0", REG_A, REG_XY }, - { "pusha0sp", REG_NONE, REG_AY }, - { "pushaFF", REG_A, REG_Y }, - { "pushax", REG_AX, REG_Y }, - { "pushaysp", REG_Y, REG_AY }, - { "pushc0", REG_NONE, REG_A | REG_Y }, - { "pushc1", REG_NONE, REG_A | REG_Y }, - { "pushc2", REG_NONE, REG_A | REG_Y }, - { "pusheax", REG_EAX, REG_Y }, - { "pushl0", REG_NONE, REG_AXY }, - { "pushw", REG_AX, REG_AXY | REG_PTR1 }, - { "pushw0sp", REG_NONE, REG_AXY }, - { "pushwidx", REG_AXY, REG_AXY | REG_PTR1 }, - { "pushwysp", REG_Y, REG_AXY }, - { "regswap", REG_AXY, REG_AXY | REG_TMP1 }, - { "regswap1", REG_XY, REG_A }, - { "regswap2", REG_XY, REG_A | REG_Y }, - { "return0", REG_NONE, REG_AX }, - { "return1", REG_NONE, REG_AX }, - { "shlax1", REG_AX, REG_AX | REG_TMP1 }, - { "shlax2", REG_AX, REG_AX | REG_TMP1 }, - { "shlax3", REG_AX, REG_AX | REG_TMP1 }, - { "shlax4", REG_AX, REG_AX | REG_TMP1 }, - { "shlaxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "shleax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "shleax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "shrax1", REG_AX, REG_AX | REG_TMP1 }, - { "shrax2", REG_AX, REG_AX | REG_TMP1 }, - { "shrax3", REG_AX, REG_AX | REG_TMP1 }, - { "shrax4", REG_AX, REG_AX | REG_TMP1 }, - { "shraxy", REG_AXY, REG_AXY | REG_TMP1 }, - { "shreax1", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax2", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax3", REG_EAX, REG_EAX | REG_TMP1 }, - { "shreax4", REG_EAX, REG_EAXY | REG_TMP1 }, - { "staspidx", REG_A | REG_Y, REG_Y | REG_TMP1 | REG_PTR1 }, - { "stax0sp", REG_AX, REG_Y }, - { "staxspidx", REG_AXY, REG_TMP1 | REG_PTR1 }, - { "staxysp", REG_AXY, REG_Y }, - { "steax0sp", REG_EAX, REG_Y }, - { "steaxysp", REG_EAXY, REG_Y }, - { "subeq0sp", REG_AX, REG_AXY }, - { "subeqysp", REG_AXY, REG_AXY }, - { "subysp", REG_Y, REG_AY }, - { "tosadd0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosadda0", REG_A, REG_AXY }, - { "tosaddax", REG_AX, REG_AXY }, - { "tosaddeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosand0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosanda0", REG_A, REG_AXY }, - { "tosandax", REG_AX, REG_AXY }, - { "tosandeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosaslax", REG_A, REG_AXY | REG_TMP1 }, - { "tosasleax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosasrax", REG_A, REG_AXY | REG_TMP1 }, - { "tosasreax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosdiv0ax", REG_AX, REG_ALL }, - { "tosdiva0", REG_A, REG_ALL }, - { "tosdivax", REG_AX, REG_ALL }, - { "tosdiveax", REG_EAX, REG_ALL }, - { "toseq00", REG_NONE, REG_AXY | REG_SREG }, - { "toseqa0", REG_A, REG_AXY | REG_SREG }, - { "toseqax", REG_AX, REG_AXY | REG_SREG }, - { "toseqeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosge00", REG_NONE, REG_AXY | REG_SREG }, - { "tosgea0", REG_A, REG_AXY | REG_SREG }, - { "tosgeax", REG_AX, REG_AXY | REG_SREG }, - { "tosgeeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosgt00", REG_NONE, REG_AXY | REG_SREG }, - { "tosgta0", REG_A, REG_AXY | REG_SREG }, - { "tosgtax", REG_AX, REG_AXY | REG_SREG }, - { "tosgteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosicmp", REG_AX, REG_AXY | REG_SREG }, - { "tosicmp0", REG_A, REG_AXY | REG_SREG }, - { "toslcmp", REG_EAX, REG_A | REG_Y | REG_PTR1 }, - { "tosle00", REG_NONE, REG_AXY | REG_SREG }, - { "toslea0", REG_A, REG_AXY | REG_SREG }, - { "tosleax", REG_AX, REG_AXY | REG_SREG }, - { "tosleeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "toslt00", REG_NONE, REG_AXY | REG_SREG }, - { "toslta0", REG_A, REG_AXY | REG_SREG }, - { "tosltax", REG_AX, REG_AXY | REG_SREG }, - { "toslteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosmod0ax", REG_AX, REG_ALL }, - { "tosmodeax", REG_EAX, REG_ALL }, - { "tosmul0ax", REG_AX, REG_ALL }, - { "tosmula0", REG_A, REG_ALL }, - { "tosmulax", REG_AX, REG_ALL }, - { "tosmuleax", REG_EAX, REG_ALL }, - { "tosne00", REG_NONE, REG_AXY | REG_SREG }, - { "tosnea0", REG_A, REG_AXY | REG_SREG }, - { "tosneax", REG_AX, REG_AXY | REG_SREG }, - { "tosneeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosor0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosora0", REG_A, REG_AXY | REG_TMP1 }, - { "tosorax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosoreax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosrsub0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosrsuba0", REG_A, REG_AXY | REG_TMP1 }, - { "tosrsubax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosrsubeax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tosshlax", REG_A, REG_AXY | REG_TMP1 }, - { "tosshleax", REG_A, REG_EAXY | REG_TMP1 }, - { "tosshrax", REG_A, REG_AXY | REG_TMP1 }, - { "tosshreax", REG_A, REG_EAXY | REG_TMP1 }, - { "tossub0ax", REG_AX, REG_EAXY }, - { "tossuba0", REG_A, REG_AXY }, - { "tossubax", REG_AX, REG_AXY }, - { "tossubeax", REG_EAX, REG_EAXY }, - { "tosudiv0ax", REG_AX, REG_ALL & ~REG_SAVE }, - { "tosudiva0", REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudivax", REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosudiveax", REG_EAX, REG_ALL & ~REG_SAVE }, - { "tosuge00", REG_NONE, REG_AXY | REG_SREG }, - { "tosugea0", REG_A, REG_AXY | REG_SREG }, - { "tosugeax", REG_AX, REG_AXY | REG_SREG }, - { "tosugeeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosugt00", REG_NONE, REG_AXY | REG_SREG }, - { "tosugta0", REG_A, REG_AXY | REG_SREG }, - { "tosugtax", REG_AX, REG_AXY | REG_SREG }, - { "tosugteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosule00", REG_NONE, REG_AXY | REG_SREG }, - { "tosulea0", REG_A, REG_AXY | REG_SREG }, - { "tosuleax", REG_AX, REG_AXY | REG_SREG }, - { "tosuleeax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosult00", REG_NONE, REG_AXY | REG_SREG }, - { "tosulta0", REG_A, REG_AXY | REG_SREG }, - { "tosultax", REG_AX, REG_AXY | REG_SREG }, - { "tosulteax", REG_EAX, REG_AXY | REG_PTR1 }, - { "tosumod0ax", REG_AX, REG_ALL & ~REG_SAVE }, - { "tosumoda0", REG_A, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodax", REG_AX, REG_EAXY | REG_PTR1 }, /* also ptr4 */ - { "tosumodeax", REG_EAX, REG_ALL & ~REG_SAVE }, - { "tosumul0ax", REG_AX, REG_ALL }, - { "tosumula0", REG_A, REG_ALL }, - { "tosumulax", REG_AX, REG_ALL }, - { "tosumuleax", REG_EAX, REG_ALL }, - { "tosxor0ax", REG_AX, REG_EAXY | REG_TMP1 }, - { "tosxora0", REG_A, REG_AXY | REG_TMP1 }, - { "tosxorax", REG_AX, REG_AXY | REG_TMP1 }, - { "tosxoreax", REG_EAX, REG_EAXY | REG_TMP1 }, - { "tsteax", REG_EAX, REG_Y }, - { "utsteax", REG_EAX, REG_Y }, + { "addeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, + { "addeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, + { "addysp", REG_Y, PSTATE_ALL | REG_NONE }, + { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "bcasta", REG_A, PSTATE_ALL | REG_AX }, + { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, + { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "bnega", REG_A, PSTATE_ALL | REG_AX }, + { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, + { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, + { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, + { "callax", REG_AX, PSTATE_ALL | REG_ALL }, + { "complax", REG_AX, PSTATE_ALL | REG_AX }, + { "decax1", REG_AX, PSTATE_ALL | REG_AX }, + { "decax2", REG_AX, PSTATE_ALL | REG_AX }, + { "decax3", REG_AX, PSTATE_ALL | REG_AX }, + { "decax4", REG_AX, PSTATE_ALL | REG_AX }, + { "decax5", REG_AX, PSTATE_ALL | REG_AX }, + { "decax6", REG_AX, PSTATE_ALL | REG_AX }, + { "decax7", REG_AX, PSTATE_ALL | REG_AX }, + { "decax8", REG_AX, PSTATE_ALL | REG_AX }, + { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, + { "decsp1", REG_NONE, PSTATE_ALL | REG_Y }, + { "decsp2", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp3", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp4", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp5", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp6", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp7", REG_NONE, PSTATE_ALL | REG_A }, + { "decsp8", REG_NONE, PSTATE_ALL | REG_A }, + { "incax1", REG_AX, PSTATE_ALL | REG_AX }, + { "incax2", REG_AX, PSTATE_ALL | REG_AX }, + { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incsp1", REG_NONE, PSTATE_ALL | REG_NONE }, + { "incsp2", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp3", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp4", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp5", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp6", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp7", REG_NONE, PSTATE_ALL | REG_Y }, + { "incsp8", REG_NONE, PSTATE_ALL | REG_Y }, + { "laddeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, + { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldax0sp", REG_NONE, PSTATE_ALL | REG_AXY }, + { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxysp", REG_Y, PSTATE_ALL | REG_AXY }, + { "ldeax0sp", REG_NONE, PSTATE_ALL | REG_EAXY }, + { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxysp", REG_Y, PSTATE_ALL | REG_EAXY }, + { "leaa0sp", REG_A, PSTATE_ALL | REG_AX }, + { "leaaxsp", REG_AX, PSTATE_ALL | REG_AX }, + { "lsubeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, + { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "negax", REG_AX, PSTATE_ALL | REG_AX }, + { "push0", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push0ax", REG_AX, PSTATE_ALL | REG_Y | REG_SREG }, + { "push1", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push2", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push3", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push4", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push5", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push6", REG_NONE, PSTATE_ALL | REG_AXY }, + { "push7", REG_NONE, PSTATE_ALL | REG_AXY }, + { "pusha", REG_A, PSTATE_ALL | REG_Y }, + { "pusha0", REG_A, PSTATE_ALL | REG_XY }, + { "pusha0sp", REG_NONE, PSTATE_ALL | REG_AY }, + { "pushaFF", REG_A, PSTATE_ALL | REG_Y }, + { "pushax", REG_AX, PSTATE_ALL | REG_Y }, + { "pushaysp", REG_Y, PSTATE_ALL | REG_AY }, + { "pushc0", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, + { "pushc1", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, + { "pushc2", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, + { "pusheax", REG_EAX, PSTATE_ALL | REG_Y }, + { "pushl0", REG_NONE, PSTATE_ALL | REG_AXY }, + { "pushw", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "pushw0sp", REG_NONE, PSTATE_ALL | REG_AXY }, + { "pushwidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "pushwysp", REG_Y, PSTATE_ALL | REG_AXY }, + { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "regswap1", REG_XY, PSTATE_ALL | REG_A }, + { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, + { "return0", REG_NONE, PSTATE_ALL | REG_AX }, + { "return1", REG_NONE, PSTATE_ALL | REG_AX }, + { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "staspidx", REG_A | REG_Y, PSTATE_ALL | REG_Y | REG_TMP1 | REG_PTR1 }, + { "stax0sp", REG_AX, PSTATE_ALL | REG_Y }, + { "staxspidx", REG_AXY, PSTATE_ALL | REG_TMP1 | REG_PTR1 }, + { "staxysp", REG_AXY, PSTATE_ALL | REG_Y }, + { "steax0sp", REG_EAX, PSTATE_ALL | REG_Y }, + { "steaxysp", REG_EAXY, PSTATE_ALL | REG_Y }, + { "subeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, + { "subeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, + { "subysp", REG_Y, PSTATE_ALL | REG_AY }, + { "tosadd0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosadda0", REG_A, PSTATE_ALL | REG_AXY }, + { "tosaddax", REG_AX, PSTATE_ALL | REG_AXY }, + { "tosaddeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosand0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosanda0", REG_A, PSTATE_ALL | REG_AXY }, + { "tosandax", REG_AX, PSTATE_ALL | REG_AXY }, + { "tosandeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosaslax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosasleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosasrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosasreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosdiv0ax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosdiva0", REG_A, PSTATE_ALL | REG_ALL }, + { "tosdivax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosdiveax", REG_EAX, PSTATE_ALL | REG_ALL }, + { "toseq00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toseqa0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toseqax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toseqeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosgt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosgteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosicmp", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosicmp0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toslcmp", REG_EAX, PSTATE_ALL | REG_A | REG_Y | REG_PTR1 }, + { "tosle00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toslea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "toslt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toslta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosltax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "toslteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosmod0ax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmodeax", REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosmul0ax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmula0", REG_A, PSTATE_ALL | REG_ALL }, + { "tosmulax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmuleax", REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosne00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosnea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosneax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosneeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosrsub0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosrsuba0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosrsubax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosrsubeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosshlax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosshleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosshrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosshreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tossub0ax", REG_AX, PSTATE_ALL | REG_EAXY }, + { "tossuba0", REG_A, PSTATE_ALL | REG_AXY }, + { "tossubax", REG_AX, PSTATE_ALL | REG_AXY }, + { "tossubeax", REG_EAX, PSTATE_ALL | REG_EAXY }, + { "tosudiv0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosudiva0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudivax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudiveax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosuge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosugt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosugteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosule00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosulea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosuleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosuleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosult00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosulta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosultax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, + { "tosulteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "tosumod0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumoda0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodeax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumul0ax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumula0", REG_A, PSTATE_ALL | REG_ALL }, + { "tosumulax", REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumuleax", REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosxor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tosxora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosxorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "tosxoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, }; #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) @@ -385,10 +385,10 @@ static int CompareFuncInfo (const void* Key, const void* Info) -fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) +fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* For the given function, lookup register information and store it into ** the given variables. If the function is unknown, assume it will use and -** load all registers. +** load all registers as well as touching the processor flags. */ { /* If the function name starts with an underline, it is an external @@ -441,6 +441,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) /* Will destroy all registers */ *Chg = REG_ALL; + /* and will destroy all processor flags */ + *Chg |= PSTATE_ALL; + /* Done */ return FNCLS_GLOBAL; } @@ -453,6 +456,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) */ *Use = REG_ALL; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; return FNCLS_NUMERIC; } else { @@ -477,15 +481,17 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) } *Use = REG_ALL; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; } return FNCLS_BUILTIN; } /* Function not found - assume that the primary register is input, and all - ** registers are changed + ** registers and processor flags are changed */ *Use = REG_EAXY; *Chg = REG_ALL; + *Chg |= PSTATE_ALL; return FNCLS_UNKNOWN; } diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 763b947bf..32bc5dd9c 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -57,7 +57,7 @@ struct CodeSeg; /* Forward to struct RegContents */ struct RegContents; -/* Defines for registers. */ +/* Defines for registers */ #define REG_NONE 0x0000U #define REG_A 0x0001U #define REG_X 0x0002U @@ -74,6 +74,21 @@ struct RegContents; #define REG_SP_LO 0x1000U #define REG_SP_HI 0x2000U +/* Defines for some special register usage */ +#define SLV_SP65 0x00200000U /* Accesses 6502 stack pointer */ +#define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ +#define SLV_PL65 0x00800000U /* Pops from 6502 stack */ + +/* Defines for processor states */ +#define PSTATE_NONE 0x00000000U +#define PSTATE_C 0x01000000U /* Carry */ +#define PSTATE_Z 0x02000000U /* Zero */ +#define PSTATE_I 0x04000000U /* Interrupt */ +#define PSTATE_D 0x08000000U /* Decimal */ +#define PSTATE_U 0x10000000U /* Unused */ +#define PSTATE_B 0x20000000U /* Break */ +#define PSTATE_V 0x40000000U /* Overflow */ +#define PSTATE_N 0x80000000U /* Negative */ /* Combined register defines */ #define REG_PTR1 (REG_PTR1_LO | REG_PTR1_HI) @@ -89,6 +104,14 @@ struct RegContents; #define REG_EAXY (REG_EAX | REG_Y) #define REG_ZP 0xFFF8U #define REG_ALL 0xFFFFU +#define PSTATE_CZ (PSTATE_C | PSTATE_Z) +#define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N) +#define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N) +#define PSTATE_ZN (PSTATE_Z | PSTATE_N) +#define PSTATE_ZVN (PSTATE_Z | PSTATE_V | PSTATE_N) +#define PSTATE_6502 0xE7000000U +#define PSTATE_ALL 0xFF000000U +#define REG_EVERYTHING 0xFFFFFFFFU @@ -139,7 +162,7 @@ typedef enum { -fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg); +fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg); /* For the given function, lookup register information and store it into ** the given variables. If the function is unknown, assume it will use and ** load all registers. diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index f2e0faf15..ad8a3148c 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -1468,6 +1468,7 @@ void CS_GenRegInfo (CodeSeg* S) /* On entry, the register contents are unknown */ RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); CurrentRegs = &Regs; /* Walk over all insns and note just the changes from one insn to the @@ -1502,6 +1503,7 @@ void CS_GenRegInfo (CodeSeg* S) Regs = J->RI->Out2; } else { RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); } Entry = 1; } else { @@ -1521,6 +1523,7 @@ void CS_GenRegInfo (CodeSeg* S) */ Done = 0; RC_Invalidate (&Regs); + RC_InvalidatePS (&Regs); break; } if (J->RI->Out2.RegA != Regs.RegA) { @@ -1541,6 +1544,9 @@ void CS_GenRegInfo (CodeSeg* S) if (J->RI->Out2.Tmp1 != Regs.Tmp1) { Regs.Tmp1 = UNKNOWN_REGVAL; } + unsigned PF = J->RI->Out2.PFlags ^ Regs.PFlags; + Regs.PFlags |= ((PF >> 8) | PF | (PF << 8)) & UNKNOWN_PFVAL_ALL; + Regs.ZNRegs &= J->RI->Out2.ZNRegs; ++Entry; } diff --git a/src/cc65/coptsize.c b/src/cc65/coptsize.c index 5c23e637c..a5da4736d 100644 --- a/src/cc65/coptsize.c +++ b/src/cc65/coptsize.c @@ -76,730 +76,730 @@ static const CallDesc CallTable [] = { { "addeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "addeq0sp" },{ "laddeq", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeq1" },{ "laddeq", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeqa" },{ "laddeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "laddeq0sp" },{ "ldaxidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldaxi" },{ "ldaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldax0sp" },{ "ldeaxidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldeaxi" },{ "ldeaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "ldeax0sp" },{ "leaaxsp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "leaa0sp" },{ "lsubeq", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeq1" },{ "lsubeq", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeqa" },{ "lsubeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "lsubeq0sp" },{ "pusha", { - /* A X Y SRegLo */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc0" },{ "pusha", { - /* A X Y SRegLo */ - 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc1" },{ "pusha", { - /* A X Y SRegLo */ - 2, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 2, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushc2" },{ "pushax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "push0" },{ "pushax", { - /* A X Y SRegLo */ - 1, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 1, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push1" },{ "pushax", { - /* A X Y SRegLo */ - 2, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 2, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push2" },{ "pushax", { - /* A X Y SRegLo */ - 3, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 3, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push3" },{ "pushax", { - /* A X Y SRegLo */ - 4, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 4, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push4" },{ "pushax", { - /* A X Y SRegLo */ - 5, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 5, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push5" },{ "pushax", { - /* A X Y SRegLo */ - 6, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 6, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push6" },{ "pushax", { - /* A X Y SRegLo */ - 7, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 7, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "push7" },{ "pushax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pusha0" },{ "pushax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0xFF, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0xFF, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_SLOWER, "pushaFF" },{ "pushaysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pusha0sp" },{ "pusheax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushl0" },{ "pusheax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "push0ax" },{ "pushwidx", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 1, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushw" },{ "pushwysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 3, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "pushw0sp" },{ "staxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "stax0sp" },{ "steaxysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "steax0sp" },{ "subeqysp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "subeq0sp" },{ "tosaddax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosadda0" },{ "tosaddeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosadd0ax" },{ "tosandax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosanda0" },{ "tosandeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosand0ax" },{ "tosdivax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosdiva0" },{ "tosdiveax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosdiv0ax" },{ "toseqax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toseq00" },{ "toseqax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toseqa0" },{ "tosgeax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosge00" },{ "tosgeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgea0" },{ "tosgtax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgt00" },{ "tosgtax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosgta0" },{ "tosicmp", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosicmp0" },{ "tosleax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosle00" },{ "tosleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslea0" },{ "tosltax", { - /* A X Y SRegLo */ - 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + 0, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslt00" },{ "tosltax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "toslta0" },{ "tosmodax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmoda0" },{ "tosmodeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmod0ax" },{ "tosmulax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmula0" },{ "tosmuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosmul0ax" },{ "tosneax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosnea0" },{ "tosorax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosora0" },{ "tosoreax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosor0ax" },{ "tosrsubax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosrsuba0" },{ "tosrsubeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosrsub0ax" },{ "tossubax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tossuba0" },{ "tossubeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tossub0ax" },{ "tosudivax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosudiva0" },{ "tosudiveax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosudiv0ax" },{ "tosugeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosugea0" },{ "tosugtax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosugta0" },{ "tosuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosulea0" },{ "tosultax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosulta0" },{ "tosumodax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumoda0" },{ "tosumodeax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumod0ax" },{ "tosumulax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumula0" },{ "tosumuleax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosumul0ax" },{ "tosxorax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosxora0" },{ "tosxoreax", { - /* A X Y SRegLo */ - UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, - /* SRegHi Ptr1Lo Ptr1Hi Tmp1 */ - 0, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL + /* A X Y SRegLo SRegHi */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, 0, 0, + /* Ptr1Lo Ptr1Hi Tmp1 PFlags ZNRegs */ + UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_REGVAL, UNKNOWN_PFVAL_ALL, ZNREG_NONE }, F_NONE, "tosxor0ax" diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 34a9b88d6..c31b0ec97 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -320,10 +320,10 @@ static void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) static int Affected (LoadRegInfo* RI, const CodeEntry* E) /* Check if the load src may be modified between the pushax and op */ { - fncls_t fncls; - unsigned short Use; - unsigned short Chg; - unsigned short UseToCheck = 0; + fncls_t fncls; + unsigned int Use; + unsigned int Chg; + unsigned int UseToCheck = 0; if ((RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { diff --git a/src/cc65/reginfo.c b/src/cc65/reginfo.c index 383c6ec24..601f0f688 100644 --- a/src/cc65/reginfo.c +++ b/src/cc65/reginfo.c @@ -74,6 +74,15 @@ void RC_InvalidateZP (RegContents* C) +void RC_InvalidatePS (RegContents* C) +/* Invalidate processor status */ +{ + C->PFlags = UNKNOWN_PFVAL_ALL; + C->ZNRegs = ZNREG_NONE; +} + + + static void RC_Dump1 (FILE* F, const char* Desc, short Val) /* Dump one register value */ { @@ -102,6 +111,44 @@ void RC_Dump (FILE* F, const RegContents* RC) +#if !defined(HAVE_INLINE) +int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return ((PFlags << (PSTATE_BITS_SHIFT - 8)) & WhatStates & PSTATE_BITS_MASK) == 0; +} +#endif + + + +#if !defined(HAVE_INLINE) +int PStatesAreSet (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be set. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == WhatStates >> PSTATE_BITS_SHIFT; +} +#endif + + + +#if !defined(HAVE_INLINE) +int PStatesAreClear (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be cleared. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == 0; +} +#endif + + + RegInfo* NewRegInfo (const RegContents* RC) /* Allocate a new register info, initialize and return it. If RC is not ** a NULL pointer, it is used to initialize both, the input and output @@ -120,6 +167,9 @@ RegInfo* NewRegInfo (const RegContents* RC) RC_Invalidate (&RI->In); RC_Invalidate (&RI->Out); RC_Invalidate (&RI->Out2); + RC_InvalidatePS (&RI->In); + RC_InvalidatePS (&RI->Out); + RC_InvalidatePS (&RI->Out2); } /* Return the new struct */ diff --git a/src/cc65/reginfo.h b/src/cc65/reginfo.h index 7354c301f..28b948e04 100644 --- a/src/cc65/reginfo.h +++ b/src/cc65/reginfo.h @@ -54,6 +54,69 @@ /* Encoding for an unknown register value */ #define UNKNOWN_REGVAL -1 +/* Encoding for an unknown processor status: +** For Bit N in the flags, ((flags >> N) & 0x0101) == 0x0101 means 'unknown' +*/ +#define UNKNOWN_PFVAL_C 0x0101U /* Carray */ +#define UNKNOWN_PFVAL_Z 0x0202U /* Zero */ +#define UNKNOWN_PFVAL_I 0x0404U /* Interrupt */ +#define UNKNOWN_PFVAL_D 0x0808U /* Decimal */ +#define UNKNOWN_PFVAL_U 0x1010U /* Unused */ +#define UNKNOWN_PFVAL_B 0x2020U /* Break */ +#define UNKNOWN_PFVAL_V 0x4040U /* Overflow */ +#define UNKNOWN_PFVAL_N 0x8080U /* Negative */ +#define UNKNOWN_PFVAL_CZ (UNKNOWN_PFVAL_C | UNKNOWN_PFVAL_Z) +#define UNKNOWN_PFVAL_CZN (UNKNOWN_PFVAL_N | UNKNOWN_PFVAL_CZ) +#define UNKNOWN_PFVAL_CZVN (UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_CZN) +#define UNKNOWN_PFVAL_ZN (UNKNOWN_PFVAL_Z | UNKNOWN_PFVAL_N) +#define UNKNOWN_PFVAL_ZVN (UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_ZN) +#define UNKNOWN_PFVAL_6502 0xE7E7U +#define UNKNOWN_PFVAL_ALL 0xFFFFU + +/* Encoding for a known processor status */ +#define PFVAL_C 0x0001U /* Carray set */ +#define PFVAL_Z 0x0002U /* Zero set */ +#define PFVAL_I 0x0004U /* Interrupt set */ +#define PFVAL_D 0x0008U /* Decimal set */ +#define PFVAL_U 0x0010U /* Unused set */ +#define PFVAL_B 0x0020U /* Break set */ +#define PFVAL_V 0x0040U /* Overflow set */ +#define PFVAL_N 0x0080U /* Negative set */ +#define PFVAL_CZ (PFVAL_C | PFVAL_Z) +#define PFVAL_CZN (PFVAL_N | PFVAL_CZ) +#define PFVAL_CZVN (PFVAL_V | PFVAL_CZN) +#define PFVAL_ZN (PFVAL_Z | PFVAL_N) +#define PFVAL_ZVN (PFVAL_V | PFVAL_ZN) +#define PFVAL_6502 0x00E7U +#define PFVAL_ALL 0x00FFU + +/* Used for functions to convert the processor states to processor flags */ +#define PSTATE_BITS_SHIFT 24 +#define PSTATE_BITS_MASK (0xFFU << PSTATE_BITS_SHIFT) + +/* Encoding for unknown Z/N status origin */ +#define UNKNOWN_ZNREG 0x0000U + +/* Encoding for known register Z/N status origins */ +#define ZNREG_NONE 0x0000U /* None */ +#define ZNREG_A REG_A +#define ZNREG_X REG_X +#define ZNREG_Y REG_Y +#define ZNREG_TMP1 REG_TMP1 +#define ZNREG_PTR1_LO REG_PTR1_LO +#define ZNREG_PTR1_HI REG_PTR1_HI +#define ZNREG_PTR2_LO REG_PTR2_LO +#define ZNREG_PTR2_HI REG_PTR2_HI +#define ZNREG_SREG_LO REG_SREG_LO +#define ZNREG_SREG_HI REG_SREG_HI +#define ZNREG_SAVE_LO REG_SAVE_LO +#define ZNREG_SAVE_HI REG_SAVE_HI +#define ZNREG_AX (REG_A | REG_X) +#define ZNREG_AY (REG_A | REG_Y) +#define ZNREG_AXY (REG_A | REG_X | REG_Y) + + + /* Register contents */ typedef struct RegContents RegContents; struct RegContents { @@ -65,6 +128,8 @@ struct RegContents { short Ptr1Lo; short Ptr1Hi; short Tmp1; + unsigned short PFlags; /* Processor flags */ + unsigned short ZNRegs; /* Which register(s) the Z/N flags reflect */ }; /* Register change info */ @@ -89,6 +154,9 @@ void RC_Invalidate (RegContents* C); void RC_InvalidateZP (RegContents* C); /* Invalidate all ZP registers */ +void RC_InvalidatePS (RegContents* C); +/* Invalidate processor status */ + void RC_Dump (FILE* F, const RegContents* RC); /* Dump the contents of the given RegContents struct */ @@ -112,6 +180,56 @@ INLINE int RegValIsUnknown (short Val) # define RegValIsUnknown(S) ((S) < 0) #endif +#if defined(HAVE_INLINE) +INLINE int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return ((PFlags << (PSTATE_BITS_SHIFT - 8)) & WhatStates & PSTATE_BITS_MASK) == 0; +} +#else +int PStatesAreKnown (unsigned short PFlags, unsigned WhatStates); +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreUnknown (unsigned short PFlags, unsigned WhatStates) +/* Return true if any queried processor states are unknown. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return !PStatesAreKnown (PFlags, WhatStates); +} +#else +# define PStatesAreUnknown(V, B) (!PStatesAreKnown (V, B)) +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreSet (unsigned short PFlags, unsigned WhatStates) +/* Return true if all queried processor states are known to be set. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == WhatStates >> PSTATE_BITS_SHIFT; +} +#else +int PStatesAreSet (unsigned short PFlags, unsigned WhatStates); +#endif + +#if defined(HAVE_INLINE) +INLINE int PStatesAreClear (unsigned short PFlags, unsigned WhatStates) +/* Return true if the queried processor states are known to be cleared. +** Note: WhatStates takes PSTATE_* rather than PFVAL_*. +*/ +{ + return (PFlags & (WhatStates >> (PSTATE_BITS_SHIFT - 8))) == 0 && + (PFlags & (WhatStates >> PSTATE_BITS_SHIFT)) == 0; +} +#else +int PStatesAreClear (unsigned short PFlags, unsigned WhatStates); +#endif + RegInfo* NewRegInfo (const RegContents* RC); /* Allocate a new register info, initialize and return it. If RC is not ** a NULL pointer, it is used to initialize both, the input and output From 810e17edfefba76b3b3241e121b936572bf14a69 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 8 Sep 2020 23:41:02 +0800 Subject: [PATCH 338/806] Fixed processor states tracking for the BIT/TRB/TSB opcode. Added new opcode descriptions about whether and how the opcode accesses memory. --- src/cc65/codeent.c | 34 ++++++++++++++++++++++++++---- src/cc65/opcodes.c | 52 +++++++++++++++++++++++----------------------- src/cc65/opcodes.h | 5 ++++- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 085130001..d54be039a 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -889,12 +889,38 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Val = In->SRegHi; break; } + } else if (CE_IsConstImm (E)) { + /* 65C02 special */ + Val = (short) E->Num; } - Out->PFlags &= ~UNKNOWN_PFVAL_V; - if (Val & PFVAL_V) { - Out->PFlags |= PFVAL_V; + + /* BIT is unique with regards to the Z/V/N flags: + ** - The Z is set/cleared according to whether the AND result is zero. + ** - The V is coped directly from Bit 6 of the orginal argument. + ** - The N is coped directly from Bit 7 of the orginal argument. + ** Note the V/N flags are not affected in imm addressing mode supported by 65c02! + */ + if (E->AM == AM65_IMM) { + if (RegValIsKnown (Val)) { + Out->PFlags &= ~(UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_N); + if (Val & PFVAL_V) { + Out->PFlags |= PFVAL_V; + } + Out->PFlags &= ~UNKNOWN_PFVAL_V; + if (Val & PFVAL_V) { + Out->PFlags |= PFVAL_V; + } + } else { + Out->PFlags |= UNKNOWN_PFVAL_V | UNKNOWN_PFVAL_N; + } } - DeduceZN (Out, Val); + if ((RegValIsKnown (Val) && RegValIsKnown (In->RegA))) { + Val &= In->RegA; + } else if (((RegValIsKnown (Val) && Val == 0) || + (RegValIsKnown (In->RegA) && In->RegA == 0))) { + Val = 0; + } + DeduceZ (Out, Val); break; case OP65_BMI: diff --git a/src/cc65/opcodes.c b/src/cc65/opcodes.c index 792b92f7c..4ec25f3bd 100644 --- a/src/cc65/opcodes.c +++ b/src/cc65/opcodes.c @@ -63,21 +63,21 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ /* flags */ }, { OP65_AND, /* opcode */ "and", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ /* flags */ }, { OP65_ASL, /* opcode */ "asl", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, { OP65_BCC, /* opcode */ "bcc", /* mnemonic */ @@ -105,7 +105,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ - OF_SETF /* flags */ + OF_READ /* flags */ }, { OP65_BMI, /* opcode */ "bmi", /* mnemonic */ @@ -189,21 +189,21 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + OF_SETF | OF_CMP | OF_READ /* flags */ }, { OP65_CPX, /* opcode */ "cpx", /* mnemonic */ 0, /* size */ REG_X, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + OF_SETF | OF_CMP | OF_READ /* flags */ }, { OP65_CPY, /* opcode */ "cpy", /* mnemonic */ 0, /* size */ REG_Y, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_CMP /* flags */ + OF_SETF | OF_CMP | OF_READ /* flags */ }, { OP65_DEA, /* opcode */ "dea", /* mnemonic */ @@ -217,7 +217,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, { OP65_DEX, /* opcode */ "dex", /* mnemonic */ @@ -238,7 +238,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ /* flags */ }, { OP65_INA, /* opcode */ "ina", /* mnemonic */ @@ -252,7 +252,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, { OP65_INX, /* opcode */ "inx", /* mnemonic */ @@ -301,7 +301,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 3, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_UBRA | OF_LBRA /* flags */ + OF_UBRA | OF_LBRA | OF_READ /* flags */ }, { OP65_JNE, /* opcode */ "jne", /* mnemonic */ @@ -322,7 +322,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 3, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_CALL /* flags */ + OF_CALL | OF_READ /* flags */ }, { OP65_JVC, /* opcode */ "jvc", /* mnemonic */ @@ -343,28 +343,28 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_NONE, /* use */ REG_A, /* chg */ - OF_LOAD | OF_SETF /* flags */ + OF_LOAD | OF_SETF | OF_READ /* flags */ }, { OP65_LDX, /* opcode */ "ldx", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_X, /* chg */ - OF_LOAD | OF_SETF /* flags */ + OF_LOAD | OF_SETF | OF_READ /* flags */ }, { OP65_LDY, /* opcode */ "ldy", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_Y, /* chg */ - OF_LOAD | OF_SETF /* flags */ + OF_LOAD | OF_SETF | OF_READ /* flags */ }, { OP65_LSR, /* opcode */ "lsr", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, { OP65_NOP, /* opcode */ "nop", /* mnemonic */ @@ -378,7 +378,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ /* flags */ }, { OP65_PHA, /* opcode */ "pha", /* mnemonic */ @@ -441,14 +441,14 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, { OP65_ROR, /* opcode */ "ror", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_SETF | OF_NOIMP /* flags */ + OF_SETF | OF_NOIMP | OF_RMW /* flags */ }, /* Mark RTI as "uses all registers but doesn't change them", so the ** optimizer won't remove preceeding loads. @@ -472,7 +472,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_A, /* chg */ - OF_SETF /* flags */ + OF_SETF | OF_READ /* flags */ }, { OP65_SEC, /* opcode */ "sec", /* mnemonic */ @@ -500,7 +500,7 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ - OF_STORE /* flags */ + OF_STORE | OF_WRITE /* flags */ }, { OP65_STP, /* opcode */ "stp", /* mnemonic */ @@ -514,21 +514,21 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_X, /* use */ REG_NONE, /* chg */ - OF_STORE /* flags */ + OF_STORE | OF_WRITE /* flags */ }, { OP65_STY, /* opcode */ "sty", /* mnemonic */ 0, /* size */ REG_Y, /* use */ REG_NONE, /* chg */ - OF_STORE /* flags */ + OF_STORE | OF_WRITE /* flags */ }, { OP65_STZ, /* opcode */ "stz", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ - OF_STORE /* flags */ + OF_STORE | OF_WRITE /* flags */ }, { OP65_TAX, /* opcode */ "tax", /* mnemonic */ @@ -549,14 +549,14 @@ const OPCDesc OPCTable[OP65_COUNT] = { 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ - OF_SETF /* flags */ + OF_RMW /* flags */ }, { OP65_TSB, /* opcode */ "tsb", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ - OF_SETF /* flags */ + OF_RMW /* flags */ }, { OP65_TSX, /* opcode */ "tsx", /* mnemonic */ diff --git a/src/cc65/opcodes.h b/src/cc65/opcodes.h index 24a289c26..162647eff 100644 --- a/src/cc65/opcodes.h +++ b/src/cc65/opcodes.h @@ -176,13 +176,16 @@ typedef enum { #define OF_XFR 0x0100U /* Transfer instruction */ #define OF_CALL 0x0200U /* A subroutine call */ #define OF_REG_INCDEC 0x0400U /* A register increment or decrement */ -#define OF_SETF 0x0800U /* Insn will set all load flags (not carry) */ +#define OF_SETF 0x0800U /* Insn will set both Z and N flags according to the result */ #define OF_CMP 0x1000U /* A compare A/X/Y instruction */ #define OF_NOIMP 0x2000U /* Implicit addressing mode is actually A */ +#define OF_READ 0x4000U /* Read from the memory address */ +#define OF_WRITE 0x8000U /* Write to the memory address */ /* Combined infos */ #define OF_BRA (OF_UBRA | OF_CBRA) /* Operation is a jump/branch */ #define OF_DEAD (OF_UBRA | OF_RET) /* Dead end - no exec behind this point */ +#define OF_RMW (OF_READ | OF_WRITE) /* Read, Modify and Write */ /* Opcode description */ typedef struct { From b8ae5c28fe18ba5d5e0791eecded61d0a16f0d0e Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 8 Sep 2020 23:41:04 +0800 Subject: [PATCH 339/806] Added debug output support for processor flags. --- src/cc65/codeent.c | 60 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index d54be039a..0f537bbaf 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -50,6 +50,7 @@ #include "codelab.h" #include "opcodes.h" #include "output.h" +#include "reginfo.h" @@ -1901,6 +1902,7 @@ static char* RegInfoDesc (unsigned U, char* Buf) strcat (Buf, U & REG_PTR2? "2" : "_"); strcat (Buf, U & REG_SAVE? "V" : "_"); strcat (Buf, U & REG_SP? "S" : "_"); + sprintf (Buf + 10, "_%02X", (U & PSTATE_ALL) >> PSTATE_BITS_SHIFT); return Buf; } @@ -1925,11 +1927,59 @@ static char* RegContentDesc (const RegContents* RC, char* Buf) } B += 5; if (RegValIsUnknown (RC->RegY)) { - strcpy (B, "Y:XX"); + strcpy (B, "Y:XX "); } else { - sprintf (B, "Y:%02X", RC->RegY); + sprintf (B, "Y:%02X ", RC->RegY); } - B += 4; + B += 5; + if (PStatesAreUnknown (RC->PFlags, PSTATE_C)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_C ? "C" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_Z)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_Z ? "Z" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_I)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_I ? "I" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_D)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_D ? "D" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_U)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_U ? "U" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_B)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_B ? "B" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_V)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_V ? "V" : "_"); + } + B += 1; + if (PStatesAreUnknown (RC->PFlags, PSTATE_N)) { + strcpy (B, "~"); + } else { + strcpy (B, RC->PFlags & PFVAL_N ? "N" : "_"); + } + B += 1; return Buf; } @@ -2024,7 +2074,7 @@ void CE_Output (const CodeEntry* E) if (Debug) { char Use [128]; char Chg [128]; - WriteOutput ("%*s; USE: %-12s CHG: %-12s SIZE: %u", + WriteOutput ("%*s; USE: %-15s CHG: %-15s SIZE: %u", (int)(30-Chars), "", RegInfoDesc (E->Use, Use), RegInfoDesc (E->Chg, Chg), @@ -2033,7 +2083,7 @@ void CE_Output (const CodeEntry* E) if (E->RI) { char RegIn[32]; char RegOut[32]; - WriteOutput (" In %s Out %s", + WriteOutput (" In %s Out %s", RegContentDesc (&E->RI->In, RegIn), RegContentDesc (&E->RI->Out, RegOut)); } From 0482e4d6e4e2bcba3116e2eee9a98e3d374b28f5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 18 Sep 2020 08:42:56 +0200 Subject: [PATCH 340/806] Fix CRLFs introduced by fe3f267 --- libsrc/runtime/bcast.s | 42 ++++++++++++++++++++--------------------- libsrc/runtime/lbcast.s | 40 +++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/libsrc/runtime/bcast.s b/libsrc/runtime/bcast.s index bb37b6ec0..bc228d1b7 100644 --- a/libsrc/runtime/bcast.s +++ b/libsrc/runtime/bcast.s @@ -1,21 +1,21 @@ -; -; acqn, 01.16.2020 -; -; CC65 runtime: boolean cast -; - - .export bcasta, bcastax - -bcastax: - cpx #0 - bne L1 - -bcasta: - tax - beq L0 ; Zero already in X - -L1: ldx #0 - lda #1 - -L0: rts - +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast +; + + .export bcasta, bcastax + +bcastax: + cpx #0 + bne L1 + +bcasta: + tax + beq L0 ; Zero already in X + +L1: ldx #0 + lda #1 + +L0: rts + diff --git a/libsrc/runtime/lbcast.s b/libsrc/runtime/lbcast.s index 3dee8d956..208442c44 100644 --- a/libsrc/runtime/lbcast.s +++ b/libsrc/runtime/lbcast.s @@ -1,20 +1,20 @@ -; -; acqn, 01.16.2020 -; -; CC65 runtime: boolean cast for longs -; - - .export bcasteax - .importzp sreg, tmp1 - -bcasteax: - stx tmp1 - ldx #0 ; High byte of result - ora tmp1 - ora sreg - ora sreg+1 - beq L0 - - lda #1 -L0: rts - +; +; acqn, 01.16.2020 +; +; CC65 runtime: boolean cast for longs +; + + .export bcasteax + .importzp sreg, tmp1 + +bcasteax: + stx tmp1 + ldx #0 ; High byte of result + ora tmp1 + ora sreg + ora sreg+1 + beq L0 + + lda #1 +L0: rts + From 86ced2bd4c35087c118c6a48bdbe7be555ff4a1c Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 13 Sep 2020 17:01:57 +0800 Subject: [PATCH 341/806] Avoided unnecessarily boosting the code label numbers with boolean AND. --- src/cc65/expr.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 826e72b09..a88710f34 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -3237,8 +3237,8 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) int HasFalseJump = 0, HasTrueJump = 0; CodeMark Start; - /* Get a label that we will use for false expressions */ - int FalseLab = GetLocalLabel (); + /* The label that we will use for false expressions */ + int FalseLab = 0; /* Get lhs */ GetCodePos (&Start); @@ -3266,8 +3266,12 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Clear the test flag */ ED_RequireNoTest (Expr); - /* Remember that the jump is used */ - HasFalseJump = 1; + if (HasFalseJump == 0) { + /* Remember that the jump is used */ + HasFalseJump = 1; + /* Get a label for false expressions */ + FalseLab = GetLocalLabel (); + } /* Generate the jump */ g_falsejump (CF_NONE, FalseLab); @@ -3304,7 +3308,12 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Do short circuit evaluation */ if (CurTok.Tok == TOK_BOOL_AND) { - HasFalseJump = 1; + if (HasFalseJump == 0) { + /* Remember that the jump is used */ + HasFalseJump = 1; + /* Get a label for false expressions */ + FalseLab = GetLocalLabel (); + } g_falsejump (CF_NONE, FalseLab); } else { /* We need the true label for the last expression */ From d02b12fa6c18259b2e8ca82bbff79cd06a6d6359 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 23 Aug 2020 01:35:18 +0800 Subject: [PATCH 342/806] Made local code/data labels really local to the functions where they live in. --- src/cc65/asmlabel.c | 37 +++++++++++++++++++++++++++---------- src/cc65/asmlabel.h | 15 ++++++++++++++- src/cc65/compile.c | 9 +++++++++ src/cc65/function.c | 16 +++++++++++++++- src/cc65/function.h | 3 +++ src/cc65/segments.c | 14 ++++++++------ src/cc65/segments.h | 2 ++ 7 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/cc65/asmlabel.c b/src/cc65/asmlabel.c index f561036ce..7d5db75e6 100644 --- a/src/cc65/asmlabel.c +++ b/src/cc65/asmlabel.c @@ -42,6 +42,17 @@ /* cc65 */ #include "asmlabel.h" #include "error.h" +#include "segments.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static struct Segments* CurrentFunctionSegment; @@ -51,19 +62,26 @@ -unsigned GetLocalLabel (void) -/* Get an unused label. Will never return zero. */ +void UseLabelPoolFromSegments (struct Segments* Seg) +/* Use the info in segments for generating new label numbers */ { - /* Number to generate unique labels */ - static unsigned NextLabel = 0; + CurrentFunctionSegment = Seg; +} + + + +unsigned GetLocalLabel (void) +/* Get an unused assembler label for the function. Will never return zero. */ +{ + PRECONDITION (CurrentFunctionSegment != 0); /* Check for an overflow */ - if (NextLabel >= 0xFFFF) { + if (CurrentFunctionSegment->NextLabel >= 0xFFFF) { Internal ("Local label overflow"); } /* Return the next label */ - return ++NextLabel; + return ++CurrentFunctionSegment->NextLabel; } @@ -104,16 +122,15 @@ int IsLocalLabelName (const char* Name) unsigned GetLocalDataLabel (void) /* Get an unused local data label. Will never return zero. */ { - /* Number to generate unique labels */ - static unsigned NextLabel = 0; + PRECONDITION (CurrentFunctionSegment != 0); /* Check for an overflow */ - if (NextLabel >= 0xFFFF) { + if (CurrentFunctionSegment->NextDataLabel >= 0xFFFF) { Internal ("Local data label overflow"); } /* Return the next label */ - return ++NextLabel; + return ++CurrentFunctionSegment->NextDataLabel; } diff --git a/src/cc65/asmlabel.h b/src/cc65/asmlabel.h index a79071400..dbfe2f443 100644 --- a/src/cc65/asmlabel.h +++ b/src/cc65/asmlabel.h @@ -38,14 +38,27 @@ +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +struct Segments; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ +void UseLabelPoolFromSegments (struct Segments* Seg); +/* Use the info in segments for generating new label numbers */ + unsigned GetLocalLabel (void); -/* Get an unused assembler label. Will never return zero. */ +/* Get an unused assembler label for the function. Will never return zero. */ const char* LocalLabelName (unsigned L); /* Make a label name from the given label number. The label name will be diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 3296968f6..00e78c2bd 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -389,6 +389,12 @@ void Compile (const char* FileName) /* Create the global code and data segments */ CreateGlobalSegments (); + /* There shouldn't be needs for local labels outside a function, but the + ** current code generator still tries to get some at times even though the + ** code were ill-formed. So just set it up with the global segment list. + */ + UseLabelPoolFromSegments (GS); + /* Initialize the literal pool */ InitLiteralPool (); @@ -488,6 +494,9 @@ void FinishCompile (void) */ for (Entry = GetGlobalSymTab ()->SymHead; Entry; Entry = Entry->NextSym) { if (SymIsOutputFunc (Entry)) { + /* Continue with previous label numbers */ + UseLabelPoolFromSegments (Entry->V.F.Seg); + /* Function which is defined and referenced or extern */ MoveLiteralPool (Entry->V.F.LitPool); CS_MergeLabels (Entry->V.F.Seg->Code); diff --git a/src/cc65/function.c b/src/cc65/function.c index 9d4f8c2f9..00755ae65 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -83,7 +83,7 @@ static Function* NewFunction (struct SymEntry* Sym, FuncDesc* D) F->ReturnType = GetFuncReturn (Sym->Type); F->Desc = D; F->Reserved = 0; - F->RetLab = GetLocalLabel (); + F->RetLab = 0; F->TopLevelSP = 0; F->RegOffs = RegisterSpace; F->Flags = IsTypeVoid (F->ReturnType) ? FF_VOID_RETURN : FF_NONE; @@ -255,6 +255,14 @@ int F_HasOldStyleIntRet (const Function* F) +void F_SetRetLab (Function* F, unsigned NewRetLab) +/* Change the return jump label */ +{ + F->RetLab = NewRetLab; +} + + + unsigned F_GetRetLab (const Function* F) /* Return the return jump label */ { @@ -540,6 +548,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Allocate code and data segments for this function */ Func->V.F.Seg = PushSegments (Func); + /* Use the info in the segments for generating new local labels */ + UseLabelPoolFromSegments (Func->V.F.Seg); + + /* Set return label. This has to be done after the segments are pushed */ + F_SetRetLab (CurrentFunc, GetLocalLabel ()); + /* Allocate a new literal pool */ PushLiteralPool (Func); diff --git a/src/cc65/function.h b/src/cc65/function.h index e0b7ef0a2..825257a60 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -120,6 +120,9 @@ int F_IsOldStyle (const Function* F); int F_HasOldStyleIntRet (const Function* F); /* Return true if this is an old style (K&R) function with an implicit int return */ +void F_SetRetLab (Function* F, unsigned NewRetLab); +/* Change the return jump label */ + unsigned F_GetRetLab (const Function* F); /* Return the return jump label */ diff --git a/src/cc65/segments.c b/src/cc65/segments.c index 6efbf68b7..7a9e32eb8 100644 --- a/src/cc65/segments.c +++ b/src/cc65/segments.c @@ -143,12 +143,14 @@ static Segments* NewSegments (SymEntry* Func) Segments* S = xmalloc (sizeof (Segments)); /* Initialize the fields */ - S->Text = NewTextSeg (Func); - S->Code = NewCodeSeg (GetSegName (SEG_CODE), Func); - S->Data = NewDataSeg (GetSegName (SEG_DATA), Func); - S->ROData = NewDataSeg (GetSegName (SEG_RODATA), Func); - S->BSS = NewDataSeg (GetSegName (SEG_BSS), Func); - S->CurDSeg = SEG_DATA; + S->Text = NewTextSeg (Func); + S->Code = NewCodeSeg (GetSegName (SEG_CODE), Func); + S->Data = NewDataSeg (GetSegName (SEG_DATA), Func); + S->ROData = NewDataSeg (GetSegName (SEG_RODATA), Func); + S->BSS = NewDataSeg (GetSegName (SEG_BSS), Func); + S->CurDSeg = SEG_DATA; + S->NextLabel = 0; + S->NextDataLabel = 0; /* Return the new struct */ return S; diff --git a/src/cc65/segments.h b/src/cc65/segments.h index f55be688b..777073559 100644 --- a/src/cc65/segments.h +++ b/src/cc65/segments.h @@ -86,6 +86,8 @@ struct Segments { struct DataSeg* ROData; /* Readonly data segment */ struct DataSeg* BSS; /* Segment for uninitialized data */ segment_t CurDSeg; /* Current data segment */ + unsigned NextLabel; /* Number to generate unique code labels */ + unsigned NextDataLabel; /* Number to generate unique data labels */ }; /* Pointer to the current segment list. Output goes here. */ From a7d6eb9190b8356597f008783e8aaa6121cf4f6b Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 13 Sep 2020 08:41:05 +0800 Subject: [PATCH 343/806] Added processor flag usage/change-tracking for non-JSR/RTS code entries. Some existing optimizations are impacted and need fixes in order to work correctly again. Fixed and improved OptPrecalc. Now it respects the C/V/Z/N flags. Fixed optimizations impacted by added support of tracking processor flags. --- src/cc65/codeent.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptind.c | 121 +++++++++++++++++++++++++++----------- src/cc65/copttest.c | 2 +- 3 files changed, 228 insertions(+), 34 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 0f537bbaf..195f7199f 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -206,6 +206,145 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) /* Keep gcc silent */ break; } + + /* Append processor flags as well as special usages */ + switch (E->OPC) { + + case OP65_ADC: + case OP65_SBC: + E->Use |= PSTATE_C; + E->Chg |= PSTATE_CZVN; + break; + case OP65_ROL: + case OP65_ROR: + E->Use |= PSTATE_C; + E->Chg |= PSTATE_CZN; + break; + case OP65_ASL: + case OP65_LSR: + E->Chg |= PSTATE_CZN; + break; + case OP65_CMP: + case OP65_CPX: + case OP65_CPY: + E->Chg |= PSTATE_CZN; + break; + case OP65_BIT: + E->Chg |= PSTATE_ZVN; + if (E->AM != AM65_IMM) { + E->Chg &= ~(PSTATE_V | PSTATE_N); + } + break; + case OP65_BRK: + E->Chg |= PSTATE_B; + break; + case OP65_CLC: + case OP65_SEC: + E->Chg |= PSTATE_C; + break; + case OP65_CLD: + case OP65_SED: + E->Chg |= PSTATE_D; + break; + case OP65_CLI: + case OP65_SEI: + E->Chg |= PSTATE_I; + break; + case OP65_CLV: + E->Chg |= PSTATE_V; + break; + case OP65_TRB: + case OP65_TSB: + E->Chg |= PSTATE_Z; + break; + case OP65_BCC: + case OP65_BCS: + case OP65_JCC: + case OP65_JCS: + E->Use |= PSTATE_C; + break; + case OP65_BEQ: + case OP65_BNE: + case OP65_JEQ: + case OP65_JNE: + E->Use |= PSTATE_Z; + break; + case OP65_BMI: + case OP65_BPL: + case OP65_JMI: + case OP65_JPL: + E->Use |= PSTATE_N; + break; + case OP65_BVC: + case OP65_BVS: + case OP65_JVC: + case OP65_JVS: + E->Use |= PSTATE_V; + break; + case OP65_BRA: + case OP65_JMP: + break; + case OP65_AND: + case OP65_EOR: + case OP65_ORA: + case OP65_DEA: + case OP65_DEC: + case OP65_DEX: + case OP65_DEY: + case OP65_INA: + case OP65_INC: + case OP65_INX: + case OP65_INY: + case OP65_LDA: + case OP65_LDX: + case OP65_LDY: + case OP65_TAX: + case OP65_TAY: + case OP65_TXA: + case OP65_TYA: + E->Chg |= PSTATE_ZN; + break; + case OP65_TSX: + E->Use |= SLV_SP65; + E->Chg |= PSTATE_ZN; + break; + case OP65_TXS: + E->Chg |= SLV_SP65; + break; + case OP65_PLA: + case OP65_PLX: + case OP65_PLY: + E->Use |= SLV_SP65; + E->Chg |= SLV_PL65 | PSTATE_ZN; + break; + case OP65_PLP: + E->Use |= SLV_SP65; + E->Chg |= SLV_PL65 | PSTATE_ALL; + break; + case OP65_PHA: + case OP65_PHX: + case OP65_PHY: + E->Use |= SLV_SP65; + E->Chg |= SLV_PH65; + break; + case OP65_PHP: + E->Use |= SLV_SP65 | PSTATE_ALL; + E->Chg |= SLV_PH65; + break; + case OP65_RTI: + E->Chg |= PSTATE_ALL; + break; + case OP65_RTS: + break; + case OP65_STA: + case OP65_STX: + case OP65_STY: + case OP65_STZ: + case OP65_JSR: + case OP65_NOP: + default: + break; + } } } diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index b506a21ca..eada61871 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -672,7 +672,7 @@ unsigned OptTransfers2 (CodeSeg* S) (N = CS_GetNextEntry (S, I)) != 0 && !CE_HasLabel (N) && (N->Info & OF_XFR) != 0 && - (GetRegInfo (S, I+2, E->Chg) & E->Chg) == 0) { + (GetRegInfo (S, I+2, E->Chg & REG_ALL) & E->Chg & REG_ALL) == 0) { CodeEntry* X = 0; @@ -796,7 +796,7 @@ unsigned OptTransfers3 (CodeSeg* S) } /* Does this insn change the target register of the transfer? */ - } else if (E->Chg & XferEntry->Chg) { + } else if (E->Chg & XferEntry->Chg & ~PSTATE_ZN) { /* We *may* add code here to remove the transfer, but I'm ** currently not sure about the consequences, so I won't @@ -823,8 +823,9 @@ unsigned OptTransfers3 (CodeSeg* S) ** isn't used later, and we have an address mode match, we can ** replace the transfer by a store and remove the store here. */ - if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 && - (StoreEntry->AM == AM65_ABS || + if ((GetRegInfo (S, I, XferEntry->Chg & REG_ALL) & + XferEntry->Chg & REG_ALL) == 0 && + (StoreEntry->AM == AM65_ABS || StoreEntry->AM == AM65_ZP) && (StoreEntry->AM != AM65_ZP || (StoreEntry->Chg & UsedRegs) == 0) && @@ -973,7 +974,7 @@ unsigned OptTransfers4 (CodeSeg* S) } /* Does this insn change the target register of the load? */ - } else if (E->Chg & LoadEntry->Chg) { + } else if (E->Chg & LoadEntry->Chg & ~PSTATE_ZN) { /* We *may* add code here to remove the load, but I'm ** currently not sure about the consequences, so I won't @@ -989,9 +990,10 @@ unsigned OptTransfers4 (CodeSeg* S) ** isn't used later, and we have an address mode match, we can ** replace the transfer by a load and remove the initial load. */ - if ((GetRegInfo (S, I, LoadEntry->Chg) & LoadEntry->Chg) == 0 && - (LoadEntry->AM == AM65_ABS || - LoadEntry->AM == AM65_ZP || + if ((GetRegInfo (S, I, LoadEntry->Chg & REG_ALL) & + LoadEntry->Chg & REG_ALL) == 0 && + (LoadEntry->AM == AM65_ABS || + LoadEntry->AM == AM65_ZP || LoadEntry->AM == AM65_IMM) && !MemAccess (S, Load+1, Xfer-1, LoadEntry)) { @@ -1246,36 +1248,70 @@ unsigned OptPrecalc (CodeSeg* S) } break; - case OP65_EOR: - if (RegValIsKnown (Out->RegA)) { - /* Accu op zp with known contents */ - Arg = MakeHexArg (Out->RegA); - } - break; - case OP65_ADC: case OP65_SBC: - /* If this is an operation with an immediate operand of zero, - ** and the register is zero, the operation won't give us any - ** results we don't already have (including the flags), so - ** remove it. Something like this is generated as a result of - ** a compare where parts of the values are known to be zero. - ** The only situation where we need to leave things as they are - ** is when V flag is being tested in the next instruction, - ** because ADC/SBC #0 always clears it. - */ - if (In->RegA == 0 && CE_IsKnownImm (E, 0x00) && - (E = CS_GetEntry (S, I + 1)) && - E->OPC != OP65_BVC && - E->OPC != OP65_BVS ) { - /* 0-0 or 0+0 -> remove */ - CS_DelEntry (S, I); - ++Changes; + if (CE_IsKnownImm (E, 0x00)) { + /* If this is an operation with an immediate operand of zero, + ** and the Z/N flags reflect the current states of the content + ** in A, then the operation won't give us any results we don't + ** already have (including the flags) as long as the C flag is + ** set normally (cleared for ADC and set for SBC) for the + ** operation. So we can remove the operation if it is the + ** normal case or the result in A is not used later. + ** Something like this is generated as a result of a compare + ** where parts of the values are known to be zero. + ** The only situation where we need to leave things as they + ** are is when an indeterminate V flag is being tested later, + ** because ADC/SBC #0 always clears it. + */ + int CondC = PStatesAreKnown (In->PFlags, PSTATE_C) && + ((E->OPC == OP65_ADC && (In->PFlags & PFVAL_C) == 0) || + (E->OPC == OP65_SBC && (In->PFlags & PFVAL_C) != 0)); + int CondV = PStatesAreKnown (In->PFlags, PSTATE_V) && (In->PFlags & PFVAL_V) == 0; + int CondZN = (In->ZNRegs & ZNREG_A) != 0; + unsigned R = 0; + if (CondC) { + R = (CondV ? 0 : PSTATE_V) | (CondZN ? 0 : PSTATE_ZN); + } else { + R = REG_A | PSTATE_CZVN; + } + if (R != 0) { + /* Collect info on all flags in one round to save time */ + R = GetRegInfo (S, I + 1, R); + } + CondV = (CondC && CondV) || (R & PSTATE_V) == 0; + CondZN = (CondC && CondZN) || (R & PSTATE_ZN) == 0; + /* This is done last as it could change the info used by the two above */ + CondC = CondC || (R & (REG_A | PSTATE_C)) == 0; + if (CondC && CondV && CondZN) { + /* ?+0, ?-0 or result unused -> remove */ + CS_DelEntry (S, I); + ++Changes; + } + } else if (E->OPC == OP65_ADC && In->RegA == 0) { + /* 0 + arg. In this case we need only care about the C/V flags and + ** let the load set the Z/N flags properly. + */ + int CondC = PStatesAreClear (In->PFlags, PSTATE_C); + int CondV = PStatesAreClear (In->PFlags, PSTATE_V); + unsigned R = (CondC ? 0 : REG_A | PSTATE_C) | (CondC && CondV ? 0 : PSTATE_V); + if (R) { + R = GetRegInfo (S, I + 1, R); + } + CondV = (CondC && CondV) || (R & PSTATE_V) == 0; + CondC = CondC || (R & (REG_A | PSTATE_C)) == 0; + if (CondC && CondV) { + /* 0 + arg -> replace with lda arg */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } } break; case OP65_AND: - if (CE_IsKnownImm (E, 0xFF)) { + if (CE_IsKnownImm (E, 0xFF) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { /* AND with 0xFF, remove */ CS_DelEntry (S, I); ++Changes; @@ -1293,7 +1329,9 @@ unsigned OptPrecalc (CodeSeg* S) break; case OP65_ORA: - if (CE_IsKnownImm (E, 0x00)) { + if (CE_IsKnownImm (E, 0x00) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { /* ORA with zero, remove */ CS_DelEntry (S, I); ++Changes; @@ -1310,6 +1348,23 @@ unsigned OptPrecalc (CodeSeg* S) } break; + case OP65_EOR: + if (CE_IsKnownImm (E, 0x00) && + ((In->ZNRegs & ZNREG_A) != 0 || + (GetRegInfo (S, I + 1, PSTATE_ZN) & PSTATE_ZN) == 0)) { + /* EOR with zero, remove */ + CS_DelEntry (S, I); + ++Changes; + } else if (RegValIsKnown (Out->RegA)) { + /* Accu op zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } else if (In->RegA == 0) { + /* EOR but A contains 0x00 - replace by lda */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } + break; + default: break; diff --git a/src/cc65/copttest.c b/src/cc65/copttest.c index 5628a42c3..f2d55244f 100644 --- a/src/cc65/copttest.c +++ b/src/cc65/copttest.c @@ -153,7 +153,7 @@ unsigned OptTest2 (CodeSeg* S) (L[2]->Info & OF_FBRA) != 0 && L[1]->AM == L[0]->AM && strcmp (L[0]->Arg, L[1]->Arg) == 0 && - (GetRegInfo (S, I+2, L[1]->Chg) & L[1]->Chg) == 0) { + (GetRegInfo (S, I+2, L[1]->Chg & ~PSTATE_ZN) & L[1]->Chg & ~PSTATE_ZN) == 0) { /* Remove the load */ CS_DelEntry (S, I+1); From 079f4a99dd32444d95fd916dca966827c6a07b65 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 13 Sep 2020 09:14:51 +0800 Subject: [PATCH 344/806] Added processor state info to the OPCDesc table. However, since some opcodes are affected by the addressing mode, this info is unused in codegen/optimizer but just as quick in-code documentation/hints. --- src/cc65/opcodes.c | 342 ++++++++++++++++++++++----------------------- src/cc65/opcodes.h | 4 +- 2 files changed, 173 insertions(+), 173 deletions(-) diff --git a/src/cc65/opcodes.c b/src/cc65/opcodes.c index 4ec25f3bd..3c02c84c4 100644 --- a/src/cc65/opcodes.c +++ b/src/cc65/opcodes.c @@ -61,394 +61,394 @@ const OPCDesc OPCTable[OP65_COUNT] = { { OP65_ADC, /* opcode */ "adc", /* mnemonic */ 0, /* size */ - REG_A, /* use */ - REG_A, /* chg */ - OF_SETF | OF_READ /* flags */ + OF_SETF | OF_READ, /* flags */ + REG_A | PSTATE_C, /* use */ + REG_A | PSTATE_CZVN /* chg */ }, { OP65_AND, /* opcode */ "and", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF | OF_READ /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_ASL, /* opcode */ "asl", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + PSTATE_CZN /* chg */ }, { OP65_BCC, /* opcode */ "bcc", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_BCS, /* opcode */ "bcs", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_BEQ, /* opcode */ "beq", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_BIT, /* opcode */ "bit", /* mnemonic */ 0, /* size */ + OF_READ, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_READ /* flags */ + PSTATE_ZVN /* chg */ }, { OP65_BMI, /* opcode */ "bmi", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_FBRA /* flags */ + OF_CBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_BNE, /* opcode */ "bne", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_BPL, /* opcode */ "bpl", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_FBRA /* flags */ + OF_CBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_BRA, /* opcode */ "bra", /* mnemonic */ 2, /* size */ + OF_UBRA, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_UBRA /* flags */ + REG_NONE /* chg */ }, { OP65_BRK, /* opcode */ "brk", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_B /* chg */ }, { OP65_BVC, /* opcode */ "bvc", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_BVS, /* opcode */ "bvs", /* mnemonic */ 2, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA /* flags */ + OF_CBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_CLC, /* opcode */ "clc", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_C /* chg */ }, { OP65_CLD, /* opcode */ "cld", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_D /* chg */ }, { OP65_CLI, /* opcode */ "cli", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_I /* chg */ }, { OP65_CLV, /* opcode */ "clv", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_V /* chg */ }, { OP65_CMP, /* opcode */ "cmp", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP | OF_READ /* flags */ + PSTATE_CZN /* chg */ }, { OP65_CPX, /* opcode */ "cpx", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP | OF_READ /* flags */ + PSTATE_CZN /* chg */ }, { OP65_CPY, /* opcode */ "cpy", /* mnemonic */ 0, /* size */ + OF_SETF | OF_CMP | OF_READ, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_CMP | OF_READ /* flags */ + PSTATE_CZN /* chg */ }, { OP65_DEA, /* opcode */ "dea", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_DEC, /* opcode */ "dec", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + PSTATE_ZN /* chg */ }, { OP65_DEX, /* opcode */ "dex", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_X, /* use */ - REG_X, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_DEY, /* opcode */ "dey", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_Y, /* use */ - REG_Y, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_EOR, /* opcode */ "eor", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF | OF_READ /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_INA, /* opcode */ "ina", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_INC, /* opcode */ "inc", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + PSTATE_ZN /* chg */ }, { OP65_INX, /* opcode */ "inx", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_X, /* use */ - REG_X, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_INY, /* opcode */ "iny", /* mnemonic */ 1, /* size */ + OF_REG_INCDEC | OF_SETF, /* flags */ REG_Y, /* use */ - REG_Y, /* chg */ - OF_REG_INCDEC | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_JCC, /* opcode */ "jcc", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_JCS, /* opcode */ "jcs", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_C, /* use */ + REG_NONE /* chg */ }, { OP65_JEQ, /* opcode */ "jeq", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_JMI, /* opcode */ "jmi", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_JMP, /* opcode */ "jmp", /* mnemonic */ 3, /* size */ + OF_UBRA | OF_LBRA | OF_READ, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_UBRA | OF_LBRA | OF_READ /* flags */ + REG_NONE /* chg */ }, { OP65_JNE, /* opcode */ "jne", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA, /* flags */ + PSTATE_Z, /* use */ + REG_NONE /* chg */ }, { OP65_JPL, /* opcode */ "jpl", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA | OF_FBRA /* flags */ + OF_CBRA | OF_LBRA | OF_FBRA, /* flags */ + PSTATE_N, /* use */ + REG_NONE /* chg */ }, { OP65_JSR, /* opcode */ "jsr", /* mnemonic */ 3, /* size */ + OF_CALL | OF_READ, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CALL | OF_READ /* flags */ + REG_NONE /* chg */ }, { OP65_JVC, /* opcode */ "jvc", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_JVS, /* opcode */ "jvs", /* mnemonic */ 5, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_CBRA | OF_LBRA /* flags */ + OF_CBRA | OF_LBRA, /* flags */ + PSTATE_V, /* use */ + REG_NONE /* chg */ }, { OP65_LDA, /* opcode */ "lda", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_A, /* chg */ - OF_LOAD | OF_SETF | OF_READ /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_LDX, /* opcode */ "ldx", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_LOAD | OF_SETF | OF_READ /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_LDY, /* opcode */ "ldy", /* mnemonic */ 0, /* size */ + OF_LOAD | OF_SETF | OF_READ, /* flags */ REG_NONE, /* use */ - REG_Y, /* chg */ - OF_LOAD | OF_SETF | OF_READ /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_LSR, /* opcode */ "lsr", /* mnemonic */ 0, /* size */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + PSTATE_CZN /* chg */ }, { OP65_NOP, /* opcode */ "nop", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_ORA, /* opcode */ "ora", /* mnemonic */ 0, /* size */ + OF_SETF | OF_READ, /* flags */ REG_A, /* use */ - REG_A, /* chg */ - OF_SETF | OF_READ /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_PHA, /* opcode */ "pha", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PHP, /* opcode */ "php", /* mnemonic */ 1, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + OF_NONE, /* flags */ + PSTATE_ALL, /* use */ + REG_NONE /* chg */ }, { OP65_PHX, /* opcode */ "phx", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PHY, /* opcode */ "phy", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_PLA, /* opcode */ "pla", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_A, /* chg */ - OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_PLP, /* opcode */ "plp", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_ALL /* chg */ }, { OP65_PLX, /* opcode */ "plx", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_PLY, /* opcode */ "ply", /* mnemonic */ 1, /* size */ + OF_SETF, /* flags */ REG_NONE, /* use */ - REG_Y, /* chg */ - OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_ROL, /* opcode */ "rol", /* mnemonic */ 0, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ + PSTATE_C, /* use */ + PSTATE_CZN /* chg */ }, { OP65_ROR, /* opcode */ "ror", /* mnemonic */ 0, /* size */ - REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_SETF | OF_NOIMP | OF_RMW /* flags */ + OF_SETF | OF_NOIMP | OF_RMW, /* flags */ + PSTATE_C, /* use */ + PSTATE_CZN /* chg */ }, /* Mark RTI as "uses all registers but doesn't change them", so the ** optimizer won't remove preceeding loads. @@ -456,135 +456,135 @@ const OPCDesc OPCTable[OP65_COUNT] = { { OP65_RTI, /* opcode */ "rti", /* mnemonic */ 1, /* size */ + OF_RET, /* flags */ REG_AXY, /* use */ - REG_NONE, /* chg */ - OF_RET /* flags */ + PSTATE_ALL /* chg */ }, { OP65_RTS, /* opcode */ "rts", /* mnemonic */ 1, /* size */ + OF_RET, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_RET /* flags */ + REG_NONE /* chg */ }, { OP65_SBC, /* opcode */ "sbc", /* mnemonic */ 0, /* size */ - REG_A, /* use */ - REG_A, /* chg */ - OF_SETF | OF_READ /* flags */ + OF_SETF | OF_READ, /* flags */ + REG_A | PSTATE_C, /* use */ + REG_A | PSTATE_CZVN /* chg */ }, { OP65_SEC, /* opcode */ "sec", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_C /* chg */ }, { OP65_SED, /* opcode */ "sed", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_D /* chg */ }, { OP65_SEI, /* opcode */ "sei", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + PSTATE_I /* chg */ }, { OP65_STA, /* opcode */ "sta", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_STORE | OF_WRITE /* flags */ + REG_NONE /* chg */ }, { OP65_STP, /* opcode */ "stp", /* mnemonic */ 1, /* size */ + OF_NONE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_NONE /* flags */ + REG_NONE /* chg */ }, { OP65_STX, /* opcode */ "stx", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_STORE | OF_WRITE /* flags */ + REG_NONE /* chg */ }, { OP65_STY, /* opcode */ "sty", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_Y, /* use */ - REG_NONE, /* chg */ - OF_STORE | OF_WRITE /* flags */ + REG_NONE /* chg */ }, { OP65_STZ, /* opcode */ "stz", /* mnemonic */ 0, /* size */ + OF_STORE | OF_WRITE, /* flags */ REG_NONE, /* use */ - REG_NONE, /* chg */ - OF_STORE | OF_WRITE /* flags */ + REG_NONE /* chg */ }, { OP65_TAX, /* opcode */ "tax", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_A, /* use */ - REG_X, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_TAY, /* opcode */ "tay", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_A, /* use */ - REG_Y, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_Y | PSTATE_ZN /* chg */ }, { OP65_TRB, /* opcode */ "trb", /* mnemonic */ 0, /* size */ + OF_RMW, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_RMW /* flags */ + PSTATE_Z /* chg */ }, { OP65_TSB, /* opcode */ "tsb", /* mnemonic */ 0, /* size */ + OF_RMW, /* flags */ REG_A, /* use */ - REG_NONE, /* chg */ - OF_RMW /* flags */ + PSTATE_Z /* chg */ }, { OP65_TSX, /* opcode */ "tsx", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_NONE, /* use */ - REG_X, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_X | PSTATE_ZN /* chg */ }, { OP65_TXA, /* opcode */ "txa", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_X, /* use */ - REG_A, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, { OP65_TXS, /* opcode */ "txs", /* mnemonic */ 1, /* size */ + OF_XFR, /* flags */ REG_X, /* use */ - REG_NONE, /* chg */ - OF_XFR /* flags */ + REG_NONE /* chg */ }, { OP65_TYA, /* opcode */ "tya", /* mnemonic */ 1, /* size */ + OF_XFR | OF_SETF, /* flags */ REG_Y, /* use */ - REG_A, /* chg */ - OF_XFR | OF_SETF /* flags */ + REG_A | PSTATE_ZN /* chg */ }, }; diff --git a/src/cc65/opcodes.h b/src/cc65/opcodes.h index 162647eff..980cc649a 100644 --- a/src/cc65/opcodes.h +++ b/src/cc65/opcodes.h @@ -192,9 +192,9 @@ typedef struct { opc_t OPC; /* Opcode */ char Mnemo[9]; /* Mnemonic */ unsigned char Size; /* Size, 0 = check addressing mode */ - unsigned short Use; /* Registers used by this insn */ - unsigned short Chg; /* Registers changed by this insn */ unsigned short Info; /* Additional information */ + unsigned int Use; /* Registers used by this insn */ + unsigned int Chg; /* Registers changed by this insn */ } OPCDesc; /* Opcode description table */ From de630a12452d76ac3ab9fd88482925d9428d8d5c Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 13 Sep 2020 09:15:07 +0800 Subject: [PATCH 345/806] Fixed quick hack for known registers after calling certain runtime functions, and new quick hack for tosshrax. --- src/cc65/codeent.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 195f7199f..3de694829 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -1418,10 +1418,10 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegX = (In->RegX ^ 0xFF); } } else if (strcmp (E->Arg, "tosandax") == 0) { - if (In->RegA == 0) { + if (RegValIsKnown (In->RegA) && In->RegA == 0) { Out->RegA = 0; } - if (In->RegX == 0) { + if (RegValIsKnown (In->RegX) && In->RegX == 0) { Out->RegX = 0; } } else if (strcmp (E->Arg, "tosaslax") == 0) { @@ -1430,16 +1430,20 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) Out->RegA = 0; } } else if (strcmp (E->Arg, "tosorax") == 0) { - if (In->RegA == 0xFF) { + if (RegValIsKnown (In->RegA) && In->RegA == 0xFF) { Out->RegA = 0xFF; } - if (In->RegX == 0xFF) { + if (RegValIsKnown (In->RegX) && In->RegX == 0xFF) { Out->RegX = 0xFF; } } else if (strcmp (E->Arg, "tosshlax") == 0) { - if ((In->RegA & 0x0F) >= 8) { + if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) { Out->RegA = 0; } + } else if (strcmp (E->Arg, "tosshrax") == 0) { + if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) { + Out->RegX = 0; + } } else if (strcmp (E->Arg, "bcastax") == 0 || strcmp (E->Arg, "bnegax") == 0 || FindBoolCmpCond (E->Arg) != CMP_INV || From bf4b19501699fe1ab286878ff8c23f4296cf82d2 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 18 Sep 2020 15:50:26 -0400 Subject: [PATCH 346/806] Added some comments that explain where the g_branch() code generator can and can't be used. --- src/cc65/codegen.c | 6 ++++++ src/cc65/codegen.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 031e4e81a..cb448d738 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -2447,6 +2447,8 @@ void g_falsejump (unsigned flags attribute ((unused)), unsigned label) void g_branch (unsigned Label) /* Branch unconditionally to Label if the CPU has the BRA instruction. ** Otherwise, jump to Label. +** Use this function, instead of g_jump(), only where it is certain that +** the label cannot be farther away from the branch than -128/+127 bytes. */ { if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0) { @@ -4542,6 +4544,10 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, AddCodeLine ("pla"); g_or (FullWidthFlags | CF_CONST, ~Mask); + /* We can generate a branch, instead of a jump, here because we know + ** that only a few instructions will be put between here and where + ** DoneLabel will be defined. + */ unsigned DoneLabel = GetLocalLabel (); g_branch (DoneLabel); diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 3054a39b3..d6d3d2370 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -416,6 +416,8 @@ void g_falsejump (unsigned flags, unsigned label); void g_branch (unsigned Label); /* Branch unconditionally to Label if the CPU has the BRA instruction. ** Otherwise, jump to Label. +** Use this function, instead of g_jump(), only where it is certain that +** the label cannot be farther away from the branch than -128/+127 bytes. */ void g_lateadjustSP (unsigned label); From 0b64ca0d7dfba4e13f5b75634704415e77054b59 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 20 Sep 2020 16:09:58 -0400 Subject: [PATCH 347/806] Fixed the ld65 cx16 Assembly configuration file. The CODE segment immediately follows the EXEHDR segment. Added a segment for the zero-page area that's free when the BASIC ROM isn't used. --- cfg/cx16-asm.cfg | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cfg/cx16-asm.cfg b/cfg/cx16-asm.cfg index 3c24bd56f..c3c08aec3 100644 --- a/cfg/cx16-asm.cfg +++ b/cfg/cx16-asm.cfg @@ -1,22 +1,27 @@ +# Assembly configuration for R38 + FEATURES { STARTADDRESS: default = $0801; } SYMBOLS { __LOADADDR__: type = import; +# Putting "-u __EXEHDR__" on cl65's command line will add a BASIC RUN stub to your program. +# __EXEHDR__: type = import; __HIMEM__: type = weak, value = $9F00; } MEMORY { ZP: file = "", start = $0022, size = $0080 - $0022, define = yes; + ZP2: file = "", start = $00A9, size = $0100 - $00A9; LOADADDR: file = %O, start = %S - 2, size = $0002; MAIN: file = %O, start = %S, size = __HIMEM__ - %S; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; - EXTZP: load = ZP, type = zp, optional = yes; + EXTZP: load = ZP2, type = zp, optional = yes; # OK if BASIC functions not used LOADADDR: load = LOADADDR, type = ro; EXEHDR: load = MAIN, type = ro, optional = yes; - LOWCODE: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; BSS: load = MAIN, type = bss, define = yes; From 41bd8d909b8b43a31187d9720a83b768096c153d Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 20 Sep 2020 19:55:25 -0400 Subject: [PATCH 348/806] Added ld65's bank attribute to the cx16 library's bank (overlay) configuration file. That attribute makes it easier for Assembly code to know which bank holds a label. --- cfg/cx16-bank.cfg | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cfg/cx16-bank.cfg b/cfg/cx16-bank.cfg index 1f998f188..36b0edb02 100644 --- a/cfg/cx16-bank.cfg +++ b/cfg/cx16-bank.cfg @@ -19,35 +19,35 @@ MEMORY { # BRAM00ADDR: file = "%O.00", start = __BANKRAMSTART__ - 2, size = $0002; # BRAM00: file = "%O.00", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; BRAM01ADDR: file = "%O.01", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM01: file = "%O.01", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM01: file = "%O.01", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $01; BRAM02ADDR: file = "%O.02", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM02: file = "%O.02", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM02: file = "%O.02", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $02; BRAM03ADDR: file = "%O.03", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM03: file = "%O.03", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM03: file = "%O.03", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $03; BRAM04ADDR: file = "%O.04", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM04: file = "%O.04", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM04: file = "%O.04", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $04; BRAM05ADDR: file = "%O.05", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM05: file = "%O.05", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM05: file = "%O.05", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $05; BRAM06ADDR: file = "%O.06", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM06: file = "%O.06", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM06: file = "%O.06", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $06; BRAM07ADDR: file = "%O.07", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM07: file = "%O.07", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM07: file = "%O.07", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $07; BRAM08ADDR: file = "%O.08", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM08: file = "%O.08", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM08: file = "%O.08", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $08; BRAM09ADDR: file = "%O.09", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM09: file = "%O.09", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM09: file = "%O.09", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $09; BRAM0AADDR: file = "%O.0a", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0A: file = "%O.0a", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0A: file = "%O.0a", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0A; BRAM0BADDR: file = "%O.0b", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0B: file = "%O.0b", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0B: file = "%O.0b", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0B; BRAM0CADDR: file = "%O.0c", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0C: file = "%O.0c", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0C: file = "%O.0c", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0C; BRAM0DADDR: file = "%O.0d", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0D: file = "%O.0d", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0D: file = "%O.0d", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0D; BRAM0EADDR: file = "%O.0e", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0E: file = "%O.0e", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0E: file = "%O.0e", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0E; BRAM0FADDR: file = "%O.0f", start = __BANKRAMSTART__ - 2, size = $0002; - BRAM0F: file = "%O.0f", start = __BANKRAMSTART__, size = __BANKRAMSIZE__; + BRAM0F: file = "%O.0f", start = __BANKRAMSTART__, size = __BANKRAMSIZE__, bank = $0F; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; From dc147519542736efa003ec6a7e627eba86914640 Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 22 Sep 2020 00:02:12 -0400 Subject: [PATCH 349/806] Exported the direct Kernal entries that were moved from target headers to "cbm_kernal.inc". --- libsrc/c128/kernal.s | 11 ++++++++++- libsrc/c16/kernal.s | 5 ++++- libsrc/c64/kernal.s | 7 ++++++- libsrc/vic20/kernal.s | 6 +++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/libsrc/c128/kernal.s b/libsrc/c128/kernal.s index f74ab3b1a..138473218 100644 --- a/libsrc/c128/kernal.s +++ b/libsrc/c128/kernal.s @@ -1,11 +1,20 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C128 kernal functions +; C128 Kernal functions ; .include "cbm_kernal.inc" + .export KBDREAD + .export CLRSCR + .export PRINT + .export NEWLINE + .export CURS_SET + .export CURS_ON + .export CURS_OFF + .export NMIEXIT + .export C64MODE .export SWAPPER .export SETBNK diff --git a/libsrc/c16/kernal.s b/libsrc/c16/kernal.s index f814b2c1f..15ce35772 100644 --- a/libsrc/c16/kernal.s +++ b/libsrc/c16/kernal.s @@ -1,11 +1,14 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C16 kernal functions +; C16 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export CINT .export IOINIT .export RAMTAS diff --git a/libsrc/c64/kernal.s b/libsrc/c64/kernal.s index acbf22370..771872082 100644 --- a/libsrc/c64/kernal.s +++ b/libsrc/c64/kernal.s @@ -1,11 +1,16 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; C64 kernal functions +; C64 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export UPDCRAMPTR + .export NMIEXIT + .export CINT .export IOINIT .export RAMTAS diff --git a/libsrc/vic20/kernal.s b/libsrc/vic20/kernal.s index ff16a019c..4539495b1 100644 --- a/libsrc/vic20/kernal.s +++ b/libsrc/vic20/kernal.s @@ -1,11 +1,15 @@ ; ; Ullrich von Bassewitz, 19.11.2002 ; -; VIC20 kernal functions +; VIC20 Kernal functions ; .include "cbm_kernal.inc" + .export CLRSCR + .export KBDREAD + .export UPDCRAMPTR + .export CINT .export IOINIT .export RAMTAS From d906204e84861149c363a82d205de4164271760c Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 22 Sep 2020 12:31:27 -0400 Subject: [PATCH 350/806] Allowed UPDCRAMPTR to be exported as a constuctor in the VIC-20 library. --- libsrc/vic20/kernal.s | 1 - 1 file changed, 1 deletion(-) diff --git a/libsrc/vic20/kernal.s b/libsrc/vic20/kernal.s index 4539495b1..94a5ec1a4 100644 --- a/libsrc/vic20/kernal.s +++ b/libsrc/vic20/kernal.s @@ -8,7 +8,6 @@ .export CLRSCR .export KBDREAD - .export UPDCRAMPTR .export CINT .export IOINIT From 07ea5259ac1cfe1ee2fcdd363fb75cc2cf251175 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 4 Sep 2020 12:06:42 -0400 Subject: [PATCH 351/806] Changed a cc65 error message to say that the sizes of bit-field types (not bit-fields) are limited. --- src/cc65/declare.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8d0a1097c..6c001c117 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -762,11 +762,11 @@ static int ParseFieldWidth (Declaration* Decl) } /* TODO: This can be relaxed to be any integral type, but - ** ParseStructInit currently only supports up to int. + ** ParseStructInit currently supports only up to int. */ if (SizeOf (Decl->Type) > SizeOf (type_uint)) { - /* Only int-sized or smaller types may be used for bit-fields for now */ - Error ("cc65 currently only supports char-sized and int-sized bit-fields"); + /* Only int-sized or smaller types may be used for bit-fields, for now */ + Error ("cc65 currently supports only char-sized and int-sized bit-field types"); return -1; } From ea957283308d45dbde8001c3067d5a2f985b36fe Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 8 Sep 2020 11:51:51 -0400 Subject: [PATCH 352/806] Avoided an avalanche of messages from bad bit-field declarations. Made cc65 replace a bad bit-field type with a good one, and always parse the field width. Shortenned a parameter name to a spelling that's consistent with other function headers. --- src/cc65/declare.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 6c001c117..0f604d45e 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -744,7 +744,7 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) -static int ParseFieldWidth (Declaration* Decl) +static int ParseFieldWidth (Declaration* D) /* Parse an optional field width. Returns -1 if no field width is specified, ** otherwise the width of the field. */ @@ -754,20 +754,26 @@ static int ParseFieldWidth (Declaration* Decl) return -1; } - if (!IsClassInt (Decl->Type)) { + if (!IsClassInt (D->Type)) { /* Only integer types may be used for bit-fields */ Error ("Bit-field has invalid type '%s', must be integral", - GetBasicTypeName (Decl->Type)); - return -1; + GetBasicTypeName (D->Type)); + + /* Avoid a diagnostic storm by giving the bit-field the widest valid + ** signed type, and continuing to parse. + */ + D->Type[0].C = T_INT; } /* TODO: This can be relaxed to be any integral type, but ** ParseStructInit currently supports only up to int. */ - if (SizeOf (Decl->Type) > SizeOf (type_uint)) { + if (SizeOf (D->Type) > SizeOf (type_uint)) { /* Only int-sized or smaller types may be used for bit-fields, for now */ Error ("cc65 currently supports only char-sized and int-sized bit-field types"); - return -1; + + /* Avoid a diagnostic storm */ + D->Type[0].C = T_INT; } /* Read the width */ @@ -778,11 +784,11 @@ static int ParseFieldWidth (Declaration* Decl) Error ("Negative width in bit-field"); return -1; } - if (Expr.IVal > (long)(SizeOf (Decl->Type) * CHAR_BITS)) { + if (Expr.IVal > (long)(SizeOf (D->Type) * CHAR_BITS)) { Error ("Width of bit-field exceeds its type"); return -1; } - if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { + if (Expr.IVal == 0 && D->Ident[0] != '\0') { Error ("Zero width for named bit-field"); return -1; } @@ -818,7 +824,7 @@ static unsigned PadWithBitField (unsigned StructSize, unsigned BitOffs) -static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) +static unsigned AliasAnonStructFields (const Declaration* D, SymEntry* Anon) /* Create alias fields from an anon union/struct in the current lexical level. ** The function returns the count of created aliases. */ @@ -827,7 +833,7 @@ static unsigned AliasAnonStructFields (const Declaration* Decl, SymEntry* Anon) SymEntry* Alias; /* Get the pointer to the symbol table entry of the anon struct */ - SymEntry* Entry = GetESUSymEntry (Decl->Type); + SymEntry* Entry = GetESUSymEntry (D->Type); /* Get the symbol table containing the fields. If it is empty, there has ** been an error before, so bail out. From 81ac28ff71b05bd829db36f544a144a83ad1d481 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 18 Sep 2020 16:33:12 -0400 Subject: [PATCH 353/806] Used TAY/TYA instead of PHA/PLA when extracting a bit-field. Without optimization, it saves a few CPU cycles. With optimization, it saves more cycles and a few bytes. --- src/cc65/codegen.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index cb448d738..bc85ba1de 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -4511,8 +4511,8 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, /* Handle signed bit-fields. */ if (IsSigned) { - /* Push A, since the sign bit test will destroy it. */ - AddCodeLine ("pha"); + /* Save .A because the sign-bit test will destroy it. */ + AddCodeLine ("tay"); /* Check sign bit */ unsigned SignBitPos = BitWidth - 1U; @@ -4520,7 +4520,7 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned SignBitPosInByte = SignBitPos % CHAR_BITS; unsigned SignBitMask = 1U << SignBitPosInByte; - /* Move the correct byte to A. This can only be X for now, + /* Move the correct byte to .A. This can be only .X for now, ** but more cases will be needed to support long. */ switch (SignBitByte) { @@ -4538,10 +4538,10 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned ZeroExtendLabel = GetLocalLabel (); AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel)); - /* Pop A back and sign-extend if required; operating on the full result needs - ** to sign-extend into high byte, too. + /* Get back .A and sign-extend if required; operating on the full result needs + ** to sign-extend into the high byte, too. */ - AddCodeLine ("pla"); + AddCodeLine ("tya"); g_or (FullWidthFlags | CF_CONST, ~Mask); /* We can generate a branch, instead of a jump, here because we know @@ -4551,11 +4551,11 @@ void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned, unsigned DoneLabel = GetLocalLabel (); g_branch (DoneLabel); - /* Pop A back, then zero-extend. We need to duplicate the PLA, rather than move it before - ** the branch to share with the other label, because PLA changes some condition codes. + /* Get back .A, then zero-extend. We need to duplicate the TYA, rather than move it before + ** the branch to share with the other label, because TYA changes some condition codes. */ g_defcodelabel (ZeroExtendLabel); - AddCodeLine ("pla"); + AddCodeLine ("tya"); /* Zero the upper bits, the same as the unsigned path. */ if (ZeroExtendMask != 0) { From 99121688f82318b966929afac278c69411054b80 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 23 Sep 2020 23:51:24 +0200 Subject: [PATCH 354/806] added test related to issue #1263 --- test/misc/Makefile | 6 ++++++ test/misc/bug1263.c | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 test/misc/bug1263.c diff --git a/test/misc/Makefile b/test/misc/Makefile index 735f83065..f8832e77e 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -94,6 +94,12 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/pptest2.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/bug1263.$1.$2.prg: pptest2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1263.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) diff --git a/test/misc/bug1263.c b/test/misc/bug1263.c new file mode 100644 index 000000000..457e64034 --- /dev/null +++ b/test/misc/bug1263.c @@ -0,0 +1,17 @@ + +/* bug #1263 - erroneous error for implicit function declaration */ + +#include + +enum E { I }; +extern int f(enum E); +int f(e) + enum E e; +{ + return 1; +} + +int main(void) +{ + return f(1) ? EXIT_SUCCESS : EXIT_FAILURE; +} From 6fdd90fa632a58edcbc08b6a44c4bad4aaa87338 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 23 Sep 2020 23:51:37 +0200 Subject: [PATCH 355/806] fix typo --- test/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/readme.txt b/test/readme.txt index a8746ba60..0523482fd 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -13,7 +13,7 @@ compiler. The makefile in this directory _expects_ the tests to fail, because of that when an issue was fixed it will break the CI. The test should get - moved to /var in the PR fixing the issue, which will make CI pass again. + moved to /val in the PR fixing the issue, which will make CI pass again. No changes to makefiles are required! /asm - contains the assembler regression tests From 46ebb15c76f56fd9482058432039c391ce2f0f7a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 23 Sep 2020 23:57:25 +0200 Subject: [PATCH 356/806] test related to issue #1245 --- test/val/bug1245.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/val/bug1245.c diff --git a/test/val/bug1245.c b/test/val/bug1245.c new file mode 100644 index 000000000..2dda93790 --- /dev/null +++ b/test/val/bug1245.c @@ -0,0 +1,12 @@ +/* bug #1245 - ICE for enums with int initializers */ + +#include + +enum E { + X = 1000, +} e = 3; + +int main(void) +{ + return EXIT_SUCCESS; +} From b3491e3d9a5d85ca6e62516398c329dcaa6cfe3d Mon Sep 17 00:00:00 2001 From: mrdudz Date: Wed, 23 Sep 2020 23:57:36 +0200 Subject: [PATCH 357/806] test related to issue #1244 --- test/val/bug1244.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/val/bug1244.c diff --git a/test/val/bug1244.c b/test/val/bug1244.c new file mode 100644 index 000000000..fb499a0c8 --- /dev/null +++ b/test/val/bug1244.c @@ -0,0 +1,18 @@ + +/* bug #1244 - ICE for enum bit-fields */ + +#include + +enum E { + L = 65535L /* or U = 65535U */ +}; + +struct S { + enum E a : 16; +} s; + +int main(void) +{ + return EXIT_SUCCESS; +} + From a86644eff1b0102a95eada977b7fb23b5d2e56fb Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 24 Sep 2020 00:08:36 +0200 Subject: [PATCH 358/806] test related to issue #1221 --- test/val/bug1221.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/val/bug1221.c diff --git a/test/val/bug1221.c b/test/val/bug1221.c new file mode 100644 index 000000000..360a71162 --- /dev/null +++ b/test/val/bug1221.c @@ -0,0 +1,12 @@ +/* bug #1221 - Structs/unions as ternary operands */ + +int a; +struct S { int a; } s1, s2; +struct U { int a; } u1, u2; + +int main() +{ + a ? s1 : s2; /* BUG: should be OK */ + a ? u1 : u2; /* BUG: should be OK */ + return 0; +} From fb8b45e4794ee491cc92376e23eef082ad8ab31d Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 24 Sep 2020 00:18:28 +0200 Subject: [PATCH 359/806] added note on how to manage the sample programs --- samples/README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/README b/samples/README index 73d048fca..3c9247c39 100644 --- a/samples/README +++ b/samples/README @@ -18,6 +18,10 @@ Please note: * Use "make disk" to build a disk image with all sample programs. + * All programs in the root of the "samples" directory have been written to + be portable and work on more than one target. Programs that are specific + to a certain target live in a subdirectory with the name of the target. + List of supplied sample programs: ----------------------------------------------------------------------------- From 97a1093ee09f5efd3569fc8bba9c10a55f4cfb36 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 24 Sep 2020 12:23:18 +0200 Subject: [PATCH 360/806] test related to issue #1222 --- test/val/bug1222.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/val/bug1222.c diff --git a/test/val/bug1222.c b/test/val/bug1222.c new file mode 100644 index 000000000..5e47e452e --- /dev/null +++ b/test/val/bug1222.c @@ -0,0 +1,12 @@ +/* bug #1222 - 'sizeof' issues */ + +#include + +int a[1]; +int b[sizeof ++a[42]]; /* should work as '++a[42]' is actually unevaluated */ + +int main(void) +{ + return EXIT_SUCCESS; +} + From 5ba9d28488a217d121cf334bb67897b71243c652 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 24 Sep 2020 16:16:16 +0200 Subject: [PATCH 361/806] test related to pr #1189 --- test/err/pr1189.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/err/pr1189.c diff --git a/test/err/pr1189.c b/test/err/pr1189.c new file mode 100644 index 000000000..af1d237f1 --- /dev/null +++ b/test/err/pr1189.c @@ -0,0 +1,12 @@ +/* pr #1189 - Fixed compiler CHECK failure when calling functions defined with duplicate param names */ + +void f(int a, int a) +{ + +} + +int main(void) +{ + f(0, 1); /* Check failed: (Param->Flags & SC_PARAM) != 0 */ + return 0; +} From 6fdb356db743dd986d479fffe994fff5e9045518 Mon Sep 17 00:00:00 2001 From: Fabrizio Caruso Date: Thu, 24 Sep 2020 18:01:56 +0200 Subject: [PATCH 362/806] Simplify Gamate tile redefinition (as already done for PCE) --- libsrc/gamate/conio.s | 6 +----- libsrc/gamate/{vga.inc => vga.s} | 7 ++++++- 2 files changed, 7 insertions(+), 6 deletions(-) rename libsrc/gamate/{vga.inc => vga.s} (99%) mode change 100644 => 100755 diff --git a/libsrc/gamate/conio.s b/libsrc/gamate/conio.s index 6ac439490..33437cc04 100644 --- a/libsrc/gamate/conio.s +++ b/libsrc/gamate/conio.s @@ -1,6 +1,7 @@ .include "gamate.inc" .include "extzp.inc" + .import fontdata .import colors .importzp ptr1, tmp1 @@ -24,8 +25,3 @@ initconio: sta BGCOLOR rts - .segment "RODATA" - - .export fontdata -fontdata: - .include "vga.inc" diff --git a/libsrc/gamate/vga.inc b/libsrc/gamate/vga.s old mode 100644 new mode 100755 similarity index 99% rename from libsrc/gamate/vga.inc rename to libsrc/gamate/vga.s index da20dd4aa..39a8df173 --- a/libsrc/gamate/vga.inc +++ b/libsrc/gamate/vga.s @@ -1,6 +1,11 @@ - ; VGA charset for the Gamate conio implementation +.export fontdata + +.rodata + +fontdata: + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; 1 From 61ebe2c34b6755fa0c71f89bfeee873a51f636f5 Mon Sep 17 00:00:00 2001 From: Fabrizio Caruso Date: Thu, 24 Sep 2020 19:32:15 +0200 Subject: [PATCH 363/806] Indentation in vga.s for gamate --- libsrc/gamate/vga.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/gamate/vga.s b/libsrc/gamate/vga.s index 39a8df173..310560015 100755 --- a/libsrc/gamate/vga.s +++ b/libsrc/gamate/vga.s @@ -1,8 +1,8 @@ ; VGA charset for the Gamate conio implementation -.export fontdata + .export fontdata -.rodata + .rodata fontdata: From 47ee1792732def69c8ba6255a3078f0492e4ddd3 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 25 Sep 2020 00:30:49 -0400 Subject: [PATCH 364/806] Fixed a copy & paste error in the test/misc/ makefile. Fixed a warning that's changed to an error in Travis CI tests. --- test/misc/Makefile | 2 +- test/misc/bug1263.c | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/test/misc/Makefile b/test/misc/Makefile index f8832e77e..deacc9464 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -95,7 +95,7 @@ $(WORKDIR)/pptest2.$1.$2.prg: pptest2.c | $(WORKDIR) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) # should compile, but gives an error -$(WORKDIR)/bug1263.$1.$2.prg: pptest2.c | $(WORKDIR) +$(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) diff --git a/test/misc/bug1263.c b/test/misc/bug1263.c index 457e64034..4e5987c58 100644 --- a/test/misc/bug1263.c +++ b/test/misc/bug1263.c @@ -1,17 +1,15 @@ +/* bug #1263 - erroneous error for K & R function declaration */ -/* bug #1263 - erroneous error for implicit function declaration */ - -#include - -enum E { I }; +enum E { I = 0 }; extern int f(enum E); + int f(e) enum E e; { - return 1; + return e; } int main(void) { - return f(1) ? EXIT_SUCCESS : EXIT_FAILURE; + return f(I); } From 61d934fd7bba94f79a297567053c81621bbf3c3b Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 25 Sep 2020 16:25:32 +0200 Subject: [PATCH 365/806] test related to issue #1265 --- test/misc/Makefile | 9 ++++++++ test/misc/bug1265.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/misc/bug1265.c diff --git a/test/misc/Makefile b/test/misc/Makefile index f8832e77e..f04bcadbd 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -100,6 +100,15 @@ $(WORKDIR)/bug1263.$1.$2.prg: pptest2.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# this one requires --std=c89, it fails with --std=c99 +# it fails currently at runtime +$(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1265.$1.$2.prg) + $(CC65) --standard c89 -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) diff --git a/test/misc/bug1265.c b/test/misc/bug1265.c new file mode 100644 index 000000000..469946739 --- /dev/null +++ b/test/misc/bug1265.c @@ -0,0 +1,51 @@ +/* bug #1265 - misadjusted stack from unprototyped function call */ + +#include +#include + +int failures = 0; + +char str1[10]; +char str2[10]; + +int f2 (int x) { return x == 2345 ? 23 : -1; } + +int main (void) { + int x, n; + + sprintf (str1, "%p\n", &x); + puts(str1); + x = 1234; + n = f1 (x); + sprintf (str2, "%p\n", &x); + puts(str2); + + if (strcmp(str1, str2)) { + puts("not equal"); + failures++; + } + if (n != 42) { + puts("f1 returns wrong value"); + failures++; + } + + sprintf (str1, "%p\n", &x); + puts(str1); + x = 2345; + n = f2 (x); + sprintf (str2, "%p\n", &x); + puts(str2); + + if (strcmp(str1, str2)) { + puts("not equal"); + failures++; + } + if (n != 23) { + puts("f2 returns wrong value"); + failures++; + } + + return failures; +} + +int f1 (int x) { return x == 1234 ? 42 : -1; } From 34177d9eddc81eb90c582690eafd3ae4d6510204 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 25 Sep 2020 20:08:32 +0200 Subject: [PATCH 366/806] test related to issue #1094 --- test/misc/Makefile | 6 +++ test/misc/bug1094.c | 99 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 test/misc/bug1094.c diff --git a/test/misc/Makefile b/test/misc/Makefile index b8c578405..06046af3c 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -100,6 +100,12 @@ $(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error +$(WORKDIR)/bug1094.$1.$2.prg: bug1094.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1094.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this one requires --std=c89, it fails with --std=c99 # it fails currently at runtime $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) diff --git a/test/misc/bug1094.c b/test/misc/bug1094.c new file mode 100644 index 000000000..05e3e6430 --- /dev/null +++ b/test/misc/bug1094.c @@ -0,0 +1,99 @@ + +/* bug #1094 - Nested struct/union initializers don't compile */ + +#include +#include +#include +#include + +typedef uint16_t u16; +typedef uint8_t u8; + +struct WW { + int a : 4; + struct { + unsigned int b : 4; + unsigned int c : 8; + } x[2]; +} wwqq = { 0, {2, 5, {3, 4}}, }; + +typedef struct { + u16 quot; + u16 rem; +} udiv_t; + +typedef struct { + u16 quot; + u16 rem; + char m[8]; +} big_t; + +union U { + struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; + }; + int u; +}; + +union U g = { 5, 3, 1 }; + +struct S { + struct { + unsigned int a : 3; + unsigned int b : 3; + unsigned int c : 3; + }; +}; + +struct S h = { 5, 3, 1 }; + +union X { + struct { + uint16_t a : 3; + union { + struct { + uint16_t b : 3; + uint16_t c : 3; + }; + uint16_t d; + }; + }; + uint16_t e; +} x = { 4, {5, 6} }; + + +udiv_t div3(udiv_t u) +{ + udiv_t v = {}; + + u.quot = 341 + u.quot; + u.rem = 1 + u.rem; + + v = u; + + return v; +} + +int main(void) +{ + udiv_t v = { 141, 32 }; + big_t b = { 141, 32 }; + + v = div3(*(udiv_t*)&b); + + printf("%d %d %d\n", (int)wwqq.a, wwqq.x[0].b, wwqq.x[0].c); + printf("%d %d %d\n", (int)wwqq.a, wwqq.x[1].b, wwqq.x[1].c); + printf("quot = %u, rem = %u\n", div3(v).quot, div3(v).rem); + printf("quot = %u, rem = %u\n", v.quot, v.rem); + printf("quot = %u, rem = %u\n", b.quot, b.rem); + printf("g.a = %u, g.b = %u, g.c = %d\n", g.a, g.b, g.c); + x.e = 1467; + printf("x.a = %d, x.b = %d, x.c = %d\n", x.a, x.b, x.c); + printf("(long)x.b = %ld, sizeof(x) = %u, sizeof((long)x.a) = %u\n", (long)x.b, sizeof(x), sizeof((long)x.a)); + printf("-x.d = %d, (long)(-x.c + 1) = %ld\n", -x.d, (long)(-x.c + 1)); + printf("h.a = %u, h.b = %u, h.c = %u\n", h.a, h.b, h.c); + + return 0; +} From d0089aef954907141bc9344558dea83577daef73 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 26 Sep 2020 08:36:17 +0200 Subject: [PATCH 367/806] Fix bit-field truncation warning message Fix copy & paste bug with warning message. struct X { signed int a : 3; }; struct X g = { 5 }; Before: s.c(4): Warning: Implicit truncation from 'int' to 'int : 3' in bit-field initializer changes value from 5 to 5 After: s.c(4): Warning: Implicit truncation from 'int' to 'int : 3' in bit-field initializer changes value from 5 to -3 Fixes #1268 --- src/cc65/declare.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 0f604d45e..8623fa08f 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2567,9 +2567,9 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); if (ED.IVal != RestoredVal) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " - "changes value from %ld to %d", + "changes value from %ld to %ld", GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), - Entry->V.B.BitWidth, ED.IVal, Val); + Entry->V.B.BitWidth, ED.IVal, RestoredVal); } } From c45a6b3685815de0bc76f9cdbf9fb7d9deb500f5 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:16:47 +0800 Subject: [PATCH 368/806] Utility function ParseOpcArgStr(). --- src/cc65/codeent.c | 100 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/codeent.h | 8 ++++ 2 files changed, 108 insertions(+) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 3de694829..aa3a960df 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -356,6 +356,106 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) +int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) +/* Break the opcode argument string into a symbol name/label part plus an offset. +** Both parts are optional, but if there are any characters in the string that +** can't be parsed, it's an failure. +** The caller is responsible for managing the StrBuf. +** Return whether parsing succeeds or not. +*/ +{ + int NewOff = 0; + const char* OffsetPart = 0; + const char* NameEnd = 0; + int Negative = 0; + + /* A numeric address is treated as an unnamed address with the numeric part as the offset */ + if (IsDigit (Arg[0]) || Arg[0] == '$') { + /* A call to a numeric address */ + SB_Clear (Name); + SB_Terminate (Name); + OffsetPart = Arg; + } else { + /* If the symbol name starts with an underline, it is an external symbol. + ** If the symbol does not start with an underline, it may be a built-in + ** symbol. + */ + if (Arg[0] == '_') { + /* Skip the underscore */ + ++Arg; + } + + /* Rip off the offset if present. */ + OffsetPart = strchr (Arg, '+'); + if (OffsetPart == 0) { + OffsetPart = strchr (Arg, '-'); + } + if (OffsetPart != 0) { + /* Get the real arg name */ + NameEnd = strchr (Arg, ' '); + if (NameEnd == 0 || NameEnd > OffsetPart) { + NameEnd = OffsetPart; + } + SB_CopyBuf (Name, Arg, NameEnd - Arg); + SB_Terminate (Name); + + } else { + /* No offset */ + *Offset = 0; + + SB_CopyStr (Name, Arg); + SB_Terminate (Name); + + return 1; + } + } + + *Offset = 0; + + /* Get the offset */ + while (OffsetPart != 0 && OffsetPart[0] != '\0') { + if (OffsetPart[0] == '+') { + Negative = 0; + ++OffsetPart; + } else if (OffsetPart[0] == '-') { + Negative = 1; + ++OffsetPart; + } + + /* Skip spaces */ + while (OffsetPart[0] == ' ') { + ++OffsetPart; + } + + if (OffsetPart[0] == '$') { + if (sscanf (OffsetPart + 1, "%X", &NewOff) != 1) { + return 0; + } + } else { + if (sscanf (OffsetPart, "%u", &NewOff) != 1) { + return 0; + } + } + + if (Negative) { + NewOff = -NewOff; + } + + *Offset += NewOff; + + /* See if there are more */ + Arg = OffsetPart; + OffsetPart = strchr (Arg, '+'); + if (OffsetPart == 0) { + OffsetPart = strchr (Arg, '-'); + } + } + + return 1; +} + + + const char* MakeHexArg (unsigned Num) /* Convert Num into a string in the form $XY, suitable for passing it as an ** argument to NewCodeEntry, and return a pointer to the string. diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 3d07670d7..57a7677bb 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -89,6 +89,14 @@ struct CodeEntry { +int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset); +/* Break the opcode argument string into a symbol name/label part plus an offset. +** Both parts are optional, but if there are any characters in the string that +** can't be parsed, it's an failure. +** The caller is responsible for managing the StrBuf. +** Return whether parsing succeeds or not. +*/ + const char* MakeHexArg (unsigned Num); /* Convert Num into a string in the form $XY, suitable for passing it as an ** argument to NewCodeEntry, and return a pointer to the string. From 28c7aa2bc8b18aeb0291acbbc3a34f6996a60d25 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:17:52 +0800 Subject: [PATCH 369/806] Replaced direct CEF_NUMARG flag checks on code entries with CE_HasNumArg(). --- src/cc65/coptcmp.c | 4 ++-- src/cc65/coptneg.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index ca0ba39a8..a4a8c6a9b 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -176,14 +176,14 @@ static int IsImmCmp16 (CodeEntry** L) { return (L[0]->OPC == OP65_CPX && L[0]->AM == AM65_IMM && - (L[0]->Flags & CEF_NUMARG) != 0 && + CE_HasNumArg (L[0]) && !CE_HasLabel (L[0]) && (L[1]->OPC == OP65_JNE || L[1]->OPC == OP65_BNE) && L[1]->JumpTo != 0 && !CE_HasLabel (L[1]) && L[2]->OPC == OP65_CMP && L[2]->AM == AM65_IMM && - (L[2]->Flags & CEF_NUMARG) != 0 && + CE_HasNumArg (L[2]) && (L[3]->Info & OF_CBRA) != 0 && L[3]->JumpTo != 0 && (L[1]->JumpTo->Owner == L[3] || L[1]->JumpTo == L[3]->JumpTo)); diff --git a/src/cc65/coptneg.c b/src/cc65/coptneg.c index 03b39eb42..0f5d589f7 100644 --- a/src/cc65/coptneg.c +++ b/src/cc65/coptneg.c @@ -68,15 +68,15 @@ unsigned OptBNegA1 (CodeSeg* S) CodeEntry* E = CS_GetEntry (S, I); /* Check for a ldx */ - if (E->OPC == OP65_LDX && - E->AM == AM65_IMM && - (E->Flags & CEF_NUMARG) != 0 && - E->Num == 0 && - CS_GetEntries (S, L, I+1, 2) && - L[0]->OPC == OP65_LDA && - (L[0]->Use & REG_X) == 0 && - !CE_HasLabel (L[0]) && - CE_IsCallTo (L[1], "bnega") && + if (E->OPC == OP65_LDX && + E->AM == AM65_IMM && + CE_HasNumArg (E) && + E->Num == 0 && + CS_GetEntries (S, L, I+1, 2) && + L[0]->OPC == OP65_LDA && + (L[0]->Use & REG_X) == 0 && + !CE_HasLabel (L[0]) && + CE_IsCallTo (L[1], "bnega") && !CE_HasLabel (L[1])) { /* Remove the ldx instruction */ From d379affc4b53d91e366bc6b17901633eb0d092eb Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:17:08 +0800 Subject: [PATCH 370/806] Moved some reusable code from cc65/coptstop.c into new files. --- src/cc65.vcxproj | 2 + src/cc65/codeoptutil.c | 993 ++++++++++++++++++++++++++++++++++++++ src/cc65/codeoptutil.h | 257 ++++++++++ src/cc65/coptstop.c | 1027 +--------------------------------------- 4 files changed, 1253 insertions(+), 1026 deletions(-) create mode 100644 src/cc65/codeoptutil.c create mode 100644 src/cc65/codeoptutil.h diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index 1015b389f..0f63a1022 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -87,6 +87,7 @@ + @@ -163,6 +164,7 @@ + diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c new file mode 100644 index 000000000..999c18d33 --- /dev/null +++ b/src/cc65/codeoptutil.c @@ -0,0 +1,993 @@ +/*****************************************************************************/ +/* */ +/* codeoptutil.c */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +/* common */ +#include "chartype.h" +#include "xmalloc.h" + +/* cc65 */ +#include "codeinfo.h" +#include "codeoptutil.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +void ClearLoadRegInfo (LoadRegInfo* RI) +/* Clear a LoadRegInfo struct */ +{ + RI->Flags = LI_NONE; + RI->LoadIndex = -1; + RI->LoadEntry = 0; + RI->LoadYIndex = -1; + RI->LoadYEntry = 0; + RI->XferIndex = -1; + RI->XferEntry = 0; + RI->Offs = 0; +} + + + +void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) +/* Copy a LoadRegInfo struct */ +{ + To->Flags = From->Flags; + To->LoadIndex = From->LoadIndex; + To->LoadEntry = From->LoadEntry; + To->LoadYIndex = From->LoadYIndex; + To->LoadYEntry = From->LoadYEntry; + To->XferIndex = From->XferIndex; + To->XferEntry = From->XferEntry; + To->Offs = From->Offs; +} + + + +void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S) +/* Prepare a LoadRegInfo struct for use */ +{ + /* Get the entries */ + if (RI->LoadIndex >= 0) { + RI->LoadEntry = CS_GetEntry (S, RI->LoadIndex); + } else { + RI->LoadEntry = 0; + } + if (RI->XferIndex >= 0) { + RI->XferEntry = CS_GetEntry (S, RI->XferIndex); + } else { + RI->XferEntry = 0; + } + /* Load from src not modified before op can be treated as direct */ + if ((RI->Flags & LI_SRC_CHG) == 0 && + (RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + RI->Flags |= LI_DIRECT; + if ((RI->Flags & LI_CHECK_Y) != 0) { + RI->Flags |= LI_RELOAD_Y; + } + } + /* We cannot ldy src,y */ + if ((RI->Flags & LI_RELOAD_Y) != 0 && + RI->LoadYEntry != 0 && + (RI->LoadYEntry->Use & REG_Y) == REG_Y) { + RI->Flags &= ~LI_DIRECT; + } +} + + + +void AdjustLoadRegInfo (LoadRegInfo* RI, int Index, int Change) +/* Adjust a load register info struct after deleting or inserting an entry +** with a given index +*/ +{ + CHECK (abs (Change) == 1); + if (Change < 0) { + /* Deletion */ + if (Index < RI->LoadIndex) { + --RI->LoadIndex; + } else if (Index == RI->LoadIndex) { + /* Has been removed */ + RI->LoadIndex = -1; + RI->LoadEntry = 0; + } + if (Index < RI->XferIndex) { + --RI->XferIndex; + } else if (Index == RI->XferIndex) { + /* Has been removed */ + RI->XferIndex = -1; + RI->XferEntry = 0; + } + } else { + /* Insertion */ + if (Index <= RI->LoadIndex) { + ++RI->LoadIndex; + } + if (Index <= RI->XferIndex) { + ++RI->XferIndex; + } + } +} + + + +void ClearLoadInfo (LoadInfo* LI) +/* Clear a LoadInfo struct */ +{ + ClearLoadRegInfo (&LI->A); + ClearLoadRegInfo (&LI->X); + ClearLoadRegInfo (&LI->Y); +} + + + +void CopyLoadInfo (LoadInfo* To, LoadInfo* From) +/* Copy a LoadInfo struct */ +{ + CopyLoadRegInfo (&To->A, &From->A); + CopyLoadRegInfo (&To->X, &From->X); + CopyLoadRegInfo (&To->Y, &From->Y); +} + + + +void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S) +/* Prepare a LoadInfo struct for use */ +{ + /* Get the entries */ + FinalizeLoadRegInfo (&LI->A, S); + FinalizeLoadRegInfo (&LI->X, S); + FinalizeLoadRegInfo (&LI->Y, S); +} + + + +void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) +/* Adjust a load info struct after deleting entry with a given index */ +{ + AdjustLoadRegInfo (&LI->A, Index, Change); + AdjustLoadRegInfo (&LI->X, Index, Change); + AdjustLoadRegInfo (&LI->Y, Index, Change); +} + + + +RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) +/* Get RegInfo of the last load insn entry */ +{ + CodeEntry* E; + + if (Reg->LoadIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->LoadIndex)) != 0) { + return E->RI; + } + + return 0; +} + + + +static int Affected (LoadRegInfo* RI, const CodeEntry* E) +/* Check if the load src may be modified between the pushax and op */ +{ + fncls_t fncls; + unsigned int Use; + unsigned int Chg; + unsigned int UseToCheck = 0; + + if ((RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { + return 0; + } + CHECK ((RI->Flags & LI_CHECK_ARG) == 0 || RI->LoadEntry != 0); + CHECK ((RI->Flags & LI_CHECK_Y) == 0 || RI->LoadYEntry != 0); + + if ((RI->Flags & LI_CHECK_ARG) != 0) { + UseToCheck |= RI->LoadEntry->Use; + } + + if ((RI->Flags & LI_CHECK_Y) != 0) { + UseToCheck |= RI->LoadYEntry->Use; + } + + if (E->OPC == OP65_JSR) { + /* Try to know about the function */ + fncls = GetFuncInfo (E->Arg, &Use, &Chg); + if ((UseToCheck & Chg & REG_ALL) == 0 && + fncls == FNCLS_BUILTIN) { + /* Builtin functions are known to be harmless */ + return 0; + } + /* Otherwise play it safe */ + return 1; + } else if (E->OPC == OP65_DEC || E->OPC == OP65_INC || + E->OPC == OP65_ASL || E->OPC == OP65_LSR || + E->OPC == OP65_ROL || E->OPC == OP65_ROR || + E->OPC == OP65_TRB || E->OPC == OP65_TSB || + E->OPC == OP65_STA || E->OPC == OP65_STX || E->OPC == OP65_STY) { + if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { + if ((RI->Flags & LI_CHECK_ARG) != 0 && + strcmp (RI->LoadEntry->Arg, E->Arg) == 0) { + return 1; + } + if ((RI->Flags & LI_CHECK_Y) != 0 && + strcmp (RI->LoadYEntry->Arg, E->Arg) == 0) { + return 1; + } + return 0; + } + /* We could've check further for more cases where the load target isn't modified, + ** But for now let's save the trouble and just play it safe. */ + return 1; + } + } + return 0; +} + + + +static void HonourUseAndChg (LoadRegInfo* RI, unsigned Reg, const CodeEntry* E, int I) +/* Honour use and change flags for an instruction */ +{ + if ((E->Chg & Reg) != 0) { + /* Remember this as an indirect load */ + ClearLoadRegInfo (RI); + RI->LoadIndex = I; + RI->XferIndex = -1; + RI->Flags = 0; + } else if (Affected (RI, E)) { + RI->Flags |= LI_SRC_CHG; + } +} + + + +unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) +/* Track loads for a code entry. +** Return used registers. +*/ +{ + unsigned Used; + CodeEntry* E = CS_GetEntry (S, I); + CHECK (E != 0); + + /* By default */ + Used = E->Use; + + /* Whether we had a load or xfer op before or not, the newly loaded value + ** will be the real one used for the pushax/op unless it's overwritten, + ** so we can just reset the flags about it in such cases. + */ + if (E->Info & OF_LOAD) { + + LoadRegInfo* RI = 0; + + /* Determine, which register was loaded */ + if (E->Chg & REG_A) { + RI = &LI->A; + } else if (E->Chg & REG_X) { + RI = &LI->X; + } else if (E->Chg & REG_Y) { + RI = &LI->Y; + } + CHECK (RI != 0); + + /* Remember the load */ + RI->LoadIndex = I; + RI->XferIndex = -1; + + /* Set load flags */ + RI->Flags = LI_LOAD_INSN; + if (E->AM == AM65_IMM) { + /* These insns are all ok and replaceable */ + RI->Flags |= LI_DIRECT; + } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + /* These insns are replaceable only if they are not modified later */ + RI->Flags |= LI_CHECK_ARG; + } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + /* These insns are replaceable only if they are not modified later */ + RI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } else if (E->AM == AM65_ZP_INDY && + strcmp (E->Arg, "sp") == 0) { + /* A load from the stack with known offset is also ok, but in this + ** case we must reload the index register later. Please note that + ** a load indirect via other zero page locations is not ok, since + ** these locations may change between the push and the actual + ** operation. + */ + RI->Flags |= LI_DIRECT | LI_CHECK_Y | LI_SP; + + /* Reg Y can be regarded as unused if this load is removed */ + Used &= ~REG_Y; + if (RI == &LI->A) { + LI->Y.Flags |= LI_USED_BY_A; + } else { + LI->Y.Flags |= LI_USED_BY_X; + } + } + + /* If the load offset has a known value, we can just remember and reload + ** it into the index register later. + */ + if ((RI->Flags & LI_CHECK_Y) != 0) { + if (RegValIsKnown (E->RI->In.RegY)) { + RI->Offs = (unsigned char)E->RI->In.RegY; + RI->Flags &= ~LI_CHECK_Y; + RI->Flags |= LI_RELOAD_Y; + } else { + /* We need to check if the src of Y is changed */ + RI->LoadYIndex = LI->Y.LoadIndex; + RI->LoadYEntry = CS_GetEntry (S, RI->LoadYIndex); + } + } + + /* Watch for any change of the load target */ + if ((RI->Flags & LI_CHECK_ARG) != 0) { + RI->LoadEntry = CS_GetEntry (S, I); + } + + } else if (E->Info & OF_XFR) { + + /* Determine source and target of the transfer and handle the TSX insn */ + LoadRegInfo* Src; + LoadRegInfo* Tgt; + switch (E->OPC) { + case OP65_TAX: + Src = &LI->A; + Tgt = &LI->X; + Used &= ~REG_A; + Src->Flags |= LI_USED_BY_X; + break; + case OP65_TAY: + Src = &LI->A; + Tgt = &LI->Y; + Used &= ~REG_A; + Src->Flags |= LI_USED_BY_Y; + break; + case OP65_TXA: + Src = &LI->X; + Tgt = &LI->A; + Used &= ~REG_X; + Src->Flags |= LI_USED_BY_A; + break; + case OP65_TYA: + Src = &LI->Y; + Tgt = &LI->A; + Used &= ~REG_Y; + Src->Flags |= LI_USED_BY_A; + break; + case OP65_TSX: + ClearLoadRegInfo (&LI->X); + return Used; + case OP65_TXS: + return Used; + default: Internal ("Unknown XFR insn in TrackLoads"); + } + + /* Transfer the data */ + Tgt->LoadIndex = Src->LoadIndex; + Tgt->LoadEntry = Src->LoadEntry; + Tgt->LoadYIndex = Src->LoadYIndex; + Tgt->LoadYEntry = Src->LoadYEntry; + Tgt->XferIndex = I; + Tgt->Offs = Src->Offs; + Tgt->Flags = Src->Flags; + + } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) { + + /* Both registers set, Y changed */ + LI->A.LoadIndex = I; + LI->A.XferIndex = -1; + LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); + LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; + + LI->X.LoadIndex = I; + LI->X.XferIndex = -1; + LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); + LI->X.Offs = (unsigned char) E->RI->In.RegY; + + /* Reg Y can be regarded as unused if this load is removed */ + Used &= ~REG_Y; + LI->Y.Flags |= LI_USED_BY_A | LI_USED_BY_X; + + } else { + HonourUseAndChg (&LI->A, REG_A, E, I); + HonourUseAndChg (&LI->X, REG_X, E, I); + HonourUseAndChg (&LI->Y, REG_Y, E, I); + + /* The other operand may be affected too */ + if (LLI != 0) { + if (Affected (&LLI->A, E)) { + LLI->A.Flags |= LI_SRC_CHG; + } + if (Affected (&LLI->X, E)) { + LLI->X.Flags |= LI_SRC_CHG; + } + if (Affected (&LLI->Y, E)) { + LLI->Y.Flags |= LI_SRC_CHG; + } + } + } + + return Used; +} + + + +void SetDontRemoveEntryFlag (LoadRegInfo* RI) +/* Flag the entry as non-removable according to register flags */ +{ + if (RI->Flags & LI_DONT_REMOVE) { + if (RI->LoadEntry != 0) { + RI->LoadEntry->Flags |= CEF_DONT_REMOVE; + } + } +} + + + +void ResetDontRemoveEntryFlag (LoadRegInfo* RI) +/* Unflag the entry as non-removable according to register flags */ +{ + if (RI->Flags & LI_DONT_REMOVE) { + if (RI->LoadEntry != 0) { + RI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; + } + } +} + + + +void SetDontRemoveEntryFlags (StackOpData* D) +/* Flag the entries as non-removable according to register flags */ +{ + SetDontRemoveEntryFlag (&D->Lhs.A); + SetDontRemoveEntryFlag (&D->Lhs.X); + SetDontRemoveEntryFlag (&D->Rhs.A); + SetDontRemoveEntryFlag (&D->Rhs.X); +} + + + +void ResetDontRemoveEntryFlags (StackOpData* D) +/* Unflag the entries as non-removable according to register flags */ +{ + ResetDontRemoveEntryFlag (&D->Lhs.A); + ResetDontRemoveEntryFlag (&D->Lhs.X); + ResetDontRemoveEntryFlag (&D->Rhs.A); + ResetDontRemoveEntryFlag (&D->Rhs.X); +} + + + +void ResetStackOpData (StackOpData* Data) +/* Reset the given data structure */ +{ + Data->OptFunc = 0; + Data->ZPUsage = REG_NONE; + Data->ZPChanged = REG_NONE; + Data->UsedRegs = REG_NONE; + Data->RhsMultiChg = 0; + + ClearLoadInfo (&Data->Lhs); + ClearLoadInfo (&Data->Rhs); + + Data->PushIndex = -1; + Data->OpIndex = -1; +} + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +void InsertEntry (StackOpData* D, CodeEntry* E, int Index) +/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will +** be adjusted by this function. +*/ +{ + /* Insert the entry into the code segment */ + CS_InsertEntry (D->Code, E, Index); + + /* Adjust register loads if necessary */ + AdjustLoadInfo (&D->Lhs, Index, 1); + AdjustLoadInfo (&D->Rhs, Index, 1); + + /* Adjust the indices if necessary */ + if (D->PushEntry && Index <= D->PushIndex) { + ++D->PushIndex; + } + if (D->OpEntry && Index <= D->OpIndex) { + ++D->OpIndex; + } +} + + + +void DelEntry (StackOpData* D, int Index) +/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be +** adjusted by this function, and PushEntry/OpEntry may get invalidated. +*/ +{ + /* Delete the entry from the code segment */ + CS_DelEntry (D->Code, Index); + + /* Adjust register loads if necessary */ + AdjustLoadInfo (&D->Lhs, Index, -1); + AdjustLoadInfo (&D->Rhs, Index, -1); + + /* Adjust the other indices if necessary */ + if (Index < D->PushIndex) { + --D->PushIndex; + } else if (Index == D->PushIndex) { + D->PushEntry = 0; + } + if (Index < D->OpIndex) { + --D->OpIndex; + } else if (Index == D->OpIndex) { + D->OpEntry = 0; + } +} + + + +void AdjustStackOffset (StackOpData* D, unsigned Offs) +/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. +** OpIndex is adjusted according to the insertions. +*/ +{ + /* Walk over all entries */ + int I = D->PushIndex + 1; + while (I < D->OpIndex) { + + CodeEntry* E = CS_GetEntry (D->Code, I); + + /* Check if this entry does a stack access, and if so, if it's a plain + ** load from stack, since this is needed later. + */ + int Correction = 0; + if ((E->Use & REG_SP) != 0) { + + /* Check for some things that should not happen */ + CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); + CHECK (strcmp (E->Arg, "sp") == 0); + /* We need to correct this one */ + Correction = (E->OPC == OP65_LDA)? 2 : 1; + + } else if (CE_IsCallTo (E, "ldaxysp")) { + /* We need to correct this one */ + Correction = 1; + } + + if (Correction) { + /* Get the code entry before this one. If it's a LDY, adjust the + ** value. + */ + CodeEntry* P = CS_GetPrevEntry (D->Code, I); + if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { + /* The Y load is just before the stack access, adjust it */ + CE_SetNumArg (P, P->Num - Offs); + } else { + /* Insert a new load instruction before the stack access */ + const char* Arg = MakeHexArg (E->RI->In.RegY - Offs); + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I++); + } + + /* If we need the value of Y later, be sure to reload it */ + if (RegYUsed (D->Code, I+1)) { + CodeEntry* N; + const char* Arg = MakeHexArg (E->RI->In.RegY); + if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && + ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { + /* The Y register is used but the load instruction loads A + ** and is followed by a branch that evaluates the zero flag. + ** This means that we cannot just insert the load insn + ** for the Y register at this place, because it would + ** destroy the Z flag. Instead place load insns at the + ** target of the branch and after it. + ** Note: There is a chance that this code won't work. The + ** jump may be a backwards jump (in which case the stack + ** offset has already been adjusted) or there may be other + ** instructions between the load and the conditional jump. + ** Currently the compiler does not generate such code, but + ** it is possible to force the optimizer into something + ** invalid by use of inline assembler. + */ + + /* Add load insn after the branch */ + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+2); + + /* Add load insn before branch target */ + CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); + CHECK (J > I); /* Must not happen */ + InsertEntry (D, Y, J); + + /* Move the label to the new insn */ + CodeLabel* L = CS_GenLabel (D->Code, Y); + CS_MoveLabelRef (D->Code, N, L); + } else { + CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + InsertEntry (D, X, I+1); + /* Skip this instruction in the next round */ + ++I; + } + } + } + + /* Next entry */ + ++I; + } + + /* If we have rhs load insns that load from stack, we'll have to adjust + ** the offsets for these also. + */ + if ((D->Rhs.A.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { + D->Rhs.A.Offs -= Offs; + } + if ((D->Rhs.X.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { + D->Rhs.X.Offs -= Offs; + } +} + + + +int IsRegVar (StackOpData* D) +/* If the value pushed is that of a zeropage variable that is unchanged until Op, +** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. +** Otherwise leave D untouched and return false. +*/ +{ + CodeEntry* LoadA = D->Lhs.A.LoadEntry; + CodeEntry* LoadX = D->Lhs.X.LoadEntry; + unsigned Len; + + /* Must be unchanged till Op */ + if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT || + (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT) { + return 0; + } + + /* Must have both load insns */ + if (LoadA == 0 || LoadX == 0) { + return 0; + } + + /* Must be loads from zp */ + if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) { + return 0; + } + + /* Must be the same zp loc with high byte in X */ + Len = strlen (LoadA->Arg); + if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0 || + strcmp (LoadX->Arg + Len, "+1") != 0) { + return 0; + } + + /* Use the zero page location directly */ + D->ZPLo = LoadA->Arg; + D->ZPHi = LoadX->Arg; + return 1; +} + + + +void AddStoreLhsA (StackOpData* D) +/* Add a store to zero page after the push insn */ +{ + CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); +} + + + +void AddStoreLhsX (StackOpData* D) +/* Add a store to zero page after the push insn */ +{ + CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI); + InsertEntry (D, X, D->PushIndex+1); +} + + + +void ReplacePushByStore (StackOpData* D) +/* Replace the call to the push subroutine by a store into the zero page +** location (actually, the push is not replaced, because we need it for +** later, but the name is still ok since the push will get removed at the +** end of each routine). +*/ +{ + /* Store the value into the zeropage instead of pushing it. Check high + ** byte first so that the store is later in A/X order. + */ + if ((D->Lhs.X.Flags & LI_DIRECT) == 0) { + AddStoreLhsX (D); + } + if ((D->Lhs.A.Flags & LI_DIRECT) == 0) { + AddStoreLhsA (D); + } +} + + + +void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI) +/* Add an op for the low byte of an operator. This function honours the +** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. +** All code is inserted at the current insertion point. +*/ +{ + CodeEntry* X; + + if ((LI->A.Flags & LI_DIRECT) != 0) { + /* Op with a variable location. If the location is on the stack, we + ** need to reload the Y register. + */ + if ((LI->A.Flags & LI_RELOAD_Y) == 0) { + + /* opc ... */ + CodeEntry* LoadA = LI->A.LoadEntry; + X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } else { + + if ((LI->A.Flags & LI_CHECK_Y) == 0) { + /* ldy #offs */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->A.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->A.LoadYEntry->AM, LI->A.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + if (LI->A.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + } + + /* In both cases, we can remove the load */ + LI->A.Flags |= LI_REMOVE; + + } else { + + /* Op with temp storage */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } +} + + + +void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) +/* Add an op for the high byte of an operator. Special cases (constant values +** or similar) have to be checked separately, the function covers only the +** generic case. Code is inserted at the insertion point. +*/ +{ + CodeEntry* X; + + if (KeepResult) { + /* pha */ + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + /* txa */ + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + if ((LI->X.Flags & LI_DIRECT) != 0) { + + if ((LI->X.Flags & LI_RELOAD_Y) == 0) { + + /* opc xxx */ + CodeEntry* LoadX = LI->X.LoadEntry; + X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + } else { + + if ((LI->A.Flags & LI_CHECK_Y) == 0) { + /* ldy #const */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->X.Offs), 0, D->OpEntry->LI); + } else { + /* ldy src */ + X = NewCodeEntry (OP65_LDY, LI->X.LoadYEntry->AM, LI->X.LoadYEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + + if (LI->X.LoadEntry->OPC == OP65_JSR) { + /* opc (sp),y */ + X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); + } else { + /* opc src,y */ + X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); + } + InsertEntry (D, X, D->IP++); + } + + /* In both cases, we can remove the load */ + LI->X.Flags |= LI_REMOVE; + + } else { + /* opc zphi */ + X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } + + if (KeepResult) { + /* tax */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + + /* pla */ + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI); + InsertEntry (D, X, D->IP++); + } +} + + + +void RemoveRegLoads (StackOpData* D, LoadInfo* LI) +/* Remove register load insns */ +{ + /* Both registers may be loaded with one insn, but DelEntry will in this + ** case clear the other one. + */ + if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) { + if (LI->A.LoadIndex >= 0 && + (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.LoadIndex); + } + if (LI->A.XferIndex >= 0 && + (LI->A.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.XferIndex); + } + } + if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) { + if (LI->X.LoadIndex >= 0 && + (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.LoadIndex); + } + if (LI->X.XferIndex >= 0 && + (LI->X.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.XferIndex); + } + } +} + + + +void RemoveRemainders (StackOpData* D) +/* Remove the code that is unnecessary after translation of the sequence */ +{ + /* Remove the register loads for lhs and rhs if nothing prevents that */ + RemoveRegLoads (D, &D->Lhs); + RemoveRegLoads (D, &D->Rhs); + + /* Remove the push and the operator routine */ + DelEntry (D, D->OpIndex); + DelEntry (D, D->PushIndex); +} + + + +static int CmpHarmless (const void* Key, const void* Entry) +/* Compare function for bsearch */ +{ + return strcmp (Key, *(const char**)Entry); +} + + + +int HarmlessCall (const char* Name) +/* Check if this is a call to a harmless subroutine that will not interrupt +** the pushax/op sequence when encountered. +*/ +{ + const char* const Tab[] = { + "aslax1", + "aslax2", + "aslax3", + "aslax4", + "aslaxy", + "asrax1", + "asrax2", + "asrax3", + "asrax4", + "asraxy", + "bcastax", + "bnegax", + "complax", + "decax1", + "decax2", + "decax3", + "decax4", + "decax5", + "decax6", + "decax7", + "decax8", + "decaxy", + "incax1", + "incax2", + "incax3", + "incax4", + "incax5", + "incax6", + "incax7", + "incax8", + "incaxy", + "ldaxidx", + "ldaxysp", + "negax", + "shlax1", + "shlax2", + "shlax3", + "shlax4", + "shlaxy", + "shrax1", + "shrax2", + "shrax3", + "shrax4", + "shraxy", + }; + + void* R = bsearch (Name, + Tab, + sizeof (Tab) / sizeof (Tab[0]), + sizeof (Tab[0]), + CmpHarmless); + return (R != 0); +} + + diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h new file mode 100644 index 000000000..49a8ce60f --- /dev/null +++ b/src/cc65/codeoptutil.h @@ -0,0 +1,257 @@ +/*****************************************************************************/ +/* */ +/* codeoptutil.h */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CODEOPTUTIL_H +#define CODEOPTUTIL_H + + + +/* cc65 */ +#include "codeent.h" +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Load tracking data */ +/*****************************************************************************/ + + + +/* LoadRegInfo flags set by DirectOp */ +typedef enum { + LI_NONE = 0x00, + LI_DIRECT = 0x01, /* Direct op may be used */ + LI_RELOAD_Y = 0x02, /* Reload index register Y */ + LI_REMOVE = 0x04, /* Load may be removed */ + LI_DONT_REMOVE = 0x08, /* Load may not be removed */ + LI_CHECK_ARG = 0x10, /* Load src might be modified later */ + LI_SRC_CHG = 0x20, /* Load src is possibly modified */ + LI_LOAD_INSN = 0x40, /* Has a load insn */ + LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ + LI_USED_BY_A = 0x100, /* Content used by RegA */ + LI_USED_BY_X = 0x200, /* Content used by RegX */ + LI_USED_BY_Y = 0x400, /* Content used by RegY */ + LI_SP = 0x800, /* Content on stack */ +} LI_FLAGS; + +/* Structure that tells us how to load the lhs values */ +typedef struct LoadRegInfo LoadRegInfo; +struct LoadRegInfo { + LI_FLAGS Flags; /* Tells us how to load */ + int LoadIndex; /* Index of load insn, -1 if invalid */ + CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ + int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ + CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ + int XferIndex; /* Index of transfer insn */ + CodeEntry* XferEntry; /* The actual transfer entry */ + int Offs; /* Stack offset if data is on stack */ +}; + +/* Now combined for both registers */ +typedef struct LoadInfo LoadInfo; +struct LoadInfo { + LoadRegInfo A; /* Info for A register */ + LoadRegInfo X; /* Info for X register */ + LoadRegInfo Y; /* Info for Y register */ +}; + +/* Structure forward decl */ +typedef struct StackOpData StackOpData; +typedef struct OptFuncDesc OptFuncDesc; + +/* Structure that holds the needed data */ +struct StackOpData { + CodeSeg* Code; /* Pointer to code segment */ + unsigned Flags; /* Flags to remember things */ + + /* Pointer to optimizer subfunction description */ + const OptFuncDesc* OptFunc; + + /* ZP register usage inside the sequence */ + unsigned ZPUsage; + unsigned ZPChanged; + + /* Freedom of registers inside the sequence */ + unsigned UsedRegs; /* Registers used */ + + /* Whether the rhs is changed multiple times */ + int RhsMultiChg; + + /* Register load information for lhs and rhs */ + LoadInfo Lhs; + LoadInfo Rhs; + + /* Several indices of insns in the code segment */ + int PushIndex; /* Index of call to pushax in codeseg */ + int OpIndex; /* Index of actual operation */ + + /* Pointers to insns in the code segment */ + CodeEntry* PrevEntry; /* Entry before the call to pushax */ + CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ + CodeEntry* OpEntry; /* Pointer to entry with op */ + CodeEntry* NextEntry; /* Entry after the op */ + + const char* ZPLo; /* Lo byte of zero page loc to use */ + const char* ZPHi; /* Hi byte of zero page loc to use */ + unsigned IP; /* Insertion point used by some routines */ +}; + + + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +void ClearLoadRegInfo (LoadRegInfo* LRI); +/* Clear a LoadRegInfo struct */ + +void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From); +/* Copy a LoadRegInfo struct */ + +void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S); +/* Prepare a LoadRegInfo struct for use */ + +void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change); +/* Adjust a load register info struct after deleting or inserting an entry +** with a given index +*/ + +void ClearLoadInfo (LoadInfo* LI); +/* Clear a LoadInfo struct */ + +void CopyLoadInfo (LoadInfo* To, LoadInfo* From); +/* Copy a LoadInfo struct */ + +void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S); +/* Prepare a LoadInfo struct for use */ + +void AdjustLoadInfo (LoadInfo* LI, int Index, int Change); +/* Adjust a load info struct after deleting entry with a given index */ + +RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg); +/* Get RegInfo of the last insn entry that changed the reg */ + +unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I); +/* Track loads for a code entry. +** Return used registers. +*/ + +void SetDontRemoveEntryFlag (LoadRegInfo* LRI); +/* Flag the entry as non-removable according to register flags */ + +void ResetDontRemoveEntryFlag (LoadRegInfo* LRI); +/* Unflag the entry as non-removable according to register flags */ + +void SetDontRemoveEntryFlags (StackOpData* D); +/* Flag the entries as non-removable according to register flags */ + +void ResetDontRemoveEntryFlags (StackOpData* D); +/* Unflag the entries as non-removable according to register flags */ + +void ResetStackOpData (StackOpData* Data); +/* Reset the given data structure */ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +void InsertEntry (StackOpData* D, CodeEntry* E, int Index); +/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will +** be adjusted by this function. +*/ + +void DelEntry (StackOpData* D, int Index); +/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be +** adjusted by this function, and PushEntry/OpEntry may get invalidated. +*/ + +void AdjustStackOffset (StackOpData* D, unsigned Offs); +/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. +** OpIndex is adjusted according to the insertions. +*/ + +int IsRegVar (StackOpData* D); +/* If the value pushed is that of a zeropage variable that is unchanged until Op, +** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. +** Otherwise leave D untouched and return false. +*/ + +void AddStoreLhsA (StackOpData* D); +/* Add a store to zero page after the push insn */ + +void AddStoreLhsX (StackOpData* D); +/* Add a store to zero page after the push insn */ + +void ReplacePushByStore (StackOpData* D); +/* Replace the call to the push subroutine by a store into the zero page +** location (actually, the push is not replaced, because we need it for +** later, but the name is still ok since the push will get removed at the +** end of each routine). +*/ + +void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI); +/* Add an op for the low byte of an operator. This function honours the +** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. +** All code is inserted at the current insertion point. +*/ + +void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult); +/* Add an op for the high byte of an operator. Special cases (constant values +** or similar) have to be checked separately, the function covers only the +** generic case. Code is inserted at the insertion point. +*/ + +void RemoveRegLoads (StackOpData* D, LoadInfo* LI); +/* Remove register load insns */ + + +void RemoveRemainders (StackOpData* D); +/* Remove the code that is unnecessary after translation of the sequence */ + +int HarmlessCall (const char* Name); +/* Check if this is a call to a harmless subroutine that will not interrupt +** the pushax/op sequence when encountered. +*/ + +/* End of codeoptutil.h */ + +#endif diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index c31b0ec97..457f9ff1f 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -41,57 +41,12 @@ /* cc65 */ #include "codeent.h" #include "codeinfo.h" +#include "codeoptutil.h" #include "coptstop.h" #include "error.h" -/*****************************************************************************/ -/* Load tracking data */ -/*****************************************************************************/ - - - -/* LoadRegInfo flags set by DirectOp */ -typedef enum { - LI_NONE = 0x00, - LI_DIRECT = 0x01, /* Direct op may be used */ - LI_RELOAD_Y = 0x02, /* Reload index register Y */ - LI_REMOVE = 0x04, /* Load may be removed */ - LI_DONT_REMOVE = 0x08, /* Load may not be removed */ - LI_CHECK_ARG = 0x10, /* Load src might be modified later */ - LI_SRC_CHG = 0x20, /* Load src is possibly modified */ - LI_LOAD_INSN = 0x40, /* Has a load insn */ - LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ - LI_USED_BY_A = 0x100, /* Content used by RegA */ - LI_USED_BY_X = 0x200, /* Content used by RegX */ - LI_USED_BY_Y = 0x400, /* Content used by RegY */ - LI_SP = 0x800, /* Content on stack */ -} LI_FLAGS; - -/* Structure that tells us how to load the lhs values */ -typedef struct LoadRegInfo LoadRegInfo; -struct LoadRegInfo { - LI_FLAGS Flags; /* Tells us how to load */ - int LoadIndex; /* Index of load insn, -1 if invalid */ - CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ - int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ - CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ - int XferIndex; /* Index of transfer insn */ - CodeEntry* XferEntry; /* The actual transfer entry */ - int Offs; /* Stack offset if data is on stack */ -}; - -/* Now combined for both registers */ -typedef struct LoadInfo LoadInfo; -struct LoadInfo { - LoadRegInfo A; /* Info for A register */ - LoadRegInfo X; /* Info for X register */ - LoadRegInfo Y; /* Info for Y register */ -}; - - - /*****************************************************************************/ /* Data */ /*****************************************************************************/ @@ -121,9 +76,6 @@ typedef enum { OP_RHS_REMOVE_DIRECT = 0xC000, /* RHS must be directly removable */ } OP_FLAGS; -/* Structure forward decl */ -typedef struct StackOpData StackOpData; - /* Structure that describes an optimizer subfunction for a specific op */ typedef unsigned (*OptFunc) (StackOpData* D); typedef struct OptFuncDesc OptFuncDesc; @@ -134,434 +86,6 @@ struct OptFuncDesc { OP_FLAGS Flags; /* Flags */ }; -/* Structure that holds the needed data */ -struct StackOpData { - CodeSeg* Code; /* Pointer to code segment */ - unsigned Flags; /* Flags to remember things */ - - /* Pointer to optimizer subfunction description */ - const OptFuncDesc* OptFunc; - - /* ZP register usage inside the sequence */ - unsigned ZPUsage; - unsigned ZPChanged; - - /* Freedom of registers inside the sequence */ - unsigned UsedRegs; /* Registers used */ - - /* Whether the rhs is changed multiple times */ - int RhsMultiChg; - - /* Register load information for lhs and rhs */ - LoadInfo Lhs; - LoadInfo Rhs; - - /* Several indices of insns in the code segment */ - int PushIndex; /* Index of call to pushax in codeseg */ - int OpIndex; /* Index of actual operation */ - - /* Pointers to insns in the code segment */ - CodeEntry* PrevEntry; /* Entry before the call to pushax */ - CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ - CodeEntry* OpEntry; /* Pointer to entry with op */ - CodeEntry* NextEntry; /* Entry after the op */ - - const char* ZPLo; /* Lo byte of zero page loc to use */ - const char* ZPHi; /* Hi byte of zero page loc to use */ - unsigned IP; /* Insertion point used by some routines */ -}; - - - -/*****************************************************************************/ -/* Load tracking code */ -/*****************************************************************************/ - - - -static void ClearLoadRegInfo (LoadRegInfo* RI) -/* Clear a LoadRegInfo struct */ -{ - RI->Flags = LI_NONE; - RI->LoadIndex = -1; - RI->LoadEntry = 0; - RI->LoadYIndex = -1; - RI->LoadYEntry = 0; - RI->XferIndex = -1; - RI->XferEntry = 0; - RI->Offs = 0; -} - - - -static void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) -/* Copy a LoadRegInfo struct */ -{ - To->Flags = From->Flags; - To->LoadIndex = From->LoadIndex; - To->LoadEntry = From->LoadEntry; - To->LoadYIndex = From->LoadYIndex; - To->LoadYEntry = From->LoadYEntry; - To->XferIndex = From->XferIndex; - To->XferEntry = From->XferEntry; - To->Offs = From->Offs; -} - - - -static void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S) -/* Prepare a LoadRegInfo struct for use */ -{ - /* Get the entries */ - if (RI->LoadIndex >= 0) { - RI->LoadEntry = CS_GetEntry (S, RI->LoadIndex); - } else { - RI->LoadEntry = 0; - } - if (RI->XferIndex >= 0) { - RI->XferEntry = CS_GetEntry (S, RI->XferIndex); - } else { - RI->XferEntry = 0; - } - /* Load from src not modified before op can be treated as direct */ - if ((RI->Flags & LI_SRC_CHG) == 0 && - (RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { - RI->Flags |= LI_DIRECT; - if ((RI->Flags & LI_CHECK_Y) != 0) { - RI->Flags |= LI_RELOAD_Y; - } - } - /* We cannot ldy src,y */ - if ((RI->Flags & LI_RELOAD_Y) != 0 && - RI->LoadYEntry != 0 && - (RI->LoadYEntry->Use & REG_Y) == REG_Y) { - RI->Flags &= ~LI_DIRECT; - } -} - - - -static void ClearLoadInfo (LoadInfo* LI) -/* Clear a LoadInfo struct */ -{ - ClearLoadRegInfo (&LI->A); - ClearLoadRegInfo (&LI->X); - ClearLoadRegInfo (&LI->Y); -} - - - -static void CopyLoadInfo (LoadInfo* To, LoadInfo* From) -/* Copy a LoadInfo struct */ -{ - CopyLoadRegInfo (&To->A, &From->A); - CopyLoadRegInfo (&To->X, &From->X); - CopyLoadRegInfo (&To->Y, &From->Y); -} - - - -static void AdjustLoadRegInfo (LoadRegInfo* RI, int Index, int Change) -/* Adjust a load register info struct after deleting or inserting an entry -** with a given index -*/ -{ - CHECK (abs (Change) == 1); - if (Change < 0) { - /* Deletion */ - if (Index < RI->LoadIndex) { - --RI->LoadIndex; - } else if (Index == RI->LoadIndex) { - /* Has been removed */ - RI->LoadIndex = -1; - RI->LoadEntry = 0; - } - if (Index < RI->XferIndex) { - --RI->XferIndex; - } else if (Index == RI->XferIndex) { - /* Has been removed */ - RI->XferIndex = -1; - RI->XferEntry = 0; - } - } else { - /* Insertion */ - if (Index <= RI->LoadIndex) { - ++RI->LoadIndex; - } - if (Index <= RI->XferIndex) { - ++RI->XferIndex; - } - } -} - - - -static void FinalizeLoadInfo (LoadInfo* LI, CodeSeg* S) -/* Prepare a LoadInfo struct for use */ -{ - /* Get the entries */ - FinalizeLoadRegInfo (&LI->A, S); - FinalizeLoadRegInfo (&LI->X, S); - FinalizeLoadRegInfo (&LI->Y, S); -} - - - -static void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) -/* Adjust a load info struct after deleting entry with a given index */ -{ - AdjustLoadRegInfo (&LI->A, Index, Change); - AdjustLoadRegInfo (&LI->X, Index, Change); - AdjustLoadRegInfo (&LI->Y, Index, Change); -} - - - -static int Affected (LoadRegInfo* RI, const CodeEntry* E) -/* Check if the load src may be modified between the pushax and op */ -{ - fncls_t fncls; - unsigned int Use; - unsigned int Chg; - unsigned int UseToCheck = 0; - - if ((RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { - if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { - return 0; - } - CHECK ((RI->Flags & LI_CHECK_ARG) == 0 || RI->LoadEntry != 0); - CHECK ((RI->Flags & LI_CHECK_Y) == 0 || RI->LoadYEntry != 0); - - if ((RI->Flags & LI_CHECK_ARG) != 0) { - UseToCheck |= RI->LoadEntry->Use; - } - - if ((RI->Flags & LI_CHECK_Y) != 0) { - UseToCheck |= RI->LoadYEntry->Use; - } - - if (E->OPC == OP65_JSR) { - /* Try to know about the function */ - fncls = GetFuncInfo (E->Arg, &Use, &Chg); - if ((UseToCheck & Chg & REG_ALL) == 0 && - fncls == FNCLS_BUILTIN) { - /* Builtin functions are known to be harmless */ - return 0; - } - /* Otherwise play it safe */ - return 1; - } else if (E->OPC == OP65_DEC || E->OPC == OP65_INC || - E->OPC == OP65_ASL || E->OPC == OP65_LSR || - E->OPC == OP65_ROL || E->OPC == OP65_ROR || - E->OPC == OP65_TRB || E->OPC == OP65_TSB || - E->OPC == OP65_STA || E->OPC == OP65_STX || E->OPC == OP65_STY) { - if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { - if ((RI->Flags & LI_CHECK_ARG) != 0 && - strcmp (RI->LoadEntry->Arg, E->Arg) == 0) { - return 1; - } - if ((RI->Flags & LI_CHECK_Y) != 0 && - strcmp (RI->LoadYEntry->Arg, E->Arg) == 0) { - return 1; - } - return 0; - } - /* We could've check further for more cases where the load target isn't modified, - ** But for now let's save the trouble and just play it safe. */ - return 1; - } - } - return 0; -} - - - -static void HonourUseAndChg (LoadRegInfo* RI, unsigned Reg, const CodeEntry* E, int I) -/* Honour use and change flags for an instruction */ -{ - if ((E->Chg & Reg) != 0) { - /* Remember this as an indirect load */ - ClearLoadRegInfo (RI); - RI->LoadIndex = I; - RI->XferIndex = -1; - RI->Flags = 0; - } else if (Affected (RI, E)) { - RI->Flags |= LI_SRC_CHG; - } -} - - - -static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) -/* Track loads for a code entry. -** Return used registers. -*/ -{ - unsigned Used; - CodeEntry* E = CS_GetEntry (S, I); - CHECK (E != 0); - - /* By default */ - Used = E->Use; - - /* Whether we had a load or xfer op before or not, the newly loaded value - ** will be the real one used for the pushax/op unless it's overwritten, - ** so we can just reset the flags about it in such cases. - */ - if (E->Info & OF_LOAD) { - - LoadRegInfo* RI = 0; - - /* Determine, which register was loaded */ - if (E->Chg & REG_A) { - RI = &LI->A; - } else if (E->Chg & REG_X) { - RI = &LI->X; - } else if (E->Chg & REG_Y) { - RI = &LI->Y; - } - CHECK (RI != 0); - - /* Remember the load */ - RI->LoadIndex = I; - RI->XferIndex = -1; - - /* Set load flags */ - RI->Flags = LI_LOAD_INSN; - if (E->AM == AM65_IMM) { - /* These insns are all ok and replaceable */ - RI->Flags |= LI_DIRECT; - } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { - /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_CHECK_ARG; - } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { - /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; - } else if (E->AM == AM65_ZP_INDY && - strcmp (E->Arg, "sp") == 0) { - /* A load from the stack with known offset is also ok, but in this - ** case we must reload the index register later. Please note that - ** a load indirect via other zero page locations is not ok, since - ** these locations may change between the push and the actual - ** operation. - */ - RI->Flags |= LI_DIRECT | LI_CHECK_Y | LI_SP; - - /* Reg Y can be regarded as unused if this load is removed */ - Used &= ~REG_Y; - if (RI == &LI->A) { - LI->Y.Flags |= LI_USED_BY_A; - } else { - LI->Y.Flags |= LI_USED_BY_X; - } - } - - /* If the load offset has a known value, we can just remember and reload - ** it into the index register later. - */ - if ((RI->Flags & LI_CHECK_Y) != 0) { - if (RegValIsKnown (E->RI->In.RegY)) { - RI->Offs = (unsigned char)E->RI->In.RegY; - RI->Flags &= ~LI_CHECK_Y; - RI->Flags |= LI_RELOAD_Y; - } else { - /* We need to check if the src of Y is changed */ - RI->LoadYIndex = LI->Y.LoadIndex; - RI->LoadYEntry = CS_GetEntry (S, RI->LoadYIndex); - } - } - - /* Watch for any change of the load target */ - if ((RI->Flags & LI_CHECK_ARG) != 0) { - RI->LoadEntry = CS_GetEntry (S, I); - } - - } else if (E->Info & OF_XFR) { - - /* Determine source and target of the transfer and handle the TSX insn */ - LoadRegInfo* Src; - LoadRegInfo* Tgt; - switch (E->OPC) { - case OP65_TAX: - Src = &LI->A; - Tgt = &LI->X; - Used &= ~REG_A; - Src->Flags |= LI_USED_BY_X; - break; - case OP65_TAY: - Src = &LI->A; - Tgt = &LI->Y; - Used &= ~REG_A; - Src->Flags |= LI_USED_BY_Y; - break; - case OP65_TXA: - Src = &LI->X; - Tgt = &LI->A; - Used &= ~REG_X; - Src->Flags |= LI_USED_BY_A; - break; - case OP65_TYA: - Src = &LI->Y; - Tgt = &LI->A; - Used &= ~REG_Y; - Src->Flags |= LI_USED_BY_A; - break; - case OP65_TSX: - ClearLoadRegInfo (&LI->X); - return Used; - case OP65_TXS: - return Used; - default: Internal ("Unknown XFR insn in TrackLoads"); - } - - /* Transfer the data */ - Tgt->LoadIndex = Src->LoadIndex; - Tgt->LoadEntry = Src->LoadEntry; - Tgt->LoadYIndex = Src->LoadYIndex; - Tgt->LoadYEntry = Src->LoadYEntry; - Tgt->XferIndex = I; - Tgt->Offs = Src->Offs; - Tgt->Flags = Src->Flags; - - } else if (CE_IsCallTo (E, "ldaxysp") && RegValIsKnown (E->RI->In.RegY)) { - - /* Both registers set, Y changed */ - LI->A.LoadIndex = I; - LI->A.XferIndex = -1; - LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); - LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; - - LI->X.LoadIndex = I; - LI->X.XferIndex = -1; - LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); - LI->X.Offs = (unsigned char) E->RI->In.RegY; - - /* Reg Y can be regarded as unused if this load is removed */ - Used &= ~REG_Y; - LI->Y.Flags |= LI_USED_BY_A | LI_USED_BY_X; - - } else { - HonourUseAndChg (&LI->A, REG_A, E, I); - HonourUseAndChg (&LI->X, REG_X, E, I); - HonourUseAndChg (&LI->Y, REG_Y, E, I); - - /* The other operand may be affected too */ - if (LLI != 0) { - if (Affected (&LLI->A, E)) { - LLI->A.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->X, E)) { - LLI->X.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->Y, E)) { - LLI->Y.Flags |= LI_SRC_CHG; - } - } - } - - return Used; -} - /*****************************************************************************/ @@ -570,421 +94,6 @@ static unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) -static void InsertEntry (StackOpData* D, CodeEntry* E, int Index) -/* Insert a new entry. Depending on Index, D->PushIndex and D->OpIndex will -** be adjusted by this function. -*/ -{ - /* Insert the entry into the code segment */ - CS_InsertEntry (D->Code, E, Index); - - /* Adjust register loads if necessary */ - AdjustLoadInfo (&D->Lhs, Index, 1); - AdjustLoadInfo (&D->Rhs, Index, 1); - - /* Adjust the indices if necessary */ - if (D->PushEntry && Index <= D->PushIndex) { - ++D->PushIndex; - } - if (D->OpEntry && Index <= D->OpIndex) { - ++D->OpIndex; - } -} - - - -static void DelEntry (StackOpData* D, int Index) -/* Delete an entry. Depending on Index, D->PushIndex and D->OpIndex will be -** adjusted by this function, and PushEntry/OpEntry may get invalidated. -*/ -{ - /* Delete the entry from the code segment */ - CS_DelEntry (D->Code, Index); - - /* Adjust register loads if necessary */ - AdjustLoadInfo (&D->Lhs, Index, -1); - AdjustLoadInfo (&D->Rhs, Index, -1); - - /* Adjust the other indices if necessary */ - if (Index < D->PushIndex) { - --D->PushIndex; - } else if (Index == D->PushIndex) { - D->PushEntry = 0; - } - if (Index < D->OpIndex) { - --D->OpIndex; - } else if (Index == D->OpIndex) { - D->OpEntry = 0; - } -} - - - -static void AdjustStackOffset (StackOpData* D, unsigned Offs) -/* Adjust the offset for all stack accesses in the range PushIndex to OpIndex. -** OpIndex is adjusted according to the insertions. -*/ -{ - /* Walk over all entries */ - int I = D->PushIndex + 1; - while (I < D->OpIndex) { - - CodeEntry* E = CS_GetEntry (D->Code, I); - - /* Check if this entry does a stack access, and if so, if it's a plain - ** load from stack, since this is needed later. - */ - int Correction = 0; - if ((E->Use & REG_SP) != 0) { - - /* Check for some things that should not happen */ - CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); - CHECK (strcmp (E->Arg, "sp") == 0); - /* We need to correct this one */ - Correction = (E->OPC == OP65_LDA)? 2 : 1; - - } else if (CE_IsCallTo (E, "ldaxysp")) { - /* We need to correct this one */ - Correction = 1; - } - - if (Correction) { - /* Get the code entry before this one. If it's a LDY, adjust the - ** value. - */ - CodeEntry* P = CS_GetPrevEntry (D->Code, I); - if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { - /* The Y load is just before the stack access, adjust it */ - CE_SetNumArg (P, P->Num - Offs); - } else { - /* Insert a new load instruction before the stack access */ - const char* Arg = MakeHexArg (E->RI->In.RegY - Offs); - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I++); - } - - /* If we need the value of Y later, be sure to reload it */ - if (RegYUsed (D->Code, I+1)) { - CodeEntry* N; - const char* Arg = MakeHexArg (E->RI->In.RegY); - if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && - ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { - /* The Y register is used but the load instruction loads A - ** and is followed by a branch that evaluates the zero flag. - ** This means that we cannot just insert the load insn - ** for the Y register at this place, because it would - ** destroy the Z flag. Instead place load insns at the - ** target of the branch and after it. - ** Note: There is a chance that this code won't work. The - ** jump may be a backwards jump (in which case the stack - ** offset has already been adjusted) or there may be other - ** instructions between the load and the conditional jump. - ** Currently the compiler does not generate such code, but - ** it is possible to force the optimizer into something - ** invalid by use of inline assembler. - */ - - /* Add load insn after the branch */ - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+2); - - /* Add load insn before branch target */ - CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); - CHECK (J > I); /* Must not happen */ - InsertEntry (D, Y, J); - - /* Move the label to the new insn */ - CodeLabel* L = CS_GenLabel (D->Code, Y); - CS_MoveLabelRef (D->Code, N, L); - } else { - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+1); - /* Skip this instruction in the next round */ - ++I; - } - } - } - - /* Next entry */ - ++I; - } - - /* If we have rhs load insns that load from stack, we'll have to adjust - ** the offsets for these also. - */ - if ((D->Rhs.A.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { - D->Rhs.A.Offs -= Offs; - } - if ((D->Rhs.X.Flags & (LI_RELOAD_Y | LI_SP | LI_CHECK_Y)) == (LI_RELOAD_Y | LI_SP)) { - D->Rhs.X.Offs -= Offs; - } -} - - - -static void AddStoreLhsA (StackOpData* D) -/* Add a store to zero page after the push insn */ -{ - CodeEntry* X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); - InsertEntry (D, X, D->PushIndex+1); -} - - - -static void AddStoreLhsX (StackOpData* D) -/* Add a store to zero page after the push insn */ -{ - CodeEntry* X = NewCodeEntry (OP65_STX, AM65_ZP, D->ZPHi, 0, D->PushEntry->LI); - InsertEntry (D, X, D->PushIndex+1); -} - - - -static void ReplacePushByStore (StackOpData* D) -/* Replace the call to the push subroutine by a store into the zero page -** location (actually, the push is not replaced, because we need it for -** later, but the name is still ok since the push will get removed at the -** end of each routine). -*/ -{ - /* Store the value into the zeropage instead of pushing it. Check high - ** byte first so that the store is later in A/X order. - */ - if ((D->Lhs.X.Flags & LI_DIRECT) == 0) { - AddStoreLhsX (D); - } - if ((D->Lhs.A.Flags & LI_DIRECT) == 0) { - AddStoreLhsA (D); - } -} - - - -static void AddOpLow (StackOpData* D, opc_t OPC, LoadInfo* LI) -/* Add an op for the low byte of an operator. This function honours the -** OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions. -** All code is inserted at the current insertion point. -*/ -{ - CodeEntry* X; - - if ((LI->A.Flags & LI_DIRECT) != 0) { - /* Op with a variable location. If the location is on the stack, we - ** need to reload the Y register. - */ - if ((LI->A.Flags & LI_RELOAD_Y) == 0) { - - /* opc ... */ - CodeEntry* LoadA = LI->A.LoadEntry; - X = NewCodeEntry (OPC, LoadA->AM, LoadA->Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } else { - - if ((LI->A.Flags & LI_CHECK_Y) == 0) { - /* ldy #offs */ - X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->A.Offs), 0, D->OpEntry->LI); - } else { - /* ldy src */ - X = NewCodeEntry (OP65_LDY, LI->A.LoadYEntry->AM, LI->A.LoadYEntry->Arg, 0, D->OpEntry->LI); - } - InsertEntry (D, X, D->IP++); - - if (LI->A.LoadEntry->OPC == OP65_JSR) { - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); - } else { - /* opc src,y */ - X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); - } - InsertEntry (D, X, D->IP++); - - } - - /* In both cases, we can remove the load */ - LI->A.Flags |= LI_REMOVE; - - } else { - - /* Op with temp storage */ - X = NewCodeEntry (OPC, AM65_ZP, D->ZPLo, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } -} - - - -static void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) -/* Add an op for the high byte of an operator. Special cases (constant values -** or similar) have to be checked separately, the function covers only the -** generic case. Code is inserted at the insertion point. -*/ -{ - CodeEntry* X; - - if (KeepResult) { - /* pha */ - X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } - - /* txa */ - X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - if ((LI->X.Flags & LI_DIRECT) != 0) { - - if ((LI->X.Flags & LI_RELOAD_Y) == 0) { - - /* opc xxx */ - CodeEntry* LoadX = LI->X.LoadEntry; - X = NewCodeEntry (OPC, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - } else { - - if ((LI->A.Flags & LI_CHECK_Y) == 0) { - /* ldy #const */ - X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LI->X.Offs), 0, D->OpEntry->LI); - } else { - /* ldy src */ - X = NewCodeEntry (OP65_LDY, LI->X.LoadYEntry->AM, LI->X.LoadYEntry->Arg, 0, D->OpEntry->LI); - } - InsertEntry (D, X, D->IP++); - - if (LI->X.LoadEntry->OPC == OP65_JSR) { - /* opc (sp),y */ - X = NewCodeEntry (OPC, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); - } else { - /* opc src,y */ - X = NewCodeEntry (OPC, LI->A.LoadEntry->AM, LI->A.LoadEntry->Arg, 0, D->OpEntry->LI); - } - InsertEntry (D, X, D->IP++); - } - - /* In both cases, we can remove the load */ - LI->X.Flags |= LI_REMOVE; - - } else { - /* opc zphi */ - X = NewCodeEntry (OPC, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } - - if (KeepResult) { - /* tax */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - - /* pla */ - X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, D->OpEntry->LI); - InsertEntry (D, X, D->IP++); - } -} - - - -static void RemoveRegLoads (StackOpData* D, LoadInfo* LI) -/* Remove register load insns */ -{ - /* Both registers may be loaded with one insn, but DelEntry will in this - ** case clear the other one. - */ - if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) { - if (LI->A.LoadIndex >= 0 && - (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->A.LoadIndex); - } - if (LI->A.XferIndex >= 0 && - (LI->A.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->A.XferIndex); - } - } - if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) { - if (LI->X.LoadIndex >= 0 && - (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->X.LoadIndex); - } - if (LI->X.XferIndex >= 0 && - (LI->X.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->X.XferIndex); - } - } -} - - - -static void RemoveRemainders (StackOpData* D) -/* Remove the code that is unnecessary after translation of the sequence */ -{ - /* Remove the register loads for lhs and rhs if nothing prevents that */ - RemoveRegLoads (D, &D->Lhs); - RemoveRegLoads (D, &D->Rhs); - - /* Remove the push and the operator routine */ - DelEntry (D, D->OpIndex); - DelEntry (D, D->PushIndex); -} - - - -static int IsRegVar (StackOpData* D) -/* If the value pushed is that of a zeropage variable that is unchanged until Op, -** replace ZPLo and ZPHi in the given StackOpData struct by the variable and return true. -** Otherwise leave D untouched and return false. -*/ -{ - CodeEntry* LoadA = D->Lhs.A.LoadEntry; - CodeEntry* LoadX = D->Lhs.X.LoadEntry; - unsigned Len; - - /* Must be unchanged till Op */ - if ((D->Lhs.A.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT || - (D->Lhs.X.Flags & (LI_DIRECT | LI_RELOAD_Y)) != LI_DIRECT) { - return 0; - } - - /* Must have both load insns */ - if (LoadA == 0 || LoadX == 0) { - return 0; - } - - /* Must be loads from zp */ - if (LoadA->AM != AM65_ZP || LoadX->AM != AM65_ZP) { - return 0; - } - - /* Must be the same zp loc with high byte in X */ - Len = strlen (LoadA->Arg); - if (strncmp (LoadA->Arg, LoadX->Arg, Len) != 0 || - strcmp (LoadX->Arg + Len, "+1") != 0) { - return 0; - } - - /* Use the zero page location directly */ - D->ZPLo = LoadA->Arg; - D->ZPHi = LoadX->Arg; - return 1; -} - - - -static RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) -/* Get RegInfo of the last load insn entry */ -{ - CodeEntry* E; - - if (Reg->LoadIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->LoadIndex)) != 0) { - return E->RI; - } - - return 0; -} - - - static int SameRegAValue (StackOpData* D) /* Check if Rhs Reg A == Lhs Reg A */ { @@ -2304,94 +1413,6 @@ static const OptFuncDesc* FindFunc (const OptFuncDesc FuncTable[], size_t Count, -static int CmpHarmless (const void* Key, const void* Entry) -/* Compare function for bsearch */ -{ - return strcmp (Key, *(const char**)Entry); -} - - - -static int HarmlessCall (const char* Name) -/* Check if this is a call to a harmless subroutine that will not interrupt -** the pushax/op sequence when encountered. -*/ -{ - static const char* const Tab[] = { - "aslax1", - "aslax2", - "aslax3", - "aslax4", - "aslaxy", - "asrax1", - "asrax2", - "asrax3", - "asrax4", - "asraxy", - "bcastax", - "bnegax", - "complax", - "decax1", - "decax2", - "decax3", - "decax4", - "decax5", - "decax6", - "decax7", - "decax8", - "decaxy", - "incax1", - "incax2", - "incax3", - "incax4", - "incax5", - "incax6", - "incax7", - "incax8", - "incaxy", - "ldaxidx", - "ldaxysp", - "negax", - "shlax1", - "shlax2", - "shlax3", - "shlax4", - "shlaxy", - "shrax1", - "shrax2", - "shrax3", - "shrax4", - "shraxy", - }; - - void* R = bsearch (Name, - Tab, - sizeof (Tab) / sizeof (Tab[0]), - sizeof (Tab[0]), - CmpHarmless); - return (R != 0); -} - - - -static void ResetStackOpData (StackOpData* Data) -/* Reset the given data structure */ -{ - Data->OptFunc = 0; - Data->ZPUsage = REG_NONE; - Data->ZPChanged = REG_NONE; - Data->UsedRegs = REG_NONE; - Data->RhsMultiChg = 0; - - ClearLoadInfo (&Data->Lhs); - ClearLoadInfo (&Data->Rhs); - - Data->PushIndex = -1; - Data->OpIndex = -1; -} - - - static int PreCondOk (StackOpData* D) /* Check if the preconditions for a call to the optimizer subfunction are ** satisfied. As a side effect, this function will also choose the zero page @@ -2701,52 +1722,6 @@ static int RegAPreCondOk (StackOpData* D) -static void SetDontRemoveEntryFlag (LoadRegInfo* RI) -/* Flag the entry as non-removable according to register flags */ -{ - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags |= CEF_DONT_REMOVE; - } - } -} - - - -static void ResetDontRemoveEntryFlag (LoadRegInfo* RI) -/* Unflag the entry as non-removable according to register flags */ -{ - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; - } - } -} - - - -static void SetDontRemoveEntryFlags (StackOpData* D) -/* Flag the entries as non-removable according to register flags */ -{ - SetDontRemoveEntryFlag (&D->Lhs.A); - SetDontRemoveEntryFlag (&D->Lhs.X); - SetDontRemoveEntryFlag (&D->Rhs.A); - SetDontRemoveEntryFlag (&D->Rhs.X); -} - - - -static void ResetDontRemoveEntryFlags (StackOpData* D) -/* Unflag the entries as non-removable according to register flags */ -{ - ResetDontRemoveEntryFlag (&D->Lhs.A); - ResetDontRemoveEntryFlag (&D->Lhs.X); - ResetDontRemoveEntryFlag (&D->Rhs.A); - ResetDontRemoveEntryFlag (&D->Rhs.X); -} - - - unsigned OptStackOps (CodeSeg* S) /* Optimize operations that take operands via the stack */ { From f3771a465d9bb001ff8c636b6e8856d36023fa73 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:17:08 +0800 Subject: [PATCH 371/806] Fixed various issues in the usage-tracking code. Added some utility functions. --- src/cc65/codeoptutil.c | 2348 +++++++++++++++++++++++++++++++++++++--- src/cc65/codeoptutil.h | 283 ++++- src/cc65/coptstop.c | 156 ++- 3 files changed, 2509 insertions(+), 278 deletions(-) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 999c18d33..46acbaaff 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -52,17 +52,17 @@ -void ClearLoadRegInfo (LoadRegInfo* RI) +void ClearLoadRegInfo (LoadRegInfo* LRI) /* Clear a LoadRegInfo struct */ { - RI->Flags = LI_NONE; - RI->LoadIndex = -1; - RI->LoadEntry = 0; - RI->LoadYIndex = -1; - RI->LoadYEntry = 0; - RI->XferIndex = -1; - RI->XferEntry = 0; - RI->Offs = 0; + LRI->Flags = LI_NONE; + LRI->LoadIndex = -1; + LRI->LoadEntry = 0; + LRI->LoadYIndex = -1; + LRI->LoadYEntry = 0; + LRI->ChgIndex = -1; + LRI->ChgEntry = 0; + LRI->Offs = 0; } @@ -75,46 +75,52 @@ void CopyLoadRegInfo (LoadRegInfo* To, LoadRegInfo* From) To->LoadEntry = From->LoadEntry; To->LoadYIndex = From->LoadYIndex; To->LoadYEntry = From->LoadYEntry; - To->XferIndex = From->XferIndex; - To->XferEntry = From->XferEntry; + To->ChgIndex = From->ChgIndex; + To->ChgEntry = From->ChgEntry; To->Offs = From->Offs; } -void FinalizeLoadRegInfo (LoadRegInfo* RI, CodeSeg* S) +void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S) /* Prepare a LoadRegInfo struct for use */ { /* Get the entries */ - if (RI->LoadIndex >= 0) { - RI->LoadEntry = CS_GetEntry (S, RI->LoadIndex); + if (LRI->LoadIndex >= 0) { + LRI->LoadEntry = CS_GetEntry (S, LRI->LoadIndex); } else { - RI->LoadEntry = 0; + LRI->LoadEntry = 0; } - if (RI->XferIndex >= 0) { - RI->XferEntry = CS_GetEntry (S, RI->XferIndex); + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); } else { - RI->XferEntry = 0; + LRI->LoadYEntry = 0; } + if (LRI->ChgIndex >= 0) { + LRI->ChgEntry = CS_GetEntry (S, LRI->ChgIndex); + } else { + LRI->ChgEntry = 0; + } + /* Load from src not modified before op can be treated as direct */ - if ((RI->Flags & LI_SRC_CHG) == 0 && - (RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { - RI->Flags |= LI_DIRECT; - if ((RI->Flags & LI_CHECK_Y) != 0) { - RI->Flags |= LI_RELOAD_Y; + if ((LRI->Flags & LI_SRC_CHG) == 0 && + (LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + LRI->Flags |= LI_DIRECT; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + LRI->Flags |= LI_RELOAD_Y; } } - /* We cannot ldy src,y */ - if ((RI->Flags & LI_RELOAD_Y) != 0 && - RI->LoadYEntry != 0 && - (RI->LoadYEntry->Use & REG_Y) == REG_Y) { - RI->Flags &= ~LI_DIRECT; + /* We cannot ldy ??? or ldy src,y */ + if ((LRI->Flags & LI_CHECK_Y) != 0 && + (LRI->LoadYEntry == 0 || + (LRI->LoadYEntry->Use & REG_Y) == REG_Y)) { + LRI->Flags &= ~LI_DIRECT; } } -void AdjustLoadRegInfo (LoadRegInfo* RI, int Index, int Change) +void AdjustLoadRegInfo (LoadRegInfo* LRI, int Index, int Change) /* Adjust a load register info struct after deleting or inserting an entry ** with a given index */ @@ -122,27 +128,37 @@ void AdjustLoadRegInfo (LoadRegInfo* RI, int Index, int Change) CHECK (abs (Change) == 1); if (Change < 0) { /* Deletion */ - if (Index < RI->LoadIndex) { - --RI->LoadIndex; - } else if (Index == RI->LoadIndex) { + if (Index < LRI->LoadIndex) { + --LRI->LoadIndex; + } else if (Index == LRI->LoadIndex) { /* Has been removed */ - RI->LoadIndex = -1; - RI->LoadEntry = 0; + LRI->LoadIndex = -1; + LRI->LoadEntry = 0; } - if (Index < RI->XferIndex) { - --RI->XferIndex; - } else if (Index == RI->XferIndex) { + if (Index < LRI->LoadYIndex) { + --LRI->LoadIndex; + } else if (Index == LRI->LoadYIndex) { /* Has been removed */ - RI->XferIndex = -1; - RI->XferEntry = 0; + LRI->LoadYIndex = -1; + LRI->LoadYEntry = 0; + } + if (Index < LRI->ChgIndex) { + --LRI->ChgIndex; + } else if (Index == LRI->ChgIndex) { + /* Has been removed */ + LRI->ChgIndex = -1; + LRI->ChgEntry = 0; } } else { /* Insertion */ - if (Index <= RI->LoadIndex) { - ++RI->LoadIndex; + if (Index <= LRI->LoadIndex) { + ++LRI->LoadIndex; } - if (Index <= RI->XferIndex) { - ++RI->XferIndex; + if (Index <= LRI->LoadYIndex) { + ++LRI->LoadYIndex; + } + if (Index <= LRI->ChgIndex) { + ++LRI->ChgIndex; } } } @@ -191,11 +207,11 @@ void AdjustLoadInfo (LoadInfo* LI, int Index, int Change) RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) -/* Get RegInfo of the last load insn entry */ +/* Get RegInfo of the last insn entry that changed the reg */ { CodeEntry* E; - if (Reg->LoadIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->LoadIndex)) != 0) { + if (Reg->ChgIndex >= 0 && (E = CS_GetEntry (D->Code, Reg->ChgIndex)) != 0) { return E->RI; } @@ -204,82 +220,265 @@ RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) -static int Affected (LoadRegInfo* RI, const CodeEntry* E) -/* Check if the load src may be modified between the pushax and op */ +static int Affected (LoadRegInfo* LRI, const CodeEntry* E) +/* Check if the result of the same loading code as in LRI may be changed by E */ { - fncls_t fncls; - unsigned int Use; - unsigned int Chg; - unsigned int UseToCheck = 0; + fncls_t fncls; + unsigned int Use; + unsigned int Chg; + unsigned int UseToCheck = 0; + StrBuf Src, YSrc, New; + int SrcOff = 0, YSrcOff = 0, NewOff = 0; + const ZPInfo* ZI = 0; - if ((RI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { - if (E->AM == AM65_IMM || E->AM == AM65_ACC || E->AM == AM65_IMP || E->AM == AM65_BRA) { - return 0; + SB_Init (&Src); + SB_Init (&YSrc); + SB_Init (&New); + + if ((LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) { + return (LRI->Flags & LI_CHECK_Y) != 0 && (E->Chg & REG_Y) != 0; } - CHECK ((RI->Flags & LI_CHECK_ARG) == 0 || RI->LoadEntry != 0); - CHECK ((RI->Flags & LI_CHECK_Y) == 0 || RI->LoadYEntry != 0); + CHECK ((LRI->Flags & LI_CHECK_ARG) == 0 || LRI->LoadIndex < 0 || LRI->LoadEntry != 0); + CHECK ((LRI->Flags & LI_CHECK_Y) == 0 || LRI->LoadYIndex < 0 || LRI->LoadYEntry != 0); - if ((RI->Flags & LI_CHECK_ARG) != 0) { - UseToCheck |= RI->LoadEntry->Use; + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + if (LRI->LoadEntry != 0) { + /* We ignore processor flags for loading args. + ** Further more, Reg A can't be used as the index. + */ + UseToCheck |= LRI->LoadEntry->Use & ~REG_A & REG_ALL; + SB_InitFromString (&Src, xstrdup (LRI->LoadEntry->Arg)); + if (!ParseOpcArgStr (LRI->LoadEntry->Arg, &Src, &SrcOff)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + ZI = GetZPInfo (SB_GetConstBuf (&Src)); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + } + } else { + /* We don't know what regs could have been used for the src. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + } } - if ((RI->Flags & LI_CHECK_Y) != 0) { - UseToCheck |= RI->LoadYEntry->Use; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + if (LRI->LoadYEntry != 0) { + UseToCheck |= LRI->LoadYEntry->Use; + SB_InitFromString (&YSrc, xstrdup (LRI->LoadYEntry->Arg)); + if (!ParseOpcArgStr (LRI->LoadYEntry->Arg, &YSrc, &YSrcOff)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + ZI = GetZPInfo (SB_GetConstBuf (&YSrc)); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + } + } else { + /* We don't know what regs could have been used by Y. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + } } if (E->OPC == OP65_JSR) { /* Try to know about the function */ - fncls = GetFuncInfo (E->Arg, &Use, &Chg); + fncls = GetFuncInfo (E->Arg, &Use, &Chg); if ((UseToCheck & Chg & REG_ALL) == 0 && fncls == FNCLS_BUILTIN) { /* Builtin functions are known to be harmless */ - return 0; + goto L_NotAffected; } /* Otherwise play it safe */ - return 1; - } else if (E->OPC == OP65_DEC || E->OPC == OP65_INC || - E->OPC == OP65_ASL || E->OPC == OP65_LSR || - E->OPC == OP65_ROL || E->OPC == OP65_ROR || - E->OPC == OP65_TRB || E->OPC == OP65_TSB || - E->OPC == OP65_STA || E->OPC == OP65_STX || E->OPC == OP65_STY) { - if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { - if ((RI->Flags & LI_CHECK_ARG) != 0 && - strcmp (RI->LoadEntry->Arg, E->Arg) == 0) { - return 1; + goto L_Affected; + + } else { + if (E->OPC == OP65_DEC || E->OPC == OP65_INC || + E->OPC == OP65_ASL || E->OPC == OP65_LSR || + E->OPC == OP65_ROL || E->OPC == OP65_ROR || + E->OPC == OP65_TRB || E->OPC == OP65_TSB || + E->OPC == OP65_STA || E->OPC == OP65_STX || + E->OPC == OP65_STY || E->OPC == OP65_STZ) { + + SB_InitFromString (&New, xstrdup (E->Arg)); + if (!ParseOpcArgStr (E->Arg, &New, &NewOff)) { + /* Bail out and play it safe*/ + goto L_Affected; } - if ((RI->Flags & LI_CHECK_Y) != 0 && - strcmp (RI->LoadYEntry->Arg, E->Arg) == 0) { - return 1; + + /* These opc may operate on memory locations */ + if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { + /* If we don't know what memory locations could have been used for the src, + ** we just assume all. + */ + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + if (LRI->LoadEntry == 0 || + (LRI->LoadEntry->AM != AM65_ABS && + LRI->LoadEntry->AM != AM65_ZP && + (LRI->LoadEntry->AM != AM65_ZP_INDY || + SB_CompareStr (&Src, "sp") != 0)) || + (SB_Compare (&Src, &New) == 0 && + SrcOff == NewOff)) { + goto L_Affected; + } + } + + /* If we don't know what memory location could have been used by Y, + ** we just assume all. + */ + if ((LRI->Flags & LI_CHECK_Y) != 0) { + if (LRI->LoadYEntry == 0 || + (LRI->LoadYEntry->AM != AM65_ABS && + LRI->LoadYEntry->AM != AM65_ZP) || + (SB_Compare (&YSrc, &New) == 0 && + YSrcOff == NewOff)) { + goto L_Affected; + } + } + + /* Not affected */ + goto L_NotAffected; + + } else if (E->AM == AM65_ZP_INDY && SB_CompareStr (&New, "sp") == 0) { + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + if (LRI->LoadEntry == 0 || + (LRI->LoadEntry->AM != AM65_ABS && + LRI->LoadEntry->AM != AM65_ZP && + (LRI->LoadEntry->AM != AM65_ZP_INDY || + SB_Compare (&Src, &New) == 0) && + SrcOff == NewOff)) { + goto L_Affected; + } + } + + /* Not affected */ + goto L_NotAffected; } - return 0; + /* We could've check further for more cases where the load target isn't modified, + ** But for now let's save the trouble and just play it safe. */ + goto L_Affected; } - /* We could've check further for more cases where the load target isn't modified, - ** But for now let's save the trouble and just play it safe. */ - return 1; } } + +L_NotAffected: + SB_Done (&Src); + SB_Done (&YSrc); + SB_Done (&New); return 0; + +L_Affected: + SB_Done (&Src); + SB_Done (&YSrc); + SB_Done (&New); + return 1; } -static void HonourUseAndChg (LoadRegInfo* RI, unsigned Reg, const CodeEntry* E, int I) +static void HonourUseAndChg (LoadRegInfo* LRI, unsigned Reg, const CodeEntry* E, int I) /* Honour use and change flags for an instruction */ { if ((E->Chg & Reg) != 0) { - /* Remember this as an indirect load */ - ClearLoadRegInfo (RI); - RI->LoadIndex = I; - RI->XferIndex = -1; - RI->Flags = 0; - } else if (Affected (RI, E)) { - RI->Flags |= LI_SRC_CHG; + /* This changes the content of the reg */ + ClearLoadRegInfo (LRI); + LRI->ChgIndex = I; + LRI->Flags = 0; + } else if (Affected (LRI, E)) { + LRI->Flags |= LI_SRC_CHG; } } -unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) +void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E) +/* Set the load src flags and remember to check for load src change if necessary */ +{ + if (E->AM == AM65_IMM) { + /* These insns are all ok and replaceable */ + LRI->Flags |= LI_DIRECT; + } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG; + } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + /* These insns are replaceable only if they are not modified later */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } else if ((E->AM == AM65_ZP_INDY) && + strcmp (E->Arg, "sp") == 0) { + /* A load from the stack with known offset is also ok, but in this + ** case we must reload the index register later. Please note that + ** a load indirect via other zero page locations is not ok, since + ** these locations may change between the push and the actual + ** operation. + */ + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + } + + /* If the load offset has a known value, we can just remember and reload + ** it into the index register later. + */ + if ((LRI->Flags & LI_CHECK_Y) != 0) { + if (RegValIsKnown (E->RI->In.RegY)) { + LRI->Offs = (unsigned char)E->RI->In.RegY; + LRI->Flags &= ~LI_CHECK_Y; + LRI->Flags |= LI_RELOAD_Y; + } + } + + /* Watch for any change of the load target */ + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + LRI->LoadIndex = CS_GetEntryIndex (S, E); + LRI->LoadEntry = E; + } + + /* We need to check if the src of Y is changed */ + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); + } else { + LRI->LoadYEntry = 0; + } +} + + + +void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E) +/* Check and flag operand src that may be affected */ +{ + if (Affected (&LLI->A, E)) { + LLI->A.Flags |= LI_SRC_CHG; + } + if (Affected (&LLI->X, E)) { + LLI->X.Flags |= LI_SRC_CHG; + } + if (Affected (&LLI->Y, E)) { + LLI->Y.Flags |= LI_SRC_CHG; + } +} + + + +void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used) +/* Check and flag operand load that may be unremovable */ +{ + /* Disallow removing the loads if the registers are used */ + if ((Used & REG_A) != 0) { + LI->A.Flags |= LI_DONT_REMOVE; + } + if ((Used & REG_X) != 0) { + LI->X.Flags |= LI_DONT_REMOVE; + } + if ((Used & REG_Y) != 0) { + LI->Y.Flags |= LI_DONT_REMOVE; + } +} + + + +unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I) /* Track loads for a code entry. ** Return used registers. */ @@ -297,33 +496,34 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) */ if (E->Info & OF_LOAD) { - LoadRegInfo* RI = 0; + LoadRegInfo* LRI = 0; /* Determine, which register was loaded */ if (E->Chg & REG_A) { - RI = &LI->A; + LRI = &LI->A; } else if (E->Chg & REG_X) { - RI = &LI->X; + LRI = &LI->X; } else if (E->Chg & REG_Y) { - RI = &LI->Y; + LRI = &LI->Y; } - CHECK (RI != 0); + CHECK (LRI != 0); /* Remember the load */ - RI->LoadIndex = I; - RI->XferIndex = -1; + LRI->LoadIndex = I; + LRI->ChgIndex = I; + LRI->LoadYIndex = -1; /* Set load flags */ - RI->Flags = LI_LOAD_INSN; + LRI->Flags = LI_LOAD_INSN; if (E->AM == AM65_IMM) { /* These insns are all ok and replaceable */ - RI->Flags |= LI_DIRECT; + LRI->Flags |= LI_DIRECT; } else if (E->AM == AM65_ZP || E->AM == AM65_ABS) { /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_CHECK_ARG; + LRI->Flags |= LI_CHECK_ARG; } else if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { /* These insns are replaceable only if they are not modified later */ - RI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y; } else if (E->AM == AM65_ZP_INDY && strcmp (E->Arg, "sp") == 0) { /* A load from the stack with known offset is also ok, but in this @@ -332,11 +532,11 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) ** these locations may change between the push and the actual ** operation. */ - RI->Flags |= LI_DIRECT | LI_CHECK_Y | LI_SP; + LRI->Flags |= LI_CHECK_ARG | LI_CHECK_Y | LI_SP; /* Reg Y can be regarded as unused if this load is removed */ Used &= ~REG_Y; - if (RI == &LI->A) { + if (LRI == &LI->A) { LI->Y.Flags |= LI_USED_BY_A; } else { LI->Y.Flags |= LI_USED_BY_X; @@ -346,21 +546,26 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) /* If the load offset has a known value, we can just remember and reload ** it into the index register later. */ - if ((RI->Flags & LI_CHECK_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) != 0) { if (RegValIsKnown (E->RI->In.RegY)) { - RI->Offs = (unsigned char)E->RI->In.RegY; - RI->Flags &= ~LI_CHECK_Y; - RI->Flags |= LI_RELOAD_Y; + LRI->Offs = (unsigned char)E->RI->In.RegY; + LRI->Flags &= ~LI_CHECK_Y; + LRI->Flags |= LI_RELOAD_Y; } else { /* We need to check if the src of Y is changed */ - RI->LoadYIndex = LI->Y.LoadIndex; - RI->LoadYEntry = CS_GetEntry (S, RI->LoadYIndex); + LRI->LoadYIndex = LI->Y.LoadIndex; } } /* Watch for any change of the load target */ - if ((RI->Flags & LI_CHECK_ARG) != 0) { - RI->LoadEntry = CS_GetEntry (S, I); + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + LRI->LoadEntry = CS_GetEntry (S, I); + } + + if (LRI->LoadYIndex >= 0) { + LRI->LoadYEntry = CS_GetEntry (S, LRI->LoadYIndex); + } else { + LRI->LoadYEntry = 0; } } else if (E->Info & OF_XFR) { @@ -376,9 +581,9 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) Src->Flags |= LI_USED_BY_X; break; case OP65_TAY: - Src = &LI->A; + Src = &LI->A; Tgt = &LI->Y; - Used &= ~REG_A; + Used &= ~REG_A; Src->Flags |= LI_USED_BY_Y; break; case OP65_TXA: @@ -406,7 +611,7 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) Tgt->LoadEntry = Src->LoadEntry; Tgt->LoadYIndex = Src->LoadYIndex; Tgt->LoadYEntry = Src->LoadYEntry; - Tgt->XferIndex = I; + Tgt->ChgIndex = I; Tgt->Offs = Src->Offs; Tgt->Flags = Src->Flags; @@ -414,12 +619,12 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) /* Both registers set, Y changed */ LI->A.LoadIndex = I; - LI->A.XferIndex = -1; + LI->A.ChgIndex = I; LI->A.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); LI->A.Offs = (unsigned char) E->RI->In.RegY - 1; LI->X.LoadIndex = I; - LI->X.XferIndex = -1; + LI->X.ChgIndex = I; LI->X.Flags = (LI_LOAD_INSN | LI_DIRECT | LI_RELOAD_Y | LI_SP); LI->X.Offs = (unsigned char) E->RI->In.RegY; @@ -431,19 +636,6 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) HonourUseAndChg (&LI->A, REG_A, E, I); HonourUseAndChg (&LI->X, REG_X, E, I); HonourUseAndChg (&LI->Y, REG_Y, E, I); - - /* The other operand may be affected too */ - if (LLI != 0) { - if (Affected (&LLI->A, E)) { - LLI->A.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->X, E)) { - LLI->X.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->Y, E)) { - LLI->Y.Flags |= LI_SRC_CHG; - } - } } return Used; @@ -451,25 +643,36 @@ unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I) -void SetDontRemoveEntryFlag (LoadRegInfo* RI) +void SetDontRemoveEntryFlag (LoadRegInfo* LRI) /* Flag the entry as non-removable according to register flags */ { - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags |= CEF_DONT_REMOVE; + if (LRI->Flags & LI_DONT_REMOVE) { + if (LRI->LoadEntry != 0) { + LRI->LoadEntry->Flags |= CEF_DONT_REMOVE; + + /* If the load requires Y, then Y shouldn't be removed either */ + if (LRI->LoadYEntry != 0) { + LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE; + } } } } -void ResetDontRemoveEntryFlag (LoadRegInfo* RI) +void ResetDontRemoveEntryFlag (LoadRegInfo* LRI) /* Unflag the entry as non-removable according to register flags */ { - if (RI->Flags & LI_DONT_REMOVE) { - if (RI->LoadEntry != 0) { - RI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; - } + if (LRI->LoadEntry != 0) { + LRI->LoadEntry->Flags &= ~CEF_DONT_REMOVE; + } + + if (LRI->LoadYEntry != 0) { + LRI->LoadYEntry->Flags &= ~CEF_DONT_REMOVE; + } + + if (LRI->ChgEntry != 0) { + LRI->ChgEntry->Flags &= ~CEF_DONT_REMOVE; } } @@ -480,8 +683,13 @@ void SetDontRemoveEntryFlags (StackOpData* D) { SetDontRemoveEntryFlag (&D->Lhs.A); SetDontRemoveEntryFlag (&D->Lhs.X); + SetDontRemoveEntryFlag (&D->Lhs.Y); SetDontRemoveEntryFlag (&D->Rhs.A); SetDontRemoveEntryFlag (&D->Rhs.X); + SetDontRemoveEntryFlag (&D->Rhs.Y); + SetDontRemoveEntryFlag (&D->Rv.A); + SetDontRemoveEntryFlag (&D->Rv.X); + SetDontRemoveEntryFlag (&D->Rv.Y); } @@ -491,8 +699,13 @@ void ResetDontRemoveEntryFlags (StackOpData* D) { ResetDontRemoveEntryFlag (&D->Lhs.A); ResetDontRemoveEntryFlag (&D->Lhs.X); + ResetDontRemoveEntryFlag (&D->Lhs.Y); ResetDontRemoveEntryFlag (&D->Rhs.A); ResetDontRemoveEntryFlag (&D->Rhs.X); + ResetDontRemoveEntryFlag (&D->Rhs.Y); + ResetDontRemoveEntryFlag (&D->Rv.A); + ResetDontRemoveEntryFlag (&D->Rv.X); + ResetDontRemoveEntryFlag (&D->Rv.Y); } @@ -508,6 +721,7 @@ void ResetStackOpData (StackOpData* Data) ClearLoadInfo (&Data->Lhs); ClearLoadInfo (&Data->Rhs); + ClearLoadInfo (&Data->Rv); Data->PushIndex = -1; Data->OpIndex = -1; @@ -881,28 +1095,50 @@ void AddOpHigh (StackOpData* D, opc_t OPC, LoadInfo* LI, int KeepResult) void RemoveRegLoads (StackOpData* D, LoadInfo* LI) /* Remove register load insns */ { - /* Both registers may be loaded with one insn, but DelEntry will in this - ** case clear the other one. - */ if ((LI->A.Flags & LI_REMOVE) == LI_REMOVE) { if (LI->A.LoadIndex >= 0 && (LI->A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { DelEntry (D, LI->A.LoadIndex); + LI->A.LoadEntry = 0; } - if (LI->A.XferIndex >= 0 && - (LI->A.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->A.XferIndex); + if (LI->A.LoadYIndex >= 0 && + (LI->A.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.LoadYIndex); + } + if (LI->A.ChgIndex >= 0 && + (LI->A.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->A.ChgIndex); } } + + if (LI->A.LoadEntry != 0 && + (LI->A.Flags & LI_RELOAD_Y) != 0 && + LI->A.LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LI->A.LoadYEntry->Flags |= CEF_DONT_REMOVE; + } + if ((LI->X.Flags & LI_REMOVE) == LI_REMOVE) { if (LI->X.LoadIndex >= 0 && (LI->X.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { DelEntry (D, LI->X.LoadIndex); + LI->X.LoadEntry = 0; } - if (LI->X.XferIndex >= 0 && - (LI->X.XferEntry->Flags & CEF_DONT_REMOVE) == 0) { - DelEntry (D, LI->X.XferIndex); + if (LI->X.LoadYIndex >= 0 && + (LI->X.LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.LoadYIndex); } + if (LI->X.ChgIndex >= 0 && + (LI->X.ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntry (D, LI->X.ChgIndex); + } + } + + if (LI->X.LoadEntry != 0 && + (LI->X.Flags & LI_RELOAD_Y) != 0 && + LI->X.LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LI->X.LoadYEntry->Flags |= CEF_DONT_REMOVE; } } @@ -935,7 +1171,7 @@ int HarmlessCall (const char* Name) ** the pushax/op sequence when encountered. */ { - const char* const Tab[] = { + static const char* const Tab[] = { "aslax1", "aslax2", "aslax3", @@ -967,6 +1203,8 @@ int HarmlessCall (const char* Name) "incax7", "incax8", "incaxy", + "ldaidx", + "ldauidx", "ldaxidx", "ldaxysp", "negax", @@ -991,3 +1229,1821 @@ int HarmlessCall (const char* Name) } + +/*****************************************************************************/ +/* Load tracking code */ +/*****************************************************************************/ + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +const char* GetZPName (unsigned ZPLoc) +/* Get the name strings of certain known ZP Regs */ +{ + if ((ZPLoc & REG_TMP1) != 0) { + return "tmp1"; + } + if ((ZPLoc & REG_PTR1_LO) != 0) { + return "ptr1"; + } + if ((ZPLoc & REG_PTR1_HI) != 0) { + return "ptr1+1"; + } + if ((ZPLoc & REG_PTR2_LO) != 0) { + return "ptr2"; + } + if ((ZPLoc & REG_PTR2_HI) != 0) { + return "ptr2+1"; + } + if ((ZPLoc & REG_SREG_LO) != 0) { + return "sreg"; + } + if ((ZPLoc & REG_SREG_HI) != 0) { + return "sreg+1"; + } + if ((ZPLoc & REG_SAVE_LO) != 0) { + return "save"; + } + if ((ZPLoc & REG_SAVE_HI) != 0) { + return "save+1"; + } + if ((ZPLoc & REG_SP_LO) != 0) { + return "sp"; + } + if ((ZPLoc & REG_SP_HI) != 0) { + return "sp+1"; + } + + return 0; +} + +unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type) +/* Find a ZP loc for storing the backup and fill in the info. +** The allowed types are specified with the Type parameter. +** For convenience, all types are aloowed if none is specified. +** Return the type of the found loc. +*/ +{ + unsigned SizeType = Type & BU_SIZE_MASK; + Type &= BU_TYPE_MASK; + if (Type == 0) { + Type = BU_TYPE_MASK; + } + + if (SizeType == BU_B8 && (Type & BU_REG) != 0 && (B->ZPUsage & REG_Y) == 0) { + /* Use the Y Reg only */ + B->Type = BU_REG | SizeType; + B->Where = REG_Y; + B->ZPUsage |= REG_Y; + return B->Type; + } + + if (SizeType == BU_B8 && (Type & BU_ZP) != 0) { + /* For now we only check for tmp1 and sreg */ + if ((B->ZPUsage & REG_TMP1) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_TMP1; + B->ZPUsage |= REG_TMP1; + return B->Type; + } + if ((B->ZPUsage & REG_SREG_LO) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_SREG_LO; + B->ZPUsage |= REG_SREG_LO; + return B->Type; + } + if ((B->ZPUsage & REG_SREG_HI) == 0) { + B->Type = BU_ZP | BU_B8; + B->Where = REG_SREG_HI; + B->ZPUsage |= REG_SREG_HI; + return B->Type; + } + } + + if (SizeType == BU_B16 && (Type & BU_ZP) != 0) { + /* For now we only check for ptr1, sreg and ptr2 */ + if ((B->ZPUsage & REG_PTR1) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_PTR1; + B->ZPUsage |= REG_PTR1; + return B->Type; + } + if ((B->ZPUsage & REG_SREG) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_SREG; + B->ZPUsage |= REG_SREG; + return B->Type; + } + if ((B->ZPUsage & REG_PTR2) == 0) { + B->Type = BU_ZP | BU_B16; + B->Where = REG_PTR2; + B->ZPUsage |= REG_PTR2; + return B->Type; + } + } + + if (SizeType == BU_B24 && (Type & BU_ZP) != 0) { + /* For now we only check for certain combinations of + ** tmp1 + (ptr1, sreg or ptr2). + */ + if ((B->ZPUsage & (REG_TMP1 | REG_PTR1)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_PTR1; + B->ZPUsage |= REG_TMP1 | REG_PTR1; + return B->Type; + } + if ((B->ZPUsage & (REG_TMP1 | REG_SREG)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_SREG; + B->ZPUsage |= REG_TMP1 | REG_SREG; + return B->Type; + } + if ((B->ZPUsage & (REG_TMP1 | REG_PTR2)) == 0) { + B->Type = BU_ZP | BU_B24; + B->Where = REG_TMP1 | REG_PTR2; + B->ZPUsage |= REG_TMP1 | REG_PTR2; + return B->Type; + } + } + + if (SizeType < BU_B32 && (Type & BU_SP6502) != 0) { + /* Even for BU_B24, we just push/pop all 3 of AXY */ + B->Type = BU_SP6502 | BU_B16; + B->Where = 0; + return B->Type; + } + + if (SizeType != BU_B24 && SizeType <= BU_B32 && (Type & BU_SP) != 0) { + /* We may also use pusha/popa, pushax/popax and pusheax/popeax */ + B->Type = BU_SP | SizeType; + B->Where = 0; + return B->Type; + } + + /* No available */ + return BU_UNKNOWN; +} + + + +void AdjustEntryIndices (Collection* Indices, int Index, int Change) +/* Adjust a load register info struct after deleting or inserting successive +** entries with a given index. +*/ +{ + int I; + int* IndexPtr; + + if (Change > 0) { + /* Insertion */ + for (I = 0; I < (int)CollCount (Indices); ++I) { + IndexPtr = CollAtUnchecked (Indices, I); + if (Index <= *IndexPtr) { + *IndexPtr += Change; + } + } + } else if (Change < 0) { + /* Deletion */ + for (I = 0; I < (int)CollCount (Indices); ++I) { + IndexPtr = CollAtUnchecked (Indices, I); + if (Index <= *IndexPtr + Change) { + *IndexPtr += Change; + } else if (Index <= *IndexPtr) { + /* Has been removed */ + *IndexPtr = -1; + //CollDelete (Indices, I); + --I; + } + } + } +} + + + +void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices) +/* Delete an entry and adjust Indices if necessary */ +{ + CS_DelEntry (S, Idx); + AdjustEntryIndices (Indices, Idx, -1); +} + + + +void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices) +/* Delete entries and adjust Indices if necessary */ +{ + CS_DelEntries (S, Idx, Count); + AdjustEntryIndices (Indices, Idx, -Count); +} + + + +void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices) +/* Remove flagged register load insns */ +{ + if ((LRI->Flags & LI_REMOVE) == LI_REMOVE) { + if (LRI->LoadIndex >= 0 && + (LRI->LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->LoadIndex, Indices); + LRI->LoadEntry = 0; + } + if (LRI->LoadYIndex >= 0 && + (LRI->LoadYEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->LoadYIndex, Indices); + } + if (LRI->ChgIndex >= 0 && + (LRI->ChgEntry->Flags & CEF_DONT_REMOVE) == 0) { + DelEntryIdx (S, LRI->ChgIndex, Indices); + } + } + + if (LRI->LoadEntry != 0 && + (LRI->Flags & LI_RELOAD_Y) != 0 && + LRI->LoadYIndex >= 0) { + /* If an entry is using Y and not removed, then its Y load mustn't be removed */ + LRI->LoadYEntry->Flags |= CEF_DONT_REMOVE; + } +} + + +void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices) +/* Remove flagged load insns */ +{ + RemoveFlaggedRegLoads (S, &LI->A, Indices); + RemoveFlaggedRegLoads (S, &LI->X, Indices); + RemoveFlaggedRegLoads (S, &LI->Y, Indices); +} + + + +static int BackupAAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of A Before or After the specified index Idx depending on the After param */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegA; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_REG: + if ((B->Where & REG_X) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } else if ((B->Where & REG_Y) != 0) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_STA, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of X before or after the specified index Idx depending on the param After */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegX)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegX; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_STX, AM65_ZP, GetZPName(B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + default: + + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of Y before or after the specified index Idx depending on the param After */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegY)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B8; + B->Imm = E->RI->In.RegY; + + } else { + FindAvailableBackupLoc (B, BU_B8); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName(B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + default: + + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + /* Done */ + return 1; +} + + + +static int BackupAXAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of AX Before or After the specified index Idx depending on the After param */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && RegValIsKnown (E->RI->In.RegA) && RegValIsKnown (E->RI->In.RegX)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B16; + B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8); + + } else { + FindAvailableBackupLoc (B, BU_B16); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + SB_AppendStr (&Arg, GetZPName (B->Where)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +static int BackupAXYAt (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices, int After) +/* Backup the content of AXY before or after the specified index Idx depending on the param After. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + /* Cannot insert after the last insn */ + CHECK ((unsigned)Idx < CollCount (&S->Entries)); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + if (E->RI != 0 && + RegValIsKnown (E->RI->In.RegA) && + RegValIsKnown (E->RI->In.RegX) && + RegValIsKnown (E->RI->In.RegY)) { + /* Just memorize the value */ + B->Type = BU_IMM | BU_B24; + B->Imm = E->RI->In.RegA | (E->RI->In.RegX << 8) | (E->RI->In.RegY << 16); + + } else { + FindAvailableBackupLoc (B, BU_B24); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + CHECK ((B->Where & REG_TMP1) != 0); + SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_STX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_STY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI); + CS_InsertEntry(S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_AY) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to do backup */ + return 0; + + case BU_REG: + /* Fallthrough */ + + default: + /* Unable to do backup */ + return 0; + } + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + if (!After) { + CS_MoveLabels (S, E, CS_GetEntry (S, OldIdx)); + } + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of A Before the specified index Idx */ +{ + return BackupAAt (S, B, Idx, Indices, 0); +} + + + +int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of X before the specified index Idx */ +{ + return BackupXAt (S, B, Idx, Indices, 0); +} + + + +int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of Y before the specified index Idx */ +{ + return BackupYAt (S, B, Idx, Indices, 0); +} + + + +int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AX before the specified index Idx */ +{ + return BackupAXAt (S, B, Idx, Indices, 0); +} + + + +int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AXY before the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + return BackupAXYAt (S, B, Idx, Indices, 0); +} + + + +int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of A after the specified index Idx */ +{ + return BackupAAt (S, B, Idx, Indices, 1); +} + + + +int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of X after the specified index Idx */ +{ + return BackupXAt (S, B, Idx, Indices, 1); +} + + + +int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of Y after the specified index Idx */ +{ + return BackupYAt (S, B, Idx, Indices, 1); +} + + + +int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AX after the specified index Idx */ +{ + return BackupAXAt (S, B, Idx, Indices, 1); +} + + + +int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Backup the content of AXY after the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ +{ + return BackupAXYAt (S, B, Idx, Indices, 1); +} + + + +int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of Y before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_X) != 0) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_Y) != 0) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDA, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of X before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_A) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_Y) != 0) { + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDX, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of Y before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_REG: + if ((B->Where & REG_A) != 0) { + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if ((B->Where & REG_X) != 0) { + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + } + break; + + case BU_ZP: + X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Done */ + return 1; +} + + + +int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of AX before the specified index Idx */ +{ + CodeEntry* E; + CodeEntry* X; + StrBuf Arg; + int OldIdx = Idx; + + SB_Init (&Arg); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_REG: + /* Just use the memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_ZP: + SB_AppendStr (&Arg, GetZPName (B->Where)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + if ((B->ZPUsage & REG_A) == 0) { + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + case BU_SP: + if ((B->ZPUsage & REG_Y) == 0) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + } + + /* Unable to restore */ + return 0; + + default: + /* Unable to restore */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + SB_Done (&Arg); + + return 1; +} + + + +int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices) +/* Restore the content of AXY before the specified index Idx. +** This only allows restore from compacted AXY backup for now. +*/ +{ + CodeEntry* E; + CodeEntry* X; + int OldIdx = Idx; + StrBuf Arg; + + SB_Init (&Arg); + + /* Get the entry at Idx */ + E = CS_GetEntry (S, Idx); + + switch (B->Type & BU_TYPE_MASK) { + case BU_IMM: + /* Just use memorized value */ + X = NewCodeEntry (OP65_LDA, AM65_IMM, MakeHexArg (B->Imm & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDX, AM65_IMM, MakeHexArg ((B->Imm >> 8) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg ((B->Imm >> 16) & 0xFF), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_ZP: + CHECK ((B->Where & REG_TMP1) != 0); + SB_AppendStr (&Arg, GetZPName (B->Where & ~REG_TMP1)); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + SB_AppendStr (&Arg, "+1"); + SB_Terminate (&Arg); + X = NewCodeEntry (OP65_LDX, AM65_ZP, SB_GetConstBuf (&Arg), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDY, AM65_ZP, GetZPName (B->Where & REG_TMP1), 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + case BU_SP: + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popa", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "popax", 0, E->LI); + CS_InsertEntry (S, X, Idx++); + break; + + default: + /* Unable to restorep */ + return 0; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + SB_Done (&Arg); + + /* Done */ + return 1; +} + + + +int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices) +/* Backup the content of the opc arg of the entry E after the specified index Idx. +** Reg A/Y will be used to transfer the content from a memory location to another +** regardless of whether it is in use. +*/ +{ + CodeEntry* X; + int OldIdx = Idx; + unsigned ArgSize; + unsigned Use, Chg; + StrBuf SrcArg; + StrBuf DstArg; + + SB_Init (&SrcArg); + SB_Init (&DstArg); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA) { + /* Get size of the arg */ + if ((E->Info & OF_LBRA) != 0 || strcmp (E->Arg, "ldaxysp") == 0) { + ArgSize = BU_B16; + } else { + ArgSize = BU_B8; + } + + if (E->AM == AM65_IMM && CE_HasNumArg (E)) { + /* Just memorize the value */ + B->Type = BU_IMM | ArgSize; + B->Imm = E->Num; + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx); + + /* Done */ + return 1; + + } + + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* We only recognize opc with an arg for now */ + FindAvailableBackupLoc (B, ArgSize); + switch (B->Type & BU_TYPE_MASK) { + case BU_ZP: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + SB_AppendStr (&DstArg, GetZPName (B->Where)); + SB_Terminate (&DstArg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize == BU_B16) { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + SB_AppendStr (&DstArg, "+1"); + SB_Terminate (&DstArg); + X = NewCodeEntry (OP65_STA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_REG: + CHECK (ArgSize == BU_B8 && B->Where == REG_Y); + if (E->AM == AM65_ZP || E->AM == AM65_ABS) { + X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_SP6502: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize == BU_B16) { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + break; + + case BU_SP: + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + if (ArgSize != BU_B16) { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + SB_AppendStr (&SrcArg, E->Arg); + SB_AppendStr (&SrcArg, "+1"); + SB_Terminate (&SrcArg); + if ((B->ZPUsage & REG_X) == 0) { + if (E->AM == AM65_ZP) { + X = NewCodeEntry (OP65_LDX, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, SB_GetConstBuf (&SrcArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pushax", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + } else { + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_LDA, AM65_ZP, SB_GetConstBuf (&DstArg), 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "pusha", 0, E->LI); + CS_InsertEntry (S, X, ++Idx); + } + } + break; + } + + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx + 1, Idx - OldIdx); + + /* Done */ + return 1; + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + ArgSize = BU_B8; + return BackupAAfter (S, B, Idx, Indices); + } else if (Use == REG_AX) { + ArgSize = BU_B16; + return BackupAXAfter (S, B, Idx, Indices); + } else if (Use == REG_AXY) { + /* This is actually a 16-bit word plus a 8-bit byte */ + ArgSize = BU_B24; + return BackupAXYAfter (S, B, Idx, Indices); + } + + /* We don't recognize other usage patterns for now */ + } + } + + SB_Done (&SrcArg); + SB_Done (&DstArg); + + /* Unable to do backup */ + return 0; +} + +static int LoadAAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into A the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + } + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + + /* For other function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_X) { + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_A) { + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_A) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +static int LoadXAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into X the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + + /* ldx does support AM65_ZPY and AM65_ABSY */ + if (E->AM == AM65_ZPY || E->AM == AM65_ABSY) { + X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + } else { + X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_Y) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_X) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +static int LoadYAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices, int After) +/* Reload into Y the same arg according to LoadRegInfo before or after Idx +** depending on the After param. +*/ +{ + CodeEntry* E; + CodeEntry* O; /* Old entry at Idx */ + CodeEntry* X; + int Success = 0; + int OldIdx; + unsigned Use, Chg; + + /* Adjust the insertion point if necessary */ + if (After) { + ++Idx; + } + OldIdx = Idx; + + E = LRI->LoadEntry; + CHECK (E != 0); + + O = CS_GetEntry (S, OldIdx); + + /* We only recognize opc with an arg for now, as well as a special case for ldaxysp */ + if ((E->OPC != OP65_JSR || strcmp (E->Arg, "ldaxysp") == 0) && + E->AM != AM65_BRA && E->AM != AM65_IMP) { + if (E->Size != 1 && E->AM != AM65_IMP) { + + /* FIXME: The load flags only reflect the situation by the time it reaches the range end */ + if ((LRI->Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) != 0) { + if ((LRI->Flags & LI_RELOAD_Y) != 0) { + if ((LRI->Flags & LI_CHECK_Y) == 0) { + X = NewCodeEntry (OP65_LDY, AM65_IMM, MakeHexArg (LRI->Offs), 0, E->LI); + } else { + X = NewCodeEntry (OP65_LDY, LRI->LoadYEntry->AM, LRI->LoadYEntry->Arg, 0, E->LI); + } + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else { + X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } + + Success = 1; + } + } + } else if (E->OPC == OP65_JSR) { + /* For function calls we load their arguments instead */ + GetFuncInfo (E->Arg, &Use, &Chg); + if ((Use & ~REG_AXY) == 0) { + if (Use == REG_A) { + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_X) { + X = NewCodeEntry (OP65_PHA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_TAY, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + X = NewCodeEntry (OP65_PLA, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, Idx++); + } else if (Use == REG_Y) { + /* nothing to do */ + } else { + /* We don't recognize other usage patterns for now */ + return 0; + } + + Success = 1; + } + } + + if (Success) { + /* Adjust all indices at once */ + AdjustEntryIndices (Indices, OldIdx, Idx - OldIdx); + + /* Move labels if it was an insertion before Idx */ + CS_MoveLabels (S, O, CS_GetEntry (S, OldIdx)); + + /* Done */ + return 1; + } + + /* Unable to load */ + return 0; +} + + + +int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into A the same arg according to LoadRegInfo at Idx */ +{ + return LoadAAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into X the same arg according to LoadRegInfo at Idx */ +{ + return LoadXAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into Y the same arg according to LoadRegInfo at Idx */ +{ + return LoadYAt (S, Idx, LRI, Indices, 0); +} + + + +int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into A the same arg according to LoadRegInfo after Idx */ +{ + return LoadAAt (S, Idx, LRI, Indices, 1); +} + + + +int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into X the same arg according to LoadRegInfo after Idx */ +{ + return LoadXAt (S, Idx, LRI, Indices, 1); +} + + + +int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices) +/* Reload into Y the same arg according to LoadRegInfo after Idx */ +{ + return LoadYAt (S, Idx, LRI, Indices, 1); +} + + + +unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last) +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last). +** The code block must be basic without any jump backwards. +*/ +{ + CodeEntry* X; + unsigned ZPAccessed = 0; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + ZPAccessed |= X->Use | X->Chg; + } + + return ZPAccessed; +} + + + +unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg) +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last) in output parameters Use and Chg. +** Return what ZP regs are used before changed in this range. +** The code block must be basic without any jump backwards. +*/ +{ + CodeEntry* X; + unsigned U = 0; + unsigned C = 0; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* Clear the output flags first */ + if (Use != 0) { + *Use = 0; + } + if (Chg != 0) { + *Chg = 0; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if (Use != 0) { + *Use |= X->Use; + } + if (Chg != 0) { + *Chg |= X->Chg; + } + /* Used before changed */ + U |= ~C & X->Use; + C |= X->Chg; + } + + return U; +} + + + +int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E) +/* Find the first possible spot where the loaded arg of E might be changed in +** the range (First, Last). The code block in the range must be basic without +** any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y)) == 0) { + /* Just bail out as if the src would change right away */ + return First + 1; + } + + /* If there's no need to check */ + if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y)) == 0) { + return Last; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if (Affected (&LRI, X)) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ +{ + CodeEntry* X; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Chg & what) != 0) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ +{ + CodeEntry* X; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Use & what) != 0) { + return First; + } + } + + /* Not found */ + return Last; +} + + + +int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. +*/ +{ + CodeEntry* X; + int Found = -1; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Chg & what) != 0) { + Found = First; + } + } + + return Found; +} + + + +int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what) +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. +*/ +{ + CodeEntry* X; + int Found = -1; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((X->Use & what) != 0) { + Found = First; + } + } + + return Found; +} diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h index 49a8ce60f..bb5158f1b 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -52,81 +52,81 @@ /* LoadRegInfo flags set by DirectOp */ typedef enum { - LI_NONE = 0x00, - LI_DIRECT = 0x01, /* Direct op may be used */ - LI_RELOAD_Y = 0x02, /* Reload index register Y */ - LI_REMOVE = 0x04, /* Load may be removed */ - LI_DONT_REMOVE = 0x08, /* Load may not be removed */ - LI_CHECK_ARG = 0x10, /* Load src might be modified later */ - LI_SRC_CHG = 0x20, /* Load src is possibly modified */ - LI_LOAD_INSN = 0x40, /* Has a load insn */ - LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ - LI_USED_BY_A = 0x100, /* Content used by RegA */ - LI_USED_BY_X = 0x200, /* Content used by RegX */ - LI_USED_BY_Y = 0x400, /* Content used by RegY */ - LI_SP = 0x800, /* Content on stack */ + LI_NONE = 0x00, + LI_DIRECT = 0x01, /* Direct op may be used */ + LI_RELOAD_Y = 0x02, /* Reload index register Y */ + LI_REMOVE = 0x04, /* Load may be removed */ + LI_DONT_REMOVE = 0x08, /* Load may not be removed */ + LI_CHECK_ARG = 0x10, /* Load src might be modified later */ + LI_SRC_CHG = 0x20, /* Load src is possibly modified */ + LI_LOAD_INSN = 0x40, /* Has a load insn */ + LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ + LI_USED_BY_A = 0x100, /* Content used by RegA */ + LI_USED_BY_X = 0x200, /* Content used by RegX */ + LI_USED_BY_Y = 0x400, /* Content used by RegY */ + LI_SP = 0x800, /* Content on stack */ } LI_FLAGS; /* Structure that tells us how to load the lhs values */ typedef struct LoadRegInfo LoadRegInfo; struct LoadRegInfo { - LI_FLAGS Flags; /* Tells us how to load */ - int LoadIndex; /* Index of load insn, -1 if invalid */ - CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ - int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ - CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ - int XferIndex; /* Index of transfer insn */ - CodeEntry* XferEntry; /* The actual transfer entry */ - int Offs; /* Stack offset if data is on stack */ + LI_FLAGS Flags; /* Tells us how to load */ + int LoadIndex; /* Index of load insn, -1 if invalid */ + CodeEntry* LoadEntry; /* The actual entry, 0 if invalid */ + int LoadYIndex; /* Index of Y-load insn, -1 if invalid */ + CodeEntry* LoadYEntry; /* The actual Y-load entry, 0 if invalid */ + int ChgIndex; /* Index of last change */ + CodeEntry* ChgEntry; /* The actual change entry */ + int Offs; /* Stack offset if data is on stack */ }; /* Now combined for both registers */ typedef struct LoadInfo LoadInfo; struct LoadInfo { - LoadRegInfo A; /* Info for A register */ - LoadRegInfo X; /* Info for X register */ - LoadRegInfo Y; /* Info for Y register */ + LoadRegInfo A; /* Info for A register */ + LoadRegInfo X; /* Info for X register */ + LoadRegInfo Y; /* Info for Y register */ }; /* Structure forward decl */ typedef struct StackOpData StackOpData; -typedef struct OptFuncDesc OptFuncDesc; /* Structure that holds the needed data */ struct StackOpData { - CodeSeg* Code; /* Pointer to code segment */ - unsigned Flags; /* Flags to remember things */ + CodeSeg* Code; /* Pointer to code segment */ + unsigned Flags; /* Flags to remember things */ /* Pointer to optimizer subfunction description */ - const OptFuncDesc* OptFunc; + const void* OptFunc; /* ZP register usage inside the sequence */ - unsigned ZPUsage; - unsigned ZPChanged; + unsigned ZPUsage; + unsigned ZPChanged; /* Freedom of registers inside the sequence */ - unsigned UsedRegs; /* Registers used */ + unsigned UsedRegs; /* Registers used */ /* Whether the rhs is changed multiple times */ - int RhsMultiChg; + int RhsMultiChg; - /* Register load information for lhs and rhs */ - LoadInfo Lhs; - LoadInfo Rhs; + /* Register load information for lhs, rhs and rv */ + LoadInfo Lhs; + LoadInfo Rhs; + LoadInfo Rv; /* Several indices of insns in the code segment */ - int PushIndex; /* Index of call to pushax in codeseg */ - int OpIndex; /* Index of actual operation */ + int PushIndex; /* Index of call to pushax in codeseg */ + int OpIndex; /* Index of actual operation */ /* Pointers to insns in the code segment */ - CodeEntry* PrevEntry; /* Entry before the call to pushax */ - CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ - CodeEntry* OpEntry; /* Pointer to entry with op */ - CodeEntry* NextEntry; /* Entry after the op */ + CodeEntry* PrevEntry; /* Entry before the call to pushax */ + CodeEntry* PushEntry; /* Pointer to entry with call to pushax */ + CodeEntry* OpEntry; /* Pointer to entry with op */ + CodeEntry* NextEntry; /* Entry after the op */ - const char* ZPLo; /* Lo byte of zero page loc to use */ - const char* ZPHi; /* Hi byte of zero page loc to use */ - unsigned IP; /* Insertion point used by some routines */ + const char* ZPLo; /* Lo byte of zero page loc to use */ + const char* ZPHi; /* Hi byte of zero page loc to use */ + unsigned IP; /* Insertion point used by some routines */ }; @@ -166,7 +166,16 @@ void AdjustLoadInfo (LoadInfo* LI, int Index, int Change); RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg); /* Get RegInfo of the last insn entry that changed the reg */ -unsigned int TrackLoads (LoadInfo* LI, LoadInfo* LLI, CodeSeg* S, int I); +void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E); +/* Set the load src flags and remember to check for load src change if necessary */ + +void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E); +/* Check and flag operand src that may be affected */ + +void SetIfOperandLoadUnremovable (LoadInfo* LI, unsigned Used); +/* Check and flag operand load that may be unremovable */ + +unsigned int TrackLoads (LoadInfo* LI, CodeSeg* S, int I); /* Track loads for a code entry. ** Return used registers. */ @@ -252,6 +261,190 @@ int HarmlessCall (const char* Name); ** the pushax/op sequence when encountered. */ + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +/* Backup location types */ +#define BU_UNKNOWN 0x00000000U /* Unknown */ +#define BU_IMM 0x00000000U /* Immediate */ +#define BU_REG 0x01000000U /* In register */ +#define BU_ZP 0x02000000U /* On ZP */ +#define BU_SP6502 0x04000000U /* On 6502 stack */ +#define BU_SP 0x08000000U /* On CC65 stack */ +#define BU_B8 0x00000000U /* Size of 8-bit */ +#define BU_B16 0x10000000U /* Size of 16-bit */ +#define BU_B24 0x20000000U /* Size of 24-bit */ +#define BU_B32 0x30000000U /* Size of 32-bit */ +#define BU_TYPE_MASK 0x0F000000U /* Type mask */ +#define BU_SIZE_MASK 0xF0000000U /* Size mask */ + +typedef struct { + unsigned Type; /* Backup location type and size */ + unsigned ZPUsage; /* ZP unusable for backup */ + union { + unsigned Where; /* Backup location */ + unsigned Imm; /* Backed-up value */ + unsigned char* Bytes; /* Pointer to backed-up value */ + }; +} BackupInfo; + + + +const char* GetZPName (unsigned ZPLoc); +/* Get the name strings of certain known ZP Regs */ + +unsigned FindAvailableBackupLoc (BackupInfo* B, unsigned Type); +/* Find a ZP loc for storing the backup and fill in the info. +** The allowed types are specified with the Type parameter. +** For convenience, all types are aloowed if none is specified. +** Return the type of the found loc. +*/ + +void AdjustEntryIndices (Collection* Indices, int Index, int Change); +/* Adjust a load register info struct after deleting or inserting successive +** entries with a given index. +*/ + +void DelEntryIdx (CodeSeg* S, int Idx, Collection* Indices); +/* Delete an entry and adjust Indices if necessary */ + +void DelEntriesIdx (CodeSeg* S, int Idx, int Count, Collection* Indices); +/* Delete entries and adjust Indices if necessary */ + +void RemoveFlaggedRegLoads (CodeSeg* S, LoadRegInfo* LRI, Collection* Indices); +/* Remove flagged register load insns */ + +void RemoveFlaggedLoads (CodeSeg* S, LoadInfo* LI, Collection* Indices); +/* Remove flagged load insns */ + +int BackupABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of A before the specified index Idx */ + +int BackupXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of X before the specified index Idx */ + +int BackupYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of Y before the specified index Idx */ + +int BackupAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AX before the specified index Idx */ + +int BackupAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AXY before the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ + +int BackupAAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of A after the specified index Idx */ + +int BackupXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of X after the specified index Idx */ + +int BackupYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of Y after the specified index Idx */ + +int BackupAXAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AX after the specified index Idx */ + +int BackupAXYAfter (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Backup the content of AXY after the specified index Idx. +** This doesn't allow separating the backup of Y from that of AX for now. +*/ + +int RestoreABefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of Y before the specified index Idx */ + +int RestoreXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of X before the specified index Idx */ + +int RestoreYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of Y before the specified index Idx */ + +int RestoreAXBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of AX before the specified index Idx */ + +int RestoreAXYBefore (CodeSeg* S, BackupInfo* B, int Idx, Collection* Indices); +/* Restore the content of AXY before the specified index Idx. +** This only allows restore from compacted AXY backup for now. +*/ + +int BackupArgAfter (CodeSeg* S, BackupInfo* B, int Idx, const CodeEntry* E, Collection* Indices); +/* Backup the content of the opc arg of the entry E after the specified index Idx. +** Reg A/Y will be used to transfer the content from a memory location to another +** regardless of whether it is in use. +*/ + +int LoadABefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into A the same arg according to LoadRegInfo at Idx */ + +int LoadXBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into X the same arg according to LoadRegInfo at Idx */ + +int LoadYBefore (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into Y the same arg according to LoadRegInfo at Idx */ + +int LoadAAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into A the same arg according to LoadRegInfo after Idx */ + +int LoadXAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into X the same arg according to LoadRegInfo after Idx */ + +int LoadYAfter (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Indices); +/* Reload into Y the same arg according to LoadRegInfo after Idx */ + +unsigned GetRegAccessedInOpenRange (CodeSeg* S, int First, int Last); +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last). +** The code block must be basic without any jump backwards. +*/ + +unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, unsigned* Chg); +/* Get what ZPs, registers or processor states are used or changed in the range +** (First, Last) in output parameters Use and Chg. +** Return what ZP regs are used before changed in this range. +** The code block must be basic without any jump backwards. +*/ + +int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E); +/* Find the first possible spot where the loaded arg of E might be changed in +** the range (First, Last). The code block in the range must be basic without +** any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ + +int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ + +int FindRegFirstUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the first possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or Last if not found. +*/ + +int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be changed in the range (First, Last). The code block in the +** range must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. +*/ + +int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what); +/* Find the last possible spot where the queried ZPs, registers and/or processor +** states might be used in the range (First, Last). The code block in the range +** must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. +*/ + /* End of codeoptutil.h */ #endif diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 457f9ff1f..41dfb5526 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -100,10 +100,10 @@ static int SameRegAValue (StackOpData* D) RegInfo* LRI = GetLastChangedRegInfo (D, &D->Lhs.A); RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.A); - /* RHS can have a -1 LoadIndex only if it is carried over from LHS */ + /* RHS can have a -1 ChgIndex only if it is carried over from LHS */ if (RRI == 0 || - (D->Rhs.A.LoadIndex >= 0 && - D->Rhs.A.LoadIndex == D->Lhs.A.LoadIndex) || + (D->Rhs.A.ChgIndex >= 0 && + D->Rhs.A.ChgIndex == D->Lhs.A.ChgIndex) || (LRI != 0 && RegValIsKnown (LRI->Out.RegA) && RegValIsKnown (RRI->Out.RegA) && @@ -125,8 +125,8 @@ static int SameRegXValue (StackOpData* D) RegInfo* RRI = GetLastChangedRegInfo (D, &D->Rhs.X); if (RRI == 0 || - (D->Rhs.X.LoadIndex >= 0 && - D->Rhs.X.LoadIndex == D->Lhs.X.LoadIndex) || + (D->Rhs.X.ChgIndex >= 0 && + D->Rhs.X.ChgIndex == D->Lhs.X.ChgIndex) || (LRI != 0 && RegValIsKnown (LRI->Out.RegX) && RegValIsKnown (RRI->Out.RegX) && @@ -1204,7 +1204,7 @@ static unsigned Opt_a_tosicmp (StackOpData* D) if (!SameRegAValue (D)) { /* Because of SameRegAValue */ - CHECK (D->Rhs.A.LoadIndex >= 0); + CHECK (D->Rhs.A.ChgIndex >= 0); /* Store LHS in ZP and reload it before op */ X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPLo, 0, D->PushEntry->LI); @@ -1217,7 +1217,7 @@ static unsigned Opt_a_tosicmp (StackOpData* D) if ((D->Rhs.A.Flags & LI_DIRECT) == 0) { /* RHS src is not directly comparable */ X = NewCodeEntry (OP65_STA, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); - InsertEntry (D, X, D->Rhs.A.LoadIndex + 1); + InsertEntry (D, X, D->Rhs.A.ChgIndex + 1); /* Cmp with stored RHS */ X = NewCodeEntry (OP65_CMP, AM65_ZP, D->ZPHi, 0, D->OpEntry->LI); @@ -1431,7 +1431,8 @@ static int PreCondOk (StackOpData* D) int Passed = 0; /* Check the flags */ - unsigned UnusedRegs = D->OptFunc->UnusedRegs; + const OptFuncDesc* Desc = D->OptFunc; + unsigned UnusedRegs = Desc->UnusedRegs; if (UnusedRegs != REG_NONE && (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) { /* Cannot optimize */ @@ -1442,15 +1443,15 @@ static int PreCondOk (StackOpData* D) LoVal = D->OpEntry->RI->In.RegA; HiVal = D->OpEntry->RI->In.RegX; /* Check normally first, then interchange A/X and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { - if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 && + if ((Desc->Flags & OP_A_KNOWN) != 0 && RegValIsUnknown (LoVal)) { /* Cannot optimize */ break; } - if ((D->OptFunc->Flags & OP_X_ZERO) != 0 && + if ((Desc->Flags & OP_X_ZERO) != 0 && HiVal != 0) { /* Cannot optimize */ break; @@ -1471,7 +1472,7 @@ static int PreCondOk (StackOpData* D) Lhs = &D->Lhs; Rhs = &D->Rhs; /* Check normally first, then interchange LHS/RHS and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { LhsLo = &Lhs->A; @@ -1482,48 +1483,48 @@ static int PreCondOk (StackOpData* D) ** so we don't need to check twice for now. */ - if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) { + if ((Desc->Flags & OP_LHS_LOAD) != 0) { if ((LhsLo->Flags & LhsHi->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_LHS_LOAD_DIRECT) != 0) { if ((LhsLo->Flags & LhsHi->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) { + if ((Desc->Flags & OP_RHS_LOAD) != 0) { if ((RhsLo->Flags & RhsHi->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_RHS_LOAD_DIRECT) != 0) { if ((RhsLo->Flags & RhsHi->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_LHS_REMOVE) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE) != 0) { /* Check if the load entries cannot be removed */ if ((LhsLo->LoadEntry != 0 && (LhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0) || (LhsHi->LoadEntry != 0 && (LhsHi->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_REMOVE) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE) != 0) { if ((RhsLo->LoadEntry != 0 && (RhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0) || (RhsHi->LoadEntry != 0 && (RhsHi->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } - if (D->RhsMultiChg && (D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + if (D->RhsMultiChg && (Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } @@ -1578,7 +1579,8 @@ static int RegAPreCondOk (StackOpData* D) int Passed = 0; /* Check the flags */ - unsigned UnusedRegs = D->OptFunc->UnusedRegs; + const OptFuncDesc* Desc = D->OptFunc; + unsigned UnusedRegs = Desc->UnusedRegs; if (UnusedRegs != REG_NONE && (GetRegInfo (D->Code, D->OpIndex+1, UnusedRegs) & UnusedRegs) != 0) { /* Cannot optimize */ @@ -1591,19 +1593,19 @@ static int RegAPreCondOk (StackOpData* D) RhsLoVal = D->OpEntry->RI->In.RegA; RhsHiVal = D->OpEntry->RI->In.RegX; /* Check normally first, then interchange A/X and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_AX_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { if (LhsHiVal != RhsHiVal) { /* Cannot optimize */ break; } - if ((D->OptFunc->Flags & OP_A_KNOWN) != 0 && + if ((Desc->Flags & OP_A_KNOWN) != 0 && RegValIsUnknown (LhsLoVal)) { /* Cannot optimize */ break; } - if ((D->OptFunc->Flags & OP_X_ZERO) != 0 && + if ((Desc->Flags & OP_X_ZERO) != 0 && LhsHiVal != 0) { /* Cannot optimize */ break; @@ -1629,7 +1631,7 @@ static int RegAPreCondOk (StackOpData* D) Lhs = &D->Lhs; Rhs = &D->Rhs; /* Check normally first, then interchange LHS/RHS and check again if necessary */ - for (I = (D->OptFunc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { + for (I = (Desc->Flags & OP_LR_INTERCHANGE ? 0 : 1); !Passed && I < 2; ++I) { do { LhsLo = &Lhs->A; @@ -1638,46 +1640,46 @@ static int RegAPreCondOk (StackOpData* D) ** so we don't need to check twice for now. */ - if ((D->OptFunc->Flags & OP_LHS_LOAD) != 0) { + if ((Desc->Flags & OP_LHS_LOAD) != 0) { if ((LhsLo->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_LHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_LHS_LOAD_DIRECT) != 0) { if ((LhsLo->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_LOAD) != 0) { + if ((Desc->Flags & OP_RHS_LOAD) != 0) { if ((RhsLo->Flags & LI_LOAD_INSN) == 0) { /* Cannot optimize */ break; - } else if ((D->OptFunc->Flags & OP_RHS_LOAD_DIRECT) != 0) { + } else if ((Desc->Flags & OP_RHS_LOAD_DIRECT) != 0) { if ((RhsLo->Flags & LI_DIRECT) == 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_LHS_REMOVE) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE) != 0) { /* Check if the load entries cannot be removed */ if ((LhsLo->LoadEntry != 0 && (LhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_LHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } - if ((D->OptFunc->Flags & OP_RHS_REMOVE) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE) != 0) { if ((RhsLo->LoadEntry != 0 && (RhsLo->LoadEntry->Flags & CEF_DONT_REMOVE) != 0)) { - if ((D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + if ((Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } } } - if (D->RhsMultiChg && (D->OptFunc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { + if (D->RhsMultiChg && (Desc->Flags & OP_RHS_REMOVE_DIRECT) != 0) { /* Cannot optimize */ break; } @@ -1731,8 +1733,8 @@ unsigned OptStackOps (CodeSeg* S) int OldEntryCount; /* Old number of entries */ unsigned Used; /* What registers would be used */ unsigned PushedRegs; /* Track if the same regs are used after the push */ - int RhsALoadIndex; /* Track if rhs is changed more than once */ - int RhsXLoadIndex; /* Track if rhs is changed more than once */ + int RhsAChgIndex; /* Track if rhs is changed more than once */ + int RhsXChgIndex; /* Track if rhs is changed more than once */ int IsRegAOptFunc = 0; /* Whether to use the RegA-only optimizations */ enum { @@ -1783,28 +1785,19 @@ unsigned OptStackOps (CodeSeg* S) */ if (CE_HasLabel (E)) { /* Currently we don't track across branches. - ** Remember this as an indirect load. + ** Treat this as a change to all regs. */ ClearLoadInfo (&Data.Lhs); - Data.Lhs.A.LoadIndex = I; - Data.Lhs.X.LoadIndex = I; - Data.Lhs.Y.LoadIndex = I; + Data.Lhs.A.ChgIndex = I; + Data.Lhs.X.ChgIndex = I; + Data.Lhs.Y.ChgIndex = I; } if (CE_IsCallTo (E, "pushax")) { - /* Disallow removing the loads if the registers are used */ - if (Data.UsedRegs & REG_A) { - Data.Lhs.A.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_X) { - Data.Lhs.X.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_Y) { - Data.Lhs.Y.Flags |= LI_DONT_REMOVE; - } + /* Disallow removing Lhs loads if the registers are used */ + SetIfOperandLoadUnremovable (&Data.Lhs, Data.UsedRegs); - /* The LHS regs are also used as the default RHS until changed */ - PushedRegs = REG_AXY; - Data.UsedRegs = REG_AXY; + /* The Lhs regs are also used as the default Rhs until changed */ + PushedRegs = REG_AXY; CopyLoadInfo (&Data.Rhs, &Data.Lhs); Data.PushIndex = I; @@ -1812,7 +1805,7 @@ unsigned OptStackOps (CodeSeg* S) State = FoundPush; } else { /* Track load insns */ - Used = TrackLoads (&Data.Lhs, 0, Data.Code, I); + Used = TrackLoads (&Data.Lhs, S, I); Data.UsedRegs &= ~E->Chg; Data.UsedRegs |= Used; } @@ -1825,15 +1818,14 @@ unsigned OptStackOps (CodeSeg* S) */ if (CE_HasLabel (E)) { /* Currently we don't track across branches. - ** Remember this as an indirect load. + ** Treat this as a change to all regs. */ ClearLoadInfo (&Data.Rhs); - Data.Rhs.A.LoadIndex = I; - Data.Rhs.X.LoadIndex = I; - Data.Rhs.Y.LoadIndex = I; + Data.Rhs.A.ChgIndex = I; + Data.Rhs.X.ChgIndex = I; + Data.Rhs.Y.ChgIndex = I; } if (E->OPC == OP65_JSR) { - /* Subroutine call: Check if this is one of the functions, ** we're going to replace. */ @@ -1846,16 +1838,9 @@ unsigned OptStackOps (CodeSeg* S) IsRegAOptFunc = 0; } if (Data.OptFunc) { - /* Disallow removing the loads if the registers are used */ - if (Data.UsedRegs & REG_A) { - Data.Rhs.A.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_X) { - Data.Rhs.X.Flags |= LI_DONT_REMOVE; - } - if (Data.UsedRegs & REG_Y) { - Data.Rhs.Y.Flags |= LI_DONT_REMOVE; - } + /* Disallow removing Rhs loads if the registers are used */ + SetIfOperandLoadUnremovable (&Data.Rhs, Data.UsedRegs); + /* Remember the op index and go on */ Data.OpIndex = I; Data.OpEntry = E; @@ -1891,11 +1876,15 @@ unsigned OptStackOps (CodeSeg* S) } /* Memorize the old rhs load indices before refreshing them */ - RhsALoadIndex = Data.Rhs.A.LoadIndex; - RhsXLoadIndex = Data.Rhs.X.LoadIndex; + RhsAChgIndex = Data.Rhs.A.ChgIndex; + RhsXChgIndex = Data.Rhs.X.ChgIndex; + + /* Keep tracking Lhs src if necessary */ + SetIfOperandSrcAffected (&Data.Lhs, E); /* Track register usage */ - Used = TrackLoads (&Data.Rhs, &Data.Lhs, Data.Code, I); + Used = TrackLoads (&Data.Rhs, S, I); + Data.ZPUsage |= (E->Use | E->Chg); /* The changes could depend on the use */ Data.UsedRegs &= ~E->Chg; @@ -1905,23 +1894,15 @@ unsigned OptStackOps (CodeSeg* S) /* Check if any parts of Lhs are used again before overwritten */ if (PushedRegs != 0) { if ((PushedRegs & E->Use) != 0) { - if ((PushedRegs & E->Use & REG_A) != 0) { - Data.Lhs.A.Flags |= LI_DONT_REMOVE; - } - if ((PushedRegs & E->Use & REG_X) != 0) { - Data.Lhs.X.Flags |= LI_DONT_REMOVE; - } - if ((PushedRegs & E->Use & REG_Y) != 0) { - Data.Lhs.Y.Flags |= LI_DONT_REMOVE; - } + SetIfOperandLoadUnremovable (&Data.Lhs, PushedRegs & E->Use); } PushedRegs &= ~E->Chg; } /* Check if rhs is changed again after the push */ - if ((RhsALoadIndex != Data.Lhs.A.LoadIndex && - RhsALoadIndex != Data.Rhs.A.LoadIndex) || - (RhsXLoadIndex != Data.Lhs.X.LoadIndex && - RhsXLoadIndex != Data.Rhs.X.LoadIndex)) { + if ((RhsAChgIndex != Data.Lhs.A.ChgIndex && + RhsAChgIndex != Data.Rhs.A.ChgIndex) || + (RhsXChgIndex != Data.Lhs.X.ChgIndex && + RhsXChgIndex != Data.Rhs.X.ChgIndex)) { /* This will disable those sub-opts that require removing ** the rhs as they can't handle such cases correctly. */ @@ -1993,7 +1974,8 @@ unsigned OptStackOps (CodeSeg* S) CS_GenRegInfo (S); /* Call the optimizer function */ - Changes += Data.OptFunc->Func (&Data); + const OptFuncDesc* Desc = Data.OptFunc; + Changes += Desc->Func (&Data); /* Unflag entries that can't be removed */ ResetDontRemoveEntryFlags (&Data); From cc0f8422f23b076b2046f0cc531dbaab9ba1f17f Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:17:52 +0800 Subject: [PATCH 372/806] More fixes and new utils to check if opcode arguments can be used elsewhere. Fixed tracking with LI_RELOAD_Y and LI_DIRECT. Fixed tracking with LI_CHECK_Y and LI_RELOAD_Y. --- src/cc65/codeoptutil.c | 442 +++++++++++++++++++++++++++-------------- src/cc65/codeoptutil.h | 33 ++- 2 files changed, 321 insertions(+), 154 deletions(-) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 46acbaaff..503f33859 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -103,15 +103,15 @@ void FinalizeLoadRegInfo (LoadRegInfo* LRI, CodeSeg* S) } /* Load from src not modified before op can be treated as direct */ - if ((LRI->Flags & LI_SRC_CHG) == 0 && + if ((LRI->Flags & (LI_SRC_CHG | LI_Y_SRC_CHG)) == 0 && (LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { LRI->Flags |= LI_DIRECT; if ((LRI->Flags & LI_CHECK_Y) != 0) { LRI->Flags |= LI_RELOAD_Y; } } - /* We cannot ldy ??? or ldy src,y */ - if ((LRI->Flags & LI_CHECK_Y) != 0 && + /* We cannot ldy src,y or reload unknown Y */ + if ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == (LI_CHECK_Y | LI_RELOAD_Y) && (LRI->LoadYEntry == 0 || (LRI->LoadYEntry->Use & REG_Y) == REG_Y)) { LRI->Flags &= ~LI_DIRECT; @@ -221,161 +221,205 @@ RegInfo* GetLastChangedRegInfo (StackOpData* D, LoadRegInfo* Reg) static int Affected (LoadRegInfo* LRI, const CodeEntry* E) -/* Check if the result of the same loading code as in LRI may be changed by E */ +/* Check if the result of the same loading code as in LRI may be changed by E. +** If any part of the arg is used, it could be unsafe to add such a store before E. +** If any part of the arg is changed, it could be unsafe to add such a load after E. +*/ { fncls_t fncls; unsigned int Use; unsigned int Chg; unsigned int UseToCheck = 0; + unsigned int ChgToCheck = 0; StrBuf Src, YSrc, New; int SrcOff = 0, YSrcOff = 0, NewOff = 0; - const ZPInfo* ZI = 0; + const ZPInfo* ZI = 0; + unsigned Res = 0; + CodeEntry* AE = 0; + CodeEntry* YE = 0; + + if ((LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Nothing to check */ + return 0; + } SB_Init (&Src); SB_Init (&YSrc); SB_Init (&New); - if ((LRI->Flags & (LI_CHECK_ARG | LI_CHECK_Y)) != 0) { - if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) { - return (LRI->Flags & LI_CHECK_Y) != 0 && (E->Chg & REG_Y) != 0; - } - CHECK ((LRI->Flags & LI_CHECK_ARG) == 0 || LRI->LoadIndex < 0 || LRI->LoadEntry != 0); - CHECK ((LRI->Flags & LI_CHECK_Y) == 0 || LRI->LoadYIndex < 0 || LRI->LoadYEntry != 0); + if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) { + goto L_Result; + } + CHECK ((LRI->Flags & LI_CHECK_ARG) == 0 || LRI->LoadIndex < 0 || LRI->LoadEntry != 0); + CHECK ((LRI->Flags & (LI_CHECK_Y | LI_RELOAD_Y)) == 0 || LRI->LoadYIndex < 0 || LRI->LoadYEntry != 0); - if ((LRI->Flags & LI_CHECK_ARG) != 0) { - if (LRI->LoadEntry != 0) { - /* We ignore processor flags for loading args. - ** Further more, Reg A can't be used as the index. - */ - UseToCheck |= LRI->LoadEntry->Use & ~REG_A & REG_ALL; - SB_InitFromString (&Src, xstrdup (LRI->LoadEntry->Arg)); - if (!ParseOpcArgStr (LRI->LoadEntry->Arg, &Src, &SrcOff)) { - /* Bail out and play it safe*/ - goto L_Affected; - } - ZI = GetZPInfo (SB_GetConstBuf (&Src)); - if (ZI != 0) { - UseToCheck |= ZI->ByteUse; - } - } else { - /* We don't know what regs could have been used for the src. - ** So we just assume all. - */ - UseToCheck |= ~REG_A & REG_ALL; + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + AE = LRI->LoadEntry; + if (AE != 0) { + /* We ignore processor flags for loading args. + ** Further more, Reg A can't be used as the index. + */ + UseToCheck |= AE->Use & ~REG_A & REG_ALL; + ChgToCheck |= AE->Chg & ~REG_A & REG_ALL; + + SB_InitFromString (&Src, xstrdup (AE->Arg)); + if (!ParseOpcArgStr (AE->Arg, &Src, &SrcOff)) { + /* Bail out and play it safe*/ + Res |= LI_SRC_USE | LI_SRC_CHG; + goto L_Result; } - } - - if ((LRI->Flags & LI_CHECK_Y) != 0) { - if (LRI->LoadYEntry != 0) { - UseToCheck |= LRI->LoadYEntry->Use; - SB_InitFromString (&YSrc, xstrdup (LRI->LoadYEntry->Arg)); - if (!ParseOpcArgStr (LRI->LoadYEntry->Arg, &YSrc, &YSrcOff)) { - /* Bail out and play it safe*/ - goto L_Affected; - } - ZI = GetZPInfo (SB_GetConstBuf (&YSrc)); - if (ZI != 0) { - UseToCheck |= ZI->ByteUse; - } - } else { - /* We don't know what regs could have been used by Y. - ** So we just assume all. - */ - UseToCheck |= ~REG_A & REG_ALL; + /* We have to manually set up the use/chg flags for builtin functions */ + ZI = GetZPInfo (SB_GetConstBuf (&Src)); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + ChgToCheck |= ZI->ByteUse; } - } - - if (E->OPC == OP65_JSR) { - /* Try to know about the function */ - fncls = GetFuncInfo (E->Arg, &Use, &Chg); - if ((UseToCheck & Chg & REG_ALL) == 0 && - fncls == FNCLS_BUILTIN) { - /* Builtin functions are known to be harmless */ - goto L_NotAffected; - } - /* Otherwise play it safe */ - goto L_Affected; - } else { - if (E->OPC == OP65_DEC || E->OPC == OP65_INC || - E->OPC == OP65_ASL || E->OPC == OP65_LSR || - E->OPC == OP65_ROL || E->OPC == OP65_ROR || - E->OPC == OP65_TRB || E->OPC == OP65_TSB || - E->OPC == OP65_STA || E->OPC == OP65_STX || - E->OPC == OP65_STY || E->OPC == OP65_STZ) { - - SB_InitFromString (&New, xstrdup (E->Arg)); - if (!ParseOpcArgStr (E->Arg, &New, &NewOff)) { - /* Bail out and play it safe*/ - goto L_Affected; - } - - /* These opc may operate on memory locations */ - if ((E->AM == AM65_ABS || E->AM == AM65_ZP)) { - /* If we don't know what memory locations could have been used for the src, - ** we just assume all. - */ - if ((LRI->Flags & LI_CHECK_ARG) != 0) { - if (LRI->LoadEntry == 0 || - (LRI->LoadEntry->AM != AM65_ABS && - LRI->LoadEntry->AM != AM65_ZP && - (LRI->LoadEntry->AM != AM65_ZP_INDY || - SB_CompareStr (&Src, "sp") != 0)) || - (SB_Compare (&Src, &New) == 0 && - SrcOff == NewOff)) { - goto L_Affected; - } - } - - /* If we don't know what memory location could have been used by Y, - ** we just assume all. - */ - if ((LRI->Flags & LI_CHECK_Y) != 0) { - if (LRI->LoadYEntry == 0 || - (LRI->LoadYEntry->AM != AM65_ABS && - LRI->LoadYEntry->AM != AM65_ZP) || - (SB_Compare (&YSrc, &New) == 0 && - YSrcOff == NewOff)) { - goto L_Affected; - } - } - - /* Not affected */ - goto L_NotAffected; - - } else if (E->AM == AM65_ZP_INDY && SB_CompareStr (&New, "sp") == 0) { - if ((LRI->Flags & LI_CHECK_ARG) != 0) { - if (LRI->LoadEntry == 0 || - (LRI->LoadEntry->AM != AM65_ABS && - LRI->LoadEntry->AM != AM65_ZP && - (LRI->LoadEntry->AM != AM65_ZP_INDY || - SB_Compare (&Src, &New) == 0) && - SrcOff == NewOff)) { - goto L_Affected; - } - } - - /* Not affected */ - goto L_NotAffected; - } - /* We could've check further for more cases where the load target isn't modified, - ** But for now let's save the trouble and just play it safe. */ - goto L_Affected; - } + /* We don't know what regs could have been used for the src. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + ChgToCheck |= ~REG_A & REG_ALL; } } -L_NotAffected: - SB_Done (&Src); - SB_Done (&YSrc); - SB_Done (&New); - return 0; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + YE = LRI->LoadYEntry; + if (YE != 0) { + UseToCheck |= YE->Use; + SB_InitFromString (&YSrc, xstrdup (YE->Arg)); + if (!ParseOpcArgStr (YE->Arg, &YSrc, &YSrcOff)) { + /* Bail out and play it safe*/ + Res |= LI_SRC_USE | LI_SRC_CHG; + goto L_Result; + } + /* We have to manually set up the use/chg flags for builtin functions */ + ZI = GetZPInfo (SB_GetConstBuf (&YSrc)); + if (ZI != 0) { + UseToCheck |= ZI->ByteUse; + ChgToCheck |= ZI->ByteUse; + } + } else { + /* We don't know what regs could have been used by Y. + ** So we just assume all. + */ + UseToCheck |= ~REG_A & REG_ALL; + ChgToCheck |= ~REG_A & REG_ALL; + } + } + + if (E->OPC == OP65_JSR) { + /* Try to know about the function */ + fncls = GetFuncInfo (E->Arg, &Use, &Chg); + if (fncls == FNCLS_BUILTIN) { + /* Builtin functions are usually harmless */ + if ((ChgToCheck & Use & REG_ALL) != 0) { + Res |= LI_SRC_USE; + } + if ((UseToCheck & Chg & REG_ALL) != 0) { + Res |= LI_SRC_CHG; + } + goto L_Result; + } + /* Otherwise play it safe */ + Res |= LI_SRC_USE | LI_SRC_CHG; + goto L_Result; + + } else { + if ((E->Info & (OF_READ | OF_WRITE)) != 0) { + + SB_InitFromString (&New, xstrdup (E->Arg)); + if (!ParseOpcArgStr (E->Arg, &New, &NewOff)) { + /* Bail out and play it safe*/ + goto L_Affected; + } + + /* These opc may operate on memory locations. In some cases we can + ** be sure that the src is unaffected as E doesn't overlap with it. + ** However, if we don't know what memory locations could have been + ** used for the src, we just assume all. + */ + if (E->AM == AM65_ABS || + E->AM == AM65_ZP || + (E->AM == AM65_ZP_INDY && SB_CompareStr (&New, "sp") == 0) + ) { + if ((LRI->Flags & LI_CHECK_ARG) != 0) { + if (AE == 0 || + (AE->AM != AM65_ABS && + AE->AM != AM65_ZP && + (AE->AM != AM65_ZP_INDY || + SB_CompareStr (&Src, "sp") != 0)) || + (SrcOff == NewOff && + SB_Compare (&Src, &New) == 0)) { + + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_SRC_USE; + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_SRC_CHG; + } + } + } + + if ((LRI->Flags & LI_CHECK_Y) != 0) { + /* If we don't know what memory location could have been used by Y, + ** we just assume all. */ + if (YE == 0 || + (YSrcOff == NewOff && SB_Compare (&YSrc, &New) == 0)) { + + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_Y_SRC_USE; + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_Y_SRC_CHG; + } + } + } + + /* Otherwise unaffected */ + goto L_Result; + } + /* We could've check further for more cases where the load target isn't + ** modified, but for now let's save the trouble and just play it safe. + */ + goto L_Affected; + } + } L_Affected: + if ((E->Info & OF_READ) != 0) { + /* Used */ + Res |= LI_SRC_USE; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + Res |= LI_Y_SRC_USE; + } + } + if ((E->Info & OF_WRITE) != 0) { + /* Changed */ + Res |= LI_SRC_CHG; + if ((LRI->Flags & LI_CHECK_Y) != 0) { + Res |= LI_Y_SRC_CHG; + } + } + +L_Result: + if ((LRI->Flags & LI_RELOAD_Y) != 0 && + (E->Use & REG_Y) != 0) { + Res |= LI_Y_USE; + } + if ((LRI->Flags & LI_CHECK_Y) != 0 && + (E->Chg & REG_Y) != 0) { + Res |= LI_Y_CHG; + } SB_Done (&Src); SB_Done (&YSrc); SB_Done (&New); - return 1; + + return Res; } @@ -388,15 +432,17 @@ static void HonourUseAndChg (LoadRegInfo* LRI, unsigned Reg, const CodeEntry* E, ClearLoadRegInfo (LRI); LRI->ChgIndex = I; LRI->Flags = 0; - } else if (Affected (LRI, E)) { - LRI->Flags |= LI_SRC_CHG; + } else { + LRI->Flags |= Affected (LRI, E); } } void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E) -/* Set the load src flags and remember to check for load src change if necessary */ +/* Set the load src flags and remember to check for load src change if necessary. +** Note: this doesn't assume reloading Y. +*/ { if (E->AM == AM65_IMM) { /* These insns are all ok and replaceable */ @@ -448,15 +494,9 @@ void PrepairLoadRegInfoForArgCheck (CodeSeg* S, LoadRegInfo* LRI, CodeEntry* E) void SetIfOperandSrcAffected (LoadInfo* LLI, CodeEntry* E) /* Check and flag operand src that may be affected */ { - if (Affected (&LLI->A, E)) { - LLI->A.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->X, E)) { - LLI->X.Flags |= LI_SRC_CHG; - } - if (Affected (&LLI->Y, E)) { - LLI->Y.Flags |= LI_SRC_CHG; - } + LLI->A.Flags |= Affected (&LLI->A, E); + LLI->X.Flags |= Affected (&LLI->X, E); + LLI->Y.Flags |= Affected (&LLI->Y, E); } @@ -2914,15 +2954,64 @@ unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, +int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E) +/* Check if the loading the opc arg gives the same result everywhere between (First, Last). +** The code block in the range must be basic without any jump backwards. +** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + unsigned CheckedFlags = LI_SRC_CHG; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Just bail out as if the src would change right away */ + return 0; + } + + /* If there's no need to check */ + if ((LRI.Flags & (LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + return 1; + } + + /* This always checks Y */ + if ((LRI.Flags & (LI_CHECK_Y | LI_RELOAD_Y)) != 0) { + LRI.Flags |= LI_CHECK_Y; + LRI.Flags &= ~LI_RELOAD_Y; + CheckedFlags |= LI_Y_CHG; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((Affected (&LRI, X) & CheckedFlags) != 0) { + return 0; + } + } + + /* No change found */ + return 1; +} + + + int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E) /* Find the first possible spot where the loaded arg of E might be changed in ** the range (First, Last). The code block in the range must be basic without ** any jump backwards. ** Return the index of the found entry, or Last if not found. +** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set. */ { LoadRegInfo LRI; CodeEntry* X; + unsigned CheckedFlags = LI_SRC_CHG; CHECK (Last <= (int)CollCount (&S->Entries)); @@ -2943,7 +3032,7 @@ int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E while (++First < Last) { X = CS_GetEntry (S, First); - if (Affected (&LRI, X)) { + if ((Affected (&LRI, X) & CheckedFlags) != 0) { return First; } } @@ -2954,6 +3043,65 @@ int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E +int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY) +/* Find the last index where the arg of E might be used or changed in the range (First, Last). +** ReloadY indicates whether Y is supposed to be reloaded. +** The code block in the range must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. +*/ +{ + LoadRegInfo LRI; + CodeEntry* X; + unsigned CheckedFlags = LI_SRC_USE | LI_SRC_CHG; + int Found = -1; + + CHECK (Last <= (int)CollCount (&S->Entries)); + + /* TODO: We'll currently give up finding the src of Y */ + ClearLoadRegInfo (&LRI); + PrepairLoadRegInfoForArgCheck (S, &LRI, E); + + /* Whether Y is to be reloaded */ + if (ReloadY) { + /* Always reload Y */ + if ((LRI.Flags & LI_CHECK_Y) != 0) { + LRI.Flags |= LI_RELOAD_Y; + } + } else if ((LRI.Flags & LI_RELOAD_Y) != 0) { + /* Always check Y */ + LRI.Flags |= LI_CHECK_Y; + LRI.Flags &= ~LI_RELOAD_Y; + } + + /* TODO: We don't currently check for all cases */ + if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) { + /* Just bail out as if the src would change right away */ + return 0; + } + + if ((LRI.Flags & LI_CHECK_Y) != 0) { + CheckedFlags |= LI_Y_SRC_USE | LI_Y_SRC_CHG; + } + + if ((LRI.Flags & LI_RELOAD_Y) != 0) { + CheckedFlags |= LI_Y_USE; + } else if ((LRI.Flags & LI_CHECK_Y) != 0) { + CheckedFlags |= LI_Y_CHG; + } + + while (++First < Last) { + X = CS_GetEntry (S, First); + if ((Affected (&LRI, X) & CheckedFlags) != 0) { + Found = First; + } + } + + /* Result */ + return Found; +} + + + int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what) /* Find the first possible spot where the queried ZPs, registers and/or processor ** states might be changed in the range (First, Last). The code block in the diff --git a/src/cc65/codeoptutil.h b/src/cc65/codeoptutil.h index bb5158f1b..c3596acd4 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -58,13 +58,18 @@ typedef enum { LI_REMOVE = 0x04, /* Load may be removed */ LI_DONT_REMOVE = 0x08, /* Load may not be removed */ LI_CHECK_ARG = 0x10, /* Load src might be modified later */ - LI_SRC_CHG = 0x20, /* Load src is possibly modified */ - LI_LOAD_INSN = 0x40, /* Has a load insn */ - LI_CHECK_Y = 0x80, /* Indexed load src might be modified later */ - LI_USED_BY_A = 0x100, /* Content used by RegA */ - LI_USED_BY_X = 0x200, /* Content used by RegX */ - LI_USED_BY_Y = 0x400, /* Content used by RegY */ - LI_SP = 0x800, /* Content on stack */ + LI_CHECK_Y = 0x20, /* Indexed load src might be modified later */ + LI_SRC_USE = 0x40, /* src of Opc argument is possibly used */ + LI_SRC_CHG = 0x80, /* src of Opc argument is possibly modified */ + LI_Y_SRC_USE = 0x0100, /* src of Opc addressing Y is possibly used */ + LI_Y_SRC_CHG = 0x0200, /* src of Opc addressing Y is possibly modified */ + LI_Y_USE = 0x0400, /* Opc addressing Y is possibly used */ + LI_Y_CHG = 0x0800, /* Opc addressing Y is possibly modified */ + LI_USED_BY_A = 0x1000, /* Content used by RegA */ + LI_USED_BY_X = 0x2000, /* Content used by RegX */ + LI_USED_BY_Y = 0x4000, /* Content used by RegY */ + LI_SP = 0x8000, /* Content on stack */ + LI_LOAD_INSN = 0x010000, /* Is a load insn */ } LI_FLAGS; /* Structure that tells us how to load the lhs values */ @@ -410,11 +415,25 @@ unsigned GetRegUsageInOpenRange (CodeSeg* S, int First, int Last, unsigned* Use, ** The code block must be basic without any jump backwards. */ +int IsArgSameInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E); +/* Check if the loading the opc arg gives the same result everywhere between (First, Last). +** The code block in the range must be basic without any jump backwards. +** Note: this always checks Y if any of the LI_CHECK_Y / LI_RELOAD_Y flags is set. +*/ + int FindArgFirstChangeInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E); /* Find the first possible spot where the loaded arg of E might be changed in ** the range (First, Last). The code block in the range must be basic without ** any jump backwards. ** Return the index of the found entry, or Last if not found. +** Note: changes of Y are always ignored even if the LI_RELOAD_Y flag is not set. +*/ + +int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E, int ReloadY); +/* Find the last index where the arg of E might be used or changed in the range (First, Last). +** ReloadY indicates whether Y is supposed to be reloaded. +** The code block in the range must be basic without any jump backwards. +** Return the index of the found entry, or -1 if not found. */ int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what); From 688342e194367cee9864b5772825063c9decd236 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:17:53 +0800 Subject: [PATCH 373/806] Now every code entry has its argument parsed to tell some info. It fixes the compiling performance regression as well. Built-in ZPs are recognized. --- src/cc65/codeent.c | 330 +++++++++++++++++++++++++++++++---------- src/cc65/codeent.h | 45 +++++- src/cc65/codelab.c | 8 +- src/cc65/codeoptutil.c | 45 +++--- 4 files changed, 319 insertions(+), 109 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index aa3a960df..2ffe4e685 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -33,6 +33,8 @@ +#include +#include #include /* common */ @@ -43,11 +45,13 @@ #include "xsprintf.h" /* cc65 */ +#include "asmlabel.h" #include "codeent.h" #include "codeinfo.h" +#include "codelab.h" #include "error.h" #include "global.h" -#include "codelab.h" +#include "ident.h" #include "opcodes.h" #include "output.h" #include "reginfo.h" @@ -95,42 +99,11 @@ static char* GetArgCopy (const char* Arg) -static int NumArg (const char* Arg, unsigned long* Num) -/* If the given argument is numerical, convert it and return true. Otherwise -** set Num to zero and return false. -*/ +static void FreeParsedArg (char* ArgBase) +/* Free a code entry parsed argument */ { - char* End; - unsigned long Val; - - /* Determine the base */ - int Base = 10; - if (*Arg == '$') { - ++Arg; - Base = 16; - } else if (*Arg == '%') { - ++Arg; - Base = 2; - } - - /* Convert the value. strtol is not exactly what we want here, but it's - ** cheap and may be replaced by something fancier later. - */ - Val = strtoul (Arg, &End, Base); - - /* Check if the conversion was successful */ - if (*End != '\0') { - - /* Could not convert */ - *Num = 0; - return 0; - - } else { - - /* Conversion ok */ - *Num = Val; - return 1; - + if (ArgBase != 0 && ArgBase != EmptyArg) { + xfree (ArgBase); } } @@ -356,7 +329,7 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) -int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) +int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Name, long* Offset) /* Break the opcode argument string into a symbol name/label part plus an offset. ** Both parts are optional, but if there are any characters in the string that ** can't be parsed, it's an failure. @@ -364,10 +337,18 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) ** Return whether parsing succeeds or not. */ { - int NewOff = 0; - const char* OffsetPart = 0; - const char* NameEnd = 0; - int Negative = 0; + unsigned short Flags = 0; + const char* OffsetPart = 0; + const char* NameEnd = 0; + int Negative = 0; + unsigned long NumVal = 0; + long long AccOffset = 0; + char* End; /* Used for checking errors */ + + if (ArgInfo != 0) { + *ArgInfo = 0; + } + *Offset = 0; /* A numeric address is treated as an unnamed address with the numeric part as the offset */ if (IsDigit (Arg[0]) || Arg[0] == '$') { @@ -381,8 +362,9 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) ** symbol. */ if (Arg[0] == '_') { - /* Skip the underscore */ - ++Arg; + Flags |= AIF_EXTERNAL; + } else { + Flags |= AIF_BUILTIN; } /* Rip off the offset if present. */ @@ -390,6 +372,7 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) if (OffsetPart == 0) { OffsetPart = strchr (Arg, '-'); } + if (OffsetPart != 0) { /* Get the real arg name */ NameEnd = strchr (Arg, ' '); @@ -401,21 +384,43 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) } else { /* No offset */ - *Offset = 0; - SB_CopyStr (Name, Arg); SB_Terminate (Name); + } - return 1; + if ((Flags & AIF_EXTERNAL) == 0) { + if (SB_GetLen (Name) > 0) { + Flags |= AIF_HAS_NAME; + + /* See if the name is a local label */ + if (IsLocalLabelName (SB_GetConstBuf (Name))) { + Flags |= AIF_LOCAL; + } + } + + } else { + if (SB_GetLen (Name) <= 0) { + /* Invalid external name */ + Flags &= ~AIF_EXTERNAL; + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + Flags |= AIF_HAS_NAME; } } - *Offset = 0; - /* Get the offset */ while (OffsetPart != 0 && OffsetPart[0] != '\0') { + /* Skip spaces */ + while (OffsetPart[0] == ' ') { + ++OffsetPart; + } + + Negative = 0; if (OffsetPart[0] == '+') { - Negative = 0; ++OffsetPart; } else if (OffsetPart[0] == '-') { Negative = 1; @@ -427,22 +432,50 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) ++OffsetPart; } + /* Determine the base and convert the value. strtol/strtoul is not + ** exactly what we want here, but it's cheap and may be replaced by + ** something fancier later. + */ if (OffsetPart[0] == '$') { - if (sscanf (OffsetPart + 1, "%X", &NewOff) != 1) { - return 0; - } + /* Base 16 hexedemical */ + NumVal = strtoul (OffsetPart+1, &End, 16); + } else if (OffsetPart[0] != '%') { + /* Base 10 decimal */ + NumVal = strtoul (OffsetPart, &End, 10); } else { - if (sscanf (OffsetPart, "%u", &NewOff) != 1) { - return 0; - } + /* Base 2 binary */ + NumVal = strtoul (OffsetPart+1, &End, 2); } + /* Check if the conversion was successful */ + if (*End != '\0' && *End != ' ' && *End != '+' && *End != '-') { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* Check for out of range result */ + if (NumVal == ULONG_MAX && errno == ERANGE) { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* This argument does have an offset */ + Flags |= AIF_HAS_OFFSET; + if (Negative) { - NewOff = -NewOff; + AccOffset -= (long long)NumVal; + } else { + AccOffset += (long long)NumVal; } - *Offset += NewOff; - /* See if there are more */ Arg = OffsetPart; OffsetPart = strchr (Arg, '+'); @@ -451,6 +484,19 @@ int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset) } } + if (AccOffset > LONG_MAX || AccOffset < LONG_MIN) { + /* Could not convert */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + *Offset = (long)AccOffset; + if (ArgInfo != 0) { + *ArgInfo = Flags & ~AIF_FAILURE; + } + return 1; } @@ -471,6 +517,35 @@ const char* MakeHexArg (unsigned Num) +void PreparseArg (CodeEntry* E) +/* Parse the argument string and memorize the result for the code entry */ +{ + StrBuf B; + SB_InitFromString (&B, xmalloc (strlen (E->Arg) + 1)); + + /* Parse the argument string */ + if (ParseOpcArgStr (E->Arg, &E->ArgInfo, &B, &E->ArgOff)) { + E->ArgBase = SB_GetBuf (&B); + + if ((E->ArgInfo & (AIF_HAS_NAME | AIF_HAS_OFFSET)) == AIF_HAS_OFFSET) { + E->Flags |= CEF_NUMARG; + + /* Use the new numerical value */ + E->Num = E->ArgOff; + } + + } else { + /* Parsing fails. Issue an error/warning so that this could be spotted and fixed. */ + E->ArgBase = EmptyArg; + SB_Done (&B); + if (Debug) { + Warning ("Parsing argument \"%s\" failed!", E->Arg); + } + } +} + + + CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeLabel* JumpTo, LineInfo* LI) /* Create a new code entry, initialize and return it */ @@ -482,15 +557,24 @@ CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeEntry* E = xmalloc (sizeof (CodeEntry)); /* Initialize the fields */ - E->OPC = D->OPC; - E->AM = AM; - E->Size = GetInsnSize (E->OPC, E->AM); - E->Arg = GetArgCopy (Arg); - E->Flags = NumArg (E->Arg, &E->Num)? CEF_NUMARG : 0; /* Needs E->Arg */ - E->Info = D->Info; - E->JumpTo = JumpTo; - E->LI = UseLineInfo (LI); - E->RI = 0; + E->OPC = D->OPC; + E->AM = AM; + E->Size = GetInsnSize (E->OPC, E->AM); + E->Arg = GetArgCopy (Arg); + E->Flags = 0; + E->Info = D->Info; + E->ArgInfo = 0; + E->JumpTo = JumpTo; + E->LI = UseLineInfo (LI); + E->RI = 0; + + /* Parse the argument string if it's given */ + if (Arg == 0 || Arg[0] == '\0') { + E->ArgBase = EmptyArg; + } else { + PreparseArg (E); + } + SetUseChgInfo (E, D); InitCollection (&E->Labels); @@ -508,6 +592,9 @@ CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, void FreeCodeEntry (CodeEntry* E) /* Free the given code entry */ { + /* Free the argument base string if we have one */ + FreeParsedArg (E->ArgBase); + /* Free the string argument if we have one */ FreeArg (E->Arg); @@ -572,9 +659,8 @@ void CE_ClearJumpTo (CodeEntry* E) /* Clear the JumpTo entry */ E->JumpTo = 0; - /* Clear the argument and assign the empty one */ - FreeArg (E->Arg); - E->Arg = EmptyArg; + /* Clear the argument */ + CE_SetArg (E, 0); } @@ -593,17 +679,84 @@ void CE_MoveLabel (CodeLabel* L, CodeEntry* E) void CE_SetArg (CodeEntry* E, const char* Arg) -/* Replace the argument by the new one. */ +/* Replace the whole argument by the new one. */ { + /* Free the old parsed argument base */ + FreeParsedArg (E->ArgBase); + /* Free the old argument */ FreeArg (E->Arg); /* Assign the new one */ E->Arg = GetArgCopy (Arg); + /* Parse the new argument string */ + PreparseArg (E); + /* Update the Use and Chg in E */ - const OPCDesc* D = GetOPCDesc (E->OPC); - SetUseChgInfo (E, D); + SetUseChgInfo (E, GetOPCDesc (E->OPC)); +} + + + +void CE_SetArgBaseAndOff (CodeEntry* E, const char* ArgBase, long ArgOff) +/* Replace the new argument base and offset. Argument base is always applied. +** Argument offset is applied if and only if E has the AIF_HAS_OFFSET flag set. +*/ +{ + if (ArgBase != 0 && ArgBase[0] != '\0') { + + /* The argument base is not blank */ + char Buf[IDENTSIZE + 16]; + char* Str = Buf; + size_t Len = strlen (ArgBase) + 16; + if (Len >= sizeof (Buf)) { + Str = xmalloc (Len); + } + + if (CE_HasArgOffset (E)) { + sprintf (Str, "%s%+ld", ArgBase, ArgOff); + } else { + sprintf (Str, "%s", ArgBase); + } + CE_SetArg (E, Str); + + if (Str != Buf) { + xfree (Str); + } + + } else { + /* The argument has no base */ + if ((E->ArgInfo & AIF_HAS_OFFSET) != 0) { + /* This is a numeric argument */ + E->Flags |= CEF_NUMARG; + CE_SetNumArg (E, ArgOff); + } else { + /* Empty argument */ + CE_SetArg (E, EmptyArg); + } + } +} + + + +void CE_SetArgBase (CodeEntry* E, const char* ArgBase) +/* Replace the argument base by the new one. +** The entry must have an existing base. +*/ +{ + /* Check that the entry has a base name */ + CHECK (CE_HasArgBase (E)); + + CE_SetArgBaseAndOff (E, ArgBase, E->ArgOff); +} + + + +void CE_SetArgOffset (CodeEntry* E, long ArgOff) +/* Replace the argument offset by the new one */ +{ + CE_SetArgBaseAndOff (E, E->ArgBase, ArgOff); } @@ -616,24 +769,45 @@ void CE_SetNumArg (CodeEntry* E, long Num) char Buf[16]; /* Check that the entry has a numerical argument */ - CHECK (E->Flags & CEF_NUMARG); + CHECK (CE_HasNumArg (E)); /* Make the new argument string */ if (E->Size == 2) { Num &= 0xFF; xsprintf (Buf, sizeof (Buf), "$%02X", (unsigned) Num); - } else if (E->Size == 3) { + } else if (E->Size == 3 || E->Size == 5) { Num &= 0xFFFF; xsprintf (Buf, sizeof (Buf), "$%04X", (unsigned) Num); } else { Internal ("Invalid instruction size in CE_SetNumArg"); } - /* Replace the argument by the new one */ + /* Replace the whole argument by the new one */ CE_SetArg (E, Buf); +} - /* Use the new numerical value */ - E->Num = Num; + + +int CE_IsArgStrParsed (const CodeEntry* E) +/* Return true if the argument of E was successfully parsed last time */ +{ + return (E->ArgInfo & AIF_FAILURE) == 0; +} + + + +int CE_HasArgBase (const CodeEntry* E) +/* Return true if the argument of E has a non-blank base name */ +{ + return (E->ArgInfo & AIF_HAS_NAME) != 0 && E->ArgBase[0] != '\0'; +} + + + +int CE_HasArgOffset (const CodeEntry* E) +/* Return true if the argument of E has a non-zero offset */ +{ + return (E->ArgInfo & AIF_HAS_OFFSET) != 0 && E->ArgOff != 0; } diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 57a7677bb..173118a7f 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -73,14 +73,32 @@ struct CodeEntry { char* Arg; /* Argument as string */ unsigned long Num; /* Numeric argument */ unsigned short Info; /* Additional code info */ + unsigned short ArgInfo; /* Additional argument info */ unsigned int Use; /* Registers used */ unsigned int Chg; /* Registers changed/destroyed */ CodeLabel* JumpTo; /* Jump label */ Collection Labels; /* Labels for this instruction */ LineInfo* LI; /* Source line info for this insn */ RegInfo* RI; /* Register info for this insn */ + char* ArgBase; /* Argument broken into a base and an offset, */ + long ArgOff; /* only done when requested. */ }; +/* */ +#define AIF_HAS_NAME 0x0001U /* Argument has a name part */ +#define AIF_HAS_OFFSET 0x0002U /* Argument has a numeric part */ +#define AIF_BUILTIN 0x0004U /* The name is built-in */ +#define AIF_EXTERNAL 0x0008U /* The name is external */ +#define AIF_LOCAL 0x0010U /* The name is a local label */ +#define AIF_ZP_NAME 0x0020U /* The name is a zp location */ +#define AIF_LOBYTE 0x0100U +#define AIF_HIBYTE 0x0200U +#define AIF_BANKBYTE 0x0400U +#define AIF_FAILURE 0x8000U /* Argument was not parsed successfully */ + +#define AIF_WORD (AIF_LOBYTE | AIF_HIBYTE) +#define AIF_FAR (AIF_LOBYTE | AIF_HIBYTE | AIF_BANKBYTE) + /*****************************************************************************/ @@ -89,7 +107,7 @@ struct CodeEntry { -int ParseOpcArgStr (const char* Arg, struct StrBuf* Name, int* Offset); +int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Name, long* Offset); /* Break the opcode argument string into a symbol name/label part plus an offset. ** Both parts are optional, but if there are any characters in the string that ** can't be parsed, it's an failure. @@ -105,6 +123,9 @@ const char* MakeHexArg (unsigned Num); ** safe). */ +void PreparseArg (CodeEntry* E); +/* Parse the argument string and memorize the result for the code entry */ + CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeLabel* JumpTo, LineInfo* LI); /* Create a new code entry, initialize and return it */ @@ -205,11 +226,33 @@ INLINE int CE_HasNumArg (const CodeEntry* E) void CE_SetArg (CodeEntry* E, const char* Arg); /* Replace the argument by the new one. */ +void CE_SetArgBaseAndOff (CodeEntry* E, const char* ArgBase, long ArgOff); +/* Replace the new argument base and offset. Argument base is always applied. +** Argument offset is applied if and only if E has the AIF_HAS_OFFSET flag set. +*/ + +void CE_SetArgBase (CodeEntry* E, const char* ArgBase); +/* Replace the argument base by the new one. +** The entry must have an existing base. +*/ + +void CE_SetArgOffset (CodeEntry* E, long ArgOff); +/* Replace the argument offset by the new one */ + void CE_SetNumArg (CodeEntry* E, long Num); /* Set a new numeric argument for the given code entry that must already ** have a numeric argument. */ +int CE_IsArgStrParsed (const CodeEntry* E); +/* Return true if the argument of E was successfully parsed last time */ + +int CE_HasArgBase (const CodeEntry* E); +/* Return true if the argument of E has a non-blank base name */ + +int CE_HasArgOffset (const CodeEntry* E); +/* Return true if the argument of E has a non-zero offset */ + int CE_IsConstImm (const CodeEntry* E); /* Return true if the argument of E is a constant immediate value */ diff --git a/src/cc65/codelab.c b/src/cc65/codelab.c index ff26645dc..0909702fd 100644 --- a/src/cc65/codelab.c +++ b/src/cc65/codelab.c @@ -90,8 +90,12 @@ void CL_AddRef (CodeLabel* L, struct CodeEntry* E) /* The insn at E jumps to this label */ E->JumpTo = L; - /* Replace the code entry argument with the name of the new label */ - CE_SetArg (E, L->Name); + if (CE_HasArgBase (E)) { + /* Replace the code entry argument base with the name of the new label */ + CE_SetArgBase (E, L->Name); + } else { + CE_SetArgBaseAndOff (E, L->Name, 0); + } /* Remember that in the label */ CollAppend (&L->JumpFrom, E); diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 503f33859..3df762eeb 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -231,8 +231,6 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) unsigned int Chg; unsigned int UseToCheck = 0; unsigned int ChgToCheck = 0; - StrBuf Src, YSrc, New; - int SrcOff = 0, YSrcOff = 0, NewOff = 0; const ZPInfo* ZI = 0; unsigned Res = 0; CodeEntry* AE = 0; @@ -243,10 +241,6 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) return 0; } - SB_Init (&Src); - SB_Init (&YSrc); - SB_Init (&New); - if (E->AM == AM65_ACC || E->AM == AM65_BRA || E->AM == AM65_IMM || E->AM == AM65_IMP) { goto L_Result; } @@ -262,14 +256,13 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) UseToCheck |= AE->Use & ~REG_A & REG_ALL; ChgToCheck |= AE->Chg & ~REG_A & REG_ALL; - SB_InitFromString (&Src, xstrdup (AE->Arg)); - if (!ParseOpcArgStr (AE->Arg, &Src, &SrcOff)) { + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (AE)) { /* Bail out and play it safe*/ - Res |= LI_SRC_USE | LI_SRC_CHG; - goto L_Result; + goto L_Affected; } /* We have to manually set up the use/chg flags for builtin functions */ - ZI = GetZPInfo (SB_GetConstBuf (&Src)); + ZI = GetZPInfo (AE->ArgBase); if (ZI != 0) { UseToCheck |= ZI->ByteUse; ChgToCheck |= ZI->ByteUse; @@ -287,14 +280,14 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) YE = LRI->LoadYEntry; if (YE != 0) { UseToCheck |= YE->Use; - SB_InitFromString (&YSrc, xstrdup (YE->Arg)); - if (!ParseOpcArgStr (YE->Arg, &YSrc, &YSrcOff)) { + + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (YE)) { /* Bail out and play it safe*/ - Res |= LI_SRC_USE | LI_SRC_CHG; - goto L_Result; + goto L_Affected; } /* We have to manually set up the use/chg flags for builtin functions */ - ZI = GetZPInfo (SB_GetConstBuf (&YSrc)); + ZI = GetZPInfo (YE->ArgBase); if (ZI != 0) { UseToCheck |= ZI->ByteUse; ChgToCheck |= ZI->ByteUse; @@ -322,14 +315,13 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) goto L_Result; } /* Otherwise play it safe */ - Res |= LI_SRC_USE | LI_SRC_CHG; - goto L_Result; + goto L_Affected; } else { if ((E->Info & (OF_READ | OF_WRITE)) != 0) { - SB_InitFromString (&New, xstrdup (E->Arg)); - if (!ParseOpcArgStr (E->Arg, &New, &NewOff)) { + /* Check if the argument has been parsed successfully */ + if (!CE_IsArgStrParsed (E)) { /* Bail out and play it safe*/ goto L_Affected; } @@ -341,16 +333,16 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) */ if (E->AM == AM65_ABS || E->AM == AM65_ZP || - (E->AM == AM65_ZP_INDY && SB_CompareStr (&New, "sp") == 0) + (E->AM == AM65_ZP_INDY && strcmp (E->ArgBase, "sp") == 0) ) { if ((LRI->Flags & LI_CHECK_ARG) != 0) { if (AE == 0 || (AE->AM != AM65_ABS && AE->AM != AM65_ZP && (AE->AM != AM65_ZP_INDY || - SB_CompareStr (&Src, "sp") != 0)) || - (SrcOff == NewOff && - SB_Compare (&Src, &New) == 0)) { + strcmp (AE->ArgBase, "sp") != 0)) || + (AE->ArgOff == E->ArgOff && + strcmp (AE->ArgBase, E->ArgBase) == 0)) { if ((E->Info & OF_READ) != 0) { /* Used */ @@ -367,7 +359,7 @@ static int Affected (LoadRegInfo* LRI, const CodeEntry* E) /* If we don't know what memory location could have been used by Y, ** we just assume all. */ if (YE == 0 || - (YSrcOff == NewOff && SB_Compare (&YSrc, &New) == 0)) { + (YE->ArgOff == E->ArgOff && strcmp (YE->ArgBase, E->ArgBase) == 0)) { if ((E->Info & OF_READ) != 0) { /* Used */ @@ -415,9 +407,6 @@ L_Result: (E->Chg & REG_Y) != 0) { Res |= LI_Y_CHG; } - SB_Done (&Src); - SB_Done (&YSrc); - SB_Done (&New); return Res; } From b525554bfee3c3ddc5ddd7e58b4a20728f6c9cb1 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:19:17 +0800 Subject: [PATCH 374/806] Added support for parsing asm byte size expressions with a pair of parentheses. --- src/cc65/codeent.c | 99 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index 2ffe4e685..f3a910652 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -341,6 +341,7 @@ int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Nam const char* OffsetPart = 0; const char* NameEnd = 0; int Negative = 0; + int Parentheses = 0; unsigned long NumVal = 0; long long AccOffset = 0; char* End; /* Used for checking errors */ @@ -357,6 +358,49 @@ int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Nam SB_Terminate (Name); OffsetPart = Arg; } else { + /* <, >, ^ */ + if (Arg[0] == '<') { + Flags |= AIF_LOBYTE; + } else if (Arg[0] == '>') { + Flags |= AIF_HIBYTE; + } else if (Arg[0] == '^') { + Flags |= AIF_BANKBYTE; + } + + if ((Flags & (AIF_FAR)) != 0) { + /* Skip this char */ + ++Arg; + } + + /* Skip spaces */ + while (Arg[0] == ' ') { + ++Arg; + } + + /* Strip parentheses off if exist */ + if (Arg[0] == '(') { + /* Skip this char */ + ++Arg; + + End = strchr (Arg, ')'); + if (End == 0 || End[1] != '\0') { + /* Not closed at the end, bail out */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } + + /* Found */ + Parentheses = 1; + + /* Skip spaces */ + while (Arg[0] == ' ') { + ++Arg; + } + } + /* If the symbol name starts with an underline, it is an external symbol. ** If the symbol does not start with an underline, it may be a built-in ** symbol. @@ -384,7 +428,11 @@ int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Nam } else { /* No offset */ - SB_CopyStr (Name, Arg); + if (Parentheses == 0) { + SB_CopyStr (Name, Arg); + } else { + SB_CopyBuf (Name, Arg, End - Arg); + } SB_Terminate (Name); } @@ -410,10 +458,27 @@ int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Nam } Flags |= AIF_HAS_NAME; } + + /* A byte size expression with no parentheses but an offset is not + ** handled correctly for now, so just bail out in such cases. + */ + if ((Flags & AIF_FAR) != 0 && + Parentheses == 0 && + OffsetPart != 0 && + OffsetPart[0] != '\0') { + /* Bail out */ + *Offset = 0; + if (ArgInfo != 0) { + *ArgInfo = Flags | AIF_FAILURE; + } + return 0; + } } /* Get the offset */ - while (OffsetPart != 0 && OffsetPart[0] != '\0') { + while (OffsetPart != 0 && + OffsetPart[0] != '\0' && + OffsetPart[0] != ')') { /* Skip spaces */ while (OffsetPart[0] == ' ') { ++OffsetPart; @@ -448,7 +513,7 @@ int ParseOpcArgStr (const char* Arg, unsigned short* ArgInfo, struct StrBuf* Nam } /* Check if the conversion was successful */ - if (*End != '\0' && *End != ' ' && *End != '+' && *End != '-') { + if (*End != '\0' && *End != ' ' && *End != '+' && *End != '-' && *End != ')') { /* Could not convert */ *Offset = 0; if (ArgInfo != 0) { @@ -714,11 +779,33 @@ void CE_SetArgBaseAndOff (CodeEntry* E, const char* ArgBase, long ArgOff) Str = xmalloc (Len); } - if (CE_HasArgOffset (E)) { - sprintf (Str, "%s%+ld", ArgBase, ArgOff); + if ((E->ArgInfo & AIF_FAR) == 0) { + if (CE_HasArgOffset (E)) { + sprintf (Str, "%s%+ld", ArgBase, ArgOff); + } else { + sprintf (Str, "%s", ArgBase); + } + CE_SetArg (E, Str); } else { - sprintf (Str, "%s", ArgBase); + /* A byte expression */ + const char* Expr = ""; + if ((E->ArgInfo & AIF_FAR) == AIF_LOBYTE) { + Expr = "<"; + } else if ((E->ArgInfo & AIF_FAR) == AIF_HIBYTE) { + Expr = ">"; + } else if ((E->ArgInfo & AIF_FAR) == AIF_BANKBYTE) { + Expr = "^"; + } else { + Internal ("Invalid byte size flag in CE_SetArgBaseAndOff"); + } + + if (CE_HasArgOffset (E)) { + sprintf (Str, "%s(%s%+ld)", Expr, ArgBase, ArgOff); + } else { + sprintf (Str, "%s(%s)", Expr, ArgBase); + } } + CE_SetArg (E, Str); if (Str != Buf) { From eb87c6d3734a804f8cccb1a4bb4947ba5f998a8e Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 18:54:30 +0200 Subject: [PATCH 375/806] rename README to readme.txt --- samples/{README => readme.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples/{README => readme.txt} (100%) diff --git a/samples/README b/samples/readme.txt similarity index 100% rename from samples/README rename to samples/readme.txt From b549e83fb27faeddeb8910b7267ee7e5531cf0e9 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 19:03:42 +0200 Subject: [PATCH 376/806] move program from testcode/assembler to test/asm and remove testcode/assembler --- test/asm/Makefile | 5 ++- {testcode/assembler => test/asm}/paramcount.s | 0 test/asm/{README => readme.txt} | 0 testcode/assembler/Makefile | 37 ------------------- 4 files changed, 4 insertions(+), 38 deletions(-) rename {testcode/assembler => test/asm}/paramcount.s (100%) rename test/asm/{README => readme.txt} (100%) delete mode 100644 testcode/assembler/Makefile diff --git a/test/asm/Makefile b/test/asm/Makefile index b157bb672..028254b68 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -38,7 +38,7 @@ CPUDETECT_REFS := $(wildcard *-cpudetect.ref) CPUDETECT_CPUS = $(foreach ref,$(CPUDETECT_REFS),$(ref:%-cpudetect.ref=%)) CPUDETECT_BINS = $(foreach cpu,$(CPUDETECT_CPUS),$(WORKDIR)/$(cpu)-cpudetect.bin) -all: $(OPCODE_BINS) $(CPUDETECT_BINS) +all: $(OPCODE_BINS) $(CPUDETECT_BINS) paramcount.o $(WORKDIR): $(call MKDIR,$(WORKDIR)) @@ -70,5 +70,8 @@ endef # CPUDETECT_template $(foreach cpu,$(CPUDETECT_CPUS),$(eval $(call CPUDETECT_template,$(cpu)))) +paramcount.o: paramcount.s + $(CA65) -o $(WORKDIR)/paramcount.o -l $(WORKDIR)/paramcount.lst paramcount.s + clean: @$(call RMDIR,$(WORKDIR)) diff --git a/testcode/assembler/paramcount.s b/test/asm/paramcount.s similarity index 100% rename from testcode/assembler/paramcount.s rename to test/asm/paramcount.s diff --git a/test/asm/README b/test/asm/readme.txt similarity index 100% rename from test/asm/README rename to test/asm/readme.txt diff --git a/testcode/assembler/Makefile b/testcode/assembler/Makefile deleted file mode 100644 index 6cb3fe1f2..000000000 --- a/testcode/assembler/Makefile +++ /dev/null @@ -1,37 +0,0 @@ - -# Just the usual way to find out if we're -# using cmd.exe to execute make rules. -ifneq ($(shell echo),) - CMD_EXE = 1 -endif - -ifdef CMD_EXE - NULLDEV = nul: - DEL = -del /f - RMDIR = rmdir /s /q -else - NULLDEV = /dev/null - DEL = $(RM) - RMDIR = $(RM) -r -endif - -ifdef CC65_HOME - AS = $(CC65_HOME)/bin/ca65 - CC = $(CC65_HOME)/bin/cc65 - CL = $(CC65_HOME)/bin/cl65 - LD = $(CC65_HOME)/bin/ld65 -else - AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) - CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) - CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) - LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) -endif - -all: paramcount.o - -paramcount.o: paramcount.s - $(AS) -o paramcount.o -l paramcount.lst paramcount.s - -clean: - $(RM) paramcount.o - $(RM) paramcount.lst From bbece736f5a1ad2a2f805a72f26cfb75125b9c97 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 19:06:05 +0200 Subject: [PATCH 377/806] move testcode/disasm into samples/ instead --- {testcode => samples}/disasm/.gitignore | 0 {testcode => samples}/disasm/Makefile | 0 {testcode => samples}/disasm/bank0.da | 0 {testcode => samples}/disasm/bank1.da | 0 {testcode => samples}/disasm/fixed.da | 0 {testcode => samples}/disasm/image.cfg | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {testcode => samples}/disasm/.gitignore (100%) rename {testcode => samples}/disasm/Makefile (100%) rename {testcode => samples}/disasm/bank0.da (100%) rename {testcode => samples}/disasm/bank1.da (100%) rename {testcode => samples}/disasm/fixed.da (100%) rename {testcode => samples}/disasm/image.cfg (100%) diff --git a/testcode/disasm/.gitignore b/samples/disasm/.gitignore similarity index 100% rename from testcode/disasm/.gitignore rename to samples/disasm/.gitignore diff --git a/testcode/disasm/Makefile b/samples/disasm/Makefile similarity index 100% rename from testcode/disasm/Makefile rename to samples/disasm/Makefile diff --git a/testcode/disasm/bank0.da b/samples/disasm/bank0.da similarity index 100% rename from testcode/disasm/bank0.da rename to samples/disasm/bank0.da diff --git a/testcode/disasm/bank1.da b/samples/disasm/bank1.da similarity index 100% rename from testcode/disasm/bank1.da rename to samples/disasm/bank1.da diff --git a/testcode/disasm/fixed.da b/samples/disasm/fixed.da similarity index 100% rename from testcode/disasm/fixed.da rename to samples/disasm/fixed.da diff --git a/testcode/disasm/image.cfg b/samples/disasm/image.cfg similarity index 100% rename from testcode/disasm/image.cfg rename to samples/disasm/image.cfg From 3d8e787e66d1e3b67bcac5cbdb0d869a71e3c214 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 19:08:40 +0200 Subject: [PATCH 378/806] move testcode/grc to samples/geos --- {testcode => samples/geos}/grc/Makefile | 0 {testcode => samples/geos}/grc/test.grc | 0 {testcode => samples/geos}/grc/vlir.grc | 0 {testcode => samples/geos}/grc/vlir0.s | 0 {testcode => samples/geos}/grc/vlir1.s | 0 {testcode => samples/geos}/grc/vlir2.s | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {testcode => samples/geos}/grc/Makefile (100%) rename {testcode => samples/geos}/grc/test.grc (100%) rename {testcode => samples/geos}/grc/vlir.grc (100%) rename {testcode => samples/geos}/grc/vlir0.s (100%) rename {testcode => samples/geos}/grc/vlir1.s (100%) rename {testcode => samples/geos}/grc/vlir2.s (100%) diff --git a/testcode/grc/Makefile b/samples/geos/grc/Makefile similarity index 100% rename from testcode/grc/Makefile rename to samples/geos/grc/Makefile diff --git a/testcode/grc/test.grc b/samples/geos/grc/test.grc similarity index 100% rename from testcode/grc/test.grc rename to samples/geos/grc/test.grc diff --git a/testcode/grc/vlir.grc b/samples/geos/grc/vlir.grc similarity index 100% rename from testcode/grc/vlir.grc rename to samples/geos/grc/vlir.grc diff --git a/testcode/grc/vlir0.s b/samples/geos/grc/vlir0.s similarity index 100% rename from testcode/grc/vlir0.s rename to samples/geos/grc/vlir0.s diff --git a/testcode/grc/vlir1.s b/samples/geos/grc/vlir1.s similarity index 100% rename from testcode/grc/vlir1.s rename to samples/geos/grc/vlir1.s diff --git a/testcode/grc/vlir2.s b/samples/geos/grc/vlir2.s similarity index 100% rename from testcode/grc/vlir2.s rename to samples/geos/grc/vlir2.s From dcee493e945a0154f73e1f3cb612026a82fcf1d9 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 19:12:34 +0200 Subject: [PATCH 379/806] move remaining stuff from testcode/lib/ one level up to testcode/ --- testcode/{lib => }/Makefile | 0 testcode/{lib => }/accelerator/Makefile | 0 testcode/{lib => }/accelerator/c64-c128-scpu-test.c | 0 testcode/{lib => }/accelerator/c64-c128-test.c | 0 testcode/{lib => }/accelerator/c64dtv-test.c | 0 testcode/{lib => }/accelerator/c65-test.c | 0 testcode/{lib => }/accelerator/chameleon-test.c | 0 testcode/{lib => }/accelerator/turbo-test.c | 0 testcode/{lib => }/accelerator/turbomaster-test.c | 0 testcode/{lib => }/apple2/Makefile | 0 testcode/{lib => }/apple2/astronaut.hgr | Bin testcode/{lib => }/apple2/catface.dhgr | Bin testcode/{lib => }/apple2/chips.hgr | Bin testcode/{lib => }/apple2/dhgrshow.c | 0 testcode/{lib => }/apple2/gatsby.dhgr | Bin testcode/{lib => }/apple2/girl.dhgr | Bin testcode/{lib => }/apple2/hgrshow.c | 0 testcode/{lib => }/apple2/hgrtest.c | 0 testcode/{lib => }/apple2/macrometer.hgr | Bin testcode/{lib => }/apple2/mariner.hgr | Bin testcode/{lib => }/apple2/monarch.dhgr | Bin testcode/{lib => }/apple2/rose.hgr | Bin testcode/{lib => }/apple2/superman.dhgr | Bin testcode/{lib => }/apple2/venice.dhgr | Bin testcode/{lib => }/apple2/werner.hgr | Bin testcode/{lib => }/apple2/werner.s | 0 testcode/{lib => }/apple2/winston.hgr | Bin testcode/{lib => }/arg-test.c | 0 testcode/{lib => }/atari/Makefile | 0 testcode/{lib => }/atari/asm-xex.s | 0 testcode/{lib => }/atari/charmapping.c | 0 testcode/{lib => }/atari/defdev.c | 0 testcode/{lib => }/atari/displaylist.c | 0 testcode/{lib => }/atari/mem.c | 0 testcode/{lib => }/atari/multi-xex.cfg | 0 testcode/{lib => }/atari/multi-xex.s | 0 testcode/{lib => }/atari/ostype.c | 0 testcode/{lib => }/atari/scrcode.s | 0 testcode/{lib => }/atari/sys.c | 0 testcode/{lib => }/atari5200/Makefile | 0 testcode/{lib => }/atari5200/hello.c | 0 testcode/{lib => }/cbm/Makefile | 0 testcode/{lib => }/cbm/petscii.c | 0 testcode/{lib => }/clock-test.c | 0 testcode/{lib => }/clock.c | 0 testcode/{lib => }/conio.c | 0 testcode/{lib => }/cpeek-test.c | 0 testcode/{lib => }/cprintf.c | 0 testcode/{lib => }/cursor.c | 0 testcode/{lib => }/deb.c | 0 testcode/{lib => }/dir-test.c | 0 testcode/{lib => }/div-test.c | 0 testcode/{lib => }/em-test.c | 0 testcode/{lib => }/exec-test1.c | 0 testcode/{lib => }/exec-test2.c | 0 testcode/{lib => }/fileio-test.c | 0 testcode/{lib => }/ft.c | 0 testcode/{lib => }/gamate/Makefile | 0 testcode/{lib => }/gamate/audiotest.s | 0 testcode/{lib => }/gamate/cga2.chr | Bin testcode/{lib => }/gamate/ctest.c | 0 testcode/{lib => }/gamate/lcdtest.s | 0 testcode/{lib => }/gamate/nachtm.c | 0 testcode/{lib => }/getopt-test.c | 0 testcode/{lib => }/getsp.s | 0 testcode/{lib => }/heaptest.c | 0 testcode/{lib => }/joy-test.c | 0 testcode/lib/.gitignore | 2 -- testcode/{lib => }/moddiv-test.c | 0 testcode/{lib => }/mouse-test.c | 0 testcode/{lib => }/mul-test.c | 0 testcode/{lib => }/pce/Makefile | 0 testcode/{lib => }/pce/conio.c | 0 testcode/{lib => }/posixio-test.c | 0 testcode/{lib => }/rename-test.c | 0 testcode/{lib => }/scanf-test.c | 0 testcode/{lib => }/seek.c | 0 testcode/{lib => }/ser-test.c | 0 testcode/{lib => }/strdup-test.c | 0 testcode/{lib => }/stroserror-test.c | 0 testcode/{lib => }/strqtok-test.c | 0 testcode/{lib => }/tinyshell.c | 0 testcode/{lib => }/uname-test.c | 0 83 files changed, 2 deletions(-) rename testcode/{lib => }/Makefile (100%) rename testcode/{lib => }/accelerator/Makefile (100%) rename testcode/{lib => }/accelerator/c64-c128-scpu-test.c (100%) rename testcode/{lib => }/accelerator/c64-c128-test.c (100%) rename testcode/{lib => }/accelerator/c64dtv-test.c (100%) rename testcode/{lib => }/accelerator/c65-test.c (100%) rename testcode/{lib => }/accelerator/chameleon-test.c (100%) rename testcode/{lib => }/accelerator/turbo-test.c (100%) rename testcode/{lib => }/accelerator/turbomaster-test.c (100%) rename testcode/{lib => }/apple2/Makefile (100%) rename testcode/{lib => }/apple2/astronaut.hgr (100%) rename testcode/{lib => }/apple2/catface.dhgr (100%) rename testcode/{lib => }/apple2/chips.hgr (100%) rename testcode/{lib => }/apple2/dhgrshow.c (100%) rename testcode/{lib => }/apple2/gatsby.dhgr (100%) rename testcode/{lib => }/apple2/girl.dhgr (100%) rename testcode/{lib => }/apple2/hgrshow.c (100%) rename testcode/{lib => }/apple2/hgrtest.c (100%) rename testcode/{lib => }/apple2/macrometer.hgr (100%) rename testcode/{lib => }/apple2/mariner.hgr (100%) rename testcode/{lib => }/apple2/monarch.dhgr (100%) rename testcode/{lib => }/apple2/rose.hgr (100%) rename testcode/{lib => }/apple2/superman.dhgr (100%) rename testcode/{lib => }/apple2/venice.dhgr (100%) rename testcode/{lib => }/apple2/werner.hgr (100%) rename testcode/{lib => }/apple2/werner.s (100%) rename testcode/{lib => }/apple2/winston.hgr (100%) rename testcode/{lib => }/arg-test.c (100%) rename testcode/{lib => }/atari/Makefile (100%) rename testcode/{lib => }/atari/asm-xex.s (100%) rename testcode/{lib => }/atari/charmapping.c (100%) rename testcode/{lib => }/atari/defdev.c (100%) rename testcode/{lib => }/atari/displaylist.c (100%) rename testcode/{lib => }/atari/mem.c (100%) rename testcode/{lib => }/atari/multi-xex.cfg (100%) rename testcode/{lib => }/atari/multi-xex.s (100%) rename testcode/{lib => }/atari/ostype.c (100%) rename testcode/{lib => }/atari/scrcode.s (100%) rename testcode/{lib => }/atari/sys.c (100%) rename testcode/{lib => }/atari5200/Makefile (100%) rename testcode/{lib => }/atari5200/hello.c (100%) rename testcode/{lib => }/cbm/Makefile (100%) rename testcode/{lib => }/cbm/petscii.c (100%) rename testcode/{lib => }/clock-test.c (100%) rename testcode/{lib => }/clock.c (100%) rename testcode/{lib => }/conio.c (100%) rename testcode/{lib => }/cpeek-test.c (100%) rename testcode/{lib => }/cprintf.c (100%) rename testcode/{lib => }/cursor.c (100%) rename testcode/{lib => }/deb.c (100%) rename testcode/{lib => }/dir-test.c (100%) rename testcode/{lib => }/div-test.c (100%) rename testcode/{lib => }/em-test.c (100%) rename testcode/{lib => }/exec-test1.c (100%) rename testcode/{lib => }/exec-test2.c (100%) rename testcode/{lib => }/fileio-test.c (100%) rename testcode/{lib => }/ft.c (100%) rename testcode/{lib => }/gamate/Makefile (100%) rename testcode/{lib => }/gamate/audiotest.s (100%) rename testcode/{lib => }/gamate/cga2.chr (100%) rename testcode/{lib => }/gamate/ctest.c (100%) rename testcode/{lib => }/gamate/lcdtest.s (100%) rename testcode/{lib => }/gamate/nachtm.c (100%) rename testcode/{lib => }/getopt-test.c (100%) rename testcode/{lib => }/getsp.s (100%) rename testcode/{lib => }/heaptest.c (100%) rename testcode/{lib => }/joy-test.c (100%) delete mode 100644 testcode/lib/.gitignore rename testcode/{lib => }/moddiv-test.c (100%) rename testcode/{lib => }/mouse-test.c (100%) rename testcode/{lib => }/mul-test.c (100%) rename testcode/{lib => }/pce/Makefile (100%) rename testcode/{lib => }/pce/conio.c (100%) rename testcode/{lib => }/posixio-test.c (100%) rename testcode/{lib => }/rename-test.c (100%) rename testcode/{lib => }/scanf-test.c (100%) rename testcode/{lib => }/seek.c (100%) rename testcode/{lib => }/ser-test.c (100%) rename testcode/{lib => }/strdup-test.c (100%) rename testcode/{lib => }/stroserror-test.c (100%) rename testcode/{lib => }/strqtok-test.c (100%) rename testcode/{lib => }/tinyshell.c (100%) rename testcode/{lib => }/uname-test.c (100%) diff --git a/testcode/lib/Makefile b/testcode/Makefile similarity index 100% rename from testcode/lib/Makefile rename to testcode/Makefile diff --git a/testcode/lib/accelerator/Makefile b/testcode/accelerator/Makefile similarity index 100% rename from testcode/lib/accelerator/Makefile rename to testcode/accelerator/Makefile diff --git a/testcode/lib/accelerator/c64-c128-scpu-test.c b/testcode/accelerator/c64-c128-scpu-test.c similarity index 100% rename from testcode/lib/accelerator/c64-c128-scpu-test.c rename to testcode/accelerator/c64-c128-scpu-test.c diff --git a/testcode/lib/accelerator/c64-c128-test.c b/testcode/accelerator/c64-c128-test.c similarity index 100% rename from testcode/lib/accelerator/c64-c128-test.c rename to testcode/accelerator/c64-c128-test.c diff --git a/testcode/lib/accelerator/c64dtv-test.c b/testcode/accelerator/c64dtv-test.c similarity index 100% rename from testcode/lib/accelerator/c64dtv-test.c rename to testcode/accelerator/c64dtv-test.c diff --git a/testcode/lib/accelerator/c65-test.c b/testcode/accelerator/c65-test.c similarity index 100% rename from testcode/lib/accelerator/c65-test.c rename to testcode/accelerator/c65-test.c diff --git a/testcode/lib/accelerator/chameleon-test.c b/testcode/accelerator/chameleon-test.c similarity index 100% rename from testcode/lib/accelerator/chameleon-test.c rename to testcode/accelerator/chameleon-test.c diff --git a/testcode/lib/accelerator/turbo-test.c b/testcode/accelerator/turbo-test.c similarity index 100% rename from testcode/lib/accelerator/turbo-test.c rename to testcode/accelerator/turbo-test.c diff --git a/testcode/lib/accelerator/turbomaster-test.c b/testcode/accelerator/turbomaster-test.c similarity index 100% rename from testcode/lib/accelerator/turbomaster-test.c rename to testcode/accelerator/turbomaster-test.c diff --git a/testcode/lib/apple2/Makefile b/testcode/apple2/Makefile similarity index 100% rename from testcode/lib/apple2/Makefile rename to testcode/apple2/Makefile diff --git a/testcode/lib/apple2/astronaut.hgr b/testcode/apple2/astronaut.hgr similarity index 100% rename from testcode/lib/apple2/astronaut.hgr rename to testcode/apple2/astronaut.hgr diff --git a/testcode/lib/apple2/catface.dhgr b/testcode/apple2/catface.dhgr similarity index 100% rename from testcode/lib/apple2/catface.dhgr rename to testcode/apple2/catface.dhgr diff --git a/testcode/lib/apple2/chips.hgr b/testcode/apple2/chips.hgr similarity index 100% rename from testcode/lib/apple2/chips.hgr rename to testcode/apple2/chips.hgr diff --git a/testcode/lib/apple2/dhgrshow.c b/testcode/apple2/dhgrshow.c similarity index 100% rename from testcode/lib/apple2/dhgrshow.c rename to testcode/apple2/dhgrshow.c diff --git a/testcode/lib/apple2/gatsby.dhgr b/testcode/apple2/gatsby.dhgr similarity index 100% rename from testcode/lib/apple2/gatsby.dhgr rename to testcode/apple2/gatsby.dhgr diff --git a/testcode/lib/apple2/girl.dhgr b/testcode/apple2/girl.dhgr similarity index 100% rename from testcode/lib/apple2/girl.dhgr rename to testcode/apple2/girl.dhgr diff --git a/testcode/lib/apple2/hgrshow.c b/testcode/apple2/hgrshow.c similarity index 100% rename from testcode/lib/apple2/hgrshow.c rename to testcode/apple2/hgrshow.c diff --git a/testcode/lib/apple2/hgrtest.c b/testcode/apple2/hgrtest.c similarity index 100% rename from testcode/lib/apple2/hgrtest.c rename to testcode/apple2/hgrtest.c diff --git a/testcode/lib/apple2/macrometer.hgr b/testcode/apple2/macrometer.hgr similarity index 100% rename from testcode/lib/apple2/macrometer.hgr rename to testcode/apple2/macrometer.hgr diff --git a/testcode/lib/apple2/mariner.hgr b/testcode/apple2/mariner.hgr similarity index 100% rename from testcode/lib/apple2/mariner.hgr rename to testcode/apple2/mariner.hgr diff --git a/testcode/lib/apple2/monarch.dhgr b/testcode/apple2/monarch.dhgr similarity index 100% rename from testcode/lib/apple2/monarch.dhgr rename to testcode/apple2/monarch.dhgr diff --git a/testcode/lib/apple2/rose.hgr b/testcode/apple2/rose.hgr similarity index 100% rename from testcode/lib/apple2/rose.hgr rename to testcode/apple2/rose.hgr diff --git a/testcode/lib/apple2/superman.dhgr b/testcode/apple2/superman.dhgr similarity index 100% rename from testcode/lib/apple2/superman.dhgr rename to testcode/apple2/superman.dhgr diff --git a/testcode/lib/apple2/venice.dhgr b/testcode/apple2/venice.dhgr similarity index 100% rename from testcode/lib/apple2/venice.dhgr rename to testcode/apple2/venice.dhgr diff --git a/testcode/lib/apple2/werner.hgr b/testcode/apple2/werner.hgr similarity index 100% rename from testcode/lib/apple2/werner.hgr rename to testcode/apple2/werner.hgr diff --git a/testcode/lib/apple2/werner.s b/testcode/apple2/werner.s similarity index 100% rename from testcode/lib/apple2/werner.s rename to testcode/apple2/werner.s diff --git a/testcode/lib/apple2/winston.hgr b/testcode/apple2/winston.hgr similarity index 100% rename from testcode/lib/apple2/winston.hgr rename to testcode/apple2/winston.hgr diff --git a/testcode/lib/arg-test.c b/testcode/arg-test.c similarity index 100% rename from testcode/lib/arg-test.c rename to testcode/arg-test.c diff --git a/testcode/lib/atari/Makefile b/testcode/atari/Makefile similarity index 100% rename from testcode/lib/atari/Makefile rename to testcode/atari/Makefile diff --git a/testcode/lib/atari/asm-xex.s b/testcode/atari/asm-xex.s similarity index 100% rename from testcode/lib/atari/asm-xex.s rename to testcode/atari/asm-xex.s diff --git a/testcode/lib/atari/charmapping.c b/testcode/atari/charmapping.c similarity index 100% rename from testcode/lib/atari/charmapping.c rename to testcode/atari/charmapping.c diff --git a/testcode/lib/atari/defdev.c b/testcode/atari/defdev.c similarity index 100% rename from testcode/lib/atari/defdev.c rename to testcode/atari/defdev.c diff --git a/testcode/lib/atari/displaylist.c b/testcode/atari/displaylist.c similarity index 100% rename from testcode/lib/atari/displaylist.c rename to testcode/atari/displaylist.c diff --git a/testcode/lib/atari/mem.c b/testcode/atari/mem.c similarity index 100% rename from testcode/lib/atari/mem.c rename to testcode/atari/mem.c diff --git a/testcode/lib/atari/multi-xex.cfg b/testcode/atari/multi-xex.cfg similarity index 100% rename from testcode/lib/atari/multi-xex.cfg rename to testcode/atari/multi-xex.cfg diff --git a/testcode/lib/atari/multi-xex.s b/testcode/atari/multi-xex.s similarity index 100% rename from testcode/lib/atari/multi-xex.s rename to testcode/atari/multi-xex.s diff --git a/testcode/lib/atari/ostype.c b/testcode/atari/ostype.c similarity index 100% rename from testcode/lib/atari/ostype.c rename to testcode/atari/ostype.c diff --git a/testcode/lib/atari/scrcode.s b/testcode/atari/scrcode.s similarity index 100% rename from testcode/lib/atari/scrcode.s rename to testcode/atari/scrcode.s diff --git a/testcode/lib/atari/sys.c b/testcode/atari/sys.c similarity index 100% rename from testcode/lib/atari/sys.c rename to testcode/atari/sys.c diff --git a/testcode/lib/atari5200/Makefile b/testcode/atari5200/Makefile similarity index 100% rename from testcode/lib/atari5200/Makefile rename to testcode/atari5200/Makefile diff --git a/testcode/lib/atari5200/hello.c b/testcode/atari5200/hello.c similarity index 100% rename from testcode/lib/atari5200/hello.c rename to testcode/atari5200/hello.c diff --git a/testcode/lib/cbm/Makefile b/testcode/cbm/Makefile similarity index 100% rename from testcode/lib/cbm/Makefile rename to testcode/cbm/Makefile diff --git a/testcode/lib/cbm/petscii.c b/testcode/cbm/petscii.c similarity index 100% rename from testcode/lib/cbm/petscii.c rename to testcode/cbm/petscii.c diff --git a/testcode/lib/clock-test.c b/testcode/clock-test.c similarity index 100% rename from testcode/lib/clock-test.c rename to testcode/clock-test.c diff --git a/testcode/lib/clock.c b/testcode/clock.c similarity index 100% rename from testcode/lib/clock.c rename to testcode/clock.c diff --git a/testcode/lib/conio.c b/testcode/conio.c similarity index 100% rename from testcode/lib/conio.c rename to testcode/conio.c diff --git a/testcode/lib/cpeek-test.c b/testcode/cpeek-test.c similarity index 100% rename from testcode/lib/cpeek-test.c rename to testcode/cpeek-test.c diff --git a/testcode/lib/cprintf.c b/testcode/cprintf.c similarity index 100% rename from testcode/lib/cprintf.c rename to testcode/cprintf.c diff --git a/testcode/lib/cursor.c b/testcode/cursor.c similarity index 100% rename from testcode/lib/cursor.c rename to testcode/cursor.c diff --git a/testcode/lib/deb.c b/testcode/deb.c similarity index 100% rename from testcode/lib/deb.c rename to testcode/deb.c diff --git a/testcode/lib/dir-test.c b/testcode/dir-test.c similarity index 100% rename from testcode/lib/dir-test.c rename to testcode/dir-test.c diff --git a/testcode/lib/div-test.c b/testcode/div-test.c similarity index 100% rename from testcode/lib/div-test.c rename to testcode/div-test.c diff --git a/testcode/lib/em-test.c b/testcode/em-test.c similarity index 100% rename from testcode/lib/em-test.c rename to testcode/em-test.c diff --git a/testcode/lib/exec-test1.c b/testcode/exec-test1.c similarity index 100% rename from testcode/lib/exec-test1.c rename to testcode/exec-test1.c diff --git a/testcode/lib/exec-test2.c b/testcode/exec-test2.c similarity index 100% rename from testcode/lib/exec-test2.c rename to testcode/exec-test2.c diff --git a/testcode/lib/fileio-test.c b/testcode/fileio-test.c similarity index 100% rename from testcode/lib/fileio-test.c rename to testcode/fileio-test.c diff --git a/testcode/lib/ft.c b/testcode/ft.c similarity index 100% rename from testcode/lib/ft.c rename to testcode/ft.c diff --git a/testcode/lib/gamate/Makefile b/testcode/gamate/Makefile similarity index 100% rename from testcode/lib/gamate/Makefile rename to testcode/gamate/Makefile diff --git a/testcode/lib/gamate/audiotest.s b/testcode/gamate/audiotest.s similarity index 100% rename from testcode/lib/gamate/audiotest.s rename to testcode/gamate/audiotest.s diff --git a/testcode/lib/gamate/cga2.chr b/testcode/gamate/cga2.chr similarity index 100% rename from testcode/lib/gamate/cga2.chr rename to testcode/gamate/cga2.chr diff --git a/testcode/lib/gamate/ctest.c b/testcode/gamate/ctest.c similarity index 100% rename from testcode/lib/gamate/ctest.c rename to testcode/gamate/ctest.c diff --git a/testcode/lib/gamate/lcdtest.s b/testcode/gamate/lcdtest.s similarity index 100% rename from testcode/lib/gamate/lcdtest.s rename to testcode/gamate/lcdtest.s diff --git a/testcode/lib/gamate/nachtm.c b/testcode/gamate/nachtm.c similarity index 100% rename from testcode/lib/gamate/nachtm.c rename to testcode/gamate/nachtm.c diff --git a/testcode/lib/getopt-test.c b/testcode/getopt-test.c similarity index 100% rename from testcode/lib/getopt-test.c rename to testcode/getopt-test.c diff --git a/testcode/lib/getsp.s b/testcode/getsp.s similarity index 100% rename from testcode/lib/getsp.s rename to testcode/getsp.s diff --git a/testcode/lib/heaptest.c b/testcode/heaptest.c similarity index 100% rename from testcode/lib/heaptest.c rename to testcode/heaptest.c diff --git a/testcode/lib/joy-test.c b/testcode/joy-test.c similarity index 100% rename from testcode/lib/joy-test.c rename to testcode/joy-test.c diff --git a/testcode/lib/.gitignore b/testcode/lib/.gitignore deleted file mode 100644 index 9bb8eaa3e..000000000 --- a/testcode/lib/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.o -em-test-* diff --git a/testcode/lib/moddiv-test.c b/testcode/moddiv-test.c similarity index 100% rename from testcode/lib/moddiv-test.c rename to testcode/moddiv-test.c diff --git a/testcode/lib/mouse-test.c b/testcode/mouse-test.c similarity index 100% rename from testcode/lib/mouse-test.c rename to testcode/mouse-test.c diff --git a/testcode/lib/mul-test.c b/testcode/mul-test.c similarity index 100% rename from testcode/lib/mul-test.c rename to testcode/mul-test.c diff --git a/testcode/lib/pce/Makefile b/testcode/pce/Makefile similarity index 100% rename from testcode/lib/pce/Makefile rename to testcode/pce/Makefile diff --git a/testcode/lib/pce/conio.c b/testcode/pce/conio.c similarity index 100% rename from testcode/lib/pce/conio.c rename to testcode/pce/conio.c diff --git a/testcode/lib/posixio-test.c b/testcode/posixio-test.c similarity index 100% rename from testcode/lib/posixio-test.c rename to testcode/posixio-test.c diff --git a/testcode/lib/rename-test.c b/testcode/rename-test.c similarity index 100% rename from testcode/lib/rename-test.c rename to testcode/rename-test.c diff --git a/testcode/lib/scanf-test.c b/testcode/scanf-test.c similarity index 100% rename from testcode/lib/scanf-test.c rename to testcode/scanf-test.c diff --git a/testcode/lib/seek.c b/testcode/seek.c similarity index 100% rename from testcode/lib/seek.c rename to testcode/seek.c diff --git a/testcode/lib/ser-test.c b/testcode/ser-test.c similarity index 100% rename from testcode/lib/ser-test.c rename to testcode/ser-test.c diff --git a/testcode/lib/strdup-test.c b/testcode/strdup-test.c similarity index 100% rename from testcode/lib/strdup-test.c rename to testcode/strdup-test.c diff --git a/testcode/lib/stroserror-test.c b/testcode/stroserror-test.c similarity index 100% rename from testcode/lib/stroserror-test.c rename to testcode/stroserror-test.c diff --git a/testcode/lib/strqtok-test.c b/testcode/strqtok-test.c similarity index 100% rename from testcode/lib/strqtok-test.c rename to testcode/strqtok-test.c diff --git a/testcode/lib/tinyshell.c b/testcode/tinyshell.c similarity index 100% rename from testcode/lib/tinyshell.c rename to testcode/tinyshell.c diff --git a/testcode/lib/uname-test.c b/testcode/uname-test.c similarity index 100% rename from testcode/lib/uname-test.c rename to testcode/uname-test.c From 4777e98f570b5227de20bac41d2721e762352b44 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Tue, 29 Sep 2020 19:14:46 +0200 Subject: [PATCH 380/806] rename testcode/ to targettest/ --- {testcode => targettest}/Makefile | 0 {testcode => targettest}/accelerator/Makefile | 0 .../accelerator/c64-c128-scpu-test.c | 0 .../accelerator/c64-c128-test.c | 0 {testcode => targettest}/accelerator/c64dtv-test.c | 0 {testcode => targettest}/accelerator/c65-test.c | 0 .../accelerator/chameleon-test.c | 0 {testcode => targettest}/accelerator/turbo-test.c | 0 .../accelerator/turbomaster-test.c | 0 {testcode => targettest}/apple2/Makefile | 0 {testcode => targettest}/apple2/astronaut.hgr | Bin {testcode => targettest}/apple2/catface.dhgr | Bin {testcode => targettest}/apple2/chips.hgr | Bin {testcode => targettest}/apple2/dhgrshow.c | 0 {testcode => targettest}/apple2/gatsby.dhgr | Bin {testcode => targettest}/apple2/girl.dhgr | Bin {testcode => targettest}/apple2/hgrshow.c | 0 {testcode => targettest}/apple2/hgrtest.c | 0 {testcode => targettest}/apple2/macrometer.hgr | Bin {testcode => targettest}/apple2/mariner.hgr | Bin {testcode => targettest}/apple2/monarch.dhgr | Bin {testcode => targettest}/apple2/rose.hgr | Bin {testcode => targettest}/apple2/superman.dhgr | Bin {testcode => targettest}/apple2/venice.dhgr | Bin {testcode => targettest}/apple2/werner.hgr | Bin {testcode => targettest}/apple2/werner.s | 0 {testcode => targettest}/apple2/winston.hgr | Bin {testcode => targettest}/arg-test.c | 0 {testcode => targettest}/atari/Makefile | 0 {testcode => targettest}/atari/asm-xex.s | 0 {testcode => targettest}/atari/charmapping.c | 0 {testcode => targettest}/atari/defdev.c | 0 {testcode => targettest}/atari/displaylist.c | 0 {testcode => targettest}/atari/mem.c | 0 {testcode => targettest}/atari/multi-xex.cfg | 0 {testcode => targettest}/atari/multi-xex.s | 0 {testcode => targettest}/atari/ostype.c | 0 {testcode => targettest}/atari/scrcode.s | 0 {testcode => targettest}/atari/sys.c | 0 {testcode => targettest}/atari5200/Makefile | 0 {testcode => targettest}/atari5200/hello.c | 0 {testcode => targettest}/cbm/Makefile | 0 {testcode => targettest}/cbm/petscii.c | 0 {testcode => targettest}/clock-test.c | 0 {testcode => targettest}/clock.c | 0 {testcode => targettest}/conio.c | 0 {testcode => targettest}/cpeek-test.c | 0 {testcode => targettest}/cprintf.c | 0 {testcode => targettest}/cursor.c | 0 {testcode => targettest}/deb.c | 0 {testcode => targettest}/dir-test.c | 0 {testcode => targettest}/div-test.c | 0 {testcode => targettest}/em-test.c | 0 {testcode => targettest}/exec-test1.c | 0 {testcode => targettest}/exec-test2.c | 0 {testcode => targettest}/fileio-test.c | 0 {testcode => targettest}/ft.c | 0 {testcode => targettest}/gamate/Makefile | 0 {testcode => targettest}/gamate/audiotest.s | 0 {testcode => targettest}/gamate/cga2.chr | Bin {testcode => targettest}/gamate/ctest.c | 0 {testcode => targettest}/gamate/lcdtest.s | 0 {testcode => targettest}/gamate/nachtm.c | 0 {testcode => targettest}/getopt-test.c | 0 {testcode => targettest}/getsp.s | 0 {testcode => targettest}/heaptest.c | 0 {testcode => targettest}/joy-test.c | 0 {testcode => targettest}/moddiv-test.c | 0 {testcode => targettest}/mouse-test.c | 0 {testcode => targettest}/mul-test.c | 0 {testcode => targettest}/pce/Makefile | 0 {testcode => targettest}/pce/conio.c | 0 {testcode => targettest}/posixio-test.c | 0 {testcode => targettest}/rename-test.c | 0 {testcode => targettest}/scanf-test.c | 0 {testcode => targettest}/seek.c | 0 {testcode => targettest}/ser-test.c | 0 {testcode => targettest}/strdup-test.c | 0 {testcode => targettest}/stroserror-test.c | 0 {testcode => targettest}/strqtok-test.c | 0 {testcode => targettest}/tinyshell.c | 0 {testcode => targettest}/uname-test.c | 0 82 files changed, 0 insertions(+), 0 deletions(-) rename {testcode => targettest}/Makefile (100%) rename {testcode => targettest}/accelerator/Makefile (100%) rename {testcode => targettest}/accelerator/c64-c128-scpu-test.c (100%) rename {testcode => targettest}/accelerator/c64-c128-test.c (100%) rename {testcode => targettest}/accelerator/c64dtv-test.c (100%) rename {testcode => targettest}/accelerator/c65-test.c (100%) rename {testcode => targettest}/accelerator/chameleon-test.c (100%) rename {testcode => targettest}/accelerator/turbo-test.c (100%) rename {testcode => targettest}/accelerator/turbomaster-test.c (100%) rename {testcode => targettest}/apple2/Makefile (100%) rename {testcode => targettest}/apple2/astronaut.hgr (100%) rename {testcode => targettest}/apple2/catface.dhgr (100%) rename {testcode => targettest}/apple2/chips.hgr (100%) rename {testcode => targettest}/apple2/dhgrshow.c (100%) rename {testcode => targettest}/apple2/gatsby.dhgr (100%) rename {testcode => targettest}/apple2/girl.dhgr (100%) rename {testcode => targettest}/apple2/hgrshow.c (100%) rename {testcode => targettest}/apple2/hgrtest.c (100%) rename {testcode => targettest}/apple2/macrometer.hgr (100%) rename {testcode => targettest}/apple2/mariner.hgr (100%) rename {testcode => targettest}/apple2/monarch.dhgr (100%) rename {testcode => targettest}/apple2/rose.hgr (100%) rename {testcode => targettest}/apple2/superman.dhgr (100%) rename {testcode => targettest}/apple2/venice.dhgr (100%) rename {testcode => targettest}/apple2/werner.hgr (100%) rename {testcode => targettest}/apple2/werner.s (100%) rename {testcode => targettest}/apple2/winston.hgr (100%) rename {testcode => targettest}/arg-test.c (100%) rename {testcode => targettest}/atari/Makefile (100%) rename {testcode => targettest}/atari/asm-xex.s (100%) rename {testcode => targettest}/atari/charmapping.c (100%) rename {testcode => targettest}/atari/defdev.c (100%) rename {testcode => targettest}/atari/displaylist.c (100%) rename {testcode => targettest}/atari/mem.c (100%) rename {testcode => targettest}/atari/multi-xex.cfg (100%) rename {testcode => targettest}/atari/multi-xex.s (100%) rename {testcode => targettest}/atari/ostype.c (100%) rename {testcode => targettest}/atari/scrcode.s (100%) rename {testcode => targettest}/atari/sys.c (100%) rename {testcode => targettest}/atari5200/Makefile (100%) rename {testcode => targettest}/atari5200/hello.c (100%) rename {testcode => targettest}/cbm/Makefile (100%) rename {testcode => targettest}/cbm/petscii.c (100%) rename {testcode => targettest}/clock-test.c (100%) rename {testcode => targettest}/clock.c (100%) rename {testcode => targettest}/conio.c (100%) rename {testcode => targettest}/cpeek-test.c (100%) rename {testcode => targettest}/cprintf.c (100%) rename {testcode => targettest}/cursor.c (100%) rename {testcode => targettest}/deb.c (100%) rename {testcode => targettest}/dir-test.c (100%) rename {testcode => targettest}/div-test.c (100%) rename {testcode => targettest}/em-test.c (100%) rename {testcode => targettest}/exec-test1.c (100%) rename {testcode => targettest}/exec-test2.c (100%) rename {testcode => targettest}/fileio-test.c (100%) rename {testcode => targettest}/ft.c (100%) rename {testcode => targettest}/gamate/Makefile (100%) rename {testcode => targettest}/gamate/audiotest.s (100%) rename {testcode => targettest}/gamate/cga2.chr (100%) rename {testcode => targettest}/gamate/ctest.c (100%) rename {testcode => targettest}/gamate/lcdtest.s (100%) rename {testcode => targettest}/gamate/nachtm.c (100%) rename {testcode => targettest}/getopt-test.c (100%) rename {testcode => targettest}/getsp.s (100%) rename {testcode => targettest}/heaptest.c (100%) rename {testcode => targettest}/joy-test.c (100%) rename {testcode => targettest}/moddiv-test.c (100%) rename {testcode => targettest}/mouse-test.c (100%) rename {testcode => targettest}/mul-test.c (100%) rename {testcode => targettest}/pce/Makefile (100%) rename {testcode => targettest}/pce/conio.c (100%) rename {testcode => targettest}/posixio-test.c (100%) rename {testcode => targettest}/rename-test.c (100%) rename {testcode => targettest}/scanf-test.c (100%) rename {testcode => targettest}/seek.c (100%) rename {testcode => targettest}/ser-test.c (100%) rename {testcode => targettest}/strdup-test.c (100%) rename {testcode => targettest}/stroserror-test.c (100%) rename {testcode => targettest}/strqtok-test.c (100%) rename {testcode => targettest}/tinyshell.c (100%) rename {testcode => targettest}/uname-test.c (100%) diff --git a/testcode/Makefile b/targettest/Makefile similarity index 100% rename from testcode/Makefile rename to targettest/Makefile diff --git a/testcode/accelerator/Makefile b/targettest/accelerator/Makefile similarity index 100% rename from testcode/accelerator/Makefile rename to targettest/accelerator/Makefile diff --git a/testcode/accelerator/c64-c128-scpu-test.c b/targettest/accelerator/c64-c128-scpu-test.c similarity index 100% rename from testcode/accelerator/c64-c128-scpu-test.c rename to targettest/accelerator/c64-c128-scpu-test.c diff --git a/testcode/accelerator/c64-c128-test.c b/targettest/accelerator/c64-c128-test.c similarity index 100% rename from testcode/accelerator/c64-c128-test.c rename to targettest/accelerator/c64-c128-test.c diff --git a/testcode/accelerator/c64dtv-test.c b/targettest/accelerator/c64dtv-test.c similarity index 100% rename from testcode/accelerator/c64dtv-test.c rename to targettest/accelerator/c64dtv-test.c diff --git a/testcode/accelerator/c65-test.c b/targettest/accelerator/c65-test.c similarity index 100% rename from testcode/accelerator/c65-test.c rename to targettest/accelerator/c65-test.c diff --git a/testcode/accelerator/chameleon-test.c b/targettest/accelerator/chameleon-test.c similarity index 100% rename from testcode/accelerator/chameleon-test.c rename to targettest/accelerator/chameleon-test.c diff --git a/testcode/accelerator/turbo-test.c b/targettest/accelerator/turbo-test.c similarity index 100% rename from testcode/accelerator/turbo-test.c rename to targettest/accelerator/turbo-test.c diff --git a/testcode/accelerator/turbomaster-test.c b/targettest/accelerator/turbomaster-test.c similarity index 100% rename from testcode/accelerator/turbomaster-test.c rename to targettest/accelerator/turbomaster-test.c diff --git a/testcode/apple2/Makefile b/targettest/apple2/Makefile similarity index 100% rename from testcode/apple2/Makefile rename to targettest/apple2/Makefile diff --git a/testcode/apple2/astronaut.hgr b/targettest/apple2/astronaut.hgr similarity index 100% rename from testcode/apple2/astronaut.hgr rename to targettest/apple2/astronaut.hgr diff --git a/testcode/apple2/catface.dhgr b/targettest/apple2/catface.dhgr similarity index 100% rename from testcode/apple2/catface.dhgr rename to targettest/apple2/catface.dhgr diff --git a/testcode/apple2/chips.hgr b/targettest/apple2/chips.hgr similarity index 100% rename from testcode/apple2/chips.hgr rename to targettest/apple2/chips.hgr diff --git a/testcode/apple2/dhgrshow.c b/targettest/apple2/dhgrshow.c similarity index 100% rename from testcode/apple2/dhgrshow.c rename to targettest/apple2/dhgrshow.c diff --git a/testcode/apple2/gatsby.dhgr b/targettest/apple2/gatsby.dhgr similarity index 100% rename from testcode/apple2/gatsby.dhgr rename to targettest/apple2/gatsby.dhgr diff --git a/testcode/apple2/girl.dhgr b/targettest/apple2/girl.dhgr similarity index 100% rename from testcode/apple2/girl.dhgr rename to targettest/apple2/girl.dhgr diff --git a/testcode/apple2/hgrshow.c b/targettest/apple2/hgrshow.c similarity index 100% rename from testcode/apple2/hgrshow.c rename to targettest/apple2/hgrshow.c diff --git a/testcode/apple2/hgrtest.c b/targettest/apple2/hgrtest.c similarity index 100% rename from testcode/apple2/hgrtest.c rename to targettest/apple2/hgrtest.c diff --git a/testcode/apple2/macrometer.hgr b/targettest/apple2/macrometer.hgr similarity index 100% rename from testcode/apple2/macrometer.hgr rename to targettest/apple2/macrometer.hgr diff --git a/testcode/apple2/mariner.hgr b/targettest/apple2/mariner.hgr similarity index 100% rename from testcode/apple2/mariner.hgr rename to targettest/apple2/mariner.hgr diff --git a/testcode/apple2/monarch.dhgr b/targettest/apple2/monarch.dhgr similarity index 100% rename from testcode/apple2/monarch.dhgr rename to targettest/apple2/monarch.dhgr diff --git a/testcode/apple2/rose.hgr b/targettest/apple2/rose.hgr similarity index 100% rename from testcode/apple2/rose.hgr rename to targettest/apple2/rose.hgr diff --git a/testcode/apple2/superman.dhgr b/targettest/apple2/superman.dhgr similarity index 100% rename from testcode/apple2/superman.dhgr rename to targettest/apple2/superman.dhgr diff --git a/testcode/apple2/venice.dhgr b/targettest/apple2/venice.dhgr similarity index 100% rename from testcode/apple2/venice.dhgr rename to targettest/apple2/venice.dhgr diff --git a/testcode/apple2/werner.hgr b/targettest/apple2/werner.hgr similarity index 100% rename from testcode/apple2/werner.hgr rename to targettest/apple2/werner.hgr diff --git a/testcode/apple2/werner.s b/targettest/apple2/werner.s similarity index 100% rename from testcode/apple2/werner.s rename to targettest/apple2/werner.s diff --git a/testcode/apple2/winston.hgr b/targettest/apple2/winston.hgr similarity index 100% rename from testcode/apple2/winston.hgr rename to targettest/apple2/winston.hgr diff --git a/testcode/arg-test.c b/targettest/arg-test.c similarity index 100% rename from testcode/arg-test.c rename to targettest/arg-test.c diff --git a/testcode/atari/Makefile b/targettest/atari/Makefile similarity index 100% rename from testcode/atari/Makefile rename to targettest/atari/Makefile diff --git a/testcode/atari/asm-xex.s b/targettest/atari/asm-xex.s similarity index 100% rename from testcode/atari/asm-xex.s rename to targettest/atari/asm-xex.s diff --git a/testcode/atari/charmapping.c b/targettest/atari/charmapping.c similarity index 100% rename from testcode/atari/charmapping.c rename to targettest/atari/charmapping.c diff --git a/testcode/atari/defdev.c b/targettest/atari/defdev.c similarity index 100% rename from testcode/atari/defdev.c rename to targettest/atari/defdev.c diff --git a/testcode/atari/displaylist.c b/targettest/atari/displaylist.c similarity index 100% rename from testcode/atari/displaylist.c rename to targettest/atari/displaylist.c diff --git a/testcode/atari/mem.c b/targettest/atari/mem.c similarity index 100% rename from testcode/atari/mem.c rename to targettest/atari/mem.c diff --git a/testcode/atari/multi-xex.cfg b/targettest/atari/multi-xex.cfg similarity index 100% rename from testcode/atari/multi-xex.cfg rename to targettest/atari/multi-xex.cfg diff --git a/testcode/atari/multi-xex.s b/targettest/atari/multi-xex.s similarity index 100% rename from testcode/atari/multi-xex.s rename to targettest/atari/multi-xex.s diff --git a/testcode/atari/ostype.c b/targettest/atari/ostype.c similarity index 100% rename from testcode/atari/ostype.c rename to targettest/atari/ostype.c diff --git a/testcode/atari/scrcode.s b/targettest/atari/scrcode.s similarity index 100% rename from testcode/atari/scrcode.s rename to targettest/atari/scrcode.s diff --git a/testcode/atari/sys.c b/targettest/atari/sys.c similarity index 100% rename from testcode/atari/sys.c rename to targettest/atari/sys.c diff --git a/testcode/atari5200/Makefile b/targettest/atari5200/Makefile similarity index 100% rename from testcode/atari5200/Makefile rename to targettest/atari5200/Makefile diff --git a/testcode/atari5200/hello.c b/targettest/atari5200/hello.c similarity index 100% rename from testcode/atari5200/hello.c rename to targettest/atari5200/hello.c diff --git a/testcode/cbm/Makefile b/targettest/cbm/Makefile similarity index 100% rename from testcode/cbm/Makefile rename to targettest/cbm/Makefile diff --git a/testcode/cbm/petscii.c b/targettest/cbm/petscii.c similarity index 100% rename from testcode/cbm/petscii.c rename to targettest/cbm/petscii.c diff --git a/testcode/clock-test.c b/targettest/clock-test.c similarity index 100% rename from testcode/clock-test.c rename to targettest/clock-test.c diff --git a/testcode/clock.c b/targettest/clock.c similarity index 100% rename from testcode/clock.c rename to targettest/clock.c diff --git a/testcode/conio.c b/targettest/conio.c similarity index 100% rename from testcode/conio.c rename to targettest/conio.c diff --git a/testcode/cpeek-test.c b/targettest/cpeek-test.c similarity index 100% rename from testcode/cpeek-test.c rename to targettest/cpeek-test.c diff --git a/testcode/cprintf.c b/targettest/cprintf.c similarity index 100% rename from testcode/cprintf.c rename to targettest/cprintf.c diff --git a/testcode/cursor.c b/targettest/cursor.c similarity index 100% rename from testcode/cursor.c rename to targettest/cursor.c diff --git a/testcode/deb.c b/targettest/deb.c similarity index 100% rename from testcode/deb.c rename to targettest/deb.c diff --git a/testcode/dir-test.c b/targettest/dir-test.c similarity index 100% rename from testcode/dir-test.c rename to targettest/dir-test.c diff --git a/testcode/div-test.c b/targettest/div-test.c similarity index 100% rename from testcode/div-test.c rename to targettest/div-test.c diff --git a/testcode/em-test.c b/targettest/em-test.c similarity index 100% rename from testcode/em-test.c rename to targettest/em-test.c diff --git a/testcode/exec-test1.c b/targettest/exec-test1.c similarity index 100% rename from testcode/exec-test1.c rename to targettest/exec-test1.c diff --git a/testcode/exec-test2.c b/targettest/exec-test2.c similarity index 100% rename from testcode/exec-test2.c rename to targettest/exec-test2.c diff --git a/testcode/fileio-test.c b/targettest/fileio-test.c similarity index 100% rename from testcode/fileio-test.c rename to targettest/fileio-test.c diff --git a/testcode/ft.c b/targettest/ft.c similarity index 100% rename from testcode/ft.c rename to targettest/ft.c diff --git a/testcode/gamate/Makefile b/targettest/gamate/Makefile similarity index 100% rename from testcode/gamate/Makefile rename to targettest/gamate/Makefile diff --git a/testcode/gamate/audiotest.s b/targettest/gamate/audiotest.s similarity index 100% rename from testcode/gamate/audiotest.s rename to targettest/gamate/audiotest.s diff --git a/testcode/gamate/cga2.chr b/targettest/gamate/cga2.chr similarity index 100% rename from testcode/gamate/cga2.chr rename to targettest/gamate/cga2.chr diff --git a/testcode/gamate/ctest.c b/targettest/gamate/ctest.c similarity index 100% rename from testcode/gamate/ctest.c rename to targettest/gamate/ctest.c diff --git a/testcode/gamate/lcdtest.s b/targettest/gamate/lcdtest.s similarity index 100% rename from testcode/gamate/lcdtest.s rename to targettest/gamate/lcdtest.s diff --git a/testcode/gamate/nachtm.c b/targettest/gamate/nachtm.c similarity index 100% rename from testcode/gamate/nachtm.c rename to targettest/gamate/nachtm.c diff --git a/testcode/getopt-test.c b/targettest/getopt-test.c similarity index 100% rename from testcode/getopt-test.c rename to targettest/getopt-test.c diff --git a/testcode/getsp.s b/targettest/getsp.s similarity index 100% rename from testcode/getsp.s rename to targettest/getsp.s diff --git a/testcode/heaptest.c b/targettest/heaptest.c similarity index 100% rename from testcode/heaptest.c rename to targettest/heaptest.c diff --git a/testcode/joy-test.c b/targettest/joy-test.c similarity index 100% rename from testcode/joy-test.c rename to targettest/joy-test.c diff --git a/testcode/moddiv-test.c b/targettest/moddiv-test.c similarity index 100% rename from testcode/moddiv-test.c rename to targettest/moddiv-test.c diff --git a/testcode/mouse-test.c b/targettest/mouse-test.c similarity index 100% rename from testcode/mouse-test.c rename to targettest/mouse-test.c diff --git a/testcode/mul-test.c b/targettest/mul-test.c similarity index 100% rename from testcode/mul-test.c rename to targettest/mul-test.c diff --git a/testcode/pce/Makefile b/targettest/pce/Makefile similarity index 100% rename from testcode/pce/Makefile rename to targettest/pce/Makefile diff --git a/testcode/pce/conio.c b/targettest/pce/conio.c similarity index 100% rename from testcode/pce/conio.c rename to targettest/pce/conio.c diff --git a/testcode/posixio-test.c b/targettest/posixio-test.c similarity index 100% rename from testcode/posixio-test.c rename to targettest/posixio-test.c diff --git a/testcode/rename-test.c b/targettest/rename-test.c similarity index 100% rename from testcode/rename-test.c rename to targettest/rename-test.c diff --git a/testcode/scanf-test.c b/targettest/scanf-test.c similarity index 100% rename from testcode/scanf-test.c rename to targettest/scanf-test.c diff --git a/testcode/seek.c b/targettest/seek.c similarity index 100% rename from testcode/seek.c rename to targettest/seek.c diff --git a/testcode/ser-test.c b/targettest/ser-test.c similarity index 100% rename from testcode/ser-test.c rename to targettest/ser-test.c diff --git a/testcode/strdup-test.c b/targettest/strdup-test.c similarity index 100% rename from testcode/strdup-test.c rename to targettest/strdup-test.c diff --git a/testcode/stroserror-test.c b/targettest/stroserror-test.c similarity index 100% rename from testcode/stroserror-test.c rename to targettest/stroserror-test.c diff --git a/testcode/strqtok-test.c b/targettest/strqtok-test.c similarity index 100% rename from testcode/strqtok-test.c rename to targettest/strqtok-test.c diff --git a/testcode/tinyshell.c b/targettest/tinyshell.c similarity index 100% rename from testcode/tinyshell.c rename to targettest/tinyshell.c diff --git a/testcode/uname-test.c b/targettest/uname-test.c similarity index 100% rename from testcode/uname-test.c rename to targettest/uname-test.c From c05a750f470e21558cc0eab561b0017c7456c52d Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 1 Oct 2020 07:25:08 -0400 Subject: [PATCH 381/806] Fixed some copy-&-paste typo mistakes about HuC6280's TMA mnemonic. --- src/ca65/instr.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 58011eb8f..d71476799 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -92,7 +92,7 @@ static void PutSEP (const InsDesc* Ins); /* Emit a SEP instruction (65816), track register sizes */ static void PutTAMn (const InsDesc* Ins); -/* Emit a TAMn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TAMn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the ** second operand byte. The TAM instruction is the more generic form, it takes ** an immediate argument. @@ -104,9 +104,9 @@ static void PutTMA (const InsDesc* Ins); */ static void PutTMAn (const InsDesc* Ins); -/* Emit a TMAn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TMAn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the -** second operand byte. The TAM instruction is the more generic form, it takes +** second operand byte. The TMA instruction is the more generic form, it takes ** an immediate argument. */ @@ -1093,7 +1093,7 @@ static int EvalEA (const InsDesc* Ins, EffAddr* A) A->AddrModeSet &= Ins->AddrMode; /* If we have an expression, check it and remove any addressing modes that - ** are too small for the expression size. Since we have to study the + ** are too small for the expression size. Because we have to study the ** expression anyway, do also replace it by a simpler one if possible. */ if (A->Expr) { @@ -1414,7 +1414,7 @@ static void PutSEP (const InsDesc* Ins) static void PutTAMn (const InsDesc* Ins) -/* Emit a TAMn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TAMn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the ** second operand byte. The TAM instruction is the more generic form, it takes ** an immediate argument. @@ -1444,7 +1444,7 @@ static void PutTMA (const InsDesc* Ins) } else { /* Make sure just one bit is set */ if ((Val & (Val - 1)) != 0) { - Error ("Argument to TAM must be a power of two"); + Error ("Argument of TMA must be a power of two"); } } } @@ -1452,9 +1452,9 @@ static void PutTMA (const InsDesc* Ins) static void PutTMAn (const InsDesc* Ins) -/* Emit a TMAn instruction (HuC6280). Since this is a two byte instruction with +/* Emit a TMAn instruction (HuC6280). Because this is a two-byte instruction with ** implicit addressing mode, the opcode byte in the table is actually the -** second operand byte. The TAM instruction is the more generic form, it takes +** second operand byte. The TMA instruction is the more generic form, it takes ** an immediate argument. */ { From 4acdc9ced9ded4c37e46d2bc531bea31894c1ccc Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sat, 3 Oct 2020 14:55:30 +0200 Subject: [PATCH 382/806] Fixed paramcount build. --- test/asm/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/asm/Makefile b/test/asm/Makefile index 028254b68..9b0aa582e 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -38,7 +38,7 @@ CPUDETECT_REFS := $(wildcard *-cpudetect.ref) CPUDETECT_CPUS = $(foreach ref,$(CPUDETECT_REFS),$(ref:%-cpudetect.ref=%)) CPUDETECT_BINS = $(foreach cpu,$(CPUDETECT_CPUS),$(WORKDIR)/$(cpu)-cpudetect.bin) -all: $(OPCODE_BINS) $(CPUDETECT_BINS) paramcount.o +all: $(OPCODE_BINS) $(CPUDETECT_BINS) $(WORKDIR)/paramcount.o $(WORKDIR): $(call MKDIR,$(WORKDIR)) @@ -70,8 +70,8 @@ endef # CPUDETECT_template $(foreach cpu,$(CPUDETECT_CPUS),$(eval $(call CPUDETECT_template,$(cpu)))) -paramcount.o: paramcount.s - $(CA65) -o $(WORKDIR)/paramcount.o -l $(WORKDIR)/paramcount.lst paramcount.s +$(WORKDIR)/%.o: %.s | $(WORKDIR) + $(CA65) -l $(@:.o=.lst) -o $(WORKDIR)/$@ $< clean: @$(call RMDIR,$(WORKDIR)) From b931e658117189cf62662d4919618bb0ffb00144 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 26 Sep 2020 14:56:28 +0200 Subject: [PATCH 383/806] Fix ICE for bit-fields with typedef Fixes #1267 Avoid ICE, but treat plain int bit-fields declared via typedef as signed rather than unsigned. It is more efficient to treat them as unsigned, but this requires distinguishing int from signed int, and this is curently not done. --- src/cc65/declare.c | 9 ++++ test/misc/Makefile | 6 --- test/{misc => val}/bug1094.c | 0 test/val/bug1267.c | 79 ++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 6 deletions(-) rename test/{misc => val}/bug1094.c (100%) create mode 100644 test/val/bug1267.c diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8623fa08f..bf27f9c90 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1468,6 +1468,15 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* It's a typedef */ NextToken (); TypeCopy (D->Type, Entry->Type); + /* If it's a typedef, we should actually use whether the signedness was + ** specified on the typedef, but that information has been lost. Treat the + ** signedness as being specified to work around the ICE in #1267. + ** Unforunately, this will cause plain int bit-fields defined via typedefs + ** to be treated as signed rather than unsigned. + */ + if (SignednessSpecified) { + *SignednessSpecified = 1; + } break; } } else { diff --git a/test/misc/Makefile b/test/misc/Makefile index 06046af3c..b8c578405 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -100,12 +100,6 @@ $(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# should compile, but gives an error -$(WORKDIR)/bug1094.$1.$2.prg: bug1094.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug1094.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # this one requires --std=c89, it fails with --std=c99 # it fails currently at runtime $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) diff --git a/test/misc/bug1094.c b/test/val/bug1094.c similarity index 100% rename from test/misc/bug1094.c rename to test/val/bug1094.c diff --git a/test/val/bug1267.c b/test/val/bug1267.c new file mode 100644 index 000000000..382903594 --- /dev/null +++ b/test/val/bug1267.c @@ -0,0 +1,79 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of bit-field signedness with typedefs; see https://github.com/cc65/cc65/issues/1267 +*/ + +#include + +static unsigned char failures = 0; + +typedef int i16; +typedef unsigned int u16; +typedef signed int s16; + +static struct ints { + i16 i : 4; + u16 u : 4; + s16 s : 4; +} si = {1, 2, 3}; + +static void test_bitfield_typedefs (void) +{ + if (si.i != 1) { + /* Note that this is another bug that i is signed. */ + printf ("Got si.a = %d, expected 1.\n", si.i); + failures++; + } + if (si.u != 2) { + printf ("Got si.u = %u, expected 2.\n", si.u); + failures++; + } + if (si.s != 3) { + printf ("Got si.s = %d, expected 3.\n", si.s); + failures++; + } + + si.i = -1; + si.u = -2; + si.s = -3; + + /* Note that this is another bug that i is signed. */ + if (si.i != -1) { + printf ("Got si.a = %d, expected -1.\n", si.i); + failures++; + } + if (si.u != 14) { + printf ("Got si.u = %u, expected 14.\n", si.u); + failures++; + } + if (si.s != -3) { + printf ("Got si.s = %d, expected -3.\n", si.s); + failures++; + } +} + +int main (void) +{ + test_bitfield_typedefs (); + printf ("failures: %u\n", failures); + return failures; +} From fd208fdf0b6f968ba7491dcec1f53be71af08abd Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sat, 3 Oct 2020 19:35:23 +0200 Subject: [PATCH 384/806] - Added support for calling subdir Makefiles for the make targets 'samples' and 'clean'. - Adjusted the 'tutorial' Makefile to actually work as expected. Note: The 'disasm' and 'geos' Makefiles don't seem to work so they are not called as of now. --- samples/Makefile | 13 +++++++++++++ samples/tutorial/Makefile | 14 +++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/samples/Makefile b/samples/Makefile index 267dc5235..2d1e25475 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -150,6 +150,11 @@ else $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -t $(SYS) -m $@.map $^ $(SYS).lib endif +# -------------------------------------------------------------------------- +# Lists of subdirectories + +DIRLIST = tutorial + # -------------------------------------------------------------------------- # Lists of executables @@ -209,10 +214,17 @@ ifndef EXELIST_$(SYS) EXELIST_$(SYS) := ${patsubst %.c,%,$(wildcard *.c)} endif +define SUBDIR_recipe + +@$(MAKE) -C $(dir) --no-print-directory $@ + +endef # SUBDIR_recipe + # -------------------------------------------------------------------------- # Rules to make the binaries and the disk samples: $(EXELIST_$(SYS)) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) disk: $(DISK_$(SYS)) @@ -338,3 +350,4 @@ mostlyclean: clean: mostlyclean @$(DEL) $(EXELIST_$(SYS)) $(DISK_$(SYS)) 2>$(NULLDEV) @$(DEL) multdemo.? ovrldemo.? 2>$(NULLDEV) + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile index 8ed91697f..2c4bcb988 100644 --- a/samples/tutorial/Makefile +++ b/samples/tutorial/Makefile @@ -25,16 +25,16 @@ ifdef CC65_HOME CL = $(CC65_HOME)/bin/cl65 LD = $(CC65_HOME)/bin/ld65 else - AS := $(if $(wildcard ../bin/ca65*),../bin/ca65,ca65) - CC := $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65) - CL := $(if $(wildcard ../bin/cl65*),../bin/cl65,cl65) - LD := $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65) + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) endif -all: hello +samples: hello hello: hello.c text.s $(CL) -t $(SYS) -o hello hello.c text.s - + clean: - $(RM) hello + @$(DEL) hello 2>$(NULLDEV) From a85d5330fab3ffd8053f6d6f54b46da18de766f8 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 4 Oct 2020 12:15:59 +0800 Subject: [PATCH 385/806] Fixed StrBuf initialization in PreparseArg(). --- src/cc65/codeent.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index f3a910652..dd2000db0 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -585,8 +585,7 @@ const char* MakeHexArg (unsigned Num) void PreparseArg (CodeEntry* E) /* Parse the argument string and memorize the result for the code entry */ { - StrBuf B; - SB_InitFromString (&B, xmalloc (strlen (E->Arg) + 1)); + StrBuf B = AUTO_STRBUF_INITIALIZER; /* Parse the argument string */ if (ParseOpcArgStr (E->Arg, &E->ArgInfo, &B, &E->ArgOff)) { From dbba5f3fc9ec1256b7226f458531b81bac25e983 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 4 Oct 2020 17:01:20 +0800 Subject: [PATCH 386/806] Fixed handling of %v in inline asm parser. --- src/cc65/asmstmt.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index bf5a0815b..d182140fd 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -213,7 +213,9 @@ static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) static void ParseGVarArg (StrBuf* T, unsigned Arg) -/* Parse the %v format specifier */ +/* Parse the %v format specifier. +** ### FIXME: Asm names should be generated in the same place. +*/ { /* Parse the symbol name parameter and check the type */ SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); @@ -225,7 +227,6 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) /* Check for external linkage */ if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) { /* External linkage or a function */ - /* ### FIXME: Asm name should be generated by codegen */ SB_AppendChar (T, '_'); SB_AppendStr (T, Sym->Name); } else if (Sym->Flags & SC_REGISTER) { @@ -234,9 +235,7 @@ static void ParseGVarArg (StrBuf* T, unsigned Arg) SB_AppendStr (T, Buf); } else { /* Static variable */ - char Buf [16]; - xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.L.Label); - SB_AppendStr (T, Buf); + SB_AppendStr (T, LocalDataLabelName (Sym->V.L.Label)); } } From 037a806036a1af63e5e6f0d2752f507ae2c4d235 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sun, 4 Oct 2020 17:20:40 +0200 Subject: [PATCH 387/806] also rename README to readme.txt in the Makefile :) --- samples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Makefile b/samples/Makefile index 2d1e25475..c9d205bf1 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -330,7 +330,7 @@ install: $(INSTALL) -d $(DESTDIR)$(samplesdir)/geos $(INSTALL) -d $(DESTDIR)$(samplesdir)/tutorial $(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir) - $(INSTALL) -m0644 README $(DESTDIR)$(samplesdir) + $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos $(INSTALL) -m0644 tutorial/*.* $(DESTDIR)$(samplesdir)/tutorial From 81550ca1ee6b7520539c047b5aaeea5a86118df4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 24 Aug 2020 22:24:00 +0200 Subject: [PATCH 388/806] CS_MergeLabels: Keep labels referenced by data Partial fix for ICE in #1211. This may fix enough to allow #1049 to be fixed. When merging labels, keep the first label with a ref that has no JumpTo; this is a data segment label, used by computed gotos. The real fix is to track and rewrite labels in data, but this is more involved. --- src/cc65/codeent.h | 10 ++++++ src/cc65/codeseg.c | 37 +++++++++++++++++++-- test/{err => val}/bug1211-ice-move-refs-1.c | 3 +- 3 files changed, 47 insertions(+), 3 deletions(-) rename test/{err => val}/bug1211-ice-move-refs-1.c (91%) diff --git a/src/cc65/codeent.h b/src/cc65/codeent.h index 173118a7f..bd542cc4c 100644 --- a/src/cc65/codeent.h +++ b/src/cc65/codeent.h @@ -180,6 +180,16 @@ INLINE CodeLabel* CE_GetLabel (CodeEntry* E, unsigned Index) # define CE_GetLabel(E, Index) CollAt (&(E)->Labels, (Index)) #endif +#if defined(HAVE_INLINE) +INLINE void CE_ReplaceLabel (CodeEntry* E, CodeLabel* L, unsigned Index) +/* Replace the code label at the specified index with L */ +{ + return CollReplace (&E->Labels, L, Index); +} +#else +# define CE_ReplaceLabel(E, L, Index) CollReplace (&(E)->Labels, (L), (Index)) +#endif + void CE_MoveLabel (CodeLabel* L, CodeEntry* E); /* Move the code label L from it's former owner to the code entry E. */ diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index ad8a3148c..8177d4fd4 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -183,6 +183,37 @@ static void CS_RemoveLabelFromHash (CodeSeg* S, CodeLabel* L) +static CodeLabel* PickRefLab (CodeEntry* E) +/* Pick a reference label and move it to index 0 in E. */ +{ + unsigned I; + unsigned LabelCount = CE_GetLabelCount (E); + CHECK (LabelCount > 0); + /* Use either the first one as reference label, or a label with a Ref that has no JumpTo. + ** This is a hack to partially work around #1211. Refs with no JumpTo are labels used + ** in data segments. (They are not tracked.) If a data segment is the only reference, + ** the label will be pruned away, but the data reference will remain, causing linking to fail. + */ + CodeLabel* L0 = CE_GetLabel (E, 0); + for (I = 1; I < LabelCount; ++I) { + unsigned J; + CodeLabel* L = CE_GetLabel (E, I); + unsigned RefCount = CL_GetRefCount (L); + for (J = 0; J < RefCount; ++J) { + CodeEntry* EJ = CL_GetRef (L, J); + if (EJ->JumpTo == NULL) { + /* Move it to the beginning since it's simpler to handle the removal this way. */ + CE_ReplaceLabel (E, L, 0); + CE_ReplaceLabel (E, L0, I); + return L; + } + } + } + return L0; +} + + + /*****************************************************************************/ /* Functions for parsing instructions */ /*****************************************************************************/ @@ -935,8 +966,10 @@ void CS_MergeLabels (CodeSeg* S) continue; } - /* We have at least one label. Use the first one as reference label. */ - RefLab = CE_GetLabel (E, 0); + /* Pick a label to keep, all references will be moved to this one, and the other labels + ** will be deleted. PickRefLab moves this to index 0. + */ + RefLab = PickRefLab (E); /* Walk through the remaining labels and change references to these ** labels to a reference to the one and only label. Delete the labels diff --git a/test/err/bug1211-ice-move-refs-1.c b/test/val/bug1211-ice-move-refs-1.c similarity index 91% rename from test/err/bug1211-ice-move-refs-1.c rename to test/val/bug1211-ice-move-refs-1.c index f28d1fcbc..9be1696ee 100644 --- a/test/err/bug1211-ice-move-refs-1.c +++ b/test/val/bug1211-ice-move-refs-1.c @@ -21,7 +21,8 @@ /* Test of indirect goto with label merge ICE. https://github.com/cc65/cc65/issues/1211 - This should compile and should be moved to tests/val/ when the bug is fixed. + This test case works because CS_MergeLabels has a hack to keep the "unreferenced" label + (i.e. the one referenced in a data segment). */ #include From f8c9dde989e501d11ebef2ec4befbe2857371e1f Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jan 2020 10:39:19 +0800 Subject: [PATCH 389/806] The instruction parser can now recognize ZP locations and set the addressing mode for them. --- src/cc65/codeinfo.c | 111 +++++++++++++++++++++++++++++++++++++------- src/cc65/codeinfo.h | 6 ++- src/cc65/codeseg.c | 7 ++- src/cc65/opcodes.c | 1 + 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 892f4cd5f..e0cda84e9 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -349,23 +349,23 @@ static const FuncInfo FuncInfoTable[] = { /* Table with names of zero page locations used by the compiler */ static const ZPInfo ZPInfoTable[] = { - { 0, "ptr1", REG_PTR1_LO, REG_PTR1 }, - { 0, "ptr1+1", REG_PTR1_HI, REG_PTR1 }, - { 0, "ptr2", REG_PTR2_LO, REG_PTR2 }, - { 0, "ptr2+1", REG_PTR2_HI, REG_PTR2 }, - { 4, "ptr3", REG_NONE, REG_NONE }, - { 4, "ptr4", REG_NONE, REG_NONE }, - { 7, "regbank", REG_NONE, REG_NONE }, - { 0, "regsave", REG_SAVE_LO, REG_SAVE }, - { 0, "regsave+1", REG_SAVE_HI, REG_SAVE }, - { 0, "sp", REG_SP_LO, REG_SP }, - { 0, "sp+1", REG_SP_HI, REG_SP }, - { 0, "sreg", REG_SREG_LO, REG_SREG }, - { 0, "sreg+1", REG_SREG_HI, REG_SREG }, - { 0, "tmp1", REG_TMP1, REG_TMP1 }, - { 0, "tmp2", REG_NONE, REG_NONE }, - { 0, "tmp3", REG_NONE, REG_NONE }, - { 0, "tmp4", REG_NONE, REG_NONE }, + { 0, "ptr1", 2, REG_PTR1_LO, REG_PTR1 }, + { 0, "ptr1+1", 1, REG_PTR1_HI, REG_PTR1 }, + { 0, "ptr2", 2, REG_PTR2_LO, REG_PTR2 }, + { 0, "ptr2+1", 1, REG_PTR2_HI, REG_PTR2 }, + { 4, "ptr3", 2, REG_NONE, REG_NONE }, + { 4, "ptr4", 2, REG_NONE, REG_NONE }, + { 7, "regbank", 6, REG_NONE, REG_NONE }, + { 0, "regsave", 4, REG_SAVE_LO, REG_SAVE }, + { 0, "regsave+1", 3, REG_SAVE_HI, REG_SAVE }, + { 0, "sp", 2, REG_SP_LO, REG_SP }, + { 0, "sp+1", 1, REG_SP_HI, REG_SP }, + { 0, "sreg", 2, REG_SREG_LO, REG_SREG }, + { 0, "sreg+1", 1, REG_SREG_HI, REG_SREG }, + { 0, "tmp1", 1, REG_TMP1, REG_TMP1 }, + { 0, "tmp2", 1, REG_NONE, REG_NONE }, + { 0, "tmp3", 1, REG_NONE, REG_NONE }, + { 0, "tmp4", 1, REG_NONE, REG_NONE }, }; #define ZPInfoCount (sizeof(ZPInfoTable) / sizeof(ZPInfoTable[0])) @@ -377,6 +377,83 @@ static const ZPInfo ZPInfoTable[] = { +static int IsAddrOnZP (long Address) +/* Return true if the Address is within the ZP range. +** FIXME: ZP range may vary depending on the CPU settings. +*/ +{ + /* ZP in range [0x00, 0xFF] */ + return Address >= 0 && Address < 0x100; +} + + + +int IsZPArg (const char* Name) +/* Exam if the main part of the arg string indicates a ZP loc */ +{ + unsigned short ArgInfo = 0; + long Offset = 0; + StrBuf NameBuf = AUTO_STRBUF_INITIALIZER; + SymEntry* E = 0; + const ZPInfo* Info = 0; + + if (!ParseOpcArgStr (Name, &ArgInfo, &NameBuf, &Offset)) { + /* Parsing failed */ + SB_Done (&NameBuf); + return 0; + } + + if ((ArgInfo & AIF_HAS_NAME) == 0) { + /* Numeric locs have no names */ + SB_Done (&NameBuf); + + /* We can check it against the ZP boundary if it is known */ + return IsAddrOnZP (Offset); + } + + if ((ArgInfo & AIF_BUILTIN) != 0) { + /* Search for the name in the list of builtin ZPs */ + Info = GetZPInfo (SB_GetConstBuf (&NameBuf)); + + SB_Done (&NameBuf); + + /* Do we know the ZP? */ + if (Info != 0) { + /* Use the information we have */ + return Offset >= 0 && Offset < (int)Info->Size; + } + + /* Assume it be non-ZP */ + return 0; + } + + if ((ArgInfo & AIF_EXTERNAL) == 0) { + /* We don't support local variables on ZP */ + SB_Done (&NameBuf); + return 0; + } + + /* Search for the symbol in the global symbol table skipping the underline + ** in its name. + */ + E = FindGlobalSym (SB_GetConstBuf (&NameBuf) + 1); + + SB_Done (&NameBuf); + + /* We are checking the offset against the symbol size rather than the actual + ** zeropage boundary, since we can't magically ensure that until linking and + ** can only trust the user in writing the correct code for now. + */ + if (E != 0 && (E->Flags & SC_ZEROPAGE) != 0) { + return Offset >= 0 && (unsigned)Offset < CheckedSizeOf (E->Type); + } + + /* Not found on ZP */ + return 0; +} + + + static int CompareFuncInfo (const void* Key, const void* Info) /* Compare function for bsearch */ { diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 32bc5dd9c..88e26cdf4 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -119,7 +119,8 @@ struct RegContents; typedef struct ZPInfo ZPInfo; struct ZPInfo { unsigned char Len; /* Length of the following string */ - char Name[11]; /* Name of zero page symbol */ + char Name[10]; /* Name of zero page symbol */ + unsigned char Size; /* Maximum buffer size of this register */ unsigned short ByteUse; /* Register info for this symbol */ unsigned short WordUse; /* Register info for 16 bit access */ }; @@ -162,6 +163,9 @@ typedef enum { +int IsZPArg (const char* Arg); +/* Exam if the main part of the arg string indicates a ZP loc */ + fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg); /* For the given function, lookup register information and store it into ** the given variables. If the function is unknown, assume it will use and diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index 8177d4fd4..0626f3d18 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -403,7 +403,7 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) if ((OPC->Info & OF_BRA) != 0) { /* Branch */ AM = AM65_BRA; - } else if (GetZPInfo(Arg) != 0) { + } else if (IsZPArg (Arg)) { AM = AM65_ZP; } else { /* Check for subroutine call to local label */ @@ -424,12 +424,15 @@ static CodeEntry* ParseInsn (CodeSeg* S, LineInfo* LI, const char* L) Reg = toupper (*L); L = SkipSpace (L+1); if (Reg == 'X') { - if (GetZPInfo(Arg) != 0) { + if (IsZPArg (Arg)) { AM = AM65_ZPX; } else { AM = AM65_ABSX; } } else if (Reg == 'Y') { + /* On 6502 only LDX and STX support AM65_ZPY. + ** We just play it simple and safe for now. + */ AM = AM65_ABSY; } else { Error ("ASM code error: syntax error"); diff --git a/src/cc65/opcodes.c b/src/cc65/opcodes.c index 3c02c84c4..86b904df9 100644 --- a/src/cc65/opcodes.c +++ b/src/cc65/opcodes.c @@ -649,6 +649,7 @@ unsigned GetInsnSize (opc_t OPC, am_t AM) case AM65_IMM: return 2; case AM65_ZP: return 2; case AM65_ZPX: return 2; + case AM65_ZPY: return 2; case AM65_ABS: return 3; case AM65_ABSX: return 3; case AM65_ABSY: return 3; From e58c84acf7958c7e21879fea9daceef423da2e6e Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jan 2020 10:39:19 +0800 Subject: [PATCH 390/806] Support for optionally specifying address size for segments with: #pragma ***-name([push,] "name"[, "addrsize"]) where "addrsize" is any addressing size supported in ca65. --- src/cc65/codegen.c | 12 +++++- src/cc65/main.c | 6 +++ src/cc65/pragma.c | 30 ++++++++++++++- src/cc65/segments.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ src/cc65/segments.h | 13 +++++++ 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index bc85ba1de..539309283 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -39,6 +39,7 @@ #include /* common */ +#include "addrsize.h" #include "check.h" #include "cpu.h" #include "inttypes.h" @@ -260,6 +261,9 @@ void g_usebss (void) void g_segname (segment_t Seg) /* Emit the name of a segment if necessary */ { + unsigned char AddrSize; + const char* Name; + /* Emit a segment directive for the data style segments */ DataSeg* S; switch (Seg) { @@ -269,7 +273,13 @@ void g_segname (segment_t Seg) default: S = 0; break; } if (S) { - DS_AddLine (S, ".segment\t\"%s\"", GetSegName (Seg)); + Name = GetSegName (Seg); + AddrSize = GetSegAddrSize (Name); + if (AddrSize != ADDR_SIZE_INVALID) { + DS_AddLine (S, ".segment\t\"%s\": %s", Name, AddrSizeToStr (AddrSize)); + } else { + DS_AddLine (S, ".segment\t\"%s\"", Name); + } } } diff --git a/src/cc65/main.c b/src/cc65/main.c index 26dd721be..fabd71ef7 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -897,6 +897,9 @@ int main (int argc, char* argv[]) /* Initialize the default segment names */ InitSegNames (); + /* Initialize the segment address sizes table */ + InitSegAddrSizes (); + /* Initialize the include search paths */ InitIncludePaths (); @@ -1089,6 +1092,9 @@ int main (int argc, char* argv[]) /* Done with tracked string buffer allocation */ DoneDiagnosticStrBufs (); + /* Free up the segment address sizes table */ + DoneSegAddrSizes (); + /* Return an apropriate exit code */ return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index c614bbfdd..cf463a832 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -37,6 +37,7 @@ #include /* common */ +#include "addrsize.h" #include "chartype.h" #include "segnames.h" #include "tgttrans.h" @@ -389,7 +390,9 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) /* Handle a pragma that expects a segment name parameter */ { const char* Name; + unsigned char AddrSize = ADDR_SIZE_INVALID; StrBuf S = AUTO_STRBUF_INITIALIZER; + StrBuf A = AUTO_STRBUF_INITIALIZER; int Push = 0; /* Check for the "push" or "pop" keywords */ @@ -430,13 +433,35 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) goto ExitPoint; } - /* Get the string */ + /* Get the name string of the segment */ Name = SB_GetConstBuf (&S); /* Check if the name is valid */ if (ValidSegName (Name)) { - /* Set the new name */ + /* Skip the following comma */ + SB_SkipWhite (B); + if (SB_Peek (B) == ',') { + SB_Skip (B); + SB_SkipWhite (B); + + /* A string argument must follow */ + if (!GetString (B, &A)) { + goto ExitPoint; + } + + /* Get the address size for the segment */ + AddrSize = AddrSizeFromStr (SB_GetConstBuf (&A)); + + /* Set the address size for the segment if valid */ + if (AddrSize != ADDR_SIZE_INVALID) { + SetSegAddrSize (Name, AddrSize); + } else { + Warning ("Invalid address size for segment!"); + } + } + + /* Set the new name and optionally address size */ if (Push) { PushSegName (Seg, Name); } else { @@ -460,6 +485,7 @@ static void SegNamePragma (StrBuf* B, segment_t Seg) ExitPoint: /* Call the string buf destructor */ SB_Done (&S); + SB_Done (&A); } diff --git a/src/cc65/segments.c b/src/cc65/segments.c index 7a9e32eb8..8283b9da5 100644 --- a/src/cc65/segments.c +++ b/src/cc65/segments.c @@ -37,6 +37,7 @@ #include /* common */ +#include "addrsize.h" #include "chartype.h" #include "check.h" #include "coll.h" @@ -61,6 +62,13 @@ +/* Table struct for address sizes of segments */ +typedef struct { + StrBuf Name; + unsigned char AddrSize; +} SegAddrSize_t; + + /* Pointer to the current segment list. Output goes here. */ Segments* CS = 0; @@ -70,6 +78,9 @@ Segments* GS = 0; /* Actual names for the segments */ static StrStack SegmentNames[SEG_COUNT]; +/* Address size for the segments */ +static Collection SegmentAddrSizes; + /* We're using a collection for the stack instead of a linked list. Since ** functions may not be nested (at least in the current implementation), the ** maximum stack depth is 2, so there is not really a need for a better @@ -85,6 +96,85 @@ static Collection SegmentStack = STATIC_COLLECTION_INITIALIZER; +void InitSegAddrSizes (void) +/* Initialize the segment address sizes */ +{ + InitCollection (&SegmentAddrSizes); +} + + + +void DoneSegAddrSizes (void) +/* Free the segment address sizes */ +{ + SegAddrSize_t* A; + int I; + for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) { + A = CollAtUnchecked (&SegmentAddrSizes, I); + SB_Done (&A->Name); + xfree (A); + } + DoneCollection (&SegmentAddrSizes); +} + + + +static SegAddrSize_t* FindSegAddrSize (const char* Name) +/* Find already specified address size for a segment by name. +** Return the found struct or 0 if not found. +*/ +{ + SegAddrSize_t* A; + int I; + for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) { + A = CollAtUnchecked (&SegmentAddrSizes, I); + if (A && strcmp (SB_GetConstBuf (&A->Name), Name) == 0) { + return A; + } + } + return 0; +} + + + +void SetSegAddrSize (const char* Name, unsigned char AddrSize) +/* Set the address size for a segment */ +{ + SegAddrSize_t* A = FindSegAddrSize (Name); + if (!A) { + /* New one */ + A = xmalloc (sizeof (SegAddrSize_t)); + SB_Init (&A->Name); + SB_CopyStr (&A->Name, Name); + SB_Terminate (&A->Name); + CollAppend (&SegmentAddrSizes, A); + } else { + /* Check for mismatching address sizes */ + if (A->AddrSize != AddrSize) { + Warning ("Segment address size changed from last time!"); + } + } + + /* Set the address size anyway */ + A->AddrSize = AddrSize; +} + + + +unsigned char GetSegAddrSize (const char* Name) +/* Get the address size of the given segment. +** Return ADDR_SIZE_INVALID if not found. +*/ +{ + SegAddrSize_t* A = FindSegAddrSize (Name); + if (A) { + return A->AddrSize; + } + return ADDR_SIZE_INVALID; +} + + + void InitSegNames (void) /* Initialize the segment names */ { diff --git a/src/cc65/segments.h b/src/cc65/segments.h index 777073559..91d702df6 100644 --- a/src/cc65/segments.h +++ b/src/cc65/segments.h @@ -103,6 +103,19 @@ extern Segments* GS; /*****************************************************************************/ +void InitSegAddrSizes (void); +/* Initialize the segment address sizes */ + +void DoneSegAddrSizes (void); +/* Free the segment address sizes */ + +void SetSegAddrSize (const char* Name, unsigned char AddrSize); +/* Set the address size for a segment */ + +unsigned char GetSegAddrSize (const char* Name); +/* Get the address size of the given segment. +** Return ADDR_SIZE_INVALID if not found. +*/ void InitSegNames (void); /* Initialize the segment names */ From 262c4235dfd41eead0e5702c0510b7ed72fc6962 Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 21 Jan 2020 10:50:12 +0800 Subject: [PATCH 391/806] Symbols in ZP segments will use '.exportzp' instead of '.export' if exported. --- src/cc65/compile.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 00e78c2bd..ec90e4b2d 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -37,6 +37,7 @@ #include /* common */ +#include "addrsize.h" #include "debugflag.h" #include "segnames.h" #include "version.h" @@ -275,6 +276,17 @@ static void Parse (void) } Entry->V.BssName = xstrdup (bssName); + /* This is to make the automatical zeropage setting of the symbol + ** work right. + */ + g_usebss (); + } + } + + /* Make the symbol zeropage according to the segment address size */ + if ((Entry->Flags & SC_EXTERN) != 0) { + if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) { + Entry->Flags |= SC_ZEROPAGE; /* Check for enum forward declaration. ** Warn about it when extensions are not allowed. */ From 4905329ff6b934a3e14415f35ecf3d7c4b6fdd8b Mon Sep 17 00:00:00 2001 From: Greg King Date: Mon, 12 Oct 2020 08:33:45 -0400 Subject: [PATCH 392/806] Fixed the misspelling of "height" in a GEOS header. --- include/geos/gstruct.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/geos/gstruct.h b/include/geos/gstruct.h index 9e5f0ddda..6d9cb1f14 100644 --- a/include/geos/gstruct.h +++ b/include/geos/gstruct.h @@ -126,8 +126,8 @@ struct VLIR_info { /* VLIR information */ }; struct process { /* process info, declare table of that type */ - unsigned pointer; /* (like: struct process proctab[2]=... */ - unsigned jiffies; /* last entry HAVE TO BE {0,0} */ + unsigned pointer; /* (like: struct process proctab[2]= ... */ + unsigned jiffies; /* last entry MUST BE {0,0} */ }; struct iconpic { /* icon/encoded bitmap description */ @@ -135,7 +135,7 @@ struct iconpic { /* icon/encoded bitmap description */ char x; /* position in cards (*8 pixels) */ char y; char width; /* in cards */ - char heigth; /* in lines (pixels) */ + char height; /* in lines (pixels) */ }; struct icondef { /* icon definition for DoIcons */ @@ -143,7 +143,7 @@ struct icondef { /* icon definition for DoIcons */ char x; /* position in cards (*8 pixels) */ char y; char width; /* of icon (in cards) */ - char heigth; /* of icon in lines (pixels) */ + char height; /* of icon in lines (pixels) */ unsigned proc_ptr; /* pointer to function handling that icon */ }; From e72e44d14fa713eef4238f9ad1066c6c9d890a78 Mon Sep 17 00:00:00 2001 From: Greg King Date: Tue, 13 Oct 2020 07:55:20 -0400 Subject: [PATCH 393/806] Shortenned the VIC-20's cputc() by 17 bytes. Changed to a modified table look-up method to convert PetSCII to screen-codes. --- libsrc/vic20/cputc.s | 81 ++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/libsrc/vic20/cputc.s b/libsrc/vic20/cputc.s index 9e818032e..a6919ceaf 100644 --- a/libsrc/vic20/cputc.s +++ b/libsrc/vic20/cputc.s @@ -1,5 +1,6 @@ ; -; Ullrich von Bassewitz, 06.08.1998 +; 1998-08-06, Ullrich von Bassewitz +; 2020-10-09, Greg King ; ; void cputcxy (unsigned char x, unsigned char y, char c); ; void cputc (char c); @@ -39,69 +40,64 @@ _cputcxy: jsr gotoxy ; Set cursor, drop x and y pla ; Restore C -; Plot a character - also used as internal function +; Plot a character -- also used as an internal function -_cputc: cmp #$0A ; CR? - bne L1 - lda #0 - sta CURS_X - beq plot ; Recalculate pointers - -L1: cmp #$0D ; LF? +_cputc: cmp #$0D ; Is it CBM '\n'? beq newline ; Recalculate pointers + cmp #$0A ; Is it CBM '\r'? + beq cr -; Printable char of some sort +; Printable char. of some sort +; Convert it from PetSCII into a screen-code - cmp #' ' - bcc cputdirect ; Other control char + cmp #$FF ; BASIC token? + bne convert + lda #$DE ; Pi symbol +convert: tay - bmi L10 - cmp #$60 - bcc L2 - and #$DF - bne cputdirect ; Branch always -L2: and #$3F + lsr a ; Divide by 256/8 + lsr a + lsr a + lsr a + lsr a + tax ; .X = %00000xxx + tya + eor pet_to_screen,x cputdirect: jsr putchar ; Write the character to the screen -; Advance cursor position +; Advance the cursor position advance: iny cpy #XSIZE bne L3 - jsr newline ; new line - ldy #0 ; + cr + jsr newline ; Wrap around + +cr: ldy #$00 ; Do carriage-return L3: sty CURS_X rts + +; Move down by one full screen-line. Note: this routine doesn't scroll. +; +; (Both screen RAM and color RAM are aligned to page boundaries. +; Therefore, the lower bytes of their addresses have the same values. +; Shorten the code by taking advantage of that fact.) + newline: clc lda #XSIZE adc SCREEN_PTR sta SCREEN_PTR - bcc L4 - inc SCREEN_PTR+1 - clc -L4: lda #XSIZE - adc CRAM_PTR sta CRAM_PTR bcc L5 + inc SCREEN_PTR+1 inc CRAM_PTR+1 L5: inc CURS_Y rts -; Handle character if high bit set - -L10: and #$7F - cmp #$7F ; PI? - bne L11 - lda #$5E ; Load screen code for PI -L11: ora #$40 - bne cputdirect - - ; Set cursor position, calculate RAM pointers @@ -111,14 +107,19 @@ plot: ldy CURS_X jmp PLOT ; Set the new cursor - -; Write one character to the screen without doing anything else, return X -; position in Y +; Write one character to the screen without doing anything else, +; return the X position in .Y putchar: ora RVS ; Set revers bit ldy CURS_X - sta (SCREEN_PTR),y ; Set char + sta (SCREEN_PTR),y ; Set char. lda CHARCOLOR sta (CRAM_PTR),y ; Set color rts + + + .rodata +pet_to_screen: + .byte %10000000,%00000000,%01000000,%00100000 ; PetSCII -> screen-code + .byte %01000000,%11000000,%10000000,%10000000 From 095de4ea52c2b488182645fda8ae0b4f7ac5e1c8 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Thu, 15 Oct 2020 12:54:01 +0200 Subject: [PATCH 394/806] Write o65 files as SEQ files. --- samples/Makefile | 18 ++++++++++++------ targettest/Makefile | 16 +++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/samples/Makefile b/samples/Makefile index c9d205bf1..e66850d35 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -260,17 +260,23 @@ endif # Rule to make a CBM disk with all samples. Needs the c1541 program that comes # with the VICE emulator. -define D64_WRITE_recipe +define D64_WRITE_PRG_recipe -$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)) >$(NULLDEV) +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV) -endef # D64_WRITE_recipe +endef # D64_WRITE_PRG_recipe + +define D64_WRITE_SEQ_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV) + +endef # D64_WRITE_SEQ_recipe samples.d64: samples @$(C1541) -format samples,AA d64 $@ >$(NULLDEV) - $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_recipe)) - $(foreach file,$(OVERLAYLIST),$(D64_WRITE_recipe)) - $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_recipe)) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) + $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) # -------------------------------------------------------------------------- # Rule to make an Apple II disk with all samples. Needs the AppleCommander diff --git a/targettest/Makefile b/targettest/Makefile index 33f725f6a..f3694335c 100644 --- a/targettest/Makefile +++ b/targettest/Makefile @@ -321,16 +321,22 @@ endif # Rule to make a CBM disk with all testcode. Needs the c1541 program that comes # with the VICE emulator. -define D64_WRITE_recipe +define D64_WRITE_PRG_recipe -$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)) >$(NULLDEV) +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV) -endef # D64_WRITE_recipe +endef # D64_WRITE_PRG_recipe + +define D64_WRITE_SEQ_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV) + +endef # D64_WRITE_SEQ_recipe testcode.d64: testcode @$(C1541) -format testcode,AA d64 $@ >$(NULLDEV) - $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_recipe)) -# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_recipe)) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) # -------------------------------------------------------------------------- # Rule to make an Apple II disk with all testcode. Needs the AppleCommander From f60af0301afed94a61036c5500934a2f5b977152 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 15 Oct 2020 18:35:54 +0200 Subject: [PATCH 395/806] fix non working Makefiles --- samples/Makefile | 3 ++- samples/disasm/Makefile | 12 ++++++------ samples/geos/Makefile | 12 ++++++------ samples/geos/grc/Makefile | 12 +++++++++++- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/samples/Makefile b/samples/Makefile index e66850d35..6e89cb802 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -153,7 +153,8 @@ endif # -------------------------------------------------------------------------- # Lists of subdirectories -DIRLIST = tutorial +# disasm depends on cpp +DIRLIST = tutorial geos # -------------------------------------------------------------------------- # Lists of executables diff --git a/samples/disasm/Makefile b/samples/disasm/Makefile index 61b37e314..5f19e943d 100644 --- a/samples/disasm/Makefile +++ b/samples/disasm/Makefile @@ -24,11 +24,11 @@ ifdef CC65_HOME LD = $(CC65_HOME)/bin/ld65 DA = $(CC65_HOME)/bin/da65 else - AS := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65) - CC := $(if $(wildcard ../../../bin/cc65*),../../../bin/cc65,cc65) - CL := $(if $(wildcard ../../../bin/cl65*),../../../bin/cl65,cl65) - LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) - DA := $(if $(wildcard ../../../bin/da65*),../../../bin/da65,da65) + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + DA := $(if $(wildcard ../../bin/da65*),../../bin/da65,da65) endif CPP = cpp @@ -39,7 +39,7 @@ DAIS = fixed.dai bank0.dai bank1.dai .SUFFIXES: .da .dai .s -all: image.bin $(ASMS) +samples: image.bin $(ASMS) $(DAIS): fixed.da diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 32a28e118..cc0d52a4e 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -26,17 +26,17 @@ ifdef CC65_HOME LD = $(CC65_HOME)/bin/ld65 SP = $(CC65_HOME)/bin/sp65 else - AS := $(if $(wildcard ../bin/ca65*),../bin/ca65,ca65) - CC := $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65) - CL := $(if $(wildcard ../bin/cl65*),../bin/cl65,cl65) - LD := $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65) - SP := $(if $(wildcard ../bin/sp65*),../bin/sp65,sp65) + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) endif # omitted: dialog.c grphstr.c inittab.c menu.c # TODO: geosconio.cvt rmvprot.cvt -all: bitmap-demo.cvt filesel.cvt geosver.cvt getid.cvt hello1.cvt hello2.cvt \ +samples: bitmap-demo.cvt filesel.cvt geosver.cvt getid.cvt hello1.cvt hello2.cvt \ overlay-demo.cvt vector-demo.cvt yesno.cvt bitmap.c: logo.pcx diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 793506890..0e5eabd81 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -29,7 +29,16 @@ else GRC := $(if $(wildcard ../../../bin/grc65*),../../../bin/grc65,grc65) endif -all: test.s vlir.cvt +DIRLIST = grc + +define SUBDIR_recipe + +@$(MAKE) -C $(dir) --no-print-directory $@ + +endef # SUBDIR_recipe + +samples: test.s vlir.cvt + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) test.s: test.grc $(GRC) -s test.s test.grc @@ -50,3 +59,4 @@ clean: $(RM) test.s test.h $(RM) vlir.s vlir.cvt vlir.c vlir.h $(RM) *.o + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) From a0dc7cd9e471be3864cddc67ea1d7e13f073065a Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 15 Oct 2020 18:41:17 +0200 Subject: [PATCH 396/806] fix grc example, fix makefiles to compile grc example correctly --- samples/geos/Makefile | 11 +++++++++++ samples/geos/grc/Makefile | 10 ---------- samples/geos/grc/vlir0.s | 8 ++++---- samples/geos/grc/vlir1.s | 8 ++++---- samples/geos/grc/vlir2.s | 8 ++++---- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index cc0d52a4e..cbc1e9885 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -33,11 +33,20 @@ else SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) endif +DIRLIST = grc + +define SUBDIR_recipe + +@$(MAKE) -C $(dir) --no-print-directory $@ + +endef # SUBDIR_recipe + # omitted: dialog.c grphstr.c inittab.c menu.c # TODO: geosconio.cvt rmvprot.cvt samples: bitmap-demo.cvt filesel.cvt geosver.cvt getid.cvt hello1.cvt hello2.cvt \ overlay-demo.cvt vector-demo.cvt yesno.cvt + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) bitmap.c: logo.pcx $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap @@ -77,6 +86,8 @@ yesno.cvt: yesnores.grc yesno.c clean: + $(RM) overlay-demores.h $(RM) bitmap.c $(RM) *.cvt $(RM) *.map + $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 0e5eabd81..4dbe51d10 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -29,16 +29,7 @@ else GRC := $(if $(wildcard ../../../bin/grc65*),../../../bin/grc65,grc65) endif -DIRLIST = grc - -define SUBDIR_recipe - -@$(MAKE) -C $(dir) --no-print-directory $@ - -endef # SUBDIR_recipe - samples: test.s vlir.cvt - $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) test.s: test.grc $(GRC) -s test.s test.grc @@ -59,4 +50,3 @@ clean: $(RM) test.s test.h $(RM) vlir.s vlir.cvt vlir.c vlir.h $(RM) *.o - $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/geos/grc/vlir0.s b/samples/geos/grc/vlir0.s index a54d406df..fd869835c 100644 --- a/samples/geos/grc/vlir0.s +++ b/samples/geos/grc/vlir0.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos-common/const.inc" - .include "../../libsrc/geos-cbm/jumptab.inc" - .include "../../libsrc/geos-cbm/geossym.inc" - .include "../../libsrc/geos-common/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" ; import load addresses for all VLIR chains ; these labels are defined upon linking with ld65 diff --git a/samples/geos/grc/vlir1.s b/samples/geos/grc/vlir1.s index 6ee3cca37..170442af7 100644 --- a/samples/geos/grc/vlir1.s +++ b/samples/geos/grc/vlir1.s @@ -5,10 +5,10 @@ ; include some GEOS defines - .include "../../libsrc/geos-common/const.inc" - .include "../../libsrc/geos-cbm/jumptab.inc" - .include "../../libsrc/geos-cbm/geossym.inc" - .include "../../libsrc/geos-common/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" ; export names of functions that will be used in the main program diff --git a/samples/geos/grc/vlir2.s b/samples/geos/grc/vlir2.s index 4c02983ff..8dfb79464 100644 --- a/samples/geos/grc/vlir2.s +++ b/samples/geos/grc/vlir2.s @@ -5,10 +5,10 @@ ; similar to vlir1.s except the fact that this is chain #2 - .include "../../libsrc/geos-common/const.inc" - .include "../../libsrc/geos-cbm/jumptab.inc" - .include "../../libsrc/geos-cbm/geossym.inc" - .include "../../libsrc/geos-common/geosmac.inc" + .include "../../../libsrc/geos-common/const.inc" + .include "../../../libsrc/geos-cbm/jumptab.inc" + .include "../../../libsrc/geos-cbm/geossym.inc" + .include "../../../libsrc/geos-common/geosmac.inc" .export OVERLAY2_Function1 .export OVERLAY2_Function2 From 6920b8be78c1a60b141de123dbd54c78438fd54c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 15 Oct 2020 18:53:56 +0200 Subject: [PATCH 397/806] $(RM) abc -> @$(DEL) abc 2>$(NULLDEV) --- samples/disasm/Makefile | 6 +++--- samples/geos/Makefile | 8 ++++---- samples/geos/grc/Makefile | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/disasm/Makefile b/samples/disasm/Makefile index 5f19e943d..0ae2e4b00 100644 --- a/samples/disasm/Makefile +++ b/samples/disasm/Makefile @@ -53,7 +53,7 @@ image.bin: image.s image.cfg $(CL) -t none -C image.cfg -o image.bin image.s clean: - $(RM) $(ASMS) - $(RM) $(DAIS) - $(RM) image.bin + $(DEL) $(ASMS) 2>$(NULLDEV) + $(DEL) $(DAIS) 2>$(NULLDEV) + $(DEL) image.bin 2>$(NULLDEV) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index cbc1e9885..2ae53b0bd 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -86,8 +86,8 @@ yesno.cvt: yesnores.grc yesno.c clean: - $(RM) overlay-demores.h - $(RM) bitmap.c - $(RM) *.cvt - $(RM) *.map + $(DEL) overlay-demores.h 2>$(NULLDEV) + $(DEL) bitmap.c 2>$(NULLDEV) + $(DEL) *.cvt 2>$(NULLDEV) + $(DEL) *.map 2>$(NULLDEV) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 4dbe51d10..5b95d9a36 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -47,6 +47,6 @@ vlir.cvt: vlir.grc vlir0.s vlir1.s vlir2.s # $(CL) -t geos-cbm -o vlir.cvt vlir.grc vlir0.s vlir1.s vlir2.s clean: - $(RM) test.s test.h - $(RM) vlir.s vlir.cvt vlir.c vlir.h - $(RM) *.o + $(DEL) test.s test.h 2>$(NULLDEV) + $(DEL) vlir.s vlir.cvt vlir.c vlir.h 2>$(NULLDEV) + $(DEL) *.o 2>$(NULLDEV) From b773bb9ded55f02ab8ddd874aefa449bb529af26 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 15 Oct 2020 19:34:40 +0200 Subject: [PATCH 398/806] some more $(RM) vs $(DEL) fixing --- samples/disasm/Makefile | 6 +++--- samples/geos/Makefile | 8 ++++---- samples/geos/grc/Makefile | 6 +++--- targettest/accelerator/Makefile | 2 +- targettest/apple2/Makefile | 8 ++++---- targettest/atari/Makefile | 18 +++++++++--------- targettest/atari5200/Makefile | 2 +- targettest/cbm/Makefile | 2 +- targettest/gamate/Makefile | 6 +++--- targettest/pce/Makefile | 2 +- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/samples/disasm/Makefile b/samples/disasm/Makefile index 0ae2e4b00..f1d93f5da 100644 --- a/samples/disasm/Makefile +++ b/samples/disasm/Makefile @@ -53,7 +53,7 @@ image.bin: image.s image.cfg $(CL) -t none -C image.cfg -o image.bin image.s clean: - $(DEL) $(ASMS) 2>$(NULLDEV) - $(DEL) $(DAIS) 2>$(NULLDEV) - $(DEL) image.bin 2>$(NULLDEV) + @$(DEL) $(ASMS) 2>$(NULLDEV) + @$(DEL) $(DAIS) 2>$(NULLDEV) + @$(DEL) image.bin 2>$(NULLDEV) diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 2ae53b0bd..00841ee8f 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -86,8 +86,8 @@ yesno.cvt: yesnores.grc yesno.c clean: - $(DEL) overlay-demores.h 2>$(NULLDEV) - $(DEL) bitmap.c 2>$(NULLDEV) - $(DEL) *.cvt 2>$(NULLDEV) - $(DEL) *.map 2>$(NULLDEV) + @$(DEL) overlay-demores.h 2>$(NULLDEV) + @$(DEL) bitmap.c 2>$(NULLDEV) + @$(DEL) *.cvt 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 5b95d9a36..81f9ca045 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -47,6 +47,6 @@ vlir.cvt: vlir.grc vlir0.s vlir1.s vlir2.s # $(CL) -t geos-cbm -o vlir.cvt vlir.grc vlir0.s vlir1.s vlir2.s clean: - $(DEL) test.s test.h 2>$(NULLDEV) - $(DEL) vlir.s vlir.cvt vlir.c vlir.h 2>$(NULLDEV) - $(DEL) *.o 2>$(NULLDEV) + @$(DEL) test.s test.h 2>$(NULLDEV) + @$(DEL) vlir.s vlir.cvt vlir.c vlir.h 2>$(NULLDEV) + @$(DEL) *.o 2>$(NULLDEV) diff --git a/targettest/accelerator/Makefile b/targettest/accelerator/Makefile index f4f651535..527b13f33 100644 --- a/targettest/accelerator/Makefile +++ b/targettest/accelerator/Makefile @@ -55,4 +55,4 @@ turbomaster-test.prg: turbomaster-test.c $(CL) -t c64 turbomaster-test.c -o turbomaster-test.prg clean: - $(RM) *.prg + @$(DEL) *.prg 2>$(NULLDEV) diff --git a/targettest/apple2/Makefile b/targettest/apple2/Makefile index 0020b4ec5..f8167b47c 100644 --- a/targettest/apple2/Makefile +++ b/targettest/apple2/Makefile @@ -65,7 +65,7 @@ dhgrshow: dhgrshow.c $(CL) -Oirs -t apple2enh --start-addr 0x4000 -m dhgrshow.map $^ clean: - $(RM) hgr.dsk dhgr.dsk - $(RM) hgrshow hgrshow.map - $(RM) hgrtest hgrtest.map - $(RM) dhgrshow dhgrshow.map + @$(DEL) hgr.dsk dhgr.dsk 2>$(NULLDEV) + @$(DEL) hgrshow hgrshow.map 2>$(NULLDEV) + @$(DEL) hgrtest hgrtest.map 2>$(NULLDEV) + @$(DEL) dhgrshow dhgrshow.map 2>$(NULLDEV) diff --git a/targettest/atari/Makefile b/targettest/atari/Makefile index a34a5f2d9..18ddf55ce 100644 --- a/targettest/atari/Makefile +++ b/targettest/atari/Makefile @@ -50,12 +50,12 @@ sys.xex: sys.c $(CL) -t atari -o sys.xex sys.c clean: - $(RM) charmapping.xex - $(RM) defdev.xex - $(RM) displaylist.xex - $(RM) mem.xex - $(RM) multi.xex - $(RM) ostype.xex - $(RM) scrcode.o - $(RM) scrcode.com - $(RM) sys.xex + @$(DEL) charmapping.xex 2>$(NULLDEV) + @$(DEL) defdev.xex 2>$(NULLDEV) + @$(DEL) displaylist.xex 2>$(NULLDEV) + @$(DEL) mem.xex 2>$(NULLDEV) + @$(DEL) multi.xex 2>$(NULLDEV) + @$(DEL) ostype.xex 2>$(NULLDEV) + @$(DEL) scrcode.o 2>$(NULLDEV) + @$(DEL) scrcode.com 2>$(NULLDEV) + @$(DEL) sys.xex 2>$(NULLDEV) diff --git a/targettest/atari5200/Makefile b/targettest/atari5200/Makefile index 990ced689..3a8114975 100644 --- a/targettest/atari5200/Makefile +++ b/targettest/atari5200/Makefile @@ -33,4 +33,4 @@ hello: hello.c $(CL) -t atari5200 -o hello hello.c clean: - $(RM) hello + @$(DEL) hello 2>$(NULLDEV) diff --git a/targettest/cbm/Makefile b/targettest/cbm/Makefile index 5217f0cc6..fb7af1a9a 100644 --- a/targettest/cbm/Makefile +++ b/targettest/cbm/Makefile @@ -36,4 +36,4 @@ petscii.prg: petscii.c $(CL) -t $(SYS) -O -o petscii.prg petscii.c clean: - $(DEL) petscii.prg + @$(DEL) petscii.prg 2>$(NULLDEV) diff --git a/targettest/gamate/Makefile b/targettest/gamate/Makefile index d4f1b9673..6cd9a3cdb 100644 --- a/targettest/gamate/Makefile +++ b/targettest/gamate/Makefile @@ -49,6 +49,6 @@ testn: nachtm.bin cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/nachtm.bin clean: - $(RM) lcdtest.o audiotest.o ctest.o - $(RM) lcdtest.bin audiotest.bin ctest.bin nachtm.bin - $(RM) audiotest.lst lcdtest.lst ctest.lst + @$(DEL) lcdtest.o audiotest.o ctest.o 2>$(NULLDEV) + @$(DEL) lcdtest.bin audiotest.bin ctest.bin nachtm.bin 2>$(NULLDEV) + @$(DEL) audiotest.lst lcdtest.lst ctest.lst 2>$(NULLDEV) diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile index 0c41778cc..f7b828e92 100644 --- a/targettest/pce/Makefile +++ b/targettest/pce/Makefile @@ -52,7 +52,7 @@ all: conio.pce ../../../bin/cl65 -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ clean: - $(RM) conio.o conio.??? + @$(DEL) conio.o conio.??? 2>$(NULLDEV) test: conio.pce mednafen -force_module pce $< From e682f7c8c30189a3844da1082669e63a371e2d08 Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 15 Oct 2020 14:00:25 -0400 Subject: [PATCH 399/806] Protect the C stack from overlays on the c64 target. --- cfg/c64-overlay.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/c64-overlay.cfg b/cfg/c64-overlay.cfg index e88bffe00..0f42434ad 100644 --- a/cfg/c64-overlay.cfg +++ b/cfg/c64-overlay.cfg @@ -14,7 +14,7 @@ MEMORY { ZP: file = "", define = yes, start = $0002, size = $001A; LOADADDR: file = %O, start = %S - 2, size = $0002; HEADER: file = %O, define = yes, start = %S, size = $000D; - MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __OVERLAYSTART__ - __HEADER_LAST__; BSS: file = "", start = __ONCE_RUN__, size = __OVERLAYSTART__ - __STACKSIZE__ - __ONCE_RUN__; OVL1ADDR: file = "%O.1", start = __OVERLAYSTART__ - 2, size = $0002; OVL1: file = "%O.1", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; From a25b28a9726c0e0c8c9c5201dcad2e00ba0f6a5d Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 15 Oct 2020 21:22:04 -0400 Subject: [PATCH 400/806] Added files to the pet library that support the overlay demo sample program. cbm_load() is needed because the Pet/CBM Kernals don't have a LOAD function that can be used by machine code programs. --- cfg/pet-overlay.cfg | 82 +++++++++++++++++++++++++++++++++++++++++++ libsrc/pet/cbm_load.c | 59 +++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 cfg/pet-overlay.cfg create mode 100644 libsrc/pet/cbm_load.c diff --git a/cfg/pet-overlay.cfg b/cfg/pet-overlay.cfg new file mode 100644 index 000000000..afd351a42 --- /dev/null +++ b/cfg/pet-overlay.cfg @@ -0,0 +1,82 @@ +FEATURES { + STARTADDRESS: default = $0401; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __OVERLAYADDR__: type = import; + __STACKSIZE__: type = weak, value = $0800; # 2K stack + __OVERLAYSIZE__: type = weak, value = $0800; # 2K overlay + __HIMEM__: type = weak, value = $8000; + __OVERLAYSTART__: type = export, value = __HIMEM__ - __STACKSIZE__ - __OVERLAYSIZE__; +} +MEMORY { + ZP: file = "", define = yes, start = $0055, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $000D; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __OVERLAYSTART__ - __HEADER_LAST__; + OVL1ADDR: file = "%O.1", start = __OVERLAYSTART__ - 2, size = $0002; + OVL1: file = "%O.1", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL2ADDR: file = "%O.2", start = __OVERLAYSTART__ - 2, size = $0002; + OVL2: file = "%O.2", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL3ADDR: file = "%O.3", start = __OVERLAYSTART__ - 2, size = $0002; + OVL3: file = "%O.3", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL4ADDR: file = "%O.4", start = __OVERLAYSTART__ - 2, size = $0002; + OVL4: file = "%O.4", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL5ADDR: file = "%O.5", start = __OVERLAYSTART__ - 2, size = $0002; + OVL5: file = "%O.5", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL6ADDR: file = "%O.6", start = __OVERLAYSTART__ - 2, size = $0002; + OVL6: file = "%O.6", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL7ADDR: file = "%O.7", start = __OVERLAYSTART__ - 2, size = $0002; + OVL7: file = "%O.7", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL8ADDR: file = "%O.8", start = __OVERLAYSTART__ - 2, size = $0002; + OVL8: file = "%O.8", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; + OVL9ADDR: file = "%O.9", start = __OVERLAYSTART__ - 2, size = $0002; + OVL9: file = "%O.9", start = __OVERLAYSTART__, size = __OVERLAYSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + ONCE: load = MAIN, type = ro, optional = yes; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; + BSS: load = MAIN, type = bss, define = yes; + OVL1ADDR: load = OVL1ADDR, type = ro; + OVERLAY1: load = OVL1, type = ro, define = yes, optional = yes; + OVL2ADDR: load = OVL2ADDR, type = ro; + OVERLAY2: load = OVL2, type = ro, define = yes, optional = yes; + OVL3ADDR: load = OVL3ADDR, type = ro; + OVERLAY3: load = OVL3, type = ro, define = yes, optional = yes; + OVL4ADDR: load = OVL4ADDR, type = ro; + OVERLAY4: load = OVL4, type = ro, define = yes, optional = yes; + OVL5ADDR: load = OVL5ADDR, type = ro; + OVERLAY5: load = OVL5, type = ro, define = yes, optional = yes; + OVL6ADDR: load = OVL6ADDR, type = ro; + OVERLAY6: load = OVL6, type = ro, define = yes, optional = yes; + OVL7ADDR: load = OVL7ADDR, type = ro; + OVERLAY7: load = OVL7, type = ro, define = yes, optional = yes; + OVL8ADDR: load = OVL8ADDR, type = ro; + OVERLAY8: load = OVL8, type = ro, define = yes, optional = yes; + OVL9ADDR: load = OVL9ADDR, type = ro; + OVERLAY9: load = OVL9, type = ro, define = yes, optional = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/libsrc/pet/cbm_load.c b/libsrc/pet/cbm_load.c new file mode 100644 index 000000000..1823f6537 --- /dev/null +++ b/libsrc/pet/cbm_load.c @@ -0,0 +1,59 @@ +/* +** 2020-10-15, Greg King +** +** unsigned int __fastcall__ cbm_load (const char* name, +** unsigned char device, +** void* data); +*/ + +#include +#include + +/* Loads file "name" from the given device to the given address, or to the load +** address of the file if "data" is the null pointer (like load"name",8,1 +** in BASIC). +** Returns the number of bytes that were loaded if loading was successful; +** otherwise 0; "_oserror" contains an error-code, then. +*/ +unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void* data) +{ + void* load; + int length; + unsigned int size = 0; + + if (cbm_open (1, device, CBM_READ, name) != 0) { + /* Can't load from a file that can't be openned. */ + return 0; + } + + /* Get the file's load address. */ + if (cbm_read (1, &load, sizeof load) != sizeof load) { + /* Either the file wasn't found, or it was too short. (Note: + ** the computer openned a file even if the drive couldn't open one.) + */ + cbm_close (1); + return 0; + } + + /* If "data" doesn't hold an address, then use the file's address. */ + if (data == (void*)0x0000) { + data = load; + } + + /* Pull the file into RAM. [Note that, if cbm_read() grabbed more + ** than 32767 bytes at a time, then its result would look negative, + ** which would cancel the load.] + */ + do { + size += (length = cbm_read (1, data, INT_MAX)); + data = (unsigned char*)data + length; + } while (length == INT_MAX && cbm_k_readst() == 0); + cbm_close (1); + + /* "length" is -1 if there was an error. */ + if (length < 0) { + size = 0; + } + + return size; +} From 77bfd163cd2460b46d4e31b17eb5e5e7ec6ab8aa Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 16 Oct 2020 15:50:18 +0200 Subject: [PATCH 401/806] makefile cleanup --- targettest/gamate/Makefile | 9 --------- targettest/pce/Makefile | 7 ++----- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/targettest/gamate/Makefile b/targettest/gamate/Makefile index 6cd9a3cdb..a14b8a854 100644 --- a/targettest/gamate/Makefile +++ b/targettest/gamate/Makefile @@ -39,15 +39,6 @@ nachtm.bin: nachtm.c $(CL) -Os -l nachtm.lst -t gamate -o nachtm.bin nachtm.c gamate-fixcart nachtm.bin -test1: lcdtest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/lcdtest.bin -test2: audiotest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/audiotest.bin -testc: ctest.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/ctest.bin -testn: nachtm.bin - cd ~/Desktop/mame/winmess/ && wine mess.exe gamate -window -skip_gameinfo -cart ~/Desktop/cc65/github/cc65/testcode/lib/gamate/nachtm.bin - clean: @$(DEL) lcdtest.o audiotest.o ctest.o 2>$(NULLDEV) @$(DEL) lcdtest.bin audiotest.bin ctest.bin nachtm.bin 2>$(NULLDEV) diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile index f7b828e92..1ecc0566f 100644 --- a/targettest/pce/Makefile +++ b/targettest/pce/Makefile @@ -48,11 +48,8 @@ all: conio.pce dd if=$< bs=8K skip=${COUNT} > $@ dd if=$< bs=8K count=${COUNT} >> $@ -%.bin: %.c ../../../lib/pce.lib - ../../../bin/cl65 -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ +%.bin: %.c + $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ clean: @$(DEL) conio.o conio.??? 2>$(NULLDEV) - -test: conio.pce - mednafen -force_module pce $< From 0f66f7569e3cb53a314a9d8ae4df9f1768400725 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 17 Oct 2020 10:36:11 -0400 Subject: [PATCH 402/806] Updated the cx16 library to the upstream project's prerelease 38. --- asminc/cbm_kernal.inc | 1 + asminc/cx16.inc | 10 ++++---- include/cx16.h | 14 ++++++----- libsrc/cx16/cputc.s | 56 +++++++++++++++++++++---------------------- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/asminc/cbm_kernal.inc b/asminc/cbm_kernal.inc index 981687206..5f41c6267 100644 --- a/asminc/cbm_kernal.inc +++ b/asminc/cbm_kernal.inc @@ -46,6 +46,7 @@ GRAPH_SET_FONT := $FF3B GRAPH_GET_CHAR_SIZE := $FF3E GRAPH_PUT_CHAR := $FF41 + MULTI_ACPTR := $FF44 RESTORE_BASIC := $FF47 CLOCK_SET_DATE_TIME := $FF4D CLOCK_GET_DATE_TIME := $FF50 diff --git a/asminc/cx16.inc b/asminc/cx16.inc index 8891c1761..4d9ce89db 100644 --- a/asminc/cx16.inc +++ b/asminc/cx16.inc @@ -1,5 +1,5 @@ ; -; CX16 r37 definitions +; CX16 r38 definitions ; ; --------------------------------------------------------------------------- @@ -439,7 +439,7 @@ NMIVec := $0318 .scope PSG ; Programmable Sound Generator .struct PITCH .word - VOL .byte ; Left, right channels; volume + VOL .byte ; Right, left sides; volume WAVEFORM .byte ; Wave shape, pulse width .endstruct LEFT = %01 << 6 @@ -544,9 +544,11 @@ NMIVec := $0318 VERALOG .byte ; Boolean: log VERA activity KEYBOARDLOG .byte ; Boolean: log keyboard data ECHO .byte ; Type of echo that's enabled - SAVEXIT .byte ; Boolean: save on exit + SAVEXIT .byte ; Boolean: save machine state on exit GIFREC .byte ; Method of recording GIF movie - .org $9FBD + .res 2 + CYCLECOUNT .dword ; Running count of CPU cycles (Read-Only) + .res 1 KEYMAP .byte ; Current keyboard layout number (Read-Only) DETECT .byte 2 ; If is "16" string, then running on emulator (RO) .endstruct diff --git a/include/cx16.h b/include/cx16.h index 87a6d37ca..f3d02bd28 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -3,7 +3,7 @@ /* cx16.h */ /* */ /* CX16 system-specific definitions */ -/* For prerelease 37 */ +/* For prerelease 38 */ /* */ /* */ /* This software is provided "as-is", without any expressed or implied */ @@ -283,12 +283,14 @@ struct __emul { unsigned char debug; /* Boolean: debugging enabled */ unsigned char vera_action; /* Boolean: displaying VERA activity */ unsigned char keyboard; /* Boolean: displaying typed keys */ - unsigned char echo; /* How Kernal output should be echoed to host */ - unsigned char save_on_exit; /* Boolean: save SD card when quitting */ + unsigned char echo; /* How to send Kernal output to host */ + unsigned char save_on_exit; /* Boolean: save machine state on exit */ unsigned char gif_method; /* How GIF movie is being recorded */ - unsigned char unused[0xD - 0x6]; - unsigned char keymap; /* Keyboard layout number */ - const char detect[2]; /* "16" if running on x16emu */ + unsigned char const unused1[2]; + unsigned long const cycle_count; /* Running total of CPU cycles (8 MHz.) */ + unsigned char const unused2[1]; + unsigned char const keymap; /* Keyboard layout number */ + char const detect[2]; /* "16" if running on x16emu */ }; #define EMULATOR (*(volatile struct __emul *)0x9FB0) diff --git a/libsrc/cx16/cputc.s b/libsrc/cx16/cputc.s index 034c3e389..4a7034e59 100644 --- a/libsrc/cx16/cputc.s +++ b/libsrc/cx16/cputc.s @@ -1,5 +1,5 @@ ; -; 2019-09-23, Greg King +; 2020-10-13, Greg King ; ; void __fastcall__ cputcxy (unsigned char x, unsigned char y, char c); ; void __fastcall__ cputc (char c); @@ -11,52 +11,47 @@ .import gotoxy, PLOT .include "cx16.inc" - .macpack generic -; First, move to a new position. +; Move to a cursor position, then print a character. _cputcxy: pha ; Save C jsr gotoxy ; Set cursor, drop x and y - pla ; Restore C + pla -; Print a character. +; Print a character -- also used as an internal function. -_cputc: cmp #$0D ; LF? +_cputc: cmp #$0D ; X16 '\n'? beq newline - cmp #$0A ; CR? - beq plotx0 + cmp #$0A ; X16 '\r'? + beq cr -; Printable char of some sort +; Printable char. of some sort. +; Convert it from PetSCII into a screen-code. - cmp #' ' - blt cputdirect ; Other control char +convert: tay - bmi L10 - cmp #$60 - blt L2 - and #<~%00100000 - bra cputdirect - -; Handle character if high bit set - -L10: and #<~%10000000 ; Remove high bit - ora #%01000000 - bra cputdirect - -L2: and #<~%01000000 + lsr a ; Divide by 256/8 + lsr a + lsr a + lsr a + lsr a + tax ; .X = %00000xxx + tya + eor pet_to_screen,x cputdirect: jsr putchar ; Write character to screen, return .Y -; Advance cursor position. +; Advance the cursor position. iny cpy LLEN ; Reached end of line? bne L3 - jsr newline ; Next line - ldy #$00 ; + CR + jsr newline ; Wrap around + +cr: ldy #$00 L3: sty CURS_X rts @@ -70,7 +65,6 @@ newline: ; Set the cursor's position, calculate RAM pointer. -plotx0: stz CURS_X plot: ldy CURS_X ldx CURS_Y clc @@ -96,3 +90,9 @@ putchar: lda CHARCOLOR sta VERA::DATA0 rts + + + .rodata +pet_to_screen: + .byte %10000000,%00000000,%01000000,%00100000 ; PetSCII -> screen-code + .byte %01000000,%11000000,%10000000,%10000000 From 4f3a96a535a94b91813ce7651dcd08de9dc95cd3 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:22:58 +0800 Subject: [PATCH 403/806] Added new opt OptPushPop2. Also renamed OptPushPop to OptPushPop1. --- src/cc65/codeopt.c | 8 ++-- src/cc65/coptind.c | 93 +++++++++++++++++++++++++++++++++++++++++++++- src/cc65/coptind.h | 7 +++- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 4a7722830..f83a8d9f8 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -167,7 +167,8 @@ static OptFunc DOptPtrStore2 = { OptPtrStore2, "OptPtrStore2", 65, 0, static OptFunc DOptPtrStore3 = { OptPtrStore3, "OptPtrStore3", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptPush1 = { OptPush1, "OptPush1", 65, 0, 0, 0, 0, 0 }; static OptFunc DOptPush2 = { OptPush2, "OptPush2", 50, 0, 0, 0, 0, 0 }; -static OptFunc DOptPushPop = { OptPushPop, "OptPushPop", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop1 = { OptPushPop1, "OptPushPop1", 0, 0, 0, 0, 0, 0 }; +static OptFunc DOptPushPop2 = { OptPushPop2, "OptPushPop2", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptRTS = { OptRTS, "OptRTS", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps1 = { OptRTSJumps1, "OptRTSJumps1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptRTSJumps2 = { OptRTSJumps2, "OptRTSJumps2", 100, 0, 0, 0, 0, 0 }; @@ -269,7 +270,7 @@ static OptFunc* OptFuncs[] = { &DOptPtrStore3, &DOptPush1, &DOptPush2, - &DOptPushPop, + &DOptPushPop1, &DOptRTS, &DOptRTSJumps1, &DOptRTSJumps2, @@ -709,7 +710,8 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptTransfers4, 1); C += RunOptFunc (S, &DOptStore1, 1); C += RunOptFunc (S, &DOptStore5, 1); - C += RunOptFunc (S, &DOptPushPop, 1); + C += RunOptFunc (S, &DOptPushPop1, 1); + C += RunOptFunc (S, &DOptPushPop2, 1); C += RunOptFunc (S, &DOptPrecalc, 1); Changes += C; diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index eada61871..4bb236692 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -1069,8 +1069,8 @@ unsigned OptTransfers4 (CodeSeg* S) -unsigned OptPushPop (CodeSeg* S) -/* Remove a PHA/PLA sequence were A is not used later */ +unsigned OptPushPop1 (CodeSeg* S) +/* Remove a PHA/PLA sequence were A not used later */ { unsigned Changes = 0; unsigned Push = 0; /* Index of push insn */ @@ -1199,6 +1199,95 @@ unsigned OptPushPop (CodeSeg* S) +unsigned OptPushPop2 (CodeSeg* S) +/* Remove a PHP/PLP sequence were no processor flags changed inside */ +{ + unsigned Changes = 0; + unsigned Push = 0; /* Index of push insn */ + unsigned Pop = 0; /* Index of pop insn */ + enum { + Searching, + FoundPush, + FoundPop + } State = Searching; + + /* Walk over the entries. Look for a push instruction that is followed by + ** a pop later, where the pop is not followed by an conditional branch, + ** and where the value of the A register is not used later on. + ** Look out for the following problems: + ** + ** - There may be another PHP/PLP inside the sequence: Restart it. + ** - All jumps inside the sequence must not go outside the sequence, + ** otherwise it would be too complicated to remove the PHP/PLP. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Searching: + if (E->OPC == OP65_PHP) { + /* Found start of sequence */ + Push = I; + State = FoundPush; + } + break; + + case FoundPush: + if (E->OPC == OP65_PHP) { + /* Inner push/pop, restart */ + Push = I; + } else if (E->OPC == OP65_PLP) { + /* Found a matching pop */ + Pop = I; + /* Check that the block between Push and Pop is a basic + ** block (one entry, one exit). Otherwise ignore it. + */ + if (CS_IsBasicBlock (S, Push, Pop)) { + State = FoundPop; + } else { + /* Go into searching mode again */ + State = Searching; + } + } else if ((E->Info & OF_BRA) == 0 && + (E->Info & OF_STORE) == 0 && + E->OPC != OP65_NOP && + E->OPC != OP65_TSX) { + /* Don't bother skipping dead code */ + State = Searching; + } + break; + + case FoundPop: + /* We can remove the PHP and PLP instructions */ + CS_DelEntry (S, Pop); + CS_DelEntry (S, Push); + + /* Correct I so we continue with THIS insn */ + I -= 3; + + /* Remember we had changes */ + ++Changes; + + /* Go into search mode again */ + State = Searching; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + unsigned OptPrecalc (CodeSeg* S) /* Replace immediate operations with the accu where the current contents are ** known by a load of the final value. diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 64acb10d8..ab9179e96 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -82,8 +82,11 @@ unsigned OptTransfers4 (CodeSeg* S); ** by a load of the second register if possible. */ -unsigned OptPushPop (CodeSeg* S); -/* Remove a PHA/PLA sequence were A is not used later */ +unsigned OptPushPop1 (CodeSeg* S); +/* Remove a PHA/PLA sequence were A not used later */ + +unsigned OptPushPop2 (CodeSeg* S); +/* Remove a PHP/PLP sequence were no processor flags changed inside */ unsigned OptPrecalc (CodeSeg* S); /* Replace immediate operations with the accu where the current contents are From 9c776a24e5e14dd34c4f30f96a6383f54442c7a9 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:22:58 +0800 Subject: [PATCH 404/806] Use bcc instead of bne in OptNegAX2 to ease optimizations. --- src/cc65/coptneg.c | 6 +++--- src/cc65/coptneg.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc65/coptneg.c b/src/cc65/coptneg.c index 0f5d589f7..27171c68d 100644 --- a/src/cc65/coptneg.c +++ b/src/cc65/coptneg.c @@ -480,7 +480,7 @@ unsigned OptNegAX2 (CodeSeg* S) ** eor #$FF ** clc ** adc #$01 -** bne L1 +** bcc L1 ** inx ** L1: ** @@ -528,8 +528,8 @@ unsigned OptNegAX2 (CodeSeg* S) /* Get the label attached to the insn following the call */ L = CS_GenLabel (S, P); - /* bne L */ - X = NewCodeEntry (OP65_BNE, AM65_BRA, L->Name, L, E->LI); + /* bcc L */ + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, E->LI); CS_InsertEntry (S, X, I+5); /* inx */ diff --git a/src/cc65/coptneg.h b/src/cc65/coptneg.h index f549fc553..844d8b886 100644 --- a/src/cc65/coptneg.h +++ b/src/cc65/coptneg.h @@ -155,7 +155,7 @@ unsigned OptNegAX2 (CodeSeg* S); ** eor #$FF ** clc ** adc #$01 -** bne L1 +** bcc L1 ** inx ** L1: ** From 0354322413db6b958b801690f593cc2ce1fdde4c Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:22:58 +0800 Subject: [PATCH 405/806] Added OptSignExtened for testing signness right after sign extention. --- src/cc65/codeopt.c | 3 ++ src/cc65/coptind.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptind.h | 25 +++++++++++++ 3 files changed, 120 insertions(+) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index f83a8d9f8..07764cd40 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -178,6 +178,7 @@ static OptFunc DOptShift3 = { OptShift3, "OptShift3", 17, 0, static OptFunc DOptShift4 = { OptShift4, "OptShift4", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift5 = { OptShift5, "OptShift5", 110, 0, 0, 0, 0, 0 }; static OptFunc DOptShift6 = { OptShift6, "OptShift6", 200, 0, 0, 0, 0, 0 }; +static OptFunc DOptSignExtended = { OptSignExtended, "OptSignExtended", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSize1 = { OptSize1, "OptSize1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSize2 = { OptSize2, "OptSize2", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptStackOps = { OptStackOps, "OptStackOps", 100, 0, 0, 0, 0, 0 }; @@ -280,6 +281,7 @@ static OptFunc* OptFuncs[] = { &DOptShift4, &DOptShift5, &DOptShift6, + &DOptSignExtended, &DOptSize1, &DOptSize2, &DOptStackOps, @@ -713,6 +715,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptPushPop1, 1); C += RunOptFunc (S, &DOptPushPop2, 1); C += RunOptFunc (S, &DOptPrecalc, 1); + C += RunOptFunc (S, &DOptSignExtended, 1); Changes += C; diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 4bb236692..5fe3b5c03 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -1474,3 +1474,95 @@ unsigned OptPrecalc (CodeSeg* S) /* Return the number of changes made */ return Changes; } + + + +unsigned OptSignExtended (CodeSeg* S) +/* Change +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$00 +** bpl L2 +** +** or +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$80 +** bcc/bmi L2 +** +** into +** lda xxx ; X is 0 +** bpl L2 +** dex/ldx #$FF +** +** provided the C flag isn't used later. +*/ +{ + unsigned Changes = 0; + CodeEntry* L[5]; + CodeEntry* X; + unsigned CheckStates; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check if it's a register load or transfer insn */ + if (L[0]->OPC == OP65_LDA && + CS_GetEntries (S, L+1, I+1, 4) && + !CS_RangeHasLabel (S, I+1, 2) && + CE_GetLabelCount (L[3]) == 1 && + L[1]->JumpTo == CE_GetLabel (L[3], 0) && + (L[1]->Info & OF_CBRA) != 0 && + GetBranchCond (L[1]->OPC) == BC_PL && + RegValIsKnown (L[2]->RI->Out.RegX) && + L[2]->RI->Out.RegX == 0xFF && + L[2]->OPC != OP65_JSR && + (L[2]->Chg & REG_AXY) == REG_X) { + + /* We find a sign extention */ + CheckStates = PSTATE_CZN; + if (L[3]->OPC == OP65_CPX && + CE_IsConstImm (L[3]) && + (L[4]->Info & OF_CBRA) != 0 && + ((L[3]->Num == 0x00 && + GetBranchCond (L[4]->OPC) == BC_PL) || + ((L[3]->Num == 0x80 && + GetBranchCond (L[4]->OPC) == BC_CC && + GetBranchCond (L[4]->OPC) == BC_MI)))) { + + /* Check if the processor states set by the CPX are unused later */ + if ((GetRegInfo (S, I+5, CheckStates) & CheckStates) == 0) { + + /* Change the target of the sign extention branch */ + X = NewCodeEntry (OP65_JPL, L[4]->AM, L[4]->Arg, L[4]->JumpTo, L[4]->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I+2); + + /* Remove the old conditional branch */ + CS_DelEntries (S, I+3, 2); + + /* Remember, we had changes */ + ++Changes; + + /* Continue with the current insn */ + continue; + } + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index ab9179e96..496b23447 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -93,6 +93,31 @@ unsigned OptPrecalc (CodeSeg* S); ** known by a load of the final value. */ +unsigned OptSignExtended (CodeSeg* S); +/* Change +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$00 +** bpl L2 +** +** or +** +** lda xxx ; X is 0 +** bpl L1 +** dex/ldx #$FF +** L1: cpx #$80 +** bcc/bmi L2 +** +** into +** lda xxx ; X is 0 +** bpl L2 +** dex/ldx #$FF +** +** provided the C flag isn't used later. +*/ + /* End of coptind.h */ From bb7b69f513e3f3612d185c3daef3d83650158ddb Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:22:58 +0800 Subject: [PATCH 406/806] Added OptShiftBack for shifting the C flag into register A and back. --- src/cc65/codeopt.c | 3 +++ src/cc65/coptind.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc65/coptind.h | 5 +++++ 3 files changed, 62 insertions(+) diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 07764cd40..3c10183e4 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -178,6 +178,7 @@ static OptFunc DOptShift3 = { OptShift3, "OptShift3", 17, 0, static OptFunc DOptShift4 = { OptShift4, "OptShift4", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptShift5 = { OptShift5, "OptShift5", 110, 0, 0, 0, 0, 0 }; static OptFunc DOptShift6 = { OptShift6, "OptShift6", 200, 0, 0, 0, 0, 0 }; +static OptFunc DOptShiftBack = { OptShiftBack, "OptShiftBack", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSignExtended = { OptSignExtended, "OptSignExtended", 0, 0, 0, 0, 0, 0 }; static OptFunc DOptSize1 = { OptSize1, "OptSize1", 100, 0, 0, 0, 0, 0 }; static OptFunc DOptSize2 = { OptSize2, "OptSize2", 100, 0, 0, 0, 0, 0 }; @@ -281,6 +282,7 @@ static OptFunc* OptFuncs[] = { &DOptShift4, &DOptShift5, &DOptShift6, + &DOptShiftBack, &DOptSignExtended, &DOptSize1, &DOptSize2, @@ -715,6 +717,7 @@ static unsigned RunOptGroup3 (CodeSeg* S) C += RunOptFunc (S, &DOptPushPop1, 1); C += RunOptFunc (S, &DOptPushPop2, 1); C += RunOptFunc (S, &DOptPrecalc, 1); + C += RunOptFunc (S, &DOptShiftBack, 1); C += RunOptFunc (S, &DOptSignExtended, 1); Changes += C; diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 5fe3b5c03..f3e17fc87 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -1477,6 +1477,60 @@ unsigned OptPrecalc (CodeSeg* S) +unsigned OptShiftBack (CodeSeg* S) +/* Remove a pair of shifts to the opposite directions if none of the bits of +** the register A or the Z/N flags modified by these shifts are used later. +*/ +{ + unsigned Changes = 0; + CodeEntry* E; + CodeEntry* N; + unsigned CheckStates; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check if it's a register load or transfer insn */ + if (E->OPC == OP65_ROL && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->OPC == OP65_LSR || + N->OPC == OP65_ROR) && + !CE_HasLabel (N)) { + + CheckStates = PSTATE_ZN; + + if (N->OPC == OP65_LSR && + !PStatesAreClear (E->RI->Out.PFlags, PSTATE_C)) { + CheckStates |= REG_A; + } + + if ((GetRegInfo (S, I+2, CheckStates) & CheckStates) == 0) { + + /* Remove the shifts */ + CS_DelEntries (S, I, 2); + + /* Remember, we had changes */ + ++Changes; + + /* Continue with next insn */ + continue; + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + unsigned OptSignExtended (CodeSeg* S) /* Change ** diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 496b23447..c7ecf4194 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -93,6 +93,11 @@ unsigned OptPrecalc (CodeSeg* S); ** known by a load of the final value. */ +unsigned OptShiftBack (CodeSeg* S); +/* Remove a pair of shifts to the opposite directions if none of the bits of +** the register A or the Z/N flags modified by these shifts are used later. +*/ + unsigned OptSignExtended (CodeSeg* S); /* Change ** From 5c43d1e04f48c51c743fb3fa66cb00b58ccc0f33 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 16 Apr 2020 17:19:16 +0800 Subject: [PATCH 407/806] Changed codegen for postfix inc/dec operations by deferring them till sequence points. This usually allows faster & smaller code. Note that deferred operations must still be called at sequence points even if the whole expressions containing them had constant values. --- src/cc65/compile.c | 7 + src/cc65/declare.c | 6 + src/cc65/expr.c | 493 +++++++++++++++++++++++++++++++++++++++----- src/cc65/expr.h | 34 ++- src/cc65/goto.c | 7 + src/cc65/locals.c | 10 + src/cc65/stdfunc.c | 15 ++ src/cc65/stmt.c | 3 + src/cc65/testexpr.c | 9 + 9 files changed, 526 insertions(+), 58 deletions(-) diff --git a/src/cc65/compile.c b/src/cc65/compile.c index ec90e4b2d..d93de96b4 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -320,6 +320,9 @@ static void Parse (void) } else { /* Parse the function body */ NewFunc (Entry, FuncDef); + + /* Make sure we aren't omitting any work */ + CheckDeferredOpAllDone (); } } @@ -395,6 +398,8 @@ void Compile (const char* FileName) /* DefineNumericMacro ("__STDC__", 1); <- not now */ DefineNumericMacro ("__STDC_HOSTED__", 1); + InitDeferredOps (); + /* Create the base lexical level */ EnterGlobalLevel (); @@ -486,6 +491,8 @@ void Compile (const char* FileName) } } + DoneDeferredOps (); + if (Debug) { PrintMacroStats (stdout); } diff --git a/src/cc65/declare.c b/src/cc65/declare.c index bf27f9c90..08739f333 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2302,6 +2302,9 @@ static unsigned ParseScalarInit (Type* T) /* Output the data */ DefineData (&ED); + /* Do this anyways for safety */ + DoDeferred (SQP_KEEP_NONE, &ED); + /* Done */ return SizeOf (T); } @@ -2321,6 +2324,9 @@ static unsigned ParsePointerInit (Type* T) /* Output the data */ DefineData (&ED); + /* Do this anyways for safety */ + DoDeferred (SQP_KEEP_NONE, &ED); + /* Close eventually opening braces */ ClosingCurlyBraces (BraceCount); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a88710f34..19572944e 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -332,6 +332,264 @@ static void WarnConstCompareResult (const ExprDesc* Expr) +typedef enum { + DOT_INC, + DOT_DEC, +} DeferredOpType; + + +typedef struct { + ExprDesc Expr; + DeferredOpType OpType; +} DeferredOp; + +Collection DeferredOps; + + + +void InitDeferredOps (void) +/* Init the collection for storing deferred ops */ +{ + InitCollection (&DeferredOps); +} + + + +void DoneDeferredOps (void) +/* Deinit the collection for storing deferred ops */ +{ + DoneCollection (&DeferredOps); +} + + + +static void DeferInc (const ExprDesc* Expr) +/* Defer the post-inc and put it in a queue */ +{ + DeferredOp* Op = xmalloc (sizeof (DeferredOp)); + memcpy (&Op->Expr, Expr, sizeof (ExprDesc)); + Op->OpType = DOT_INC; + CollAppend (&DeferredOps, Op); +} + + + +static void DeferDec (const ExprDesc* Expr) +/* Defer the post-dec and put it in a queue */ +{ + DeferredOp* Op = xmalloc (sizeof (DeferredOp)); + memcpy (&Op->Expr, Expr, sizeof (ExprDesc)); + Op->OpType = DOT_DEC; + CollAppend (&DeferredOps, Op); +} + + + +static void DeferredInc (ExprDesc* Expr) +/* Do the deferred post-inc */ +{ + unsigned Flags; + unsigned long Val; + + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; + + /* Get the increment value in bytes */ + Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute: numeric address or const */ + g_addeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + /* Global variable */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STATIC: + case E_LOC_LITERAL: + /* Static variable or literal in the literal pool */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_REGISTER: + /* Register variable */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_addeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_inc (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression in the primary register */ + g_addeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DeferredInc(): 0x%04X", ED_GetLoc (Expr)); + } +} + + + +static void DeferredDec (ExprDesc* Expr) +/* Do the deferred post-dec */ +{ + unsigned Flags; + unsigned long Val; + + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; + + /* Get the increment value in bytes */ + Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute: numeric address or const */ + g_subeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + /* Global variable */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STATIC: + case E_LOC_LITERAL: + /* Static variable or literal in the literal pool */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_REGISTER: + /* Register variable */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_subeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_dec (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression in the primary register */ + g_subeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DeferredDec(): 0x%04X", ED_GetLoc (Expr)); + } +} + + + +int GetDeferredOpCount (void) +/* Return how many deferred operations are still waiting in the queque */ +{ + return (int)CollCount (&DeferredOps); +} + + + +void CheckDeferredOpAllDone (void) +/* Check if all deferred operations are done at sequence points. +** Die off if check fails. +*/ +{ + if (GetDeferredOpCount () > 0) { + Internal ("Code generation messed up: missing operations past sequence points."); + } +} + + + +void DoDeferred (unsigned Flags, ExprDesc* Expr) +/* Do deferred operations such as post-inc/dec at sequence points */ +{ + int I; + unsigned Size = 0; + int Count = GetDeferredOpCount (); + + /* Nothing to be done */ + if (Count <= 0) { + return; + } + + /* Backup some regs/processor flags around the inc/dec */ + if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + /* Sufficient to add a pair of PHP/PLP for all cases */ + AddCodeLine ("php"); + } + + /* Backup the content of EAX around the inc/dec */ + if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { + /* Get the size */ + Size = CheckedSizeOf (Expr->Type); + + if (Size < 2) { + AddCodeLine ("pha"); + } else if (Size < 3) { + AddCodeLine ("sta regsave"); + AddCodeLine ("stx regsave+1"); + } else { + AddCodeLine ("jsr saveeax"); + } + } + + for (I = 0; I < Count; ++I) { + DeferredOp* Op = CollAtUnchecked (&DeferredOps, I); + switch (Op->OpType) { + + case DOT_INC: + DeferredInc (&Op->Expr); + break; + + case DOT_DEC: + DeferredDec (&Op->Expr); + break; + } + xfree (&Op->Expr); + } + CollDeleteAll (&DeferredOps); + + /* Restore the content of EAX around the inc/dec */ + if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) { + if (Size < 2) { + AddCodeLine ("pla"); + } else if (Size < 3) { + AddCodeLine ("lda regsave"); + AddCodeLine ("ldx regsave+1"); + } else { + AddCodeLine ("jsr resteax"); + } + } + + /* Restore the regs/processor flags around the inc/dec */ + if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) { + /* Sufficient to pop the processor flags */ + AddCodeLine ("plp"); + } +} + + static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Parse a function parameter list, and pass the arguments to the called ** function. Depending on several criteria, this may be done by just pushing @@ -388,11 +646,17 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) } } + /* The info of the last argument could be needed out of the loop */ + ExprDesc Expr; + ED_Init (&Expr); + Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; + /* Parse the actual argument list */ while (CurTok.Tok != TOK_RPAREN) { unsigned Flags; /* Code generator flags, not expression flags */ - ExprDesc Expr; + + /* This way the info of the last parameter won't be cleared */ ED_Init (&Expr); Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; @@ -514,6 +778,11 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) Error ("Too few arguments in function call"); } + /* Append deferred inc/dec before the function is called. + ** The last parameter needs to be restored if it is passed with AX/EAX Regs. + */ + DoDeferred (IsFastcall ? SQP_KEEP_EAX : SQP_KEEP_NONE, &Expr); + /* The function returns the size of all arguments pushed onto the stack. ** However, if there are parameters missed (which is an error, and was ** flagged by the compiler), AND a stack frame was preallocated above, @@ -1728,7 +1997,7 @@ static void PreDec (ExprDesc* Expr) static void PostInc (ExprDesc* Expr) /* Handle the postincrement operator */ { - unsigned Flags; + unsigned Flags, Loc; NextToken (); @@ -1746,33 +2015,49 @@ static void PostInc (ExprDesc* Expr) /* Get the data type */ Flags = TypeOf (Expr->Type); + /* We are allowed by the C standard to defer the inc operation until + ** the this expression is used, so that we don't need to save and reload + ** the original value. + */ + /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) { + if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { LoadExpr (CF_NONE, Expr); - AddCodeLine ("inc %s", ED_GetLabelName(Expr, 0)); + AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0)); } else { - /* Push the address if needed */ - PushAddr (Expr); + Loc = ED_GetLoc (Expr); + if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { + /* Push the address if needed */ + PushAddr (Expr); - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); + /* Fetch the value and save it (since it's the result of the expression) */ + LoadExpr (CF_NONE, Expr); + g_save (Flags | CF_FORCECHAR); + + /* If we have a pointer expression, increment by the size of the type */ + if (IsTypePtr (Expr->Type)) { + g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); + } else { + g_inc (Flags | CF_CONST | CF_FORCECHAR, 1); + } + + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value in the primary register */ + g_restore (Flags | CF_FORCECHAR); - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); } else { - g_inc (Flags | CF_CONST | CF_FORCECHAR, 1); + + /* Fetch the value and use it (since it's the result of the expression) */ + LoadExpr (CF_NONE, Expr); + + /* Defer the increment until the value of this expression is used */; + DeferInc (Expr); } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); } /* The result is always an expression, no reference */ @@ -1784,7 +2069,7 @@ static void PostInc (ExprDesc* Expr) static void PostDec (ExprDesc* Expr) /* Handle the postdecrement operator */ { - unsigned Flags; + unsigned Flags, Loc; NextToken (); @@ -1803,32 +2088,43 @@ static void PostDec (ExprDesc* Expr) Flags = TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) { + if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { LoadExpr (CF_NONE, Expr); - AddCodeLine ("dec %s", ED_GetLabelName(Expr, 0)); + AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0)); } else { - /* Push the address if needed */ - PushAddr (Expr); + Loc = ED_GetLoc (Expr); + if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { + /* Push the address if needed */ + PushAddr (Expr); - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); + /* Fetch the value and save it (since it's the result of the expression) */ + LoadExpr (CF_NONE, Expr); + g_save (Flags | CF_FORCECHAR); + + /* If we have a pointer expression, increment by the size of the type */ + if (IsTypePtr (Expr->Type)) { + g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); + } else { + g_dec (Flags | CF_CONST | CF_FORCECHAR, 1); + } + + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value in the primary register */ + g_restore (Flags | CF_FORCECHAR); - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); } else { - g_dec (Flags | CF_CONST | CF_FORCECHAR, 1); + + /* Fetch the value and save it (since it's the result of the expression) */ + LoadExpr (CF_NONE, Expr); + + /* Defer the decrement until the value of this expression is used */; + DeferDec (Expr); } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); } /* The result is always an expression, no reference */ @@ -3263,6 +3559,9 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Load the value */ LoadExpr (CF_FORCECHAR, Expr); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); + /* Clear the test flag */ ED_RequireNoTest (Expr); @@ -3275,9 +3574,17 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* Generate the jump */ g_falsejump (CF_NONE, FalseLab); - } else if (Expr->IVal == 0 && !ED_IsAddrExpr (Expr)) { - /* Skip remaining */ - Flags |= E_EVAL_UNEVAL; + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (Expr->IVal == 0 && !ED_IsAddrExpr (Expr)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } } } @@ -3306,6 +3613,9 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr2); + /* Do short circuit evaluation */ if (CurTok.Tok == TOK_BOOL_AND) { if (HasFalseJump == 0) { @@ -3319,11 +3629,19 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) /* We need the true label for the last expression */ HasTrueJump = 1; } - } else if (Expr2.IVal == 0 && !ED_IsAddrExpr (&Expr2)) { - /* Skip remaining */ - Flags |= E_EVAL_UNEVAL; - /* The value of the expression will be false */ - ED_MakeConstBool (Expr, 0); + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); + + if (Expr2.IVal == 0 && !ED_IsAddrExpr (&Expr2)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The value of the expression will be false */ + ED_MakeConstBool (Expr, 0); + } } } } @@ -3408,6 +3726,9 @@ static void hieOr (ExprDesc *Expr) /* Get first expr */ LoadExpr (CF_FORCECHAR, Expr); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); /* Clear the test flag */ ED_RequireNoTest (Expr); @@ -3421,9 +3742,17 @@ static void hieOr (ExprDesc *Expr) /* Jump to TrueLab if true */ g_truejump (CF_NONE, TrueLab); } - } else if (Expr->IVal != 0 || ED_IsAddrExpr (Expr)) { - /* Skip remaining */ - Flags |= E_EVAL_UNEVAL; + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence + ** point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (Expr->IVal != 0 || ED_IsAddrExpr (Expr)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } } } @@ -3456,17 +3785,28 @@ static void hieOr (ExprDesc *Expr) ED_RequireTest (&Expr2); LoadExpr (CF_FORCECHAR, &Expr2); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr2); + if (HasTrueJump == 0) { TrueLab = GetLocalLabel(); HasTrueJump = 1; } g_truejump (CF_NONE, TrueLab); } - } else if (Expr2.IVal != 0 || ED_IsAddrExpr (&Expr2)) { - /* Skip remaining */ - Flags |= E_EVAL_UNEVAL; - /* The result is always true */ - ED_MakeConstBool (Expr, 1); + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); + + if (Expr2.IVal != 0 || ED_IsAddrExpr (&Expr2)) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The result is always true */ + ED_MakeConstBool (Expr, 1); + } } } @@ -3547,11 +3887,22 @@ static void hieQuest (ExprDesc* Expr) /* Condition codes not set, request a test */ ED_RequireTest (Expr); LoadExpr (CF_NONE, Expr); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, Expr); + FalseLab = GetLocalLabel (); g_falsejump (CF_NONE, FalseLab); - } else if (Expr->IVal == 0) { - /* Remember the current code position */ - GetCodePos (&SkippedBranch); + } else { + /* Constant boolean subexpression could still have deferred inc/dec + ** operations, so just flush their side-effects at this sequence point. + */ + DoDeferred (SQP_KEEP_NONE, Expr); + + if (Expr->IVal == 0) { + /* Remember the current code position */ + GetCodePos (&SkippedBranch); + } } /* Parse second expression. Remember for later if it is a NULL pointer @@ -3563,7 +3914,17 @@ static void hieQuest (ExprDesc* Expr) if (!ConstantCond || !ED_IsConst (&Expr2)) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr2); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, &Expr2); + ED_FinalizeRValLoad (&Expr2); + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr2); } Expr2.Type = PtrConversion (Expr2.Type); } @@ -3601,7 +3962,17 @@ static void hieQuest (ExprDesc* Expr) if (!ConstantCond || !ED_IsConst (&Expr3)) { /* Load it into the primary */ LoadExpr (CF_NONE, &Expr3); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, &Expr3); + ED_FinalizeRValLoad (&Expr3); + } else { + /* Constant boolean subexpression could still have deferred inc/ + ** dec operations, so just flush their side-effects at this + ** sequence point. + */ + DoDeferred (SQP_KEEP_NONE, &Expr3); } Expr3.Type = PtrConversion (Expr3.Type); } @@ -4011,6 +4382,9 @@ void hie0 (ExprDesc *Expr) hie1 (Expr); while (CurTok.Tok == TOK_COMMA) { + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, Expr); + /* If the expression didn't generate code or isn't cast to type void, ** emit a warning. */ @@ -4037,7 +4411,9 @@ void hie0 (ExprDesc *Expr) void Expression0 (ExprDesc* Expr) -/* Evaluate an expression via hie0 and put the result into the primary register */ +/* Evaluate an expression via hie0 and put the result into the primary register. +** The expression is completely evaluated and all side effects complete. +*/ { unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; @@ -4051,6 +4427,9 @@ void Expression0 (ExprDesc* Expr) if (ED_YetToLoad (Expr)) { LoadExpr (CF_NONE, Expr); } + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EXPR, Expr); } diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 806a376bb..d0a9988af 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -17,6 +17,19 @@ +/*****************************************************************************/ +/* data */ +/*****************************************************************************/ + + + +#define SQP_KEEP_NONE 0x00 +#define SQP_KEEP_TEST 0x01U +#define SQP_KEEP_EAX 0x02U +#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ + + + /*****************************************************************************/ /* code */ /*****************************************************************************/ @@ -38,6 +51,23 @@ void PushAddr (const ExprDesc* Expr); ** must be saved if it's not constant, before evaluating the rhs. */ +void InitDeferredOps (void); +/* Init the collection for storing deferred ops */ + +void DoneDeferredOps (void); +/* Deinit the collection for storing deferred ops */ + +int GetDeferredOpCount (void); +/* Return how many deferred operations are still waiting in the queque */ + +void CheckDeferredOpAllDone (void); +/* Check if all deferred operations are done at sequence points. +** Die off if check fails. +*/ + +void DoDeferred (unsigned Flags, ExprDesc* Expr); +/* Do deferred operations such as post-inc/dec at sequence points */ + void Store (ExprDesc* Expr, const Type* StoreType); /* Store the primary register into the location denoted by lval. If StoreType ** is given, use this type when storing instead of lval->Type. If StoreType @@ -52,7 +82,9 @@ int evalexpr (unsigned flags, void (*Func) (ExprDesc*), ExprDesc* Expr); */ void Expression0 (ExprDesc* Expr); -/* Evaluate an expression via hie0 and put the result into the primary register */ +/* Evaluate an expression via hie0 and put the result into the primary register. +** The expression is completely evaluated and all side effects complete. +*/ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Will evaluate an expression via the given function. If the result is not diff --git a/src/cc65/goto.c b/src/cc65/goto.c index 06364068f..7d3ff1a6a 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -105,6 +105,9 @@ void GotoStatement (void) val = (unsigned char)CurTok.IVal; NextToken (); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &desc); + if (CPUIsets[CPU] & CPU_ISET_65SC02) { AddCodeLine ("ldx #$%02X", val * 2); AddCodeLine ("jmp (.loword(%s),x)", arr->AsmName); @@ -118,6 +121,10 @@ void GotoStatement (void) (idx = FindSym (CurTok.Ident))) { hie10 (&desc); LoadExpr (CF_NONE, &desc); + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EAX, &desc); + AddCodeLine ("asl a"); if (CPUIsets[CPU] & CPU_ISET_65SC02) { diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 81d0cea09..7812acebd 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -165,6 +165,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Store the value into the variable */ g_putstatic (CF_REGVAR | TypeOf (Sym->Type), Reg, 0); + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -273,6 +275,8 @@ static void ParseAutoDecl (Declaration* Decl) /* Push the value */ g_push (Flags | TypeOf (Sym->Type), Expr.IVal); + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -349,6 +353,9 @@ static void ParseAutoDecl (Declaration* Decl) /* Store the value into the variable */ g_putstatic (CF_STATIC | TypeOf (Sym->Type), DataLabel, 0); + + /* This has to be done at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); } /* Mark the variable as referenced */ @@ -525,6 +532,9 @@ static void ParseOneDecl (const DeclSpec* Spec) } } + + /* Make sure we aren't missing some work */ + CheckDeferredOpAllDone (); } diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 74579212e..c1fb6c735 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -245,6 +245,9 @@ static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) LoadExpr (CF_NONE, &Arg3.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg3.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_memcpy, ParamSize); @@ -594,6 +597,9 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) LoadExpr (CF_NONE, &Arg3.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg3.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize); @@ -808,6 +814,9 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) LoadExpr (CF_NONE, &Arg2.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg2.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_strcmp, ParamSize); @@ -1007,6 +1016,9 @@ static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) LoadExpr (CF_NONE, &Arg2.Expr); } + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg2.Expr); + /* Emit the actual function call. This will also cleanup the stack. */ g_call (CF_FIXARGC, Func_strcpy, ParamSize); @@ -1193,6 +1205,9 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) /* Evaluate the parameter */ hie1 (&Arg); + /* We still need to append deferred inc/dec before calling into the function */ + DoDeferred (SQP_KEEP_EAX, &Arg); + /* Check if the argument is an array. If so, remember the element count. ** Otherwise set the element count to undefined. */ diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 74b841493..9890c5467 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -352,6 +352,9 @@ static void ReturnStatement (void) LoadExpr (CF_NONE, &Expr); } } + + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_EAX, &Expr); } } else if (!F_HasVoidReturn (CurrentFunc) && !F_HasOldStyleIntRet (CurrentFunc)) { diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index 80fe4bc3d..eefaeb74a 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -66,6 +66,9 @@ unsigned Test (unsigned Label, int Invert) /* Check for a constant expression */ if (ED_IsConstAbs (&Expr)) { + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); + /* Result is constant, so we know the outcome */ Result = (Expr.IVal != 0); @@ -79,6 +82,9 @@ unsigned Test (unsigned Label, int Invert) } else if (ED_IsAddrExpr (&Expr)) { + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_NONE, &Expr); + /* Object addresses are non-NULL */ Result = 1; @@ -93,6 +99,9 @@ unsigned Test (unsigned Label, int Invert) /* Load the value into the primary register */ LoadExpr (CF_FORCECHAR, &Expr); + /* Append deferred inc/dec at sequence point */ + DoDeferred (SQP_KEEP_TEST, &Expr); + /* Generate the jump */ if (Invert) { g_truejump (CF_NONE, Label); From 947dd9aca040076b3b073ce43951875a31dc48da Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Thu, 22 Oct 2020 08:54:07 +0200 Subject: [PATCH 408/806] Adjusted ChkDkGEOS. --- doc/geos.sgml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/geos.sgml b/doc/geos.sgml index ec9d66b1a..a10ade5d5 100644 --- a/doc/geos.sgml +++ b/doc/geos.sgml @@ -722,9 +722,9 @@ disk. Otherwise they will be lost. Operating area is the ChkDskGEOS +ChkDkGEOS

- This functions checks In GEOS there can be only one file opened at a time. Upon opening a VLIR file some information about it is copied into memory. You can retrieve the records table at OpenRecordFile From 8e685a00717348d5b1af9cac180269b15fa25b71 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Fri, 23 Oct 2020 18:35:14 +0200 Subject: [PATCH 409/806] Mention recursion. --- doc/cc65.sgml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 578217307..115f0a30c 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -446,10 +446,10 @@ Here is a description of all the command line options: Use static storage for local variables instead of storage on the stack. Since the stack is emulated in software, this gives shorter and usually - faster code, but the code is no longer reentrant. The difference between - void f (void) From b8889bf37ecffce7946917bb6291d983fcc2d0ce Mon Sep 17 00:00:00 2001 From: jede Date: Fri, 23 Oct 2020 23:47:30 +0200 Subject: [PATCH 410/806] Now getchar works --- libsrc/telestrat/read.s | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/libsrc/telestrat/read.s b/libsrc/telestrat/read.s index 76de9d0ac..0782c4d21 100644 --- a/libsrc/telestrat/read.s +++ b/libsrc/telestrat/read.s @@ -8,21 +8,33 @@ .include "zeropage.inc" .include "telestrat.inc" + .include "fcntl.inc" ;int read (int fd, void* buf, unsigned count); .proc _read - sta ptr1 ; count - stx ptr1+1 ; count - jsr popax ; get buf + sta ptr1 ; count + stx ptr1+1 ; count + jsr popax ; get buf sta PTR_READ_DEST stx PTR_READ_DEST+1 - sta ptr2 ; in order to calculate nb of bytes read + sta ptr2 ; in order to calculate nb of bytes read stx ptr2+1 ; - ; jsr popax ; fp pointer don't care in this version + jsr popax ; fp pointer don't care in this version + cpx #$00 + bne @is_not_stdin + cmp #STDIN_FILENO + bne @is_not_stdin + ; stdin +@L1: + BRK_TELEMON XRD0 ; waits until key is pressed + bcs @L1 + + rts +@is_not_stdin: lda ptr1 ; ldy ptr1+1 ; BRK_TELEMON XFREAD ; calls telemon30 routine From 2fcd8b934ac4cbc577a02a83cc4c83f9f90d7561 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sat, 24 Oct 2020 13:55:10 +0200 Subject: [PATCH 411/806] Made HGR segment obligatory. Requiring the HGR segment makes the configs a little less flexible, but for the intended use case the HGR segment actually is necessary. And as we learned now, making the HGR segment obligatory helps users to not shoot themselves in the foot. --- cfg/apple2-hgr.cfg | 4 ++-- cfg/apple2enh-hgr.cfg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cfg/apple2-hgr.cfg b/cfg/apple2-hgr.cfg index 3ccf7b6f3..cfe577e00 100644 --- a/cfg/apple2-hgr.cfg +++ b/cfg/apple2-hgr.cfg @@ -23,8 +23,8 @@ SEGMENTS { EXEHDR: load = HEADER, type = ro, optional = yes; STARTUP: load = MAIN, type = ro; LOWCODE: load = MAIN, type = ro, optional = yes; - HGR: load = MAIN, type = rw, optional = yes, start = $2000; - CODE: load = MAIN, type = ro start = $4000; + HGR: load = MAIN, type = rw, start = $2000; + CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; INIT: load = MAIN, type = rw; diff --git a/cfg/apple2enh-hgr.cfg b/cfg/apple2enh-hgr.cfg index 3ccf7b6f3..cfe577e00 100644 --- a/cfg/apple2enh-hgr.cfg +++ b/cfg/apple2enh-hgr.cfg @@ -23,8 +23,8 @@ SEGMENTS { EXEHDR: load = HEADER, type = ro, optional = yes; STARTUP: load = MAIN, type = ro; LOWCODE: load = MAIN, type = ro, optional = yes; - HGR: load = MAIN, type = rw, optional = yes, start = $2000; - CODE: load = MAIN, type = ro start = $4000; + HGR: load = MAIN, type = rw, start = $2000; + CODE: load = MAIN, type = ro start = $4000; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; INIT: load = MAIN, type = rw; From 07cc6a3d208b28ba6bcb42f9a02b8cb765c61bd8 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Sep 2020 20:23:02 +0800 Subject: [PATCH 412/806] Made optimization steps aware of long branches better. --- src/cc65/coptcmp.c | 4 +++- src/cc65/coptptrload.c | 2 +- src/cc65/coptshift.c | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cc65/coptcmp.c b/src/cc65/coptcmp.c index a4a8c6a9b..fda23ae0a 100644 --- a/src/cc65/coptcmp.c +++ b/src/cc65/coptcmp.c @@ -944,7 +944,9 @@ unsigned OptCmp9 (CodeSeg* S) if (L[0]->OPC == OP65_SBC && CS_GetEntries (S, L+1, I+1, 4) && (L[1]->OPC == OP65_BVC || - L[1]->OPC == OP65_BVS) && + L[1]->OPC == OP65_BVS || + L[1]->OPC == OP65_JVC || + L[1]->OPC == OP65_JVS) && L[1]->JumpTo != 0 && L[1]->JumpTo->Owner == L[3] && L[2]->OPC == OP65_EOR && diff --git a/src/cc65/coptptrload.c b/src/cc65/coptptrload.c index 0534a1fa2..046e65d79 100644 --- a/src/cc65/coptptrload.c +++ b/src/cc65/coptptrload.c @@ -988,7 +988,7 @@ unsigned OptPtrLoad12 (CodeSeg* S) L[4]->OPC == OP65_CLC && L[5]->OPC == OP65_ADC && CE_IsKnownImm (L[5], 1) && - L[6]->OPC == OP65_BCC && + (L[6]->OPC == OP65_BCC || L[6]->OPC == OP65_JCC) && L[6]->JumpTo != 0 && L[6]->JumpTo->Owner == L[8] && L[7]->OPC == OP65_INX && diff --git a/src/cc65/coptshift.c b/src/cc65/coptshift.c index 92210ebb5..aef3f64bc 100644 --- a/src/cc65/coptshift.c +++ b/src/cc65/coptshift.c @@ -341,7 +341,10 @@ unsigned OptShift2 (CodeSeg* S) L[0] = CS_GetEntry (S, I); /* Check for the sequence */ - if ((L[0]->OPC == OP65_BPL || L[0]->OPC == OP65_BCC) && + if ((L[0]->OPC == OP65_BPL || + L[0]->OPC == OP65_BCC || + L[0]->OPC == OP65_JPL || + L[0]->OPC == OP65_JCC) && L[0]->JumpTo != 0 && CS_GetEntries (S, L+1, I+1, 3) && L[1]->OPC == OP65_DEX && From f723147f04f19ddc7e39bad9211a6f26082ffbc5 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 25 Oct 2020 14:06:44 +0100 Subject: [PATCH 413/806] Streamlined clock rate handling. * Docs say that CLK_TCK is an obsolete alias of CLOCKS_PER_SEC so there's no point in individual definitions. * All targets determining the clock rate at runtime can use a common handling. --- include/time.h | 46 ++++++++++++---------------------------- libsrc/atari/clock.s | 8 ++++--- libsrc/atari5200/clock.s | 1 - libsrc/lynx/clock.s | 14 +++++------- 4 files changed, 24 insertions(+), 45 deletions(-) diff --git a/include/time.h b/include/time.h index 4ee658da9..49d7e6870 100644 --- a/include/time.h +++ b/include/time.h @@ -83,46 +83,31 @@ extern struct _timezone { -#if defined(__ATARI__) -/* The clock depends on the video standard, so read it at runtime */ -unsigned _clocks_per_sec (void); -# define CLK_TCK _clocks_per_sec() -# define CLOCKS_PER_SEC _clocks_per_sec() -#elif defined(__ATARI5200__) -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ +#if defined(__ATARI5200__) +# define CLOCKS_PER_SEC 60 #elif defined(__ATMOS__) -# define CLK_TCK 100 /* POSIX */ -# define CLOCKS_PER_SEC 100 /* ANSI */ +# define CLOCKS_PER_SEC 100 #elif defined(__CBM__) # if defined(__CBM510__) || defined(__CBM610__) /* The 510/610 gets its clock from the AC current */ -# define CLK_TCK 50 /* POSIX */ -# define CLOCKS_PER_SEC 50 /* ANSI */ +# define CLOCKS_PER_SEC 50 # else -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ +# define CLOCKS_PER_SEC 60 # endif #elif defined(__NES__) -# define CLK_TCK 50 /* POSIX */ -# define CLOCKS_PER_SEC 50 /* ANSI */ +# define CLOCKS_PER_SEC 50 #elif defined(__PCE__) -# define CLK_TCK 60 /* POSIX */ -# define CLOCKS_PER_SEC 60 /* ANSI */ +# define CLOCKS_PER_SEC 60 #elif defined(__GAMATE__) -# define CLK_TCK 135 /* POSIX */ /* FIXME */ -# define CLOCKS_PER_SEC 135 /* ANSI */ /* FIXME */ +# define CLOCKS_PER_SEC 135 /* FIXME */ #elif defined(__GEOS__) -# define CLK_TCK 1 /* POSIX */ -# define CLOCKS_PER_SEC 1 /* ANSI */ -#elif defined(__LYNX__) -/* The clock-rate depends on the video scan-rate; -** so, read it at run-time. -*/ -extern clock_t _clk_tck (void); -# define CLK_TCK _clk_tck() -# define CLOCKS_PER_SEC _clk_tck() +# define CLOCKS_PER_SEC 1 +#else +/* Read the clock rate at runtime */ +clock_t _clocks_per_sec (void); +# define CLOCKS_PER_SEC _clocks_per_sec() #endif +#define CLK_TCK CLOCKS_PER_SEC #define CLOCK_REALTIME 0 @@ -149,6 +134,3 @@ int __fastcall__ clock_settime (clockid_t clock_id, const struct timespec *tp); /* End of time.h */ #endif - - - diff --git a/libsrc/atari/clock.s b/libsrc/atari/clock.s index e04416c18..853870520 100644 --- a/libsrc/atari/clock.s +++ b/libsrc/atari/clock.s @@ -3,7 +3,7 @@ ; originally by Ullrich von Bassewitz and Sidney Cadot ; ; clock_t clock (void); -; unsigned _clocks_per_sec (void); +; clock_t _clocks_per_sec (void); ; .export _clock, __clocks_per_sec @@ -30,8 +30,10 @@ .proc __clocks_per_sec - ldx #$00 ; Clear high byte of return value - lda PAL ; use hw register, PALNTS is only supported on XL/XE ROM + ldx #$00 ; Clear byte 1 of return value + stx sreg ; Clear byte 2 of return value + stx sreg+1 ; Clear byte 3 of return value + lda PAL ; Use hw register, PALNTS is only supported on XL/XE ROM and #$0e bne @NTSC lda #50 diff --git a/libsrc/atari5200/clock.s b/libsrc/atari5200/clock.s index f2ef85b0b..fff766093 100644 --- a/libsrc/atari5200/clock.s +++ b/libsrc/atari5200/clock.s @@ -2,7 +2,6 @@ ; from Atari computer version by Christian Groessler, 2014 ; ; clock_t clock (void); -; unsigned _clocks_per_sec (void); ; .export _clock diff --git a/libsrc/lynx/clock.s b/libsrc/lynx/clock.s index dbccb32cb..e29799df6 100644 --- a/libsrc/lynx/clock.s +++ b/libsrc/lynx/clock.s @@ -2,18 +2,14 @@ ; 2003-04-13, Ullrich von Bassewitz ; 2012-02-06, Greg King ; -; #include +; clock_t clock (void); +; clock_t _clocks_per_sec (void); ; -; typedef unsigned long int clock_t; -; clock_t _clk_tck(void); -; #define CLOCKS_PER_SEC _clk_tck() -; clock_t clock(void); -; -; clk_tck()'s test-values are based on the numbers in "set_tv.s". +; clocks_per_sec()'s test-values are based on the numbers in "set_tv.s". ; If you change the numbers there, then change them here, too. ; - .export _clock, __clk_tck, clock_count + .export _clock, __clocks_per_sec, clock_count .interruptor update_clock, 2 ; (low priority) .constructor init_clock @@ -42,7 +38,7 @@ ;----------------------------------------------------------------------------- ; Return the number of clock ticks in one second. ; -__clk_tck: +__clocks_per_sec: ldx #$00 ; >50, >60, >75 ldy PBKUP lda #<75 From 79cf1e13a7afe1f2bf9e3a0010e9caca776cf347 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sun, 25 Oct 2020 21:33:08 +0100 Subject: [PATCH 414/806] Adjusted to recent change in time.h --- samples/mandelbrot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/mandelbrot.c b/samples/mandelbrot.c index 595bcfbbb..519f3d823 100644 --- a/samples/mandelbrot.c +++ b/samples/mandelbrot.c @@ -31,6 +31,7 @@ /* Workaround missing clock stuff */ #ifdef __APPLE2__ # define clock() 0 +# undef CLK_TCK # define CLK_TCK 1 #endif From 353721067470fa1671a0413944dd38b744e9f0bb Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Tue, 27 Oct 2020 22:25:58 +0100 Subject: [PATCH 415/806] add waitvsync() for atari and atari5200 --- doc/atari.sgml | 1 + doc/atari5200.sgml | 2 +- include/atari.h | 1 + include/atari5200.h | 3 +++ libsrc/atari/waitvsync.s | 15 +++++++++++++++ libsrc/atari5200/waitvsync.s | 15 +++++++++++++++ 6 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 libsrc/atari/waitvsync.s create mode 100644 libsrc/atari5200/waitvsync.s diff --git a/doc/atari.sgml b/doc/atari.sgml index eecd1b803..7fc929f42 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -332,6 +332,7 @@ See the for declaration and u _scroll _setcolor _setcolor_low +waitvsync diff --git a/doc/atari5200.sgml b/doc/atari5200.sgml index 00d3dfbd2..032b0ef6c 100644 --- a/doc/atari5200.sgml +++ b/doc/atari5200.sgml @@ -77,7 +77,7 @@ Programs containing Atari 5200 specific code may use the Atari 5200 specific functions

-TBD. +waitvsync diff --git a/include/atari.h b/include/atari.h index 1a00a4c67..b2fca0b0e 100644 --- a/include/atari.h +++ b/include/atari.h @@ -227,6 +227,7 @@ extern unsigned char __fastcall__ _getcolor (unsigned char color_reg); /* Other screen functions */ /*****************************************************************************/ +extern void waitvsync (void); /* Wait for start of next frame */ extern int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */ extern void __fastcall__ _scroll (signed char numlines); /* numlines > 0 scrolls up */ diff --git a/include/atari5200.h b/include/atari5200.h index d6c2561b2..9662fe98e 100644 --- a/include/atari5200.h +++ b/include/atari5200.h @@ -89,5 +89,8 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ */ #define _bordercolor(color) 0 +extern void waitvsync (void); +/* Wait for start of next frame */ + /* End of atari5200.h */ #endif diff --git a/libsrc/atari/waitvsync.s b/libsrc/atari/waitvsync.s new file mode 100644 index 000000000..a19b2375d --- /dev/null +++ b/libsrc/atari/waitvsync.s @@ -0,0 +1,15 @@ +; +; Written by Christian Groessler +; +; void waitvsync (void); +; + + .include "atari.inc" + .export _waitvsync + +.proc _waitvsync + lda RTCLOK+2 +@lp: cmp RTCLOK+2 + beq @lp + rts +.endproc diff --git a/libsrc/atari5200/waitvsync.s b/libsrc/atari5200/waitvsync.s new file mode 100644 index 000000000..9a67c6429 --- /dev/null +++ b/libsrc/atari5200/waitvsync.s @@ -0,0 +1,15 @@ +; +; Written by Christian Groessler +; +; void waitvsync (void); +; + + .include "atari5200.inc" + .export _waitvsync + +.proc _waitvsync + lda RTCLOK+1 +@lp: cmp RTCLOK+1 + beq @lp + rts +.endproc From 262631039dca1c7440457586d3a64d10ae9248db Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Wed, 28 Oct 2020 17:37:50 +0100 Subject: [PATCH 416/806] atari.h, atari5200.h: style fixes --- include/atari.h | 2 +- include/atari5200.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/atari.h b/include/atari.h index b2fca0b0e..f5916a284 100644 --- a/include/atari.h +++ b/include/atari.h @@ -227,7 +227,7 @@ extern unsigned char __fastcall__ _getcolor (unsigned char color_reg); /* Other screen functions */ /*****************************************************************************/ -extern void waitvsync (void); /* Wait for start of next frame */ +extern void waitvsync (void); /* wait for start of next frame */ extern int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */ extern void __fastcall__ _scroll (signed char numlines); /* numlines > 0 scrolls up */ diff --git a/include/atari5200.h b/include/atari5200.h index 9662fe98e..a18360c61 100644 --- a/include/atari5200.h +++ b/include/atari5200.h @@ -35,7 +35,7 @@ -/* Check for errors */ +/* check for errors */ #if !defined(__ATARI5200__) # error This module may only be used when compiling for the Atari 5200! #endif @@ -46,14 +46,14 @@ /* the addresses of the static drivers */ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ -/* Masks for joy_read */ +/* masks for joy_read */ #define JOY_UP_MASK 0x01 #define JOY_DOWN_MASK 0x02 #define JOY_LEFT_MASK 0x04 #define JOY_RIGHT_MASK 0x08 #define JOY_BTN_1_MASK 0x10 -/* Character codes */ +/* character codes */ #define CH_ULCORNER 0x0B /* '+' sign */ #define CH_URCORNER 0x0B #define CH_LLCORNER 0x0B @@ -65,7 +65,7 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ #define AT_NTSC 0 #define AT_PAL 1 -/* Define hardware */ +/* define hardware */ #include <_gtia.h> #define GTIA_READ (*(struct __gtia_read*)0xC000) #define GTIA_WRITE (*(struct __gtia_write*)0xC000) @@ -89,8 +89,8 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ */ #define _bordercolor(color) 0 +/* wait for start of next frame */ extern void waitvsync (void); -/* Wait for start of next frame */ -/* End of atari5200.h */ +/* end of atari5200.h */ #endif From a686988d0ebb0b42d965ed006b9d3a78b78d947c Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 28 Oct 2020 09:35:49 +0100 Subject: [PATCH 417/806] Add test cases for integral promotion of chars Both signed and unsigned chars are promoted to int by C's evaluation rules. It is more efficient to use unsigned operations when possible, however. These tests will help test the correctness of optimizations doing that. See #1308. --- test/val/char-promote.c | 127 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 test/val/char-promote.c diff --git a/test/val/char-promote.c b/test/val/char-promote.c new file mode 100644 index 000000000..1d6360d45 --- /dev/null +++ b/test/val/char-promote.c @@ -0,0 +1,127 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of promotions of character types. +*/ + +#include + +typedef unsigned char u8; + +static unsigned char failures = 0; + +void test_sub (void) +{ + const u8 one = 1, two = 2; + + /* For any unsigned type other than unsigned char, (T) 1 - (T) 2 > 0. */ + if (1U - 2U < 0) { + fprintf (stderr, "Expected 1U - 2U > 0\n"); + failures++; + } + + /* The unsigned chars get promoted to int, so this is negative. */ + if (one - two > 0) { + fprintf (stderr, "Expected one - two < 0\n"); + failures++; + } + + /* Test the constant expression code paths. */ + if ((u8) 1 - (u8) 2 > 0) { + fprintf (stderr, "Expected (u8) 1 - (u8) 2 < 0\n"); + failures++; + } +} + +void test_mul (void) +{ + const u8 two_fifty_five = 255; + + if (255U * 255U != 65025U) { + fprintf (stderr, "Expected 255U * 255U == 65025U\n"); + failures++; + } +#if 0 + /* Disabled pending fix of #1310. */ + if (255 * 255 != -511) { + fprintf (stderr, "Expected 255 * 255 == -511, got: %d\n", 255 * 255); + failures++; + } +#endif + + /* The unsigned chars get promoted to int, so this is -511. + ** We should also be able to observe that the generated code uses mul, not umul. + */ + if (two_fifty_five * two_fifty_five != -511) { + fprintf (stderr, "Expected two_fifty_five * two_fifty_five == -511\n"); + failures++; + } +#if 0 + /* Disabled pending fix of #1310. */ + if ((u8) 255 * (u8) 255 != -511) { + fprintf (stderr, "Expected (u8) 255 * (u8) 255 == -511, got: %d\n", + (u8) 255 * (u8) 255); + failures++; + } +#endif +} + +void test_div (void) +{ + const u8 seventeen = 17; + const u8 three = 3; + + /* We should also be able to observe that the generated code uses div, not udiv. */ + if (seventeen / three != 5) { + fprintf (stderr, "Expected seventeen / three == 5, got: %d\n", seventeen / three); + failures++; + } + if ((u8) 17 / (u8) 3 != 5) { + fprintf (stderr, "Expected (u8) 17 / (u8) 3 == 5, got: %d\n", (u8) 17 / (u8) 3); + failures++; + } +} + +void test_shr (void) +{ + const unsigned int forty_two = 42; + const unsigned int two = 2; + + /* We should also be able to observe that the generated code uses asr, not shr. */ + if (forty_two >> two != 10) { + fprintf (stderr, "Expected forty_two / two == 10, got: %d\n", forty_two >> two); + failures++; + } + if ((u8) 42 >> (u8) 2 != 10) { + fprintf (stderr, "Expected (u8) 42 >> (u8) 2 == 10, got: %d\n", (u8) 42 >> (u8) 3); + failures++; + } +} + +int main (void) +{ + test_sub (); + test_mul (); + test_div (); + test_shr (); + printf ("failures: %u\n", failures); + return failures; +} From 944ebbc23cf6d74ffe1f5f1d5a9ca001066d7d23 Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Thu, 29 Oct 2020 16:19:38 +0100 Subject: [PATCH 418/806] atarixl configs: make size of CHARGEN configurable If text mode is not used, its space can be reclaimed by setting __CHARGENSIZE__ to 0. Following a suggestion from issue #1314. --- cfg/atarixl-largehimem.cfg | 33 +++++++++++++++++---------------- cfg/atarixl-overlay.cfg | 37 +++++++++++++++++++------------------ cfg/atarixl-xex.cfg | 21 +++++++++++---------- cfg/atarixl.cfg | 29 +++++++++++++++-------------- 4 files changed, 62 insertions(+), 58 deletions(-) diff --git a/cfg/atarixl-largehimem.cfg b/cfg/atarixl-largehimem.cfg index 56d2af15b..5e60f32bd 100644 --- a/cfg/atarixl-largehimem.cfg +++ b/cfg/atarixl-largehimem.cfg @@ -8,39 +8,40 @@ FEATURES { } SYMBOLS { __EXEHDR__: type = import; - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __AUTOSTART__: type = import; # force inclusion of autostart "trailer" - __STACKSIZE__: type = weak, value = $0800; # 2k stack + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __AUTOSTART__: type = import; # force inclusion of autostart "trailer" + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAINHDR: file = %O, start = $0000, size = $0004; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # address of relocated character generator - CHARGEN: file = "", define = yes, start = $D800, size = $0400; + CHARGEN: file = "", define = yes, start = $D800, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $DC00, size = $FFFA - $DC00; + HIDDEN_RAM: file = "", define = yes, start = $D800 + __CHARGENSIZE__, size = $FFFA - $D800 - __CHARGENSIZE__; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; diff --git a/cfg/atarixl-overlay.cfg b/cfg/atarixl-overlay.cfg index 923436497..2bb4105cd 100644 --- a/cfg/atarixl-overlay.cfg +++ b/cfg/atarixl-overlay.cfg @@ -3,44 +3,45 @@ FEATURES { } SYMBOLS { __EXEHDR__: type = import; - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __AUTOSTART__: type = import; # force inclusion of autostart "trailer" - __STACKSIZE__: type = weak, value = $0800; # 2k stack - __OVERLAYSIZE__: type = weak, value = $1000; # 4k overlay + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __AUTOSTART__: type = import; # force inclusion of autostart "trailer" + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; + __OVERLAYSIZE__: type = weak, value = $1000; # 4k overlay __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = $7C20 - %S - __OVERLAYSIZE__ - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S + __OVERLAYSIZE__, size = $7C20 - %S - __OVERLAYSIZE__ - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; + MAINHDR: file = %O, start = $0000, size = $0004; MAIN: file = %O, define = yes, start = %S + __OVERLAYSIZE__ + - __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __OVERLAYSIZE__ - __LOWBSS_SIZE__; + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __OVERLAYSIZE__ - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; # overlays OVL1: file = "%O.1", start = %S, size = __OVERLAYSIZE__; diff --git a/cfg/atarixl-xex.cfg b/cfg/atarixl-xex.cfg index 0b1fe9ca1..6b6601c59 100644 --- a/cfg/atarixl-xex.cfg +++ b/cfg/atarixl-xex.cfg @@ -4,35 +4,36 @@ FEATURES { STARTADDRESS: default = $2400; } SYMBOLS { - __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk - __STACKSIZE__: type = weak, value = $0800; # 2k stack + __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk + __STACKSIZE__: type = weak, value = $0800; # 2k stack __STARTADDRESS__: type = export, value = %S; + __CHARGENSIZE__: type = weak, value = $0400; __SYSCHKHDR__: type = export, value = 0; # Disable system check header __SYSCHKTRL__: type = export, value = 0; # Disable system check trailer } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # "system check" load chunk - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; # "shadow RAM preparation" load chunk - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned # "main program" load chunk - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; # UNUSED - hide - UNUSED: file = "", start = $0, size = $10; + UNUSED: file = "", start = $0, size = $10; } FILES { %O: format = atari; diff --git a/cfg/atarixl.cfg b/cfg/atarixl.cfg index 197daace6..d4e466ae1 100644 --- a/cfg/atarixl.cfg +++ b/cfg/atarixl.cfg @@ -6,39 +6,40 @@ SYMBOLS { __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk __AUTOSTART__: type = import; # force inclusion of autostart "trailer" __STACKSIZE__: type = weak, value = $0800; # 2k stack + __CHARGENSIZE__: type = weak, value = $0400; __STARTADDRESS__: type = export, value = %S; } MEMORY { - ZP: file = "", define = yes, start = $0082, size = $007E; + ZP: file = "", define = yes, start = $0082, size = $007E; # just $FFFF - HEADER: file = %O, start = $0000, size = $0002; + HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk - SYSCHKHDR: file = %O, start = $0000, size = $0004; - SYSCHKCHNK: file = %O, start = $2E00, size = $0300; - SYSCHKTRL: file = %O, start = $0000, size = $0006; + SYSCHKHDR: file = %O, start = $0000, size = $0004; + SYSCHKCHNK: file = %O, start = $2E00, size = $0300; + SYSCHKTRL: file = %O, start = $0000, size = $0006; # "shadow RAM preparation" load chunk - SRPREPHDR: file = %O, start = $0000, size = $0004; - SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned - SRPREPTRL: file = %O, start = $0000, size = $0006; + SRPREPHDR: file = %O, start = $0000, size = $0004; + SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned + SRPREPTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk - MAINHDR: file = %O, start = $0000, size = $0004; - MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; + MAINHDR: file = %O, start = $0000, size = $0004; + MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__; # defines entry point into program - TRAILER: file = %O, start = $0000, size = $0006; + TRAILER: file = %O, start = $0000, size = $0006; # memory beneath the ROM preceeding the character generator - HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; + HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800; # address of relocated character generator (same addess as ROM version) - CHARGEN: file = "", define = yes, start = $E000, size = $0400; + CHARGEN: file = "", define = yes, start = $E000, size = __CHARGENSIZE__; # memory beneath the ROM - HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400; + HIDDEN_RAM: file = "", define = yes, start = $E000 + __CHARGENSIZE__, size = $FFFA - $E000 - __CHARGENSIZE__; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; From d8e6fa61bbcbb86c7bcf287a988f42246f22376f Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Thu, 29 Oct 2020 17:42:36 +0100 Subject: [PATCH 419/806] Return NULL on error (or end of directory). --- libsrc/geos-common/file/get1stdirentry.s | 8 ++++++-- libsrc/geos-common/file/getnxtdirentry.s | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/libsrc/geos-common/file/get1stdirentry.s b/libsrc/geos-common/file/get1stdirentry.s index 3b99554eb..381a35c6d 100644 --- a/libsrc/geos-common/file/get1stdirentry.s +++ b/libsrc/geos-common/file/get1stdirentry.s @@ -5,7 +5,7 @@ ; struct filehandle* Get1stDirEntry (void); - .import __oserror + .import __oserror, return0 .export _Get1stDirEntry .include "diskdrv.inc" @@ -14,6 +14,10 @@ _Get1stDirEntry: jsr Get1stDirEntry stx __oserror - lda r5L + txa + beq L0 ; error? + jmp return0 ; return NULL + +L0: lda r5L ldx r5H rts diff --git a/libsrc/geos-common/file/getnxtdirentry.s b/libsrc/geos-common/file/getnxtdirentry.s index 912f48ca4..16a31edcf 100644 --- a/libsrc/geos-common/file/getnxtdirentry.s +++ b/libsrc/geos-common/file/getnxtdirentry.s @@ -5,7 +5,7 @@ ; struct filehandle* GetNxtDirEntry (void); - .import __oserror + .import __oserror, return0 .export _GetNxtDirEntry .include "diskdrv.inc" @@ -14,6 +14,12 @@ _GetNxtDirEntry: jsr GetNxtDirEntry stx __oserror - lda r5L + txa + beq L0 ; error? + tya + beq L0 ; end of dir? + jmp return0 ; return NULL + +L0: lda r5L ldx r5H rts From aad17a6f057dc57424c010c0d832e1f49592f044 Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 29 Oct 2020 17:54:56 -0400 Subject: [PATCH 420/806] Made two GEOS directory functions return NULL if they can't give a valid entry. --- doc/geos.sgml | 19 ++++++++++--------- libsrc/geos-common/file/get1stdirentry.s | 12 ++++++------ libsrc/geos-common/file/getnxtdirentry.s | 14 +++++++------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/doc/geos.sgml b/doc/geos.sgml index a10ade5d5..11ff9c534 100644 --- a/doc/geos.sgml +++ b/doc/geos.sgml @@ -858,20 +858,21 @@ The functions described here are common for SEQ and VLIR structures.

-These two functions are best suited for scanning the whole directory for particular files. Note that -the returned filehandles describe all file slots in the directory - even those with deleted files. -The return value can be obtained by casting both sides to geos/gdisk.h). The current +directory track and sector numbers are in FindFile

-This function scans the whole directory for the given filename. It returns either 0 (success) or 5 -(FILE_NOT_FOUND, defined in geos/gdisk.h), or any other fatal disk read error. After a successful +FindFTypes

diff --git a/libsrc/geos-common/file/get1stdirentry.s b/libsrc/geos-common/file/get1stdirentry.s index 381a35c6d..f0ad59388 100644 --- a/libsrc/geos-common/file/get1stdirentry.s +++ b/libsrc/geos-common/file/get1stdirentry.s @@ -1,7 +1,7 @@ ; -; Maciej 'YTM/Alliance' Witkowiak +; 1999-10-26, Maciej 'YTM/Alliance' Witkowiak +; 2020-10-29, Greg King ; -; 26.10.99 ; struct filehandle* Get1stDirEntry (void); @@ -15,9 +15,9 @@ _Get1stDirEntry: jsr Get1stDirEntry stx __oserror txa - beq L0 ; error? - jmp return0 ; return NULL - -L0: lda r5L + bne L1 ; jump if disk error + lda r5L ldx r5H rts + +L1: jmp return0 ; return NULL if not valid entry diff --git a/libsrc/geos-common/file/getnxtdirentry.s b/libsrc/geos-common/file/getnxtdirentry.s index 16a31edcf..e8ccbf3a2 100644 --- a/libsrc/geos-common/file/getnxtdirentry.s +++ b/libsrc/geos-common/file/getnxtdirentry.s @@ -1,7 +1,7 @@ ; -; Maciej 'YTM/Alliance' Witkowiak +; 1999-10-26, Maciej 'YTM/Alliance' Witkowiak +; 2020-10-29, Greg King ; -; 26.10.99 ; struct filehandle* GetNxtDirEntry (void); @@ -15,11 +15,11 @@ _GetNxtDirEntry: jsr GetNxtDirEntry stx __oserror txa - beq L0 ; error? + bne L1 ; jump if disk error tya - beq L0 ; end of dir? - jmp return0 ; return NULL - -L0: lda r5L + bne L1 ; jump when no more entries + lda r5L ldx r5H rts + +L1: jmp return0 ; return NULL if not valid entry From 39c0abed549708a127a0211d94aab204dde681c2 Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Fri, 30 Oct 2020 15:01:47 +0100 Subject: [PATCH 421/806] atarixl: fix compilation problem when CHARGEN_RELOC is defined --- libsrc/atari/shadow_ram_timerirq1.s | 1 + libsrc/atari/shadow_ram_timerirq2.s | 1 + 2 files changed, 2 insertions(+) diff --git a/libsrc/atari/shadow_ram_timerirq1.s b/libsrc/atari/shadow_ram_timerirq1.s index f8a3e9b4d..e1b7d831b 100644 --- a/libsrc/atari/shadow_ram_timerirq1.s +++ b/libsrc/atari/shadow_ram_timerirq1.s @@ -11,6 +11,7 @@ SHRAM_HANDLERS = 1 .include "atari.inc" .include "romswitch.inc" + .import __CHARGEN_START__ .export set_VTIMR1_handler diff --git a/libsrc/atari/shadow_ram_timerirq2.s b/libsrc/atari/shadow_ram_timerirq2.s index b2cdaecd2..d6d581937 100644 --- a/libsrc/atari/shadow_ram_timerirq2.s +++ b/libsrc/atari/shadow_ram_timerirq2.s @@ -11,6 +11,7 @@ SHRAM_HANDLERS = 1 .include "atari.inc" .include "romswitch.inc" + .import __CHARGEN_START__ .export set_VTIMR2_handler From 0e482c7f922b3563113ea68839054f518f852fda Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 28 Oct 2020 09:52:54 +0100 Subject: [PATCH 422/806] Add test for issue #1310 --- test/todo/bug1310.c | 73 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 test/todo/bug1310.c diff --git a/test/todo/bug1310.c b/test/todo/bug1310.c new file mode 100644 index 000000000..936145928 --- /dev/null +++ b/test/todo/bug1310.c @@ -0,0 +1,73 @@ +/* + Copyright 2020 The cc65 Authors + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + Tests of constant expressions. https://github.com/cc65/cc65/issues/1310 +*/ + +#include + +static unsigned char failures = 0; + +int main (void) +{ + /* 255 * 255 is signed integer overflow, so UB, but it would be nice if + ** (1) there were a warning, and (2) it did the "obvious" thing. + */ + const int two_fifty_five = 255; + const int two_fifty_five_squared = 255 * 255; + + /* Unsigned overflow is not UB, but has similar problems with comparison. */ + const int two_fifty_six = 256U; + const int two_fifty_six_squared = 256U * 256U; + + if (255 * 255 != -511) { + fprintf (stderr, "Expected 255 * 255 == -511, got: %d\n", 255 * 255); + failures++; + } + if (two_fifty_five * two_fifty_five != -511) { + fprintf (stderr, "Expected two_fifty_five * two_fifty_five == -511, got: %d\n", + two_fifty_five * two_fifty_five); + failures++; + } + if (two_fifty_five_squared != -511) { + fprintf (stderr, "Expected two_fifty_five_squared == -511, got: %d\n", + two_fifty_five_squared); + failures++; + } + + if (256U * 256U != 0) { + fprintf (stderr, "Expected 256U * 256U == 0, got: %d\n", 256U * 256U); + failures++; + } + if (two_fifty_six * two_fifty_six != 0) { + fprintf (stderr, "Expected two_fifty_six * two_fifty_six == 0, got: %d\n", + two_fifty_six * two_fifty_six); + failures++; + } + if (two_fifty_six_squared != 0) { + fprintf (stderr, "Expected two_fifty_six_squared == 0, got: %d\n", + two_fifty_six_squared); + failures++; + } + + printf ("failures: %u\n", failures); + return failures; +} From 65193c6aaf6227b731b8da03cccab521f6ffe3cd Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 2 Nov 2020 09:03:06 +0100 Subject: [PATCH 423/806] Add stdint.h constants INT32_MIN and UINT32_MAX These were missing before --- src/common/inttypes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/inttypes.h b/src/common/inttypes.h index 0d8caf32a..ca30ceb71 100644 --- a/src/common/inttypes.h +++ b/src/common/inttypes.h @@ -64,9 +64,11 @@ typedef size_t uintmax_t; #define INT8_MIN (-INT8_MAX - 1) #define INT16_MIN (-INT16_MAX - 1) +#define INT32_MIN (-INT32_MAX - 1) #define UINT8_MAX (0xFF) #define UINT16_MAX (0xFFFF) +#define UINT32_MAX (0xFFFFFFFF) #endif From b5f0c0468dd4191415a37e29acf91995d714ee6b Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Tue, 3 Nov 2020 08:26:31 +0100 Subject: [PATCH 424/806] Add stdint.h types for C89 compilers Add `intN_t` and `uintN_t` for N = 8, 16, 32. --- src/common/inttypes.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/common/inttypes.h b/src/common/inttypes.h index ca30ceb71..28ffb2cc5 100644 --- a/src/common/inttypes.h +++ b/src/common/inttypes.h @@ -51,6 +51,7 @@ /* Assume that ptrdiff_t and size_t are wide enough to hold pointers. ** Assume that they are the widest type. */ +#include #include typedef ptrdiff_t intptr_t; @@ -70,6 +71,50 @@ typedef size_t uintmax_t; #define UINT16_MAX (0xFFFF) #define UINT32_MAX (0xFFFFFFFF) +#if UCHAR_MAX == UINT8_MAX +typedef unsigned char uint8_t; +#else +#error "No suitable type for uint8_t found." +#endif + +#if SCHAR_MIN == INT8_MIN && SCHAR_MAX == INT8_MAX +typedef signed char int8_t; +#else +#error "No suitable type for int8_t found." +#endif + +#if UINT_MAX == UINT16_MAX +typedef unsigned int uint16_t; +#elif USHRT_MAX == UINT16_MAX +typedef unsigned short uint16_t; +#else +#error "No suitable type for uint16_t found." +#endif + +#if INT_MIN == INT16_MIN && INT_MAX == INT16_MAX +typedef int int16_t; +#elif SHRT_MIN == INT16_MIN && SHRT_MAX == INT16_MAX +typedef short int16_t; +#else +#error "No suitable type for int16_t found." +#endif + +#if UINT_MAX == UINT32_MAX +typedef unsigned int uint32_t; +#elif ULONG_MAX == UINT32_MAX +typedef unsigned long uint32_t; +#else +#error "No suitable type for uint32_t found." +#endif + +#if INT_MIN == INT32_MIN && INT_MAX == INT32_MAX +typedef int int32_t; +#elif LONG_MIN == INT32_MIN && LONG_MAX == INT32_MAX +typedef long int32_t; +#else +#error "No suitable type for int32_t found." +#endif + #endif From 81edc3f582767134253d378cc2e9aa4cea8c0cab Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 8 Nov 2020 17:45:54 -0500 Subject: [PATCH 425/806] Updated a comment about Kernal's STATUS variable. --- libsrc/cx16/getdevice.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/cx16/getdevice.s b/libsrc/cx16/getdevice.s index 9b554900f..5f2e74af4 100644 --- a/libsrc/cx16/getdevice.s +++ b/libsrc/cx16/getdevice.s @@ -51,7 +51,7 @@ next: inx jsr closecmdchannel ldx tmp2 -; As we had to reference ST above anyway, we can do so, as well, +; As we had to reference STATUS above anyway, we can do so, as well, ; here too (instead of calling READST). lda STATUS From 5db74b4b195cd6f730a0866e4f7b4c7bfcefb98f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Wed, 28 Oct 2020 08:13:30 +0100 Subject: [PATCH 426/806] Use u16 codegen for u8 x u8 ops In g_typeadjust, before we apply the integral promotions, we check if both types are unsigned char. If so, we promote to unsigned int, rather than int, which would be chosen by the standard rules. This is only a performance optimization and does not affect correctness, as the flags returned by g_typeadjust are only used for code generation, and not to determine types of other expressions containing this one. All unsigned char bit-patterns are valid as both int and unsigned int and represent the same value, so either signed or unsigned int operations can be used. This special case part is not duplicated by ArithmeticConvert. Partial fix for #1308. --- src/cc65/codegen.c | 13 +++++++++++++ test/val/char-promote.c | 11 ++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 539309283..7a25594b6 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1432,6 +1432,19 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) /* Note that this logic is largely duplicated by ArithmeticConvert. */ + /* Before we apply the integral promotions, we check if both types are unsigned char. + ** If so, we return unsigned int, rather than int, which would be returned by the standard + ** rules. This is only a performance optimization and does not affect correctness, as + ** the flags are only used for code generation, and not to determine types of other + ** expressions containing this one. All unsigned char bit-patterns are valid as both int + ** and unsigned int and represent the same value, so either signed or unsigned int operations + ** can be used. This special case part is not duplicated by ArithmeticConvert. + */ + if ((lhs & CF_TYPEMASK) == CF_CHAR && (lhs & CF_UNSIGNED) && + (rhs & CF_TYPEMASK) == CF_CHAR && (rhs & CF_UNSIGNED)) { + return const_flag | CF_UNSIGNED | CF_INT; + } + /* Apply integral promotions for types char/short. */ lhs = g_intpromotion (lhs); rhs = g_intpromotion (rhs); diff --git a/test/val/char-promote.c b/test/val/char-promote.c index 1d6360d45..380a13378 100644 --- a/test/val/char-promote.c +++ b/test/val/char-promote.c @@ -68,7 +68,8 @@ void test_mul (void) #endif /* The unsigned chars get promoted to int, so this is -511. - ** We should also be able to observe that the generated code uses mul, not umul. + ** We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses umul, not mul. */ if (two_fifty_five * two_fifty_five != -511) { fprintf (stderr, "Expected two_fifty_five * two_fifty_five == -511\n"); @@ -89,7 +90,9 @@ void test_div (void) const u8 seventeen = 17; const u8 three = 3; - /* We should also be able to observe that the generated code uses div, not udiv. */ + /* We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses udiv, not div. + */ if (seventeen / three != 5) { fprintf (stderr, "Expected seventeen / three == 5, got: %d\n", seventeen / three); failures++; @@ -105,7 +108,9 @@ void test_shr (void) const unsigned int forty_two = 42; const unsigned int two = 2; - /* We should also be able to observe that the generated code uses asr, not shr. */ + /* We should also be able to observe that, due to optimizations from #1315, the generated code + ** uses shr, not asr. + */ if (forty_two >> two != 10) { fprintf (stderr, "Expected forty_two / two == 10, got: %d\n", forty_two >> two); failures++; From 93145246fd3533cab0525def93ac4825ec6c405c Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 31 Oct 2020 18:20:15 +0100 Subject: [PATCH 427/806] Add tests for u8 op s16_const Test expressions like `unsigned char x = ...; ... = x / 2;` These use `int` constants with values representable by `unsigned int` / `unsigned char`, so using unsigned codegen should be possible. Additional tests for #1308. These are things we want to generate better code for, so add tests that the behavior doesn't change. --- test/val/char-promote.c | 46 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/test/val/char-promote.c b/test/val/char-promote.c index 380a13378..0d2dad04e 100644 --- a/test/val/char-promote.c +++ b/test/val/char-promote.c @@ -54,6 +54,8 @@ void test_sub (void) void test_mul (void) { const u8 two_fifty_five = 255; + const u8 sixteen = 16; + int x; if (255U * 255U != 65025U) { fprintf (stderr, "Expected 255U * 255U == 65025U\n"); @@ -83,12 +85,20 @@ void test_mul (void) failures++; } #endif + + /* This should compile to a shift. */ + x = sixteen * 4; + if (x != 64) { + fprintf (stderr, "Expected sixteen * 4 == 64, got: %d\n", x); + failures++; + } } void test_div (void) { const u8 seventeen = 17; const u8 three = 3; + int x; /* We should also be able to observe that, due to optimizations from #1315, the generated code ** uses udiv, not div. @@ -101,24 +111,57 @@ void test_div (void) fprintf (stderr, "Expected (u8) 17 / (u8) 3 == 5, got: %d\n", (u8) 17 / (u8) 3); failures++; } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = seventeen / 4; + if (x != 4) { + fprintf (stderr, "Expected seventeen / 4 == 4, got: %d\n", x); + failures++; + } +} + +void test_mod (void) +{ + const u8 seventeen = 17; + /* Ideally, this would compile to a bitwise and, but that does not happen currently. */ + int x = seventeen % 4; + if (x != 1) { + fprintf (stderr, "Expected seventeen %% 4 == 1, got: %d\n", x); + failures++; + } } void test_shr (void) { const unsigned int forty_two = 42; const unsigned int two = 2; + int x; /* We should also be able to observe that, due to optimizations from #1315, the generated code ** uses shr, not asr. */ if (forty_two >> two != 10) { - fprintf (stderr, "Expected forty_two / two == 10, got: %d\n", forty_two >> two); + fprintf (stderr, "Expected forty_two >> two == 10, got: %d\n", forty_two >> two); failures++; } if ((u8) 42 >> (u8) 2 != 10) { fprintf (stderr, "Expected (u8) 42 >> (u8) 2 == 10, got: %d\n", (u8) 42 >> (u8) 3); failures++; } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = forty_two >> 2; + if (x != 10) { + fprintf (stderr, "Expected forty_two >> 2 == 10, got: %d\n", x); + failures++; + } + + /* Ideally, this would compile to a logical shift, but that does not happen currently. */ + x = 42 >> two; + if (x != 10) { + fprintf (stderr, "Expected 42 >> two == 10, got: %d\n", x); + failures++; + } } int main (void) @@ -126,6 +169,7 @@ int main (void) test_sub (); test_mul (); test_div (); + test_mod (); test_shr (); printf ("failures: %u\n", failures); return failures; From 53f055fb1d9dd614888260a0ecd57d7e0b1cb9fa Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Wed, 11 Nov 2020 12:15:09 +0100 Subject: [PATCH 428/806] Reduce stack size to 256B for unexpanded VICs. --- cfg/vic20.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/vic20.cfg b/cfg/vic20.cfg index ceaee3a87..a14491e9f 100644 --- a/cfg/vic20.cfg +++ b/cfg/vic20.cfg @@ -1,7 +1,7 @@ SYMBOLS { __LOADADDR__: type = import; __EXEHDR__: type = import; - __STACKSIZE__: type = weak, value = $0400; # 1k stack + __STACKSIZE__: type = weak, value = $0100 } MEMORY { ZP: file = "", define = yes, start = $0002, size = $001A; From 77da8d5490ea88396cc003417bbfdafa8a1973f6 Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Wed, 11 Nov 2020 17:20:25 +0100 Subject: [PATCH 429/806] vic20.cfg: add missing comma --- cfg/vic20.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/vic20.cfg b/cfg/vic20.cfg index a14491e9f..8efeae229 100644 --- a/cfg/vic20.cfg +++ b/cfg/vic20.cfg @@ -1,7 +1,7 @@ SYMBOLS { __LOADADDR__: type = import; __EXEHDR__: type = import; - __STACKSIZE__: type = weak, value = $0100 + __STACKSIZE__: type = weak, value = $0100; } MEMORY { ZP: file = "", define = yes, start = $0002, size = $001A; From b33b05330713a46adad4f0cbb137da06f15dfd1a Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Tue, 10 Nov 2020 10:32:29 +0100 Subject: [PATCH 430/806] add c64dtv support --- asminc/cpu.mac | 2 + asminc/opcodes.inc | 8 ++ doc/ca65.sgml | 2 +- src/ca65/instr.c | 92 +++++++++++++ src/cc65/codegen.c | 3 +- src/cc65/coptjmp.c | 2 +- src/cc65/main.c | 3 +- src/cc65/opcodes.c | 2 +- src/common/cpu.c | 2 + src/common/cpu.h | 2 + src/da65.vcxproj | 2 + src/da65/opc6502dtv.c | 308 ++++++++++++++++++++++++++++++++++++++++++ src/da65/opc6502dtv.h | 61 +++++++++ src/da65/opctable.c | 2 + 14 files changed, 486 insertions(+), 5 deletions(-) create mode 100644 src/da65/opc6502dtv.c create mode 100644 src/da65/opc6502dtv.h diff --git a/asminc/cpu.mac b/asminc/cpu.mac index 6b9cb9947..55727c93e 100644 --- a/asminc/cpu.mac +++ b/asminc/cpu.mac @@ -9,6 +9,7 @@ CPU_ISET_SWEET16 = $0040 CPU_ISET_HUC6280 = $0080 ;CPU_ISET_M740 = $0100 not actually implemented CPU_ISET_4510 = $0200 +CPU_ISET_6502DTV = $0400 ; CPU capabilities CPU_NONE = CPU_ISET_NONE @@ -20,3 +21,4 @@ CPU_65816 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65816 CPU_SWEET16 = CPU_ISET_SWEET16 CPU_HUC6280 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65C02|CPU_ISET_HUC6280 CPU_4510 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65C02|CPU_ISET_4510 +CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502X|CPU_ISET_6502DTV diff --git a/asminc/opcodes.inc b/asminc/opcodes.inc index aa7a65f00..e6b7e73df 100644 --- a/asminc/opcodes.inc +++ b/asminc/opcodes.inc @@ -505,4 +505,12 @@ OPC_ISC_aby = $FB OPC_NOP_abx = $FC OPC_ISC_abx = $FF +.if (.cpu .bitand ::CPU_ISET_6502DTV) + +OPC_BRA = $12 +OPC_SAC_imm = $32 +OPC_SIR_imm = $42 + +.endif + .endif diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 8d97bddfd..25a47b29a 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -151,7 +151,7 @@ Here is a description of all the command line options: Set the default for the CPU type. The option takes a parameter, which may be one of - 6502, 6502X, 65SC02, 65C02, 65816, sweet16, HuC6280, 4510 + 6502, 6502X, 6502DTV, 65SC02, 65C02, 65816, sweet16, HuC6280, 4510

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

Conditional assembly: Check if the assembler is currently in 65SC02 mode @@ -3585,6 +3593,14 @@ Here's a list of all control commands and a description, what they do: +.PDTV

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

Pop the last CPU setting from the stack, and activate it. @@ -3848,10 +3864,11 @@ Here's a list of all control commands and a description, what they do: Switch the CPU instruction set. The command is followed by a string that specifies the CPU. Possible values are those that can also be supplied to the command line option, - namely: 6502, 6502X, 65SC02, 65C02, 65816, 4510 and HuC6280. + namely: 6502, 6502X, 6502DTV, 65SC02, 65C02, 65816, 4510 and HuC6280. See: , , + , , , , @@ -4586,6 +4603,7 @@ each supported CPU a constant similar to CPU_SWEET16 CPU_HUC6280 CPU_4510 + CPU_6502DTV is defined. These constants may be used to determine the exact type of the @@ -4600,6 +4618,7 @@ another constant is defined: CPU_ISET_SWEET16 CPU_ISET_HUC6280 CPU_ISET_4510 + CPU_ISET_6502DTV The value read from the / pseudo variable may diff --git a/src/ca65/condasm.c b/src/ca65/condasm.c index b8bda4c7d..6198f4017 100644 --- a/src/ca65/condasm.c +++ b/src/ca65/condasm.c @@ -416,6 +416,16 @@ void DoConditionals (void) CalcOverallIfCond (); break; + case TOK_IFPDTV: + D = AllocIf (".IFPDTV", 1); + NextTok (); + if (IfCond) { + SetIfCond (D, GetCPU() == CPU_6502DTV); + } + ExpectSep (); + CalcOverallIfCond (); + break; + case TOK_IFPSC02: D = AllocIf (".IFPSC02", 1); NextTok (); @@ -470,6 +480,7 @@ int CheckConditionals (void) case TOK_IFP4510: case TOK_IFP816: case TOK_IFPC02: + case TOK_IFPDTV: case TOK_IFPSC02: case TOK_IFREF: DoConditionals (); diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 5b2ef0573..26d01cbf1 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1552,6 +1552,14 @@ static void DoP4510 (void) +static void DoPDTV (void) +/* Switch to C64DTV CPU */ +{ + SetCPU (CPU_6502DTV); +} + + + static void DoPageLength (void) /* Set the page length for the listing */ { @@ -2058,6 +2066,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccKeepToken, DoConditionals }, /* .IFP4510 */ { ccKeepToken, DoConditionals }, /* .IFP816 */ { ccKeepToken, DoConditionals }, /* .IFPC02 */ + { ccKeepToken, DoConditionals }, /* .IFPDTV */ { ccKeepToken, DoConditionals }, /* .IFPSC02 */ { ccKeepToken, DoConditionals }, /* .IFREF */ { ccNone, DoImport }, @@ -2091,6 +2100,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoPageLength }, { ccNone, DoUnexpected }, /* .PARAMCOUNT */ { ccNone, DoPC02 }, + { ccNone, DoPDTV }, { ccNone, DoPopCPU }, { ccNone, DoPopSeg }, { ccNone, DoProc }, diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index 361a817c1..fb9905809 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -219,6 +219,7 @@ struct DotKeyword { { ".IFP4510", TOK_IFP4510 }, { ".IFP816", TOK_IFP816 }, { ".IFPC02", TOK_IFPC02 }, + { ".IFPDTV", TOK_IFPDTV }, { ".IFPSC02", TOK_IFPSC02 }, { ".IFREF", TOK_IFREF }, { ".IMPORT", TOK_IMPORT }, @@ -258,6 +259,7 @@ struct DotKeyword { { ".PAGELENGTH", TOK_PAGELENGTH }, { ".PARAMCOUNT", TOK_PARAMCOUNT }, { ".PC02", TOK_PC02 }, + { ".PDTV", TOK_PDTV }, { ".POPCPU", TOK_POPCPU }, { ".POPSEG", TOK_POPSEG }, { ".PROC", TOK_PROC }, diff --git a/src/ca65/token.h b/src/ca65/token.h index 8998cc162..ab36028fd 100644 --- a/src/ca65/token.h +++ b/src/ca65/token.h @@ -196,6 +196,7 @@ typedef enum token_t { TOK_IFP4510, TOK_IFP816, TOK_IFPC02, + TOK_IFPDTV, TOK_IFPSC02, TOK_IFREF, TOK_IMPORT, @@ -229,6 +230,7 @@ typedef enum token_t { TOK_PAGELENGTH, TOK_PARAMCOUNT, TOK_PC02, + TOK_PDTV, TOK_POPCPU, TOK_POPSEG, TOK_PROC, From 843b94388a792b1343ca1f5824b29c0ab8401419 Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Mon, 16 Nov 2020 22:16:40 +0100 Subject: [PATCH 454/806] added asm test for 6502DTV cpu --- test/asm/cpudetect.s | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/asm/cpudetect.s b/test/asm/cpudetect.s index adad7c1dc..6362b98bc 100644 --- a/test/asm/cpudetect.s +++ b/test/asm/cpudetect.s @@ -24,6 +24,10 @@ taz .endif +.ifpdtv + sac #00 +.endif + ; step 2: check for bitwise compatibility of instructions sets ; (made verbose for better reading with hexdump/hd(1)) @@ -64,3 +68,7 @@ .byte 0,"CPU_ISET_4510" .endif +.if (.cpu .bitand CPU_ISET_6502DTV) + .byte 0,"CPU_ISET_6502DTV" +.endif + From 1c5c07406ca3c0a203b57df49f73c1f4067f78ee Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Wed, 18 Nov 2020 09:51:01 +0100 Subject: [PATCH 455/806] cpudetect.s needs this file to run its test for 6502dtv cpu --- test/asm/6502dtv-cpudetect.ref | Bin 0 -> 33 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/asm/6502dtv-cpudetect.ref diff --git a/test/asm/6502dtv-cpudetect.ref b/test/asm/6502dtv-cpudetect.ref new file mode 100644 index 0000000000000000000000000000000000000000..59278e80a126c65cc81e93110e153813468d5455 GIT binary patch literal 33 ccmcC!U~moyjrR<84T(21H84WuxrBrP0EvDGdH?_b literal 0 HcmV?d00001 From 0e98818db54b93c0db7c4918068146bacfa1e1fb Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Wed, 18 Nov 2020 15:26:23 +0100 Subject: [PATCH 456/806] assembled SAC and SIR opcodes of 6502DTV cpu were wrong --- src/ca65/instr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index c5e3b7fef..77a865319 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -367,7 +367,7 @@ static const struct { { "RRA", 0x000A26C, 0x63, 0, PutAll }, /* X */ { "RTI", 0x0000001, 0x40, 0, PutAll }, { "RTS", 0x0000001, 0x60, 0, PutAll }, - { "SAC", 0x0800000, 0x32, 0, PutAll }, /* DTV */ + { "SAC", 0x0800000, 0x32, 1, PutAll }, /* DTV */ { "SBC", 0x080A26C, 0xe0, 0, PutAll }, { "SEC", 0x0000001, 0x38, 0, PutAll }, { "SED", 0x0000001, 0xf8, 0, PutAll }, @@ -375,7 +375,7 @@ static const struct { { "SHA", 0x0002200, 0x93, 1, PutAll }, /* X */ { "SHX", 0x0000200, 0x9e, 1, PutAll }, /* X */ { "SHY", 0x0000040, 0x9c, 1, PutAll }, /* X */ - { "SIR", 0x0800000, 0x32, 0, PutAll }, /* DTV */ + { "SIR", 0x0800000, 0x42, 1, PutAll }, /* DTV */ { "STA", 0x000A26C, 0x80, 0, PutAll }, { "STX", 0x000010c, 0x82, 1, PutAll }, { "STY", 0x000002c, 0x80, 1, PutAll }, @@ -1031,9 +1031,9 @@ const InsTable* InsTab = (const InsTable*) &InsTab6502; */ static unsigned char EATab[12][AM65I_COUNT] = { { /* Table 0 */ - 0x00, 0x00, 0x05, 0x0D, 0x0F, 0x15, 0x1D, 0x1F, - 0x00, 0x19, 0x12, 0x00, 0x07, 0x11, 0x17, 0x01, - 0x00, 0x00, 0x00, 0x03, 0x13, 0x09, 0x00, 0x09, + 0x00, 0x00, 0x05 /*zp*/, 0x0D /*abs*/, 0x0F, 0x15 /*zpx*/, 0x1D /*abx*/, 0x1F, + 0x00, 0x19 /*aby*/, 0x12, 0x00, 0x07, 0x11 /*izy*/, 0x17, 0x01 /*izx*/, + 0x00, 0x00, 0x00, 0x03, 0x13, 0x09 /*imm*/, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00 }, { /* Table 1 */ From 527500cedca1d18163e969e5acefb0f5e4ac5151 Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Wed, 18 Nov 2020 15:28:15 +0100 Subject: [PATCH 457/806] instruction table contained wrong parameters at SIR opcode --- src/da65/opc6502dtv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/da65/opc6502dtv.c b/src/da65/opc6502dtv.c index 15b860c4e..fe83ad598 100644 --- a/src/da65/opc6502dtv.c +++ b/src/da65/opc6502dtv.c @@ -115,7 +115,7 @@ const OpcDesc OpcTable_6502DTV[256] = { { "rla", 3, flUseLabel|flAbsOverride, OH_AbsoluteX }, /* $3f */ { "rti", 1, flNone, OH_Rts }, /* $40 */ { "eor", 2, flUseLabel, OH_DirectXIndirect }, /* $41 */ - { "sir", 1, flNone, OH_Implicit }, /* $42 */ + { "sir", 2, flNone, OH_Immediate }, /* $42 */ { "", 1, flIllegal, OH_Illegal, }, /* $43 */ { "nop", 2, flUseLabel, OH_Direct }, /* $44 */ { "eor", 2, flUseLabel, OH_Direct }, /* $45 */ From 36f5dcbb6e2265ff1b2409b2ced05af23460e35a Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Wed, 18 Nov 2020 15:29:56 +0100 Subject: [PATCH 458/806] added 6502dtv opdoces testcases and corrected cpudetect --- test/asm/6502dtv-cpudetect.ref | Bin 33 -> 33 bytes test/asm/6502dtv-opcodes.ref | Bin 0 -> 498 bytes test/asm/6502dtv-opcodes.s | 258 +++++++++++++++++++++++++++++++++ test/asm/readme.txt | 2 +- 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 test/asm/6502dtv-opcodes.ref create mode 100644 test/asm/6502dtv-opcodes.s diff --git a/test/asm/6502dtv-cpudetect.ref b/test/asm/6502dtv-cpudetect.ref index 59278e80a126c65cc81e93110e153813468d5455..77a865eb42566cdbce5cd3ef00c8654346e2c889 100644 GIT binary patch delta 7 OcmY#XWHg${r~m*2Rskpg delta 7 OcmY#XWVD{hr~m*2dI2#2 diff --git a/test/asm/6502dtv-opcodes.ref b/test/asm/6502dtv-opcodes.ref new file mode 100644 index 0000000000000000000000000000000000000000..408d89be2e4dd43683a6e8e3401d01e8020b6039 GIT binary patch literal 498 zcmV~$0{~D507cPk-fUwTFMHX3Vc9l*_TrXpZnf-X+qP|6=Y$AJsL){uOE|(uh)AT! zM6se;(X8k(f|wA*icK8i5-)xND`BF-nYry3>Q6 z1ih@@eXPD#KdZkrz#7OP1~X(R!}ycoBStc6^cd?e>u+o9xL`aJCQf29QVmEu(yN~@G;NYRd968D{>$r8oI(aHM&6%_3IL`$xUb<{uxq8jIZr!kM-r_cQ uxO Date: Wed, 18 Nov 2020 15:47:09 +0100 Subject: [PATCH 459/806] adding c64dtv device to the supported ones --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3d78fda50..3d7b32289 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ including - VIC20 - C16/C116 and Plus/4 - C64 + - C64DTV - C128 - CBM 510 (aka P500) - the 600/700 family From adad3d2e870ea09551681a8b020660f36fbaa9af Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Thu, 19 Nov 2020 12:37:51 +0100 Subject: [PATCH 460/806] Revert "adding c64dtv device to the supported ones" This reverts commit 1d9e378fe3e3304000763d821d72afa841f954a3. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3d7b32289..3d78fda50 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ including - VIC20 - C16/C116 and Plus/4 - C64 - - C64DTV - C128 - CBM 510 (aka P500) - the 600/700 family From 63543dee07d5f0f7c7096b3251ef4095e16060e8 Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Thu, 19 Nov 2020 12:44:33 +0100 Subject: [PATCH 461/806] Revert transient modification of EATab Table 0 comment --- src/ca65/instr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ca65/instr.c b/src/ca65/instr.c index 77a865319..faeff2026 100644 --- a/src/ca65/instr.c +++ b/src/ca65/instr.c @@ -1031,9 +1031,9 @@ const InsTable* InsTab = (const InsTable*) &InsTab6502; */ static unsigned char EATab[12][AM65I_COUNT] = { { /* Table 0 */ - 0x00, 0x00, 0x05 /*zp*/, 0x0D /*abs*/, 0x0F, 0x15 /*zpx*/, 0x1D /*abx*/, 0x1F, - 0x00, 0x19 /*aby*/, 0x12, 0x00, 0x07, 0x11 /*izy*/, 0x17, 0x01 /*izx*/, - 0x00, 0x00, 0x00, 0x03, 0x13, 0x09 /*imm*/, 0x00, 0x09, + 0x00, 0x00, 0x05, 0x0D, 0x0F, 0x15, 0x1D, 0x1F, + 0x00, 0x19, 0x12, 0x00, 0x07, 0x11, 0x17, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x13, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00 }, { /* Table 1 */ From 032b4e3979c378af81dfed2c6aa2ad0edb8a8fee Mon Sep 17 00:00:00 2001 From: Zsolt Branyiczky Date: Thu, 19 Nov 2020 21:22:40 +0100 Subject: [PATCH 462/806] Fixed typo --- doc/ca65.sgml | 2 +- test/asm/cpudetect.s | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ca65.sgml b/doc/ca65.sgml index 9f45f8c54..cebdca12e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -3598,7 +3598,7 @@ Here's a list of all control commands and a description, what they do: Enable the 6502DTV instruction set. This is a superset of the 6502 instruction set. - See: + See: .POPCPU

diff --git a/test/asm/cpudetect.s b/test/asm/cpudetect.s index 6362b98bc..7b2363b7f 100644 --- a/test/asm/cpudetect.s +++ b/test/asm/cpudetect.s @@ -25,7 +25,7 @@ .endif .ifpdtv - sac #00 + sac #$00 .endif From 23273584a0a49867d5975aa59edc7a9853f7f8d4 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 19 Nov 2020 23:12:16 +0100 Subject: [PATCH 463/806] testcase for issue #1348 --- test/misc/Makefile | 8 +++++++ test/misc/bug1348.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/misc/bug1348.c diff --git a/test/misc/Makefile b/test/misc/Makefile index bd4826d54..853e9d1e0 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -117,6 +117,14 @@ $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) +# this one fails when optimization are enabled +$(WORKDIR)/bug1348.$1.$2.prg: bug1348.c | $(WORKDIR) + $(if $(QUIET),echo misc/bug1348.$1.$2.prg) + $(CC65) -Osr -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) + $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) + $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) + $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) + # these need reference data that can't be generated by a host-compiled program, # in a useful way $(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) diff --git a/test/misc/bug1348.c b/test/misc/bug1348.c new file mode 100644 index 000000000..913849482 --- /dev/null +++ b/test/misc/bug1348.c @@ -0,0 +1,54 @@ + +/* bug#1348, wrongly optimized integer promotion in comparison */ + +#include + +int notrandtab[] = { + 0xffff, 0x7fff, 0x3fff, 0x1fff, + 0x0fff, 0x07ff, 0x03ff, 0x01ff, + 0x00ff, 0x007f, 0x003f, 0x001f, + 0x000f, 0x0007, 0x0003, 0x0001 +}; + +unsigned char notrandcount = 0; + +int notrand(void) +{ + return notrandtab[notrandcount & 0x0f]; +} + +unsigned char n1, n2; +unsigned char i, ii, s; +unsigned char err = 0; + +unsigned char cmptab[] = { + 0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01, + 0x80, 0x40, 0x20, 0x10, + 0x08, 0x04, 0x02, 0x01 +}; + +int main(void) +{ + for (ii = 0; ii < 16; ++ii) { + s = cmptab[ii]; + for (i = 0; i < 16; ++i) { + n1 = n2 = 0; + if ((notrand() & 0xff) > s) { + n1 = 1; + } + if ((notrand() & 0xffu) > s) { + n2 = 1; + } + printf("%5d>%3d %d(%02x) %d(%02x) %s\n", + notrandtab[notrandcount & 0x0f], s, + n1, (notrand() & 0xff), + n2, (notrand() & 0xffu), + n1 == n2 ? "=" : "!=" + ); + if (n1 != n2) err = 1; + notrandcount++; + } + } + return err; +} From 48710af55ae0df0a5ed81f50da40a34b314978ac Mon Sep 17 00:00:00 2001 From: mrdudz Date: Thu, 19 Nov 2020 23:21:06 +0100 Subject: [PATCH 464/806] make plasma sample work again despite issue #1348 --- samples/plasma.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/samples/plasma.c b/samples/plasma.c index ac17265f3..f48d6dc77 100644 --- a/samples/plasma.c +++ b/samples/plasma.c @@ -1,7 +1,7 @@ /*****************************************************************************\ ** plasma test program for cc65. ** ** ** -** (w)2001 by groepaz/hitmen ** +** (w)2001 by groepaz ** ** ** ** Cleanup and porting by Ullrich von Bassewitz. ** ** ** @@ -54,7 +54,6 @@ #pragma static-locals (1); - static const unsigned char sinustable[0x100] = { 0x80, 0x7d, 0x7a, 0x77, 0x74, 0x70, 0x6d, 0x6a, 0x67, 0x64, 0x61, 0x5e, 0x5b, 0x58, 0x55, 0x52, @@ -131,8 +130,6 @@ static void doplasma (register unsigned char* scrn) } } - - static void makechar (void) { static const unsigned char bittab[8] = { @@ -147,7 +144,7 @@ static void makechar (void) for (i = 0; i < 8; ++i){ b = 0; for (ii = 0; ii < 8; ++ii) { - if ((rand() & 0xFF) > s) { + if ((rand() & 0xFFu) > s) { b |= bittab[ii]; } } From c11e389a9400a32a4cf4a0672e23fe5fcfd8c426 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Fri, 20 Nov 2020 17:25:10 +0100 Subject: [PATCH 465/806] move testcase for issue #1320 into test/misc --- test/misc/Makefile | 6 ++++++ test/{err => misc}/bug1320.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) rename test/{err => misc}/bug1320.c (93%) diff --git a/test/misc/Makefile b/test/misc/Makefile index 853e9d1e0..3fab957cb 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -100,6 +100,12 @@ $(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error (segfault) +$(WORKDIR)/bug1320.$1.$2.prg: bug1320.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1320.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this one requires --std=c89, it fails with --std=c99 # it fails currently at runtime $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) diff --git a/test/err/bug1320.c b/test/misc/bug1320.c similarity index 93% rename from test/err/bug1320.c rename to test/misc/bug1320.c index 15482434c..3599d60de 100644 --- a/test/err/bug1320.c +++ b/test/misc/bug1320.c @@ -24,7 +24,7 @@ https://github.com/cc65/cc65/issues/1320 - After the bug is fixed, this file should be moved to "test/misc/". + After the bug is fixed, this file should be moved to "test/val/". */ static char *var; @@ -35,4 +35,5 @@ char bar (void); void main (void) { foo (var++, bar ()); + return 0; } From cffcbce60fc41d26c25891edc8992e23a4a8d4c9 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Fri, 20 Nov 2020 19:11:20 +0100 Subject: [PATCH 466/806] Bumped version. I placed the Git tag V2.19 in hindsight at https://github.com/cc65/cc65/commit/555282497c3ecf8b313d87d5973093af19c35bd5. But I certainly don't want to rewrite the Git history just for the reported version, so I simply set the reported version at today's HEAD to 2.19. --- src/common/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/version.c b/src/common/version.c index 202c61cc3..992be45ee 100644 --- a/src/common/version.c +++ b/src/common/version.c @@ -47,7 +47,7 @@ #define VER_MAJOR 2U -#define VER_MINOR 18U +#define VER_MINOR 19U From 79bdc2d51f1bf67830ef6fcde3e6cc6bf9c72bfa Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Fri, 20 Nov 2020 19:19:55 +0100 Subject: [PATCH 467/806] Set correct prerequisite. See https://github.com/cc65/cc65/issues/1318 --- libsrc/geos-common/disk/calcblksfree.s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libsrc/geos-common/disk/calcblksfree.s b/libsrc/geos-common/disk/calcblksfree.s index 520c8ca61..5d7b98aba 100644 --- a/libsrc/geos-common/disk/calcblksfree.s +++ b/libsrc/geos-common/disk/calcblksfree.s @@ -13,6 +13,10 @@ .include "geossym.inc" _CalcBlksFree: + lda #curDirHead + sta r5L + stx r5H jsr CalcBlksFree stx __oserror lda r4L From 8b42f570e998df727c75d3cac7cfe6683478d900 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 20 Nov 2020 17:29:26 -0500 Subject: [PATCH 468/806] Fixed code that caused a seg-fault after parsing a (deferred) post-count argument followed by a (nested) function-call argument. The old broken code defers the count until the end of the (parent function's) argument list. But, a nested function call clears the pointer to the deferred type. That leads to an access violation. The new code defers only until the end of each argument. Fixes #1320. --- src/cc65/expr.c | 44 +++++++++++++++++------------------- test/misc/Makefile | 10 ++------ test/{misc => val}/bug1320.c | 17 +++++++++----- 3 files changed, 34 insertions(+), 37 deletions(-) rename test/{misc => val}/bug1320.c (85%) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 1c8c25020..0cf650d3b 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1,7 +1,7 @@ /* expr.c ** ** 1998-06-21, Ullrich von Bassewitz -** 2020-01-25, Greg King +** 2020-11-20, Greg King */ @@ -608,6 +608,8 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) ** The function returns the size of the arguments pushed in bytes. */ { + ExprDesc Expr; + /* Initialize variables */ SymEntry* Param = 0; /* Keep gcc silent */ unsigned PushedSize = 0; /* Size of arguments pushed */ @@ -634,7 +636,6 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) ** */ if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) { - /* Calculate the number and size of the parameters */ FrameParams = Func->ParamCount; FrameSize = Func->ParamSize; @@ -656,18 +657,13 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) } } - /* The info of the last argument could be needed out of the loop */ - ExprDesc Expr; - ED_Init (&Expr); - Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; - /* Parse the actual argument list */ while (CurTok.Tok != TOK_RPAREN) { - unsigned Flags; /* Code generator flags, not expression flags */ - - /* This way the info of the last parameter won't be cleared */ + ED_Init (&Expr); + + /* This way, the info of the last parameter won't be cleared */ Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR; /* Count arguments */ @@ -781,18 +777,20 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) Error ("Argument expected after comma"); break; } + + DoDeferred (SQP_KEEP_NONE, &Expr); } + /* Append last deferred inc/dec before the function is called. + ** The last parameter needs to be preserved if it is passed in AX/EAX Regs. + */ + DoDeferred (IsFastcall ? SQP_KEEP_EAX : SQP_KEEP_NONE, &Expr); + /* Check if we had enough arguments */ if (PushedCount < Func->ParamCount) { Error ("Too few arguments in function call"); } - /* Append deferred inc/dec before the function is called. - ** The last parameter needs to be restored if it is passed with AX/EAX Regs. - */ - DoDeferred (IsFastcall ? SQP_KEEP_EAX : SQP_KEEP_NONE, &Expr); - /* The function returns the size of all arguments pushed onto the stack. ** However, if there are parameters missed (which is an error, and was ** flagged by the compiler), AND a stack frame was preallocated above, @@ -1395,7 +1393,7 @@ static void ArrayRef (ExprDesc* Expr) /* The array subscript is a constant. Since we can have the element ** address directly as base+offset, we can remove the array address ** push onto the stack before if loading subscript doesn't tamper that - ** address in the primary. + ** address in the primary. */ if (!ConstBaseAddr) { RemoveCode (&Mark2); @@ -1419,7 +1417,7 @@ static void ArrayRef (ExprDesc* Expr) ** remove the code that loaded the address into the primary. */ if (!IsTypeArray (Expr->Type)) { - + /* It's a pointer, so we do have to load it into the primary ** first (if it's not already there). */ @@ -2025,8 +2023,8 @@ static void PostInc (ExprDesc* Expr) /* Get the data type */ Flags = TypeOf (Expr->Type); - /* We are allowed by the C standard to defer the inc operation until - ** the this expression is used, so that we don't need to save and reload + /* We are allowed by the C standard to defer the inc operation until after + ** the expression is used, so that we don't need to save and reload ** the original value. */ @@ -2065,7 +2063,7 @@ static void PostInc (ExprDesc* Expr) /* Fetch the value and use it (since it's the result of the expression) */ LoadExpr (CF_NONE, Expr); - /* Defer the increment until the value of this expression is used */; + /* Defer the increment until after the value of this expression is used */ DeferInc (Expr); } } @@ -2132,7 +2130,7 @@ static void PostDec (ExprDesc* Expr) /* Fetch the value and save it (since it's the result of the expression) */ LoadExpr (CF_NONE, Expr); - /* Defer the decrement until the value of this expression is used */; + /* Defer the decrement until after the value of this expression is used */ DeferDec (Expr); } } @@ -3738,7 +3736,7 @@ static void hieOr (ExprDesc *Expr) Error ("Scalar expression expected"); ED_MakeConstBool (Expr, 0); } else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - + if (!ED_IsConstBool (Expr)) { /* Test the lhs if we haven't had && operators. If we had them, the ** jump is already in place and there's no need to do the test. @@ -3749,7 +3747,7 @@ static void hieOr (ExprDesc *Expr) /* Get first expr */ LoadExpr (CF_FORCECHAR, Expr); - + /* Append deferred inc/dec at sequence point */ DoDeferred (SQP_KEEP_TEST, Expr); diff --git a/test/misc/Makefile b/test/misc/Makefile index 3fab957cb..ad31e8554 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -100,12 +100,6 @@ $(WORKDIR)/bug1263.$1.$2.prg: bug1263.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1263.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) -# should compile, but gives an error (segfault) -$(WORKDIR)/bug1320.$1.$2.prg: bug1320.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug1320.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # this one requires --std=c89, it fails with --std=c99 # it fails currently at runtime $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) @@ -114,7 +108,7 @@ $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) - + # should compile, but then hangs in an endless loop $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(if $(QUIET),echo misc/endless.$1.$2.prg) @@ -130,7 +124,7 @@ $(WORKDIR)/bug1348.$1.$2.prg: bug1348.c | $(WORKDIR) $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) - + # these need reference data that can't be generated by a host-compiled program, # in a useful way $(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) diff --git a/test/misc/bug1320.c b/test/val/bug1320.c similarity index 85% rename from test/misc/bug1320.c rename to test/val/bug1320.c index 3599d60de..824b50071 100644 --- a/test/misc/bug1320.c +++ b/test/val/bug1320.c @@ -1,5 +1,5 @@ /* - Copyright 2020 The cc65 Authors + Copyright 2020, The cc65 Authors This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,18 +21,23 @@ /* Test of a post-counted pointer argument, followed by a (nested) function-call argument. + Test that compiling it doesn't cause a seg-fault. https://github.com/cc65/cc65/issues/1320 - - After the bug is fixed, this file should be moved to "test/val/". */ static char *var; -void foo (char *, char); -char bar (void); +static void foo (char *, char) +{ +} -void main (void) +static char bar (void) +{ + return 'b'; +} + +int main (void) { foo (var++, bar ()); return 0; From a0d986faf8a646b8d01ed43e88727a42f1476be7 Mon Sep 17 00:00:00 2001 From: Greg King Date: Fri, 27 Nov 2020 21:19:49 -0500 Subject: [PATCH 469/806] Fixed the horizontal movement of the mouse pointer on platforms with the VIC-II display chip. ca65's logical (Boolean) NOT operator was used where bitwise NOT should be used. The effect was that all sprites were shifted to the left side of a screen when the mouse sprite was put on the left side. --- libsrc/c128/mcbdefault.s | 12 ++++++------ libsrc/c64/mcbdefault.s | 10 +++++----- libsrc/cbm510/mcbdefault.s | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libsrc/c128/mcbdefault.s b/libsrc/c128/mcbdefault.s index eb521eb3a..a3ca3d0d1 100644 --- a/libsrc/c128/mcbdefault.s +++ b/libsrc/c128/mcbdefault.s @@ -19,12 +19,12 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on this value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MEM = $0E00 ; Memory location -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MEM = $0E00 ; Memory location +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. diff --git a/libsrc/c64/mcbdefault.s b/libsrc/c64/mcbdefault.s index dd2f7cee7..79a8d93e5 100644 --- a/libsrc/c64/mcbdefault.s +++ b/libsrc/c64/mcbdefault.s @@ -21,11 +21,11 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on this value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. diff --git a/libsrc/cbm510/mcbdefault.s b/libsrc/cbm510/mcbdefault.s index 700dcebb1..53eb804c0 100644 --- a/libsrc/cbm510/mcbdefault.s +++ b/libsrc/cbm510/mcbdefault.s @@ -21,12 +21,12 @@ ; Sprite definitions. The first value can be changed to adjust the number ; of the sprite used for the mouse. All others depend on that value. -MOUSE_SPR = 0 ; Sprite used for the mouse -MOUSE_SPR_MEM = $F400 ; Memory location -MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask -MOUSE_SPR_NMASK = .lobyte(.not MOUSE_SPR_MASK) ; Negative mask -VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register -VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register +MOUSE_SPR = 0 ; Sprite used for the mouse +MOUSE_SPR_MEM = $F400 ; Memory location +MOUSE_SPR_MASK = $01 .shl MOUSE_SPR ; Positive mask +MOUSE_SPR_NMASK = .lobyte(.bitnot MOUSE_SPR_MASK) ; Negative mask +VIC_SPR_X = (VIC_SPR0_X + 2*MOUSE_SPR) ; Sprite X register +VIC_SPR_Y = (VIC_SPR0_Y + 2*MOUSE_SPR) ; Sprite Y register ; -------------------------------------------------------------------------- ; Initialize the mouse sprite. From 9538ca29b214ac2881adf2cb5a43b1c95467e062 Mon Sep 17 00:00:00 2001 From: Polluks Date: Fri, 20 Nov 2020 00:54:15 +0100 Subject: [PATCH 470/806] Unified #1345 --- cfg/supervision-128k.cfg | 2 +- cfg/supervision-16k.cfg | 2 +- cfg/supervision-64k.cfg | 2 +- cfg/supervision.cfg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cfg/supervision-128k.cfg b/cfg/supervision-128k.cfg index a03ab0e1b..3d7a9efd0 100644 --- a/cfg/supervision-128k.cfg +++ b/cfg/supervision-128k.cfg @@ -33,5 +33,5 @@ SEGMENTS { ZEROPAGE: load = RAM, type = bss, define = yes; DATA: load = RAM, type = bss, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTOR: load = ROM, type = ro, offset = $3FFA; + VECTORS: load = ROM, type = ro, start = $3FFA; } diff --git a/cfg/supervision-16k.cfg b/cfg/supervision-16k.cfg index e0b54be23..4a431621f 100644 --- a/cfg/supervision-16k.cfg +++ b/cfg/supervision-16k.cfg @@ -21,7 +21,7 @@ SEGMENTS { RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; FFF0: load = ROM, type = ro, offset = $3FF0; - VECTOR: load = ROM, type = ro, offset = $3FFA; + VECTORS: load = ROM, type = ro, start = $3FFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { diff --git a/cfg/supervision-64k.cfg b/cfg/supervision-64k.cfg index 9d5f15e45..e0cb68a6f 100644 --- a/cfg/supervision-64k.cfg +++ b/cfg/supervision-64k.cfg @@ -26,5 +26,5 @@ SEGMENTS { ZEROPAGE: load = RAM, type = bss, define = yes; DATA: load = RAM, type = bss, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTOR: load = ROM, type = ro, offset = $3FFA; + VECTORS: load = ROM, type = ro, start = $3FFA; } diff --git a/cfg/supervision.cfg b/cfg/supervision.cfg index d9f189f2b..4ec3e49c7 100644 --- a/cfg/supervision.cfg +++ b/cfg/supervision.cfg @@ -20,7 +20,7 @@ SEGMENTS { RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; FFF0: load = ROM, type = ro, offset = $7FF0; - VECTOR: load = ROM, type = ro, offset = $7FFA; + VECTORS: load = ROM, type = ro, start = $7FFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { From a538188d90239235600e76ceb62bd55343a114d0 Mon Sep 17 00:00:00 2001 From: Polluks Date: Thu, 26 Nov 2020 12:48:58 +0100 Subject: [PATCH 471/806] Fixed some config mistakes --- cfg/supervision-128k.cfg | 6 +++--- cfg/supervision-16k.cfg | 2 +- cfg/supervision-64k.cfg | 6 +++--- cfg/supervision.cfg | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cfg/supervision-128k.cfg b/cfg/supervision-128k.cfg index 3d7a9efd0..535e93c1d 100644 --- a/cfg/supervision-128k.cfg +++ b/cfg/supervision-128k.cfg @@ -30,8 +30,8 @@ SEGMENTS { BANK5: load = BANKROM5, type = ro; BANK6: load = BANKROM6, type = ro; BANK7: load = BANKROM7, type = ro; - ZEROPAGE: load = RAM, type = bss, define = yes; - DATA: load = RAM, type = bss, define = yes, offset = $0200; + ZEROPAGE: load = RAM, type = zp, define = yes; + DATA: load = RAM, type = rw, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTORS: load = ROM, type = ro, start = $3FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; } diff --git a/cfg/supervision-16k.cfg b/cfg/supervision-16k.cfg index 4a431621f..0a9bdf0f9 100644 --- a/cfg/supervision-16k.cfg +++ b/cfg/supervision-16k.cfg @@ -21,7 +21,7 @@ SEGMENTS { RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; FFF0: load = ROM, type = ro, offset = $3FF0; - VECTORS: load = ROM, type = ro, start = $3FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { diff --git a/cfg/supervision-64k.cfg b/cfg/supervision-64k.cfg index e0cb68a6f..404ece6fc 100644 --- a/cfg/supervision-64k.cfg +++ b/cfg/supervision-64k.cfg @@ -23,8 +23,8 @@ SEGMENTS { BANK1: load = BANKROM1, type = ro; BANK2: load = BANKROM2, type = ro; BANK3: load = BANKROM3, type = ro; - ZEROPAGE: load = RAM, type = bss, define = yes; - DATA: load = RAM, type = bss, define = yes, offset = $0200; + ZEROPAGE: load = RAM, type = zp, define = yes; + DATA: load = RAM, type = rw, define = yes, offset = $0200; BSS: load = RAM, type = bss, define = yes; - VECTORS: load = ROM, type = ro, start = $3FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; } diff --git a/cfg/supervision.cfg b/cfg/supervision.cfg index 4ec3e49c7..1a745234f 100644 --- a/cfg/supervision.cfg +++ b/cfg/supervision.cfg @@ -20,7 +20,7 @@ SEGMENTS { RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; FFF0: load = ROM, type = ro, offset = $7FF0; - VECTORS: load = ROM, type = ro, start = $7FFA; + VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } FEATURES { From c663f64542d49cd5000aa01c41cc151b442f065a Mon Sep 17 00:00:00 2001 From: Polluks Date: Fri, 27 Nov 2020 15:05:07 +0100 Subject: [PATCH 472/806] Added features; changed FFF0 segments --- cfg/supervision-128k.cfg | 17 ++++++++++++++++- cfg/supervision-16k.cfg | 2 +- cfg/supervision-64k.cfg | 15 +++++++++++++++ cfg/supervision.cfg | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/cfg/supervision-128k.cfg b/cfg/supervision-128k.cfg index 535e93c1d..c90367aa1 100644 --- a/cfg/supervision-128k.cfg +++ b/cfg/supervision-128k.cfg @@ -1,4 +1,4 @@ -# supervision 1284kbyte cartridge with bankswitching +# supervision 128kbyte cartridge with bankswitching # for assembler # ld65 config file @@ -35,3 +35,18 @@ SEGMENTS { BSS: load = RAM, type = bss, define = yes; VECTORS: load = ROM, type = ro, start = $FFFA; } +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/cfg/supervision-16k.cfg b/cfg/supervision-16k.cfg index 0a9bdf0f9..dc6130b33 100644 --- a/cfg/supervision-16k.cfg +++ b/cfg/supervision-16k.cfg @@ -20,7 +20,7 @@ SEGMENTS { CODE: load = ROM, type = ro, define = yes; RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; - FFF0: load = ROM, type = ro, offset = $3FF0; + FFF0: load = ROM, type = ro, start = $FFF0; VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } diff --git a/cfg/supervision-64k.cfg b/cfg/supervision-64k.cfg index 404ece6fc..d252c5d25 100644 --- a/cfg/supervision-64k.cfg +++ b/cfg/supervision-64k.cfg @@ -28,3 +28,18 @@ SEGMENTS { BSS: load = RAM, type = bss, define = yes; VECTORS: load = ROM, type = ro, start = $FFFA; } +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/cfg/supervision.cfg b/cfg/supervision.cfg index 1a745234f..1177660b8 100644 --- a/cfg/supervision.cfg +++ b/cfg/supervision.cfg @@ -19,7 +19,7 @@ SEGMENTS { CODE: load = ROM, type = ro, define = yes; RODATA: load = ROM, type = ro, define = yes; DATA: load = ROM, run = RAM, type = rw, define = yes; - FFF0: load = ROM, type = ro, offset = $7FF0; + FFF0: load = ROM, type = ro, start = $FFF0; VECTORS: load = ROM, type = ro, start = $FFFA; BSS: load = RAM, type = bss, define = yes; } From 2646c06f61b46ed142bbefc723acb8c2113a23f9 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sun, 29 Nov 2020 18:23:52 +1100 Subject: [PATCH 473/806] docs: fix simple typo, paramater -> parameter There is a small typo in src/cc65/pragma.c. Should read `parameter` rather than `paramater`. --- src/cc65/pragma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index cf463a832..1ea86545d 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -677,7 +677,7 @@ static void WarnPragma (StrBuf* B) static void FlagPragma (StrBuf* B, IntStack* Stack) -/* Handle a pragma that expects a boolean paramater */ +/* Handle a pragma that expects a boolean parameter */ { StrBuf Ident = AUTO_STRBUF_INITIALIZER; long Val; @@ -727,7 +727,7 @@ ExitPoint: static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) -/* Handle a pragma that expects an int paramater */ +/* Handle a pragma that expects an int parameter */ { long Val; int Push; From 95635418707e5a6ed270a341c02a743751631099 Mon Sep 17 00:00:00 2001 From: Polluks Date: Sun, 29 Nov 2020 17:10:34 +0100 Subject: [PATCH 474/806] crt0 clean-up --- libsrc/atari5200/crt0.s | 1 - libsrc/c16/crt0.s | 1 - libsrc/c64/crt0.s | 1 - libsrc/pet/crt0.s | 1 - libsrc/plus4/crt0.s | 2 -- libsrc/supervision/crt0.s | 2 -- libsrc/telestrat/crt0.s | 1 - libsrc/vic20/crt0.s | 1 - 8 files changed, 10 deletions(-) diff --git a/libsrc/atari5200/crt0.s b/libsrc/atari5200/crt0.s index ee3d0de4f..9538d3bb4 100644 --- a/libsrc/atari5200/crt0.s +++ b/libsrc/atari5200/crt0.s @@ -13,7 +13,6 @@ .import zerobss, copydata .include "zeropage.inc" - .include "atari5200.inc" start: diff --git a/libsrc/c16/crt0.s b/libsrc/c16/crt0.s index bee81a113..1df1e5c62 100644 --- a/libsrc/c16/crt0.s +++ b/libsrc/c16/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "plus4.inc" ; ------------------------------------------------------------------------ ; Startup code diff --git a/libsrc/c64/crt0.s b/libsrc/c64/crt0.s index 7bd294ca7..4e5c7c9d4 100644 --- a/libsrc/c64/crt0.s +++ b/libsrc/c64/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "c64.inc" ; ------------------------------------------------------------------------ diff --git a/libsrc/pet/crt0.s b/libsrc/pet/crt0.s index 520a147f7..e56a7eca4 100644 --- a/libsrc/pet/crt0.s +++ b/libsrc/pet/crt0.s @@ -12,7 +12,6 @@ .include "zeropage.inc" .include "pet.inc" - .include "../cbm/cbm.inc" ; ------------------------------------------------------------------------ ; Startup code diff --git a/libsrc/plus4/crt0.s b/libsrc/plus4/crt0.s index 2262b4c42..6b44a2b7e 100644 --- a/libsrc/plus4/crt0.s +++ b/libsrc/plus4/crt0.s @@ -198,5 +198,3 @@ irqcount: .byte 0 .segment "INIT" zpsave: .res zpspace - - diff --git a/libsrc/supervision/crt0.s b/libsrc/supervision/crt0.s index ac61c8215..203c681b8 100644 --- a/libsrc/supervision/crt0.s +++ b/libsrc/supervision/crt0.s @@ -78,5 +78,3 @@ not_dma: .word nmi .word reset32kcode .word irq - - diff --git a/libsrc/telestrat/crt0.s b/libsrc/telestrat/crt0.s index 59b1ea642..df75520ce 100644 --- a/libsrc/telestrat/crt0.s +++ b/libsrc/telestrat/crt0.s @@ -12,7 +12,6 @@ .import __MAIN_START__, __MAIN_SIZE__ .include "zeropage.inc" - .include "telestrat.inc" ; ------------------------------------------------------------------------ ; Place the startup code in a special segment. diff --git a/libsrc/vic20/crt0.s b/libsrc/vic20/crt0.s index 68ab3ed12..c5486063b 100644 --- a/libsrc/vic20/crt0.s +++ b/libsrc/vic20/crt0.s @@ -13,7 +13,6 @@ .importzp ST .include "zeropage.inc" - .include "vic20.inc" ; ------------------------------------------------------------------------ ; Startup code From b22d8c7441d29657fa772cd4da08b2ca5dd61946 Mon Sep 17 00:00:00 2001 From: Olli Savia Date: Sat, 28 Nov 2020 13:03:48 +0200 Subject: [PATCH 475/806] Added STATUS (0) definition --- asminc/c128.inc | 1 + asminc/c64.inc | 1 + asminc/plus4.inc | 1 + asminc/vic20.inc | 1 + 4 files changed, 4 insertions(+) diff --git a/asminc/c128.inc b/asminc/c128.inc index 2852631f3..749b4168c 100644 --- a/asminc/c128.inc +++ b/asminc/c128.inc @@ -7,6 +7,7 @@ ; Zero page, Commodore stuff TXTPTR := $3D ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address diff --git a/asminc/c64.inc b/asminc/c64.inc index 1d10f673d..2cfc50db4 100644 --- a/asminc/c64.inc +++ b/asminc/c64.inc @@ -9,6 +9,7 @@ VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $7A ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60 HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address diff --git a/asminc/plus4.inc b/asminc/plus4.inc index 774722e93..6c6017a78 100644 --- a/asminc/plus4.inc +++ b/asminc/plus4.inc @@ -10,6 +10,7 @@ TMPPTR := $22 ; Temporary ptr used by BASIC VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $3B ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A3 ; 60HZ clock FNAM_LEN := $AB ; Length of filename LFN := $AC ; Logical file number diff --git a/asminc/vic20.inc b/asminc/vic20.inc index b82874f56..fa9fdfa8d 100644 --- a/asminc/vic20.inc +++ b/asminc/vic20.inc @@ -9,6 +9,7 @@ VARTAB := $2D ; Pointer to start of BASIC variables MEMSIZE := $37 ; Pointer to highest BASIC RAM location (+1) TXTPTR := $7A ; Pointer into BASIC source code +STATUS := $90 ; Kernal I/O completion status TIME := $A0 ; 60HZ clock FNAM_LEN := $B7 ; Length of filename SECADR := $B9 ; Secondary address From 59c58acbe3229a4e860981e97d3d5f3a23adf2bc Mon Sep 17 00:00:00 2001 From: Greg King Date: Sat, 5 Dec 2020 23:04:48 -0500 Subject: [PATCH 476/806] Documented the address size argument of the bss-name, code-name, data-name, and rodata-name pragmas. --- doc/cc65.sgml | 143 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 47 deletions(-) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 115f0a30c..e273ced9c 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -6,8 +6,9 @@ -cc65 is a C compiler for 6502 targets. It supports several 6502 based home -computers like the Commodore and Atari machines, but it is easily retargetable. +cc65 is a C compiler for 6502 targets. It supports several 6502-based home +computers such as the Commodore and Atari machines, but it easily is +retargetable. @@ -1011,7 +1012,7 @@ The compiler defines several macros at startup: __TELESTRAT__ This macro is defined if the target is the Telestrat (-t telestrat). - + __TIME__ This macro expands to the time of translation of the preprocessing @@ -1045,26 +1046,39 @@ parameter with the #pragma bss-name ([push,] <name>)

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

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

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

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

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

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

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

- This pragma changes the name used for the RODATA segment (the RODATA - segment is used to store readonly data). The argument is a string - enclosed in double quotes. + This pragma changes the name used for the RODATA segment (the RODATA segment + is used to store read-only data). The + "zp", "zeropage", "direct" + "abs", "absolute", "near", "default" + "far" + "long", "dword" + - The - #pragma rodata-name ("MyRODATA") + #pragma rodata-name ("MyRODATA") + #pragma rodata-name (push, "MyRODATA") From 0f4cb443b4d34dfceef35c0f9b31abcf69d00c67 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Sat, 19 Dec 2020 19:54:12 +0100 Subject: [PATCH 477/806] Improved device I/O under DOS 3.3 Certain scenarios (e.g. not running any Applesoft program at all since booting DOS 3.3) can make DOS 3.3 consider cc65 device input (e.g. getchar()) that reads a CR interpreting the command in the keyboard buffer. Setting the hibyte of the Applesoft currently executed line number to some value <> $FF (beside setting the input prompt to some value <> ']') makes DOS 3.3 understand that we're not in intermediate mode and that therefore I/O not preceded with ctrl-d mustn't be fiddled with (see DOS 3.3 routine at $A65E). --- asminc/apple2.inc | 1 + libsrc/apple2/read.s | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/asminc/apple2.inc b/asminc/apple2.inc index 5ebf73164..528c463a1 100644 --- a/asminc/apple2.inc +++ b/asminc/apple2.inc @@ -15,6 +15,7 @@ PROMPT := $33 ; Used by GETLN RNDL := $4E ; Random counter low RNDH := $4F ; Random counter high HIMEM := $73 ; Highest available memory address+1 +CURLIN := $75 ; Current line number being executed ;----------------------------------------------------------------------------- ; Vectors diff --git a/libsrc/apple2/read.s b/libsrc/apple2/read.s index 14c80b7e2..99c189cbd 100644 --- a/libsrc/apple2/read.s +++ b/libsrc/apple2/read.s @@ -19,11 +19,14 @@ .segment "ONCE" initprompt: - ; Set prompt <> ']' to let DOS 3.3 know that we're - ; not in Applesoft immediate mode and thus keep it - ; from scanning our device I/O for DOS commands. + ; Set prompt <> ']' and currently executed Applesoft + ; line number hibyte <> $FF to let DOS 3.3 (at $A65E) + ; know that we're not in Applesoft immediate mode and + ; thus keep it from scanning our device I/O for DOS + ; commands. lda #$80 ; Same value used at $D52C sta PROMPT + sta CURLIN+1 ; Any value <> $FF will do rts .code From b2c1a77bb389bf20b5043f796f2dee496c07fdbd Mon Sep 17 00:00:00 2001 From: Greg King Date: Thu, 24 Dec 2020 12:27:09 -0500 Subject: [PATCH 478/806] Fixed the cc65 code that optimizes 16-bit compares when the high bytes are known to be equal. Only the low bytes are compared. Originally, signed 16-bit compares were optimized into signed 8-bit compares. But, the sign bits are in the high bytes; and, they're equal. Therefore, the low bytes always must be compared as unsigned numbers. Fixes #1348. --- src/cc65/coptstop.c | 78 +++++++++++------------------------- test/misc/Makefile | 8 ---- test/{misc => val}/bug1348.c | 28 ++++++------- 3 files changed, 37 insertions(+), 77 deletions(-) rename test/{misc => val}/bug1348.c (61%) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 59bbe6dc9..77b79281b 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -287,7 +287,7 @@ static unsigned Opt_tosshift (StackOpData* D, const char* Name) /* ldx */ X = NewCodeEntry (OP65_LDX, LoadX->AM, LoadX->Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - + /* Lhs load entries can be removed if not used later */ D->Lhs.X.Flags |= LI_REMOVE; D->Lhs.A.Flags |= LI_REMOVE; @@ -1100,7 +1100,7 @@ static unsigned Opt_tosxorax (StackOpData* D) static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) -/* Optimize the tos compare sequence with a bool transformer */ +/* Optimize the TOS compare sequence with a bool transformer */ { CodeEntry* X; cmp_t Cond; @@ -1119,9 +1119,8 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) D->Rhs.A.Flags |= LI_REMOVE; } else if ((D->Lhs.A.Flags & LI_DIRECT) != 0) { - - /* If the lhs is direct (but not stack relative), encode compares with lhs - ** effectively reverting the order (which doesn't matter for ==). + /* If the lhs is direct (but not stack relative), encode compares with lhs, + ** effectively reversing the order (which doesn't matter for == and !=). */ Cond = FindBoolCmpCond (BoolTransformer); Cond = GetRevertedCond (Cond); @@ -1138,7 +1137,6 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) D->Lhs.A.Flags |= LI_REMOVE; } else { - /* We'll do reverse-compare */ Cond = FindBoolCmpCond (BoolTransformer); Cond = GetRevertedCond (Cond); @@ -1162,7 +1160,7 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) X = NewCodeEntry (OP65_JSR, AM65_ABS, BoolTransformer, 0, D->OpEntry->LI); InsertEntry (D, X, D->IP++); - /* Remove the push and the call to the tosgeax function */ + /* Remove the push and the call to the TOS function */ RemoveRemainders (D); /* We changed the sequence */ @@ -1179,22 +1177,6 @@ static unsigned Opt_a_toseq (StackOpData* D) -static unsigned Opt_a_tosge (StackOpData* D) -/* Optimize the tosgeax sequence */ -{ - return Opt_a_toscmpbool (D, "boolge"); -} - - - -static unsigned Opt_a_tosgt (StackOpData* D) -/* Optimize the tosgtax sequence */ -{ - return Opt_a_toscmpbool (D, "boolgt"); -} - - - static unsigned Opt_a_tosicmp (StackOpData* D) /* Replace tosicmp with CMP */ { @@ -1236,7 +1218,7 @@ static unsigned Opt_a_tosicmp (StackOpData* D) } InsertEntry (D, X, D->IP++); - /* cmp src,y OR cmp (sp),y*/ + /* cmp src,y OR cmp (sp),y */ if (D->Rhs.A.LoadEntry->OPC == OP65_JSR) { /* opc (sp),y */ X = NewCodeEntry (OP65_CMP, AM65_ZP_INDY, "sp", 0, D->OpEntry->LI); @@ -1268,18 +1250,18 @@ static unsigned Opt_a_tosicmp (StackOpData* D) InsertEntry (D, X, D->IP-3); } else { - /* Just clear A,Z,N and set C */ + /* Just clear A,Z,N; and set C */ + Arg = MakeHexArg (0); if ((RI = GetLastChangedRegInfo (D, &D->Lhs.A)) != 0 && RegValIsKnown (RI->Out.RegA) && (RI->Out.RegA & 0xFF) == 0) { - Arg = MakeHexArg (0); - X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->OpIndex + 1); } else { - Arg = MakeHexArg (0); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->OpIndex + 1); - X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); + X = NewCodeEntry (OP65_CMP, AM65_IMM, Arg, 0, D->OpEntry->LI); InsertEntry (D, X, D->OpIndex + 2); } } @@ -1292,24 +1274,8 @@ static unsigned Opt_a_tosicmp (StackOpData* D) -static unsigned Opt_a_tosle (StackOpData* D) -/* Optimize the tosleax sequence */ -{ - return Opt_a_toscmpbool (D, "boolle"); -} - - - -static unsigned Opt_a_toslt (StackOpData* D) -/* Optimize the tosltax sequence */ -{ - return Opt_a_toscmpbool (D, "boollt"); -} - - - static unsigned Opt_a_tosne (StackOpData* D) -/* Optimize the toseqax sequence */ +/* Optimize the tosneax sequence */ { return Opt_a_toscmpbool (D, "boolne"); } @@ -1317,7 +1283,7 @@ static unsigned Opt_a_tosne (StackOpData* D) static unsigned Opt_a_tosuge (StackOpData* D) -/* Optimize the tosugeax sequence */ +/* Optimize the tosgeax and tosugeax sequences */ { return Opt_a_toscmpbool (D, "booluge"); } @@ -1325,7 +1291,7 @@ static unsigned Opt_a_tosuge (StackOpData* D) static unsigned Opt_a_tosugt (StackOpData* D) -/* Optimize the tosugtax sequence */ +/* Optimize the tosgtax and tosugtax sequences */ { return Opt_a_toscmpbool (D, "boolugt"); } @@ -1333,7 +1299,7 @@ static unsigned Opt_a_tosugt (StackOpData* D) static unsigned Opt_a_tosule (StackOpData* D) -/* Optimize the tosuleax sequence */ +/* Optimize the tosleax and tosuleax sequences */ { return Opt_a_toscmpbool (D, "boolule"); } @@ -1341,7 +1307,7 @@ static unsigned Opt_a_tosule (StackOpData* D) static unsigned Opt_a_tosult (StackOpData* D) -/* Optimize the tosultax sequence */ +/* Optimize the tosltax and tosultax sequences */ { return Opt_a_toscmpbool (D, "boolult"); } @@ -1354,6 +1320,8 @@ static unsigned Opt_a_tosult (StackOpData* D) +/* The first column of these two tables must be sorted in lexical order */ + static const OptFuncDesc FuncTable[] = { { "__bzero", Opt___bzero, REG_NONE, OP_X_ZERO | OP_A_KNOWN }, { "staspidx", Opt_staspidx, REG_NONE, OP_NONE }, @@ -1379,11 +1347,11 @@ static const OptFuncDesc FuncTable[] = { static const OptFuncDesc FuncRegATable[] = { { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, - { "tosgeax", Opt_a_tosge, REG_NONE, OP_NONE }, - { "tosgtax", Opt_a_tosgt, REG_NONE, OP_NONE }, + { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, + { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosicmp", Opt_a_tosicmp, REG_NONE, OP_NONE }, - { "tosleax", Opt_a_tosle, REG_NONE, OP_NONE }, - { "tosltax", Opt_a_toslt, REG_NONE, OP_NONE }, + { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, + { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, diff --git a/test/misc/Makefile b/test/misc/Makefile index ad31e8554..81a09c693 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -117,14 +117,6 @@ $(WORKDIR)/endless.$1.$2.prg: endless.c | $(WORKDIR) $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) -# this one fails when optimization are enabled -$(WORKDIR)/bug1348.$1.$2.prg: bug1348.c | $(WORKDIR) - $(if $(QUIET),echo misc/bug1348.$1.$2.prg) - $(CC65) -Osr -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLERR) - $(CA65) -t sim$2 -o $$(@:.prg=.o) $$(@:.prg=.s) $(NULLERR) - $(LD65) -t sim$2 -o $$@ $$(@:.prg=.o) sim$2.lib $(NULLERR) - $(NOT) $(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT) $(NULLERR) - # these need reference data that can't be generated by a host-compiled program, # in a useful way $(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR) diff --git a/test/misc/bug1348.c b/test/val/bug1348.c similarity index 61% rename from test/misc/bug1348.c rename to test/val/bug1348.c index 913849482..c40d9ac84 100644 --- a/test/misc/bug1348.c +++ b/test/val/bug1348.c @@ -1,27 +1,26 @@ - -/* bug#1348, wrongly optimized integer promotion in comparison */ +/* bug #1348, wrongly optimized integer promotion in comparison */ #include -int notrandtab[] = { +static const int notrandtab[] = { 0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff, 0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001 }; -unsigned char notrandcount = 0; +static unsigned char notrandcount = 0; -int notrand(void) +static int notrand(void) { return notrandtab[notrandcount & 0x0f]; } -unsigned char n1, n2; -unsigned char i, ii, s; -unsigned char err = 0; +static unsigned char n1, n2; +static unsigned char i, ii, s; +static unsigned char err = 0; -unsigned char cmptab[] = { +static const unsigned char cmptab[] = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x80, 0x40, 0x20, 0x10, @@ -40,13 +39,14 @@ int main(void) if ((notrand() & 0xffu) > s) { n2 = 1; } - printf("%5d>%3d %d(%02x) %d(%02x) %s\n", - notrandtab[notrandcount & 0x0f], s, + printf("%5d > %3d %u(%02x) %u(%02x) %s\n", + notrandtab[i], s, n1, (notrand() & 0xff), n2, (notrand() & 0xffu), - n1 == n2 ? "=" : "!=" - ); - if (n1 != n2) err = 1; + n1 == n2 ? "=" : "!="); + if (n1 != n2) { + err = 1; + } notrandcount++; } } From dfd047ce6a693a2bc32a8778b06bd08c3d39aa0b Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 13 Nov 2020 09:01:26 +0100 Subject: [PATCH 479/806] g_typeadjust: Use CF_CHAR for char args If lhs and rhs are either both signed char or both unsigned char, return flags for that type instead of (unsigned) int. The flags are used only for codegen. Currently, this does nothing, since codegen treats chars as ints unless CF_FORCECHAR is set, but it allows more efficient char x char -> int codegen to be added in the future. --- src/cc65/codegen.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index f56abcd95..a58484cf1 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1433,17 +1433,20 @@ unsigned g_typeadjust (unsigned lhs, unsigned rhs) /* Note that this logic is largely duplicated by ArithmeticConvert. */ - /* Before we apply the integral promotions, we check if both types are unsigned char. - ** If so, we return unsigned int, rather than int, which would be returned by the standard - ** rules. This is only a performance optimization and does not affect correctness, as - ** the flags are only used for code generation, and not to determine types of other - ** expressions containing this one. All unsigned char bit-patterns are valid as both int - ** and unsigned int and represent the same value, so either signed or unsigned int operations - ** can be used. This special case part is not duplicated by ArithmeticConvert. + /* Before we apply the integral promotions, we check if both types are the same character type. + ** If so, we return that type, rather than int, which would be returned by the standard + ** rules. This is only a performance optimization allowing the use of unsigned and/or char + ** operations; it does not affect correctness, as the flags are only used for code generation, + ** and not to determine types of other expressions containing this one. For codgen, CF_CHAR + ** means the operands are char and the result is int (unless CF_FORCECHAR is also set, in + ** which case the result is char). This special case part is not duplicated by + ** ArithmeticConvert. */ - if ((lhs & CF_TYPEMASK) == CF_CHAR && (lhs & CF_UNSIGNED) && - (rhs & CF_TYPEMASK) == CF_CHAR && (rhs & CF_UNSIGNED)) { - return const_flag | CF_UNSIGNED | CF_INT; + if ((lhs & CF_TYPEMASK) == CF_CHAR && (rhs & CF_TYPEMASK) == CF_CHAR && + (lhs & CF_UNSIGNED) == (rhs & CF_UNSIGNED)) { + /* Signedness flags are the same, so just use one of them. */ + const unsigned unsigned_flag = lhs & CF_UNSIGNED; + return const_flag | unsigned_flag | CF_CHAR; } /* Apply integral promotions for types char/short. */ From 1c72da490479b6876557c3cf75577e7ed55f5e15 Mon Sep 17 00:00:00 2001 From: baktrasf Date: Mon, 21 Dec 2020 21:31:53 +0100 Subject: [PATCH 480/806] Add operating system symbols for the Atari 5200 target --- include/_atari5200os.h | 80 ++++++++++++++++++++++++++++++++++++++++++ include/atari5200.h | 4 +++ 2 files changed, 84 insertions(+) create mode 100644 include/_atari5200os.h diff --git a/include/_atari5200os.h b/include/_atari5200os.h new file mode 100644 index 000000000..77ccf14ed --- /dev/null +++ b/include/_atari5200os.h @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* */ +/* _atari5200os.h */ +/* */ +/* Internal include file, do not use directly */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + +#ifndef __ATARI5200OS_H +#define __ATARI5200OS_H + + +struct __os { + + /*Page zero*/ + unsigned char pokmsk; // = $00 System mask for POKEY IRQ enable + unsigned char rtclok[2]; // = $01,$02 Real time clock + unsigned char critic; // = $03 Critical section flag + unsigned char atract; // = $04 Attract mode counter + + union { + struct { + unsigned char sdlstl; // = $05 Save display list LO + unsigned char sdlsth; // = $06 Save display list HI + }; + void* sdlst; // = $05,$06 Display list shadow + }; + + unsigned char sdmctl; // = $07 DMACTL shadow + unsigned char pcolr0; // = $08 PM color 0 + unsigned char pcolr1; // = $09 PM color 1 + unsigned char pcolr2; // = $0A PM color 2 + unsigned char pcolr3; // = $0B PM color 3 + unsigned char color0; // = $0C PF Color 0 + unsigned char color1; // = $0D PF Color 1 + unsigned char color2; // = $0E PF Color 2 + unsigned char color3; // = $0F PF Color 3 + unsigned char color4; // = $10 PF Color 4 + unsigned char __filler[0xEF]; // = $11-$FF Filler + + /*Stack*/ + unsigned char stack[0x100]; // = $100-$1FF Stack + + /*Page 2 OS variables*/ + void (*vinter)(void); // = $200 Immediate IRQ vector + void (*vvblki)(void); // = $202 Immediate VBI vector + void (*vvblkd)(void); // = $204 Deferred VBI vector + void (*vdslst)(void); // = $206 DLI vector + void (*vkeybd)(void); // = $208 Keyboard IRQ vector + void (*vkeypd)(void); // = $20A Keypad continue vector + void (*vbrkky)(void); // = $20C Break key interrupt vector + void (*vbreak)(void); // = $20E Break instruction interrupt vector + void (*vserin)(void); // = $210 Serial input ready vector + void (*vseror)(void); // = $212 Serial output data needed vector + void (*vseroc)(void); // = $214 Serial output completed vector + void (*vtimr1)(void); // = $216 POKEY timer 1 IRQ vector + void (*vtimr2)(void); // = $218 POKEY timer 2 IRQ vector + void (*vtimr4)(void); // = $21A POKEY timer 4 IRQ vector + +}; + +#endif \ No newline at end of file diff --git a/include/atari5200.h b/include/atari5200.h index a18360c61..ff176c15b 100644 --- a/include/atari5200.h +++ b/include/atari5200.h @@ -65,6 +65,10 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */ #define AT_NTSC 0 #define AT_PAL 1 +/* Define variables used by the OS*/ +#include <_atari5200os.h> +#define OS (*(struct __os*)0x0000) + /* define hardware */ #include <_gtia.h> #define GTIA_READ (*(struct __gtia_read*)0xC000) From 2e9bada1f20b645ea94343adaf03d85da15a1d45 Mon Sep 17 00:00:00 2001 From: baktrasf Date: Wed, 23 Dec 2020 23:35:09 +0100 Subject: [PATCH 481/806] Atari 5200 OS header refinements --- doc/atari5200.sgml | 10 ++++++++++ include/_atari5200os.h | 18 +++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/doc/atari5200.sgml b/doc/atari5200.sgml index 032b0ef6c..2ec3d08f0 100644 --- a/doc/atari5200.sgml +++ b/doc/atari5200.sgml @@ -73,7 +73,17 @@ Special locations: Programs containing Atari 5200 specific code may use the +... + OS.sdmctl = 0x00; // screen off + OS.color4 = 14; // white frame +... tics = OS.rtclok[1] // get ticks + + Atari 5200 specific functions

diff --git a/include/_atari5200os.h b/include/_atari5200os.h index 77ccf14ed..db0f7f0c9 100644 --- a/include/_atari5200os.h +++ b/include/_atari5200os.h @@ -49,12 +49,12 @@ struct __os { unsigned char pcolr1; // = $09 PM color 1 unsigned char pcolr2; // = $0A PM color 2 unsigned char pcolr3; // = $0B PM color 3 - unsigned char color0; // = $0C PF Color 0 - unsigned char color1; // = $0D PF Color 1 - unsigned char color2; // = $0E PF Color 2 - unsigned char color3; // = $0F PF Color 3 - unsigned char color4; // = $10 PF Color 4 - unsigned char __filler[0xEF]; // = $11-$FF Filler + unsigned char color0; // = $0C PF color 0 + unsigned char color1; // = $0D PF color 1 + unsigned char color2; // = $0E PF color 2 + unsigned char color3; // = $0F PF color 3 + unsigned char color4; // = $10 PF color 4 + unsigned char _free_1[0xEF]; // = $11-$FF User space /*Stack*/ unsigned char stack[0x100]; // = $100-$1FF Stack @@ -65,9 +65,9 @@ struct __os { void (*vvblkd)(void); // = $204 Deferred VBI vector void (*vdslst)(void); // = $206 DLI vector void (*vkeybd)(void); // = $208 Keyboard IRQ vector - void (*vkeypd)(void); // = $20A Keypad continue vector + void (*vkeypd)(void); // = $20A Keyboard continuation vector void (*vbrkky)(void); // = $20C Break key interrupt vector - void (*vbreak)(void); // = $20E Break instruction interrupt vector + void (*vbreak)(void); // = $20E BRK instruction interrupt vector void (*vserin)(void); // = $210 Serial input ready vector void (*vseror)(void); // = $212 Serial output data needed vector void (*vseroc)(void); // = $214 Serial output completed vector @@ -77,4 +77,4 @@ struct __os { }; -#endif \ No newline at end of file +#endif From d67b955e528d032d287b1bdec450643b0d5a8a6c Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 23 Dec 2020 23:50:10 +0100 Subject: [PATCH 482/806] Fixed example of the OS struct usage for Atari 5200 --- doc/atari5200.sgml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/atari5200.sgml b/doc/atari5200.sgml index 2ec3d08f0..aff212b15 100644 --- a/doc/atari5200.sgml +++ b/doc/atari5200.sgml @@ -81,7 +81,8 @@ The names are the usual ones you can find in system reference manuals. Example: ... OS.sdmctl = 0x00; // screen off OS.color4 = 14; // white frame -... tics = OS.rtclok[1] // get ticks + tics = OS.rtclok[1] // get ticks +... Atari 5200 specific functions

From ef258bdc1913b2bc97011e64affb7925b80c1e5a Mon Sep 17 00:00:00 2001 From: Christian Groessler Date: Fri, 25 Dec 2020 07:16:26 +0100 Subject: [PATCH 483/806] remove TABs which again slipped in.... --- src/cc65/expr.h | 8 +- test/val/binlit.c | 1044 ++++++++++++++++++++++---------------------- test/val/bug1071.c | 40 +- test/val/rand.c | 134 +++--- 4 files changed, 613 insertions(+), 613 deletions(-) diff --git a/src/cc65/expr.h b/src/cc65/expr.h index d0a9988af..71dbe8e0d 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -23,10 +23,10 @@ -#define SQP_KEEP_NONE 0x00 -#define SQP_KEEP_TEST 0x01U -#define SQP_KEEP_EAX 0x02U -#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ +#define SQP_KEEP_NONE 0x00 +#define SQP_KEEP_TEST 0x01U +#define SQP_KEEP_EAX 0x02U +#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ diff --git a/test/val/binlit.c b/test/val/binlit.c index 47e7a196e..f89ca60fc 100644 --- a/test/val/binlit.c +++ b/test/val/binlit.c @@ -3,531 +3,531 @@ static unsigned big[256]; int main() { - unsigned i; + unsigned i; - small[0] = 0b0; - small[1] = 0b1; - small[2] = 0b10; - small[3] = 0b11; - small[4] = 0b100; - small[5] = 0b101; - small[6] = 0b110; - small[7] = 0b000111; - small[8] = 0b1000; - small[9] = 0b1001; - small[10] = 0b0001010; - small[11] = 0b0001011; - small[12] = 0b1100; - small[13] = 0b1101; - small[14] = 0b1110; - small[15] = 0b1111; - small[16] = 0b10000; - small[17] = 0b10001; - small[18] = 0b00010010; - small[19] = 0b00010011; - small[20] = 0b00010100; - small[21] = 0b00010101; - small[22] = 0b10110; - small[23] = 0b10111; - small[24] = 0b11000; - small[25] = 0b11001; - small[26] = 0b11010; - small[27] = 0b11011; - small[28] = 0b11100; - small[29] = 0b11101; - small[30] = 0b11110; - small[31] = 0b11111; - small[32] = 0b00000000100000; - small[33] = 0b00000000100001; - small[34] = 0b100010; - small[35] = 0b100011; - small[36] = 0b100100; - small[37] = 0b100101; - small[38] = 0b100110; - small[39] = 0b100111; - small[40] = 0b101000; - small[41] = 0b101001; - small[42] = 0b101010; - small[43] = 0b101011; - small[44] = 0b101100; - small[45] = 0b101101; - small[46] = 0b101110; - small[47] = 0b101111; - small[48] = 0b110000; - small[49] = 0b110001; - small[50] = 0b110010; - small[51] = 0b110011; - small[52] = 0b110100; - small[53] = 0b110101; - small[54] = 0b110110; - small[55] = 0b110111; - small[56] = 0b111000; - small[57] = 0b111001; - small[58] = 0b111010; - small[59] = 0b111011; - small[60] = 0b111100; - small[61] = 0b111101; - small[62] = 0b111110; - small[63] = 0b111111; - small[64] = 0b1000000; - small[65] = 0b1000001; - small[66] = 0b1000010; - small[67] = 0b1000011; - small[68] = 0b1000100; - small[69] = 0b1000101; - small[70] = 0b1000110; - small[71] = 0b1000111; - small[72] = 0b1001000; - small[73] = 0b1001001; - small[74] = 0b1001010; - small[75] = 0b1001011; - small[76] = 0b1001100; - small[77] = 0b1001101; - small[78] = 0b1001110; - small[79] = 0b1001111; - small[80] = 0b1010000; - small[81] = 0b1010001; - small[82] = 0b1010010; - small[83] = 0b1010011; - small[84] = 0b1010100; - small[85] = 0b1010101; - small[86] = 0b1010110; - small[87] = 0b1010111; - small[88] = 0b1011000; - small[89] = 0b1011001; - small[90] = 0b1011010; - small[91] = 0b1011011; - small[92] = 0b1011100; - small[93] = 0b1011101; - small[94] = 0b1011110; - small[95] = 0b1011111; - small[96] = 0b1100000; - small[97] = 0b1100001; - small[98] = 0b1100010; - small[99] = 0b1100011; - small[100] = 0b1100100; - small[101] = 0b1100101; - small[102] = 0b1100110; - small[103] = 0b1100111; - small[104] = 0b1101000; - small[105] = 0b1101001; - small[106] = 0b1101010; - small[107] = 0b1101011; - small[108] = 0b1101100; - small[109] = 0b1101101; - small[110] = 0b1101110; - small[111] = 0b1101111; - small[112] = 0b1110000; - small[113] = 0b1110001; - small[114] = 0b1110010; - small[115] = 0b1110011; - small[116] = 0b1110100; - small[117] = 0b1110101; - small[118] = 0b1110110; - small[119] = 0b1110111; - small[120] = 0b1111000; - small[121] = 0b1111001; - small[122] = 0b1111010; - small[123] = 0b1111011; - small[124] = 0b1111100; - small[125] = 0b1111101; - small[126] = 0b1111110; - small[127] = 0b1111111; - small[128] = 0b10000000; - small[129] = 0b10000001; - small[130] = 0b10000010; - small[131] = 0b10000011; - small[132] = 0b10000100; - small[133] = 0b10000101; - small[134] = 0b10000110; - small[135] = 0b10000111; - small[136] = 0b10001000; - small[137] = 0b10001001; - small[138] = 0b10001010; - small[139] = 0b10001011; - small[140] = 0b10001100; - small[141] = 0b10001101; - small[142] = 0b10001110; - small[143] = 0b10001111; - small[144] = 0b10010000; - small[145] = 0b10010001; - small[146] = 0b10010010; - small[147] = 0b10010011; - small[148] = 0b10010100; - small[149] = 0b10010101; - small[150] = 0b10010110; - small[151] = 0b10010111; - small[152] = 0b10011000; - small[153] = 0b10011001; - small[154] = 0b10011010; - small[155] = 0b10011011; - small[156] = 0b10011100; - small[157] = 0b10011101; - small[158] = 0b10011110; - small[159] = 0b10011111; - small[160] = 0b10100000; - small[161] = 0b10100001; - small[162] = 0b10100010; - small[163] = 0b10100011; - small[164] = 0b10100100; - small[165] = 0b10100101; - small[166] = 0b10100110; - small[167] = 0b10100111; - small[168] = 0b10101000; - small[169] = 0b10101001; - small[170] = 0b10101010; - small[171] = 0b10101011; - small[172] = 0b10101100; - small[173] = 0b10101101; - small[174] = 0b10101110; - small[175] = 0b10101111; - small[176] = 0b10110000; - small[177] = 0b10110001; - small[178] = 0b10110010; - small[179] = 0b10110011; - small[180] = 0b10110100; - small[181] = 0b10110101; - small[182] = 0b10110110; - small[183] = 0b10110111; - small[184] = 0b10111000; - small[185] = 0b10111001; - small[186] = 0b10111010; - small[187] = 0b10111011; - small[188] = 0b10111100; - small[189] = 0b10111101; - small[190] = 0b10111110; - small[191] = 0b10111111; - small[192] = 0b11000000; - small[193] = 0b11000001; - small[194] = 0b11000010; - small[195] = 0b11000011; - small[196] = 0b11000100; - small[197] = 0b11000101; - small[198] = 0b11000110; - small[199] = 0b11000111; - small[200] = 0b11001000; - small[201] = 0b11001001; - small[202] = 0b11001010; - small[203] = 0b11001011; - small[204] = 0b11001100; - small[205] = 0b11001101; - small[206] = 0b11001110; - small[207] = 0b11001111; - small[208] = 0b11010000; - small[209] = 0b11010001; - small[210] = 0b11010010; - small[211] = 0b11010011; - small[212] = 0b11010100; - small[213] = 0b11010101; - small[214] = 0b11010110; - small[215] = 0b11010111; - small[216] = 0b11011000; - small[217] = 0b11011001; - small[218] = 0b11011010; - small[219] = 0b11011011; - small[220] = 0b11011100; - small[221] = 0b11011101; - small[222] = 0b11011110; - small[223] = 0b11011111; - small[224] = 0b11100000; - small[225] = 0b11100001; - small[226] = 0b11100010; - small[227] = 0b11100011; - small[228] = 0b11100100; - small[229] = 0b11100101; - small[230] = 0b11100110; - small[231] = 0b11100111; - small[232] = 0b11101000; - small[233] = 0b11101001; - small[234] = 0b11101010; - small[235] = 0b11101011; - small[236] = 0b11101100; - small[237] = 0b11101101; - small[238] = 0b11101110; - small[239] = 0b11101111; - small[240] = 0b11110000; - small[241] = 0b11110001; - small[242] = 0b11110010; - small[243] = 0b11110011; - small[244] = 0b11110100; - small[245] = 0b11110101; - small[246] = 0b11110110; - small[247] = 0b11110111; - small[248] = 0b11111000; - small[249] = 0b11111001; - small[250] = 0b11111010; - small[251] = 0b11111011; - small[252] = 0b11111100; - small[253] = 0b11111101; - small[254] = 0b11111110; - small[255] = 0b11111111; + small[0] = 0b0; + small[1] = 0b1; + small[2] = 0b10; + small[3] = 0b11; + small[4] = 0b100; + small[5] = 0b101; + small[6] = 0b110; + small[7] = 0b000111; + small[8] = 0b1000; + small[9] = 0b1001; + small[10] = 0b0001010; + small[11] = 0b0001011; + small[12] = 0b1100; + small[13] = 0b1101; + small[14] = 0b1110; + small[15] = 0b1111; + small[16] = 0b10000; + small[17] = 0b10001; + small[18] = 0b00010010; + small[19] = 0b00010011; + small[20] = 0b00010100; + small[21] = 0b00010101; + small[22] = 0b10110; + small[23] = 0b10111; + small[24] = 0b11000; + small[25] = 0b11001; + small[26] = 0b11010; + small[27] = 0b11011; + small[28] = 0b11100; + small[29] = 0b11101; + small[30] = 0b11110; + small[31] = 0b11111; + small[32] = 0b00000000100000; + small[33] = 0b00000000100001; + small[34] = 0b100010; + small[35] = 0b100011; + small[36] = 0b100100; + small[37] = 0b100101; + small[38] = 0b100110; + small[39] = 0b100111; + small[40] = 0b101000; + small[41] = 0b101001; + small[42] = 0b101010; + small[43] = 0b101011; + small[44] = 0b101100; + small[45] = 0b101101; + small[46] = 0b101110; + small[47] = 0b101111; + small[48] = 0b110000; + small[49] = 0b110001; + small[50] = 0b110010; + small[51] = 0b110011; + small[52] = 0b110100; + small[53] = 0b110101; + small[54] = 0b110110; + small[55] = 0b110111; + small[56] = 0b111000; + small[57] = 0b111001; + small[58] = 0b111010; + small[59] = 0b111011; + small[60] = 0b111100; + small[61] = 0b111101; + small[62] = 0b111110; + small[63] = 0b111111; + small[64] = 0b1000000; + small[65] = 0b1000001; + small[66] = 0b1000010; + small[67] = 0b1000011; + small[68] = 0b1000100; + small[69] = 0b1000101; + small[70] = 0b1000110; + small[71] = 0b1000111; + small[72] = 0b1001000; + small[73] = 0b1001001; + small[74] = 0b1001010; + small[75] = 0b1001011; + small[76] = 0b1001100; + small[77] = 0b1001101; + small[78] = 0b1001110; + small[79] = 0b1001111; + small[80] = 0b1010000; + small[81] = 0b1010001; + small[82] = 0b1010010; + small[83] = 0b1010011; + small[84] = 0b1010100; + small[85] = 0b1010101; + small[86] = 0b1010110; + small[87] = 0b1010111; + small[88] = 0b1011000; + small[89] = 0b1011001; + small[90] = 0b1011010; + small[91] = 0b1011011; + small[92] = 0b1011100; + small[93] = 0b1011101; + small[94] = 0b1011110; + small[95] = 0b1011111; + small[96] = 0b1100000; + small[97] = 0b1100001; + small[98] = 0b1100010; + small[99] = 0b1100011; + small[100] = 0b1100100; + small[101] = 0b1100101; + small[102] = 0b1100110; + small[103] = 0b1100111; + small[104] = 0b1101000; + small[105] = 0b1101001; + small[106] = 0b1101010; + small[107] = 0b1101011; + small[108] = 0b1101100; + small[109] = 0b1101101; + small[110] = 0b1101110; + small[111] = 0b1101111; + small[112] = 0b1110000; + small[113] = 0b1110001; + small[114] = 0b1110010; + small[115] = 0b1110011; + small[116] = 0b1110100; + small[117] = 0b1110101; + small[118] = 0b1110110; + small[119] = 0b1110111; + small[120] = 0b1111000; + small[121] = 0b1111001; + small[122] = 0b1111010; + small[123] = 0b1111011; + small[124] = 0b1111100; + small[125] = 0b1111101; + small[126] = 0b1111110; + small[127] = 0b1111111; + small[128] = 0b10000000; + small[129] = 0b10000001; + small[130] = 0b10000010; + small[131] = 0b10000011; + small[132] = 0b10000100; + small[133] = 0b10000101; + small[134] = 0b10000110; + small[135] = 0b10000111; + small[136] = 0b10001000; + small[137] = 0b10001001; + small[138] = 0b10001010; + small[139] = 0b10001011; + small[140] = 0b10001100; + small[141] = 0b10001101; + small[142] = 0b10001110; + small[143] = 0b10001111; + small[144] = 0b10010000; + small[145] = 0b10010001; + small[146] = 0b10010010; + small[147] = 0b10010011; + small[148] = 0b10010100; + small[149] = 0b10010101; + small[150] = 0b10010110; + small[151] = 0b10010111; + small[152] = 0b10011000; + small[153] = 0b10011001; + small[154] = 0b10011010; + small[155] = 0b10011011; + small[156] = 0b10011100; + small[157] = 0b10011101; + small[158] = 0b10011110; + small[159] = 0b10011111; + small[160] = 0b10100000; + small[161] = 0b10100001; + small[162] = 0b10100010; + small[163] = 0b10100011; + small[164] = 0b10100100; + small[165] = 0b10100101; + small[166] = 0b10100110; + small[167] = 0b10100111; + small[168] = 0b10101000; + small[169] = 0b10101001; + small[170] = 0b10101010; + small[171] = 0b10101011; + small[172] = 0b10101100; + small[173] = 0b10101101; + small[174] = 0b10101110; + small[175] = 0b10101111; + small[176] = 0b10110000; + small[177] = 0b10110001; + small[178] = 0b10110010; + small[179] = 0b10110011; + small[180] = 0b10110100; + small[181] = 0b10110101; + small[182] = 0b10110110; + small[183] = 0b10110111; + small[184] = 0b10111000; + small[185] = 0b10111001; + small[186] = 0b10111010; + small[187] = 0b10111011; + small[188] = 0b10111100; + small[189] = 0b10111101; + small[190] = 0b10111110; + small[191] = 0b10111111; + small[192] = 0b11000000; + small[193] = 0b11000001; + small[194] = 0b11000010; + small[195] = 0b11000011; + small[196] = 0b11000100; + small[197] = 0b11000101; + small[198] = 0b11000110; + small[199] = 0b11000111; + small[200] = 0b11001000; + small[201] = 0b11001001; + small[202] = 0b11001010; + small[203] = 0b11001011; + small[204] = 0b11001100; + small[205] = 0b11001101; + small[206] = 0b11001110; + small[207] = 0b11001111; + small[208] = 0b11010000; + small[209] = 0b11010001; + small[210] = 0b11010010; + small[211] = 0b11010011; + small[212] = 0b11010100; + small[213] = 0b11010101; + small[214] = 0b11010110; + small[215] = 0b11010111; + small[216] = 0b11011000; + small[217] = 0b11011001; + small[218] = 0b11011010; + small[219] = 0b11011011; + small[220] = 0b11011100; + small[221] = 0b11011101; + small[222] = 0b11011110; + small[223] = 0b11011111; + small[224] = 0b11100000; + small[225] = 0b11100001; + small[226] = 0b11100010; + small[227] = 0b11100011; + small[228] = 0b11100100; + small[229] = 0b11100101; + small[230] = 0b11100110; + small[231] = 0b11100111; + small[232] = 0b11101000; + small[233] = 0b11101001; + small[234] = 0b11101010; + small[235] = 0b11101011; + small[236] = 0b11101100; + small[237] = 0b11101101; + small[238] = 0b11101110; + small[239] = 0b11101111; + small[240] = 0b11110000; + small[241] = 0b11110001; + small[242] = 0b11110010; + small[243] = 0b11110011; + small[244] = 0b11110100; + small[245] = 0b11110101; + small[246] = 0b11110110; + small[247] = 0b11110111; + small[248] = 0b11111000; + small[249] = 0b11111001; + small[250] = 0b11111010; + small[251] = 0b11111011; + small[252] = 0b11111100; + small[253] = 0b11111101; + small[254] = 0b11111110; + small[255] = 0b11111111; - for (i = 0; i < 256; i++) { - if (small[i] != i) - return 1; - } + for (i = 0; i < 256; i++) { + if (small[i] != i) + return 1; + } - big[0] = 0b1111111100000000; - big[1] = 0b1111111100000001; - big[2] = 0b1111111100000010; - big[3] = 0b1111111100000011; - big[4] = 0b1111111100000100; - big[5] = 0b1111111100000101; - big[6] = 0b1111111100000110; - big[7] = 0b1111111100000111; - big[8] = 0b1111111100001000; - big[9] = 0b1111111100001001; - big[10] = 0b1111111100001010; - big[11] = 0b1111111100001011; - big[12] = 0b1111111100001100; - big[13] = 0b1111111100001101; - big[14] = 0b1111111100001110; - big[15] = 0b1111111100001111; - big[16] = 0b1111111100010000; - big[17] = 0b1111111100010001; - big[18] = 0b1111111100010010; - big[19] = 0b1111111100010011; - big[20] = 0b1111111100010100; - big[21] = 0b1111111100010101; - big[22] = 0b1111111100010110; - big[23] = 0b1111111100010111; - big[24] = 0b1111111100011000; - big[25] = 0b1111111100011001; - big[26] = 0b1111111100011010; - big[27] = 0b1111111100011011; - big[28] = 0b1111111100011100; - big[29] = 0b1111111100011101; - big[30] = 0b1111111100011110; - big[31] = 0b1111111100011111; - big[32] = 0b1111111100100000; - big[33] = 0b1111111100100001; - big[34] = 0b1111111100100010; - big[35] = 0b1111111100100011; - big[36] = 0b1111111100100100; - big[37] = 0b1111111100100101; - big[38] = 0b1111111100100110; - big[39] = 0b1111111100100111; - big[40] = 0b1111111100101000; - big[41] = 0b1111111100101001; - big[42] = 0b1111111100101010; - big[43] = 0b1111111100101011; - big[44] = 0b1111111100101100; - big[45] = 0b1111111100101101; - big[46] = 0b1111111100101110; - big[47] = 0b1111111100101111; - big[48] = 0b1111111100110000; - big[49] = 0b1111111100110001; - big[50] = 0b1111111100110010; - big[51] = 0b1111111100110011; - big[52] = 0b1111111100110100; - big[53] = 0b1111111100110101; - big[54] = 0b1111111100110110; - big[55] = 0b1111111100110111; - big[56] = 0b1111111100111000; - big[57] = 0b1111111100111001; - big[58] = 0b1111111100111010; - big[59] = 0b1111111100111011; - big[60] = 0b1111111100111100; - big[61] = 0b1111111100111101; - big[62] = 0b1111111100111110; - big[63] = 0b1111111100111111; - big[64] = 0b1111111101000000; - big[65] = 0b1111111101000001; - big[66] = 0b1111111101000010; - big[67] = 0b1111111101000011; - big[68] = 0b1111111101000100; - big[69] = 0b1111111101000101; - big[70] = 0b1111111101000110; - big[71] = 0b1111111101000111; - big[72] = 0b1111111101001000; - big[73] = 0b1111111101001001; - big[74] = 0b1111111101001010; - big[75] = 0b1111111101001011; - big[76] = 0b1111111101001100; - big[77] = 0b1111111101001101; - big[78] = 0b1111111101001110; - big[79] = 0b1111111101001111; - big[80] = 0b1111111101010000; - big[81] = 0b1111111101010001; - big[82] = 0b1111111101010010; - big[83] = 0b1111111101010011; - big[84] = 0b1111111101010100; - big[85] = 0b1111111101010101; - big[86] = 0b1111111101010110; - big[87] = 0b1111111101010111; - big[88] = 0b1111111101011000; - big[89] = 0b1111111101011001; - big[90] = 0b1111111101011010; - big[91] = 0b1111111101011011; - big[92] = 0b1111111101011100; - big[93] = 0b1111111101011101; - big[94] = 0b1111111101011110; - big[95] = 0b1111111101011111; - big[96] = 0b1111111101100000; - big[97] = 0b1111111101100001; - big[98] = 0b1111111101100010; - big[99] = 0b1111111101100011; - big[100] = 0b1111111101100100; - big[101] = 0b1111111101100101; - big[102] = 0b1111111101100110; - big[103] = 0b1111111101100111; - big[104] = 0b1111111101101000; - big[105] = 0b1111111101101001; - big[106] = 0b1111111101101010; - big[107] = 0b1111111101101011; - big[108] = 0b1111111101101100; - big[109] = 0b1111111101101101; - big[110] = 0b1111111101101110; - big[111] = 0b1111111101101111; - big[112] = 0b1111111101110000; - big[113] = 0b1111111101110001; - big[114] = 0b1111111101110010; - big[115] = 0b1111111101110011; - big[116] = 0b1111111101110100; - big[117] = 0b1111111101110101; - big[118] = 0b1111111101110110; - big[119] = 0b1111111101110111; - big[120] = 0b1111111101111000; - big[121] = 0b1111111101111001; - big[122] = 0b1111111101111010; - big[123] = 0b1111111101111011; - big[124] = 0b1111111101111100; - big[125] = 0b1111111101111101; - big[126] = 0b1111111101111110; - big[127] = 0b1111111101111111; - big[128] = 0b1111111110000000; - big[129] = 0b1111111110000001; - big[130] = 0b1111111110000010; - big[131] = 0b1111111110000011; - big[132] = 0b1111111110000100; - big[133] = 0b1111111110000101; - big[134] = 0b1111111110000110; - big[135] = 0b1111111110000111; - big[136] = 0b1111111110001000; - big[137] = 0b1111111110001001; - big[138] = 0b1111111110001010; - big[139] = 0b1111111110001011; - big[140] = 0b1111111110001100; - big[141] = 0b1111111110001101; - big[142] = 0b1111111110001110; - big[143] = 0b1111111110001111; - big[144] = 0b1111111110010000; - big[145] = 0b1111111110010001; - big[146] = 0b1111111110010010; - big[147] = 0b1111111110010011; - big[148] = 0b1111111110010100; - big[149] = 0b1111111110010101; - big[150] = 0b1111111110010110; - big[151] = 0b1111111110010111; - big[152] = 0b1111111110011000; - big[153] = 0b1111111110011001; - big[154] = 0b1111111110011010; - big[155] = 0b1111111110011011; - big[156] = 0b1111111110011100; - big[157] = 0b1111111110011101; - big[158] = 0b1111111110011110; - big[159] = 0b1111111110011111; - big[160] = 0b1111111110100000; - big[161] = 0b1111111110100001; - big[162] = 0b1111111110100010; - big[163] = 0b1111111110100011; - big[164] = 0b1111111110100100; - big[165] = 0b1111111110100101; - big[166] = 0b1111111110100110; - big[167] = 0b1111111110100111; - big[168] = 0b1111111110101000; - big[169] = 0b1111111110101001; - big[170] = 0b1111111110101010; - big[171] = 0b1111111110101011; - big[172] = 0b1111111110101100; - big[173] = 0b1111111110101101; - big[174] = 0b1111111110101110; - big[175] = 0b1111111110101111; - big[176] = 0b1111111110110000; - big[177] = 0b1111111110110001; - big[178] = 0b1111111110110010; - big[179] = 0b1111111110110011; - big[180] = 0b1111111110110100; - big[181] = 0b1111111110110101; - big[182] = 0b1111111110110110; - big[183] = 0b1111111110110111; - big[184] = 0b1111111110111000; - big[185] = 0b1111111110111001; - big[186] = 0b1111111110111010; - big[187] = 0b1111111110111011; - big[188] = 0b1111111110111100; - big[189] = 0b1111111110111101; - big[190] = 0b1111111110111110; - big[191] = 0b1111111110111111; - big[192] = 0b1111111111000000; - big[193] = 0b1111111111000001; - big[194] = 0b1111111111000010; - big[195] = 0b1111111111000011; - big[196] = 0b1111111111000100; - big[197] = 0b1111111111000101; - big[198] = 0b1111111111000110; - big[199] = 0b1111111111000111; - big[200] = 0b1111111111001000; - big[201] = 0b1111111111001001; - big[202] = 0b1111111111001010; - big[203] = 0b1111111111001011; - big[204] = 0b1111111111001100; - big[205] = 0b1111111111001101; - big[206] = 0b1111111111001110; - big[207] = 0b1111111111001111; - big[208] = 0b1111111111010000; - big[209] = 0b1111111111010001; - big[210] = 0b1111111111010010; - big[211] = 0b1111111111010011; - big[212] = 0b1111111111010100; - big[213] = 0b1111111111010101; - big[214] = 0b1111111111010110; - big[215] = 0b1111111111010111; - big[216] = 0b1111111111011000; - big[217] = 0b1111111111011001; - big[218] = 0b1111111111011010; - big[219] = 0b1111111111011011; - big[220] = 0b1111111111011100; - big[221] = 0b1111111111011101; - big[222] = 0b1111111111011110; - big[223] = 0b1111111111011111; - big[224] = 0b1111111111100000; - big[225] = 0b1111111111100001; - big[226] = 0b1111111111100010; - big[227] = 0b1111111111100011; - big[228] = 0b1111111111100100; - big[229] = 0b1111111111100101; - big[230] = 0b1111111111100110; - big[231] = 0b1111111111100111; - big[232] = 0b1111111111101000; - big[233] = 0b1111111111101001; - big[234] = 0b1111111111101010; - big[235] = 0b1111111111101011; - big[236] = 0b1111111111101100; - big[237] = 0b1111111111101101; - big[238] = 0b1111111111101110; - big[239] = 0b1111111111101111; - big[240] = 0b1111111111110000; - big[241] = 0b1111111111110001; - big[242] = 0b1111111111110010; - big[243] = 0b1111111111110011; - big[244] = 0b1111111111110100; - big[245] = 0b1111111111110101; - big[246] = 0b1111111111110110; - big[247] = 0b1111111111110111; - big[248] = 0b1111111111111000; - big[249] = 0b1111111111111001; - big[250] = 0b1111111111111010; - big[251] = 0b1111111111111011; - big[252] = 0b1111111111111100; - big[253] = 0b1111111111111101; - big[254] = 0b1111111111111110; - big[255] = 0b1111111111111111; + big[0] = 0b1111111100000000; + big[1] = 0b1111111100000001; + big[2] = 0b1111111100000010; + big[3] = 0b1111111100000011; + big[4] = 0b1111111100000100; + big[5] = 0b1111111100000101; + big[6] = 0b1111111100000110; + big[7] = 0b1111111100000111; + big[8] = 0b1111111100001000; + big[9] = 0b1111111100001001; + big[10] = 0b1111111100001010; + big[11] = 0b1111111100001011; + big[12] = 0b1111111100001100; + big[13] = 0b1111111100001101; + big[14] = 0b1111111100001110; + big[15] = 0b1111111100001111; + big[16] = 0b1111111100010000; + big[17] = 0b1111111100010001; + big[18] = 0b1111111100010010; + big[19] = 0b1111111100010011; + big[20] = 0b1111111100010100; + big[21] = 0b1111111100010101; + big[22] = 0b1111111100010110; + big[23] = 0b1111111100010111; + big[24] = 0b1111111100011000; + big[25] = 0b1111111100011001; + big[26] = 0b1111111100011010; + big[27] = 0b1111111100011011; + big[28] = 0b1111111100011100; + big[29] = 0b1111111100011101; + big[30] = 0b1111111100011110; + big[31] = 0b1111111100011111; + big[32] = 0b1111111100100000; + big[33] = 0b1111111100100001; + big[34] = 0b1111111100100010; + big[35] = 0b1111111100100011; + big[36] = 0b1111111100100100; + big[37] = 0b1111111100100101; + big[38] = 0b1111111100100110; + big[39] = 0b1111111100100111; + big[40] = 0b1111111100101000; + big[41] = 0b1111111100101001; + big[42] = 0b1111111100101010; + big[43] = 0b1111111100101011; + big[44] = 0b1111111100101100; + big[45] = 0b1111111100101101; + big[46] = 0b1111111100101110; + big[47] = 0b1111111100101111; + big[48] = 0b1111111100110000; + big[49] = 0b1111111100110001; + big[50] = 0b1111111100110010; + big[51] = 0b1111111100110011; + big[52] = 0b1111111100110100; + big[53] = 0b1111111100110101; + big[54] = 0b1111111100110110; + big[55] = 0b1111111100110111; + big[56] = 0b1111111100111000; + big[57] = 0b1111111100111001; + big[58] = 0b1111111100111010; + big[59] = 0b1111111100111011; + big[60] = 0b1111111100111100; + big[61] = 0b1111111100111101; + big[62] = 0b1111111100111110; + big[63] = 0b1111111100111111; + big[64] = 0b1111111101000000; + big[65] = 0b1111111101000001; + big[66] = 0b1111111101000010; + big[67] = 0b1111111101000011; + big[68] = 0b1111111101000100; + big[69] = 0b1111111101000101; + big[70] = 0b1111111101000110; + big[71] = 0b1111111101000111; + big[72] = 0b1111111101001000; + big[73] = 0b1111111101001001; + big[74] = 0b1111111101001010; + big[75] = 0b1111111101001011; + big[76] = 0b1111111101001100; + big[77] = 0b1111111101001101; + big[78] = 0b1111111101001110; + big[79] = 0b1111111101001111; + big[80] = 0b1111111101010000; + big[81] = 0b1111111101010001; + big[82] = 0b1111111101010010; + big[83] = 0b1111111101010011; + big[84] = 0b1111111101010100; + big[85] = 0b1111111101010101; + big[86] = 0b1111111101010110; + big[87] = 0b1111111101010111; + big[88] = 0b1111111101011000; + big[89] = 0b1111111101011001; + big[90] = 0b1111111101011010; + big[91] = 0b1111111101011011; + big[92] = 0b1111111101011100; + big[93] = 0b1111111101011101; + big[94] = 0b1111111101011110; + big[95] = 0b1111111101011111; + big[96] = 0b1111111101100000; + big[97] = 0b1111111101100001; + big[98] = 0b1111111101100010; + big[99] = 0b1111111101100011; + big[100] = 0b1111111101100100; + big[101] = 0b1111111101100101; + big[102] = 0b1111111101100110; + big[103] = 0b1111111101100111; + big[104] = 0b1111111101101000; + big[105] = 0b1111111101101001; + big[106] = 0b1111111101101010; + big[107] = 0b1111111101101011; + big[108] = 0b1111111101101100; + big[109] = 0b1111111101101101; + big[110] = 0b1111111101101110; + big[111] = 0b1111111101101111; + big[112] = 0b1111111101110000; + big[113] = 0b1111111101110001; + big[114] = 0b1111111101110010; + big[115] = 0b1111111101110011; + big[116] = 0b1111111101110100; + big[117] = 0b1111111101110101; + big[118] = 0b1111111101110110; + big[119] = 0b1111111101110111; + big[120] = 0b1111111101111000; + big[121] = 0b1111111101111001; + big[122] = 0b1111111101111010; + big[123] = 0b1111111101111011; + big[124] = 0b1111111101111100; + big[125] = 0b1111111101111101; + big[126] = 0b1111111101111110; + big[127] = 0b1111111101111111; + big[128] = 0b1111111110000000; + big[129] = 0b1111111110000001; + big[130] = 0b1111111110000010; + big[131] = 0b1111111110000011; + big[132] = 0b1111111110000100; + big[133] = 0b1111111110000101; + big[134] = 0b1111111110000110; + big[135] = 0b1111111110000111; + big[136] = 0b1111111110001000; + big[137] = 0b1111111110001001; + big[138] = 0b1111111110001010; + big[139] = 0b1111111110001011; + big[140] = 0b1111111110001100; + big[141] = 0b1111111110001101; + big[142] = 0b1111111110001110; + big[143] = 0b1111111110001111; + big[144] = 0b1111111110010000; + big[145] = 0b1111111110010001; + big[146] = 0b1111111110010010; + big[147] = 0b1111111110010011; + big[148] = 0b1111111110010100; + big[149] = 0b1111111110010101; + big[150] = 0b1111111110010110; + big[151] = 0b1111111110010111; + big[152] = 0b1111111110011000; + big[153] = 0b1111111110011001; + big[154] = 0b1111111110011010; + big[155] = 0b1111111110011011; + big[156] = 0b1111111110011100; + big[157] = 0b1111111110011101; + big[158] = 0b1111111110011110; + big[159] = 0b1111111110011111; + big[160] = 0b1111111110100000; + big[161] = 0b1111111110100001; + big[162] = 0b1111111110100010; + big[163] = 0b1111111110100011; + big[164] = 0b1111111110100100; + big[165] = 0b1111111110100101; + big[166] = 0b1111111110100110; + big[167] = 0b1111111110100111; + big[168] = 0b1111111110101000; + big[169] = 0b1111111110101001; + big[170] = 0b1111111110101010; + big[171] = 0b1111111110101011; + big[172] = 0b1111111110101100; + big[173] = 0b1111111110101101; + big[174] = 0b1111111110101110; + big[175] = 0b1111111110101111; + big[176] = 0b1111111110110000; + big[177] = 0b1111111110110001; + big[178] = 0b1111111110110010; + big[179] = 0b1111111110110011; + big[180] = 0b1111111110110100; + big[181] = 0b1111111110110101; + big[182] = 0b1111111110110110; + big[183] = 0b1111111110110111; + big[184] = 0b1111111110111000; + big[185] = 0b1111111110111001; + big[186] = 0b1111111110111010; + big[187] = 0b1111111110111011; + big[188] = 0b1111111110111100; + big[189] = 0b1111111110111101; + big[190] = 0b1111111110111110; + big[191] = 0b1111111110111111; + big[192] = 0b1111111111000000; + big[193] = 0b1111111111000001; + big[194] = 0b1111111111000010; + big[195] = 0b1111111111000011; + big[196] = 0b1111111111000100; + big[197] = 0b1111111111000101; + big[198] = 0b1111111111000110; + big[199] = 0b1111111111000111; + big[200] = 0b1111111111001000; + big[201] = 0b1111111111001001; + big[202] = 0b1111111111001010; + big[203] = 0b1111111111001011; + big[204] = 0b1111111111001100; + big[205] = 0b1111111111001101; + big[206] = 0b1111111111001110; + big[207] = 0b1111111111001111; + big[208] = 0b1111111111010000; + big[209] = 0b1111111111010001; + big[210] = 0b1111111111010010; + big[211] = 0b1111111111010011; + big[212] = 0b1111111111010100; + big[213] = 0b1111111111010101; + big[214] = 0b1111111111010110; + big[215] = 0b1111111111010111; + big[216] = 0b1111111111011000; + big[217] = 0b1111111111011001; + big[218] = 0b1111111111011010; + big[219] = 0b1111111111011011; + big[220] = 0b1111111111011100; + big[221] = 0b1111111111011101; + big[222] = 0b1111111111011110; + big[223] = 0b1111111111011111; + big[224] = 0b1111111111100000; + big[225] = 0b1111111111100001; + big[226] = 0b1111111111100010; + big[227] = 0b1111111111100011; + big[228] = 0b1111111111100100; + big[229] = 0b1111111111100101; + big[230] = 0b1111111111100110; + big[231] = 0b1111111111100111; + big[232] = 0b1111111111101000; + big[233] = 0b1111111111101001; + big[234] = 0b1111111111101010; + big[235] = 0b1111111111101011; + big[236] = 0b1111111111101100; + big[237] = 0b1111111111101101; + big[238] = 0b1111111111101110; + big[239] = 0b1111111111101111; + big[240] = 0b1111111111110000; + big[241] = 0b1111111111110001; + big[242] = 0b1111111111110010; + big[243] = 0b1111111111110011; + big[244] = 0b1111111111110100; + big[245] = 0b1111111111110101; + big[246] = 0b1111111111110110; + big[247] = 0b1111111111110111; + big[248] = 0b1111111111111000; + big[249] = 0b1111111111111001; + big[250] = 0b1111111111111010; + big[251] = 0b1111111111111011; + big[252] = 0b1111111111111100; + big[253] = 0b1111111111111101; + big[254] = 0b1111111111111110; + big[255] = 0b1111111111111111; - for (i = 0; i < 256; i++) { - if (big[i] != i + 65280U) - return 1; - } + for (i = 0; i < 256; i++) { + if (big[i] != i + 65280U) + return 1; + } - return 0; + return 0; } diff --git a/test/val/bug1071.c b/test/val/bug1071.c index 02b069de0..66e298b25 100644 --- a/test/val/bug1071.c +++ b/test/val/bug1071.c @@ -7,24 +7,24 @@ struct ImageStruct { - uint8_t _imageData; - #if !defined(NO_COLOR) - uint8_t _color; - #endif + uint8_t _imageData; + #if !defined(NO_COLOR) + uint8_t _color; + #endif }; typedef struct ImageStruct Image; struct CharacterStruct { - // character coordinates - uint8_t _x; - uint8_t _y; + // character coordinates + uint8_t _x; + uint8_t _y; - // _status decides whether the character is active - uint8_t _status; + // _status decides whether the character is active + uint8_t _status; - Image* _imagePtr; + Image* _imagePtr; }; typedef struct CharacterStruct Character; @@ -53,20 +53,20 @@ Character bombs[BOMBS_NUMBER]; uint16_t test1(void) { - if((loop> 0) & 0xFF; - uint16_t s1 = (seed >> 8) & 0xFF; - uint16_t s2 = (seed >> 16) & 0xFF; - uint16_t s3 = (seed >> 24) & 0xFF; - uint16_t o0 = s3 ^ s1; - uint16_t o1 = s2 ^ s0; - output = o0 | (o1 << 8); - } - return (int)(output & 0x7FFF); + uint16_t output; + /* seed follows the LCG sequence * 0x01010101 + 0xB3B3B3B3 */ + seed = seed * 0x01010101UL + 0xB3B3B3B3UL; + /* output uses the top two bytes (reversed) XOR with bottom two bytes */ + { + uint16_t s0 = (seed >> 0) & 0xFF; + uint16_t s1 = (seed >> 8) & 0xFF; + uint16_t s2 = (seed >> 16) & 0xFF; + uint16_t s3 = (seed >> 24) & 0xFF; + uint16_t o0 = s3 ^ s1; + uint16_t o1 = s2 ^ s0; + output = o0 | (o1 << 8); + } + return (int)(output & 0x7FFF); } void ref_srand(int ax) { - uint32_t s = (unsigned int)ax; - seed = s | (s << 16); /* low 16 bits is convenient filler for high 16 bits */ - ref_rand(); /* one pre-call "shuffles" the first rand() result so it isn't too predictable */ + uint32_t s = (unsigned int)ax; + seed = s | (s << 16); /* low 16 bits is convenient filler for high 16 bits */ + ref_rand(); /* one pre-call "shuffles" the first rand() result so it isn't too predictable */ } int main(void) { - unsigned int i,j; - int a,b; + unsigned int i,j; + int a,b; - /* test that startup state is equivalent to srand(1) */ - { - //srand(1); // implied - ref_srand(1); - for (j=0; j Date: Sun, 15 Nov 2020 23:03:01 +0100 Subject: [PATCH 484/806] g_asr, g_asl: Use ROL/ROR for char shifts by >= 6 Instead of `val` right (left) shifts, we can also do `9 - val` left (right) rotates and a mask. This saves 3 bytes and 8 cycles for `val == 7` and 1 byte and 4 cycles for `val == 6`. --- src/cc65/codegen.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index a58484cf1..3ca9d81e6 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3114,9 +3114,26 @@ void g_asr (unsigned flags, unsigned long val) switch (flags & CF_TYPEMASK) { case CF_CHAR: if (flags & CF_FORCECHAR) { - if ((flags & CF_UNSIGNED) != 0 && val < 8) { - while (val--) { - AddCodeLine ("lsr a"); + val &= 7; + if ((flags & CF_UNSIGNED) != 0) { + /* Instead of `val` right shifts, we can also do `9 - val` left rotates + ** and a mask. This saves 3 bytes and 8 cycles for `val == 7` and + ** 1 byte and 4 cycles for `val == 6`. + */ + if (val < 6) { + while (val--) { + AddCodeLine ("lsr a"); /* 1 byte, 2 cycles */ + } + } else { + unsigned i; + /* The first ROL shifts in garbage and sets carry to the high bit. + ** The garbage is cleaned up by the mask. + */ + for (i = val; i < 9; ++i) { + AddCodeLine ("rol a"); /* 1 byte, 2 cycles */ + } + /* 2 bytes, 2 cycles */ + AddCodeLine ("and #$%02X", 0xFF >> val); } return; } else if (val <= 2) { @@ -3270,9 +3287,21 @@ void g_asl (unsigned flags, unsigned long val) if (flags & CF_CONST) { switch (flags & CF_TYPEMASK) { case CF_CHAR: - if ((flags & CF_FORCECHAR) != 0 && val <= 6) { - while (val--) { - AddCodeLine ("asl a"); + if ((flags & CF_FORCECHAR) != 0) { + val &= 7; + /* Large shifts are faster and smaller with ROR. See g_asr for detailed + ** byte and cycle counts. + */ + if (val < 6) { + while (val--) { + AddCodeLine ("asl a"); + } + } else { + unsigned i; + for (i = val; i < 9; ++i) { + AddCodeLine ("ror a"); + } + AddCodeLine ("and #$%02X", (~0U << val) & 0xFF); } return; } From 39573109500d06b415026af2d07de4b3c8b52a47 Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Sun, 25 Oct 2020 14:27:26 +0100 Subject: [PATCH 485/806] Knock off two bytes from getcwd(), cbm_read() and cbm_write(). --- libsrc/cbm/cbm_read.s | 14 +++++++------- libsrc/cbm/cbm_write.s | 14 +++++++------- libsrc/common/getcwd.s | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index b010966a3..a30983302 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -45,11 +45,11 @@ _cbm_read: - eor #$FF - sta ptr1 - txa - eor #$FF - sta ptr1+1 ; Save -size-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; Save size with each byte incremented. jsr popax sta ptr2 @@ -92,9 +92,9 @@ _cbm_read: bne @L3 inc ptr3+1 ; ++bytesread; -@L3: inc ptr1 +@L3: dec ptr1 bne @L1 - inc ptr1+1 + dec ptr1+1 bne @L1 @L4: jsr CLRCH diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index 2d932d04a..a199c1d90 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -39,11 +39,11 @@ _cbm_write: sta ptr3 stx ptr3+1 ; Save size - eor #$FF - sta ptr1 - txa - eor #$FF - sta ptr1+1 ; Save -size-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; Save size with each byte incremented jsr popax sta ptr2 @@ -69,9 +69,9 @@ _cbm_write: @L2: jsr BSOUT ; cbm_k_bsout (A); -@L3: inc ptr1 ; --size; +@L3: dec ptr1 ; --size; bne @L1 - inc ptr1+1 + dec ptr1+1 bne @L1 jsr CLRCH diff --git a/libsrc/common/getcwd.s b/libsrc/common/getcwd.s index 22b6ceded..c0a1c3634 100644 --- a/libsrc/common/getcwd.s +++ b/libsrc/common/getcwd.s @@ -17,22 +17,22 @@ .proc _getcwd -; Remember -size-1 because this simplifies the following loop +; Remember size with each byte incremented because this simplifies the following loop - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 + inx + stx ptr2+1 + tax + inx + stx ptr2 jsr popptr1 ; Get buf to ptr1 ; Copy __cwd to the given buffer checking the length ; ldy #$00 is guaranteed by popptr1 -loop: inc ptr2 +loop: dec ptr2 bne @L1 - inc ptr2+1 + dec ptr2+1 beq overflow ; Copy one character, end the loop if the zero terminator is reached. We From 99c0815cdb6b55ee89a1945e58674a5eefbb8126 Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Tue, 27 Oct 2020 06:08:04 +0100 Subject: [PATCH 486/806] Clear up comments a bit. --- libsrc/cbm/cbm_read.s | 2 +- libsrc/cbm/cbm_write.s | 2 +- libsrc/common/getcwd.s | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s index a30983302..29e0e1f19 100644 --- a/libsrc/cbm/cbm_read.s +++ b/libsrc/cbm/cbm_read.s @@ -49,7 +49,7 @@ _cbm_read: stx ptr1+1 tax inx - stx ptr1 ; Save size with each byte incremented. + stx ptr1 ; Save size with both bytes incremented separately. jsr popax sta ptr2 diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s index a199c1d90..5ac07209c 100644 --- a/libsrc/cbm/cbm_write.s +++ b/libsrc/cbm/cbm_write.s @@ -43,7 +43,7 @@ _cbm_write: stx ptr1+1 tax inx - stx ptr1 ; Save size with each byte incremented + stx ptr1 ; Save size with both bytes incremented separately jsr popax sta ptr2 diff --git a/libsrc/common/getcwd.s b/libsrc/common/getcwd.s index c0a1c3634..4bfc0a5b6 100644 --- a/libsrc/common/getcwd.s +++ b/libsrc/common/getcwd.s @@ -23,7 +23,7 @@ stx ptr2+1 tax inx - stx ptr2 + stx ptr2 ; Save size with each byte incremented separately jsr popptr1 ; Get buf to ptr1 From f59cb9af0660e5401f18fe9e86329b2d0fd6b739 Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Sun, 1 Nov 2020 22:59:07 +0100 Subject: [PATCH 487/806] Use more compact loops. --- libsrc/atmos/read.s | 15 ++++++++------- libsrc/atmos/write.s | 14 +++++++------- libsrc/cbm/read.s | 4 ++-- libsrc/cbm/rwcommon.s | 10 +++++----- libsrc/cbm/write.s | 4 ++-- libsrc/common/memcmp.s | 15 +++++++-------- libsrc/common/strncat.s | 22 +++++++++++----------- libsrc/common/strncpy.s | 18 +++++++++--------- libsrc/common/strnicmp.s | 20 +++++++------------- libsrc/conio/vcprintf.s | 16 ++++++++-------- libsrc/geos-common/drivers/fio_module.s | 15 ++++++++------- libsrc/osic1p/bootstrap.s | 17 +++++++++-------- libsrc/telestrat/write.s | 14 +++++++------- 13 files changed, 90 insertions(+), 94 deletions(-) diff --git a/libsrc/atmos/read.s b/libsrc/atmos/read.s index 83aa8024e..3f22d8d0e 100644 --- a/libsrc/atmos/read.s +++ b/libsrc/atmos/read.s @@ -20,18 +20,19 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard -L1: inc ptr2 +L1: dec ptr2 bnz L2 - inc ptr2+1 + dec ptr2+1 bze L9 ; no more room in buf ; If there are no more characters in BASIC's input buffer, then get a line from diff --git a/libsrc/atmos/write.s b/libsrc/atmos/write.s index 7865b5698..4a68994ec 100644 --- a/libsrc/atmos/write.s +++ b/libsrc/atmos/write.s @@ -17,17 +17,17 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard -L1: inc ptr2 +L1: dec ptr2 bne L2 - inc ptr2+1 + dec ptr2+1 beq L9 L2: ldy #0 lda (ptr1),y diff --git a/libsrc/cbm/read.s b/libsrc/cbm/read.s index ee01596aa..fb0bb3171 100644 --- a/libsrc/cbm/read.s +++ b/libsrc/cbm/read.s @@ -106,9 +106,9 @@ ; Decrement the count -@L3: inc ptr2 +@L3: dec ptr2 bne @L0 - inc ptr2+1 + dec ptr2+1 bne @L0 beq done ; Branch always diff --git a/libsrc/cbm/rwcommon.s b/libsrc/cbm/rwcommon.s index a1f92be8c..d13c478b5 100644 --- a/libsrc/cbm/rwcommon.s +++ b/libsrc/cbm/rwcommon.s @@ -21,11 +21,11 @@ .proc rwcommon - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; Remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; Save count with each byte incremented separately jsr popptr1 ; Get buf to ptr1, Y=0 by call diff --git a/libsrc/cbm/write.s b/libsrc/cbm/write.s index 7a27f0044..ebc44a0ac 100644 --- a/libsrc/cbm/write.s +++ b/libsrc/cbm/write.s @@ -83,9 +83,9 @@ ; Decrement count -@L2: inc ptr2 +@L2: dec ptr2 bne @L0 - inc ptr2+1 + dec ptr2+1 bne @L0 ; Wrote all chars or disk full. Close the output channel diff --git a/libsrc/common/memcmp.s b/libsrc/common/memcmp.s index 93a2c28dc..5879a433d 100644 --- a/libsrc/common/memcmp.s +++ b/libsrc/common/memcmp.s @@ -13,11 +13,11 @@ _memcmp: ; Calculate (-count-1) and store it into ptr3. This is some overhead here but ; saves time in the compare loop - eor #$FF - sta ptr3 - txa - eor #$FF - sta ptr3+1 + inx + stx ptr3+1 + tax + inx + stx ptr3 ; Save count with each byte incremented separately ; Get the pointer parameters @@ -33,7 +33,7 @@ _memcmp: ; Head of compare loop: Test for the end condition -Loop: inx ; Bump low byte of (-count-1) +Loop: dex ; Bump low byte of (-count-1) beq BumpHiCnt ; Jump on overflow ; Do the compare @@ -53,7 +53,7 @@ Comp: lda (ptr1),y ; Entry on low counter byte overflow BumpHiCnt: - inc ptr3+1 ; Bump high byte of (-count-1) + dec ptr3+1 ; Bump high byte of (-count-1) bne Comp ; Jump if not done jmp return0 ; Count is zero, areas are identical @@ -67,4 +67,3 @@ NotEqual: Greater: ldx #$01 ; Make result positive rts - diff --git a/libsrc/common/strncat.s b/libsrc/common/strncat.s index caa6804fc..060378442 100644 --- a/libsrc/common/strncat.s +++ b/libsrc/common/strncat.s @@ -5,17 +5,17 @@ ; char* strncat (char* dest, const char* src, size_t n); ; - .export _strncat - .import popax, popptr1 - .importzp ptr1, ptr2, ptr3, tmp1, tmp2 - .macpack cpu +.export _strncat +.import popax, popptr1 +.importzp ptr1, ptr2, ptr3, tmp1, tmp2 +.macpack cpu _strncat: - eor #$FF ; one's complement to count upwards - sta tmp1 - txa - eor #$FF - sta tmp2 + inx + stx tmp2 + tax + inx + stx tmp1 ; save count with each byte incremented separately jsr popptr1 ; get src @@ -49,9 +49,9 @@ L2: sty ptr2 L3: ldy #0 ldx tmp1 ; low counter byte -L4: inx +L4: dex bne L5 - inc tmp2 + dec tmp2 beq L6 ; jump if done L5: lda (ptr1),y sta (ptr2),y diff --git a/libsrc/common/strncpy.s b/libsrc/common/strncpy.s index 56387f880..49bd90b32 100644 --- a/libsrc/common/strncpy.s +++ b/libsrc/common/strncpy.s @@ -10,11 +10,11 @@ .proc _strncpy - eor #$FF - sta tmp1 - txa - eor #$FF - sta tmp2 ; Store -size - 1 + inx + stx tmp2 + tax + inx + stx tmp1 ; save count with each byte incremented separately jsr popptr1 ; get src jsr popax ; get dest @@ -26,9 +26,9 @@ ldx tmp1 ; Load low byte of ones complement of size ldy #$00 -L1: inx +L1: dex bne L2 - inc tmp2 + dec tmp2 beq L9 L2: lda (ptr1),y ; Copy one character @@ -42,7 +42,7 @@ L2: lda (ptr1),y ; Copy one character ; Fill the remaining bytes. -L3: inx ; Counter low byte +L3: dex ; Counter low byte beq L6 ; Branch on overflow L4: sta (ptr2),y ; Clear one byte L5: iny ; Bump pointer @@ -52,7 +52,7 @@ L5: iny ; Bump pointer ; Bump the counter high byte -L6: inc tmp2 +L6: dec tmp2 bne L4 ; Done, return dest diff --git a/libsrc/common/strnicmp.s b/libsrc/common/strnicmp.s index 43d6d0d50..f447b9683 100644 --- a/libsrc/common/strnicmp.s +++ b/libsrc/common/strnicmp.s @@ -15,17 +15,11 @@ _strnicmp: _strncasecmp: -; Convert the given counter value in a/x from a downward counter into an -; upward counter, so we can increment the counter in the loop below instead -; of decrementing it. This adds some overhead now, but is cheaper than -; executing a more complex test in each iteration of the loop. We do also -; correct the value by one, so we can do the test on top of the loop. - - eor #$FF - sta ptr3 - txa - eor #$FF - sta ptr3+1 + inx + stx ptr3+1 + tax + inx + stx ptr3 ; save count with each byte incremented separately ; Get the remaining arguments @@ -40,7 +34,7 @@ _strncasecmp: ; Start of compare loop. Check the counter. -Loop: inc ptr3 +Loop: dec ptr3 beq IncHi ; increment high byte ; Compare a byte from the strings @@ -79,7 +73,7 @@ L2: ldx tmp1 ; Increment hi byte -IncHi: inc ptr3+1 +IncHi: dec ptr3+1 bne Comp ; jump if counter not zero ; Exit code if strings are equal. a/x not set diff --git a/libsrc/conio/vcprintf.s b/libsrc/conio/vcprintf.s index 3a9ddf9d7..084efe089 100644 --- a/libsrc/conio/vcprintf.s +++ b/libsrc/conio/vcprintf.s @@ -47,12 +47,12 @@ outdesc: ; Static outdesc structure out: jsr popax ; count sta ptr2 - eor #$FF - sta outdesc+6 - txa - sta ptr2+1 - eor #$FF - sta outdesc+7 + stx ptr2+1 + inx + stx outdesc+7 + tax + inx + stx outdesc+6 jsr popptr1 ; buf @@ -74,7 +74,7 @@ out: jsr popax ; count ; Loop outputting characters -@L1: inc outdesc+6 +@L1: dec outdesc+6 beq @L4 @L2: ldy tmp1 lda (ptr1),y @@ -85,7 +85,7 @@ out: jsr popax ; count jsr _cputc jmp @L1 -@L4: inc outdesc+7 +@L4: dec outdesc+7 bne @L2 rts diff --git a/libsrc/geos-common/drivers/fio_module.s b/libsrc/geos-common/drivers/fio_module.s index 0be5015a7..937ef292e 100644 --- a/libsrc/geos-common/drivers/fio_module.s +++ b/libsrc/geos-common/drivers/fio_module.s @@ -94,11 +94,12 @@ _read: ; popax - fd, must be == to the above one ; return -1+__oserror or number of bytes read - eor #$ff - sta ptr1 - txa - eor #$ff - sta ptr1+1 ; -(# of bytes to read)-1 + inx + stx ptr1+1 + tax + inx + stx ptr1 ; save count with each byte incremented separately + jsr popax sta ptr2 stx ptr2+1 ; buffer ptr @@ -152,9 +153,9 @@ _read: beq @done ; yes, we're done jmp __mappederrno ; no, we're screwed -@L3: inc ptr1 ; decrement the count +@L3: dec ptr1 ; decrement the count bne @L0 - inc ptr1+1 + dec ptr1+1 bne @L0 @done: diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index ed2ade222..efb480cb8 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -50,16 +50,17 @@ LINEDIST = $20 ; Offset in video RAM between two lines ldx #>load_addr sta load stx load+1 - lda #load_size - eor #$FF - sta count+1 -L1: inc count ; pre-count one's-complement upwards + ldx #load_size + inx + stx count+1 ; save size with each byte incremented separately + +L1: dec count ; pre-count one's-complement upwards bnz L2 - inc count+1 + dec count+1 bze L3 L2: jsr GETCHAR ; (doesn't change .Y) sta (load),y diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s index 68aef42d6..215db3e52 100644 --- a/libsrc/telestrat/write.s +++ b/libsrc/telestrat/write.s @@ -13,11 +13,11 @@ sta ptr3 stx ptr3+1 ; save count as result - eor #$FF - sta ptr2 - txa - eor #$FF - sta ptr2+1 ; remember -count-1 + inx + stx ptr2+1 + tax + inx + stx ptr2 ; save count with each byte incremented separately jsr popptr1 ; get buf jsr popax ; get fd and discard @@ -51,9 +51,9 @@ next: rts -L1: inc ptr2 +L1: dec ptr2 bne L2 - inc ptr2+1 + dec ptr2+1 beq L9 L2: ldy #0 lda (ptr1),y From 6201300816b3c6bcd9cd688f40c0f34b10d3584e Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Tue, 3 Nov 2020 11:52:58 +0100 Subject: [PATCH 488/806] Fold constant calculation. --- libsrc/osic1p/bootstrap.s | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index efb480cb8..031c4f651 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -51,11 +51,9 @@ LINEDIST = $20 ; Offset in video RAM between two lines sta load stx load+1 - ldx #load_size - inx + ldx #(>load_size) + 1 stx count+1 ; save size with each byte incremented separately L1: dec count ; pre-count one's-complement upwards From db312049503dc0effa872d66467b4f36de11831b Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Tue, 3 Nov 2020 11:53:56 +0100 Subject: [PATCH 489/806] Remove stale comment. --- libsrc/osic1p/bootstrap.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/osic1p/bootstrap.s b/libsrc/osic1p/bootstrap.s index 031c4f651..e88e257fd 100644 --- a/libsrc/osic1p/bootstrap.s +++ b/libsrc/osic1p/bootstrap.s @@ -56,7 +56,7 @@ LINEDIST = $20 ; Offset in video RAM between two lines ldx #(>load_size) + 1 stx count+1 ; save size with each byte incremented separately -L1: dec count ; pre-count one's-complement upwards +L1: dec count bnz L2 dec count+1 bze L3 From 9d62abb7ac19e2e117d7d162cb4d73482bca06ca Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Tue, 3 Nov 2020 11:54:50 +0100 Subject: [PATCH 490/806] Fix comment. --- libsrc/common/strnicmp.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/common/strnicmp.s b/libsrc/common/strnicmp.s index f447b9683..477aa3d66 100644 --- a/libsrc/common/strnicmp.s +++ b/libsrc/common/strnicmp.s @@ -34,8 +34,8 @@ _strncasecmp: ; Start of compare loop. Check the counter. -Loop: dec ptr3 - beq IncHi ; increment high byte +Loop: dec ptr3 ; decrement high byte + beq IncHi ; Compare a byte from the strings From 9800555bbb399decb5aaf50260a831076517332d Mon Sep 17 00:00:00 2001 From: Sven Michael Klose Date: Fri, 20 Nov 2020 03:50:19 +0100 Subject: [PATCH 491/806] Remove stale comments. --- libsrc/common/strncpy.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/common/strncpy.s b/libsrc/common/strncpy.s index 49bd90b32..138413ecb 100644 --- a/libsrc/common/strncpy.s +++ b/libsrc/common/strncpy.s @@ -24,7 +24,7 @@ ; Copy src -> dest up to size bytes - ldx tmp1 ; Load low byte of ones complement of size + ldx tmp1 ldy #$00 L1: dex bne L2 @@ -43,7 +43,7 @@ L2: lda (ptr1),y ; Copy one character ; Fill the remaining bytes. L3: dex ; Counter low byte - beq L6 ; Branch on overflow + beq L6 L4: sta (ptr2),y ; Clear one byte L5: iny ; Bump pointer bne L3 From d90cd112125a0a51c286b019c4e76f2131499683 Mon Sep 17 00:00:00 2001 From: Greg King Date: Sun, 27 Dec 2020 18:22:12 -0500 Subject: [PATCH 492/806] Fixed outdated comments. --- libsrc/common/memcmp.s | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libsrc/common/memcmp.s b/libsrc/common/memcmp.s index 5879a433d..d651a3cfc 100644 --- a/libsrc/common/memcmp.s +++ b/libsrc/common/memcmp.s @@ -10,8 +10,8 @@ _memcmp: -; Calculate (-count-1) and store it into ptr3. This is some overhead here but -; saves time in the compare loop +; Calculate a special count, and store it into ptr3. That is some overhead here, +; but saves time in the compare loop inx stx ptr3+1 @@ -29,12 +29,12 @@ _memcmp: ; Loop initialization ;ldy #$00 ; Initialize pointer (Y=0 guaranteed by popptr1) - ldx ptr3 ; Load low counter byte into X + ldx ptr3 ; Load inner counter byte into .X ; Head of compare loop: Test for the end condition -Loop: dex ; Bump low byte of (-count-1) - beq BumpHiCnt ; Jump on overflow +Loop: dex + beq BumpHiCnt ; Jump on end of inner count ; Do the compare @@ -50,10 +50,10 @@ Comp: lda (ptr1),y inc ptr2+1 bne Loop ; Branch always (pointer wrap is illegal) -; Entry on low counter byte overflow +; Entry on inner loop end BumpHiCnt: - dec ptr3+1 ; Bump high byte of (-count-1) + dec ptr3+1 bne Comp ; Jump if not done jmp return0 ; Count is zero, areas are identical From a9b71b6207c837833aacda2e5252dac4fd8fa8e5 Mon Sep 17 00:00:00 2001 From: Rocky Date: Fri, 1 Jan 2021 15:39:23 +0100 Subject: [PATCH 493/806] return-type - new warning suppression type added --- src/cc65/error.c | 2 ++ src/cc65/error.h | 1 + src/cc65/function.c | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cc65/error.c b/src/cc65/error.c index 132bf331d..feb43565d 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -74,6 +74,7 @@ IntStack WarnUnknownPragma = INTSTACK(1); /* - unknown #pragmas */ IntStack WarnUnusedLabel = INTSTACK(1); /* - unused labels */ IntStack WarnUnusedParam = INTSTACK(1); /* - unused parameters */ IntStack WarnUnusedVar = INTSTACK(1); /* - unused variables */ +IntStack WarnReturnType = INTSTACK(1); /* - control reaches end of non-void function */ /* Map the name of a warning to the intstack that holds its state */ typedef struct WarnMapEntry WarnMapEntry; @@ -92,6 +93,7 @@ static WarnMapEntry WarnMap[] = { { &WarnUnusedLabel, "unused-label" }, { &WarnUnusedParam, "unused-param" }, { &WarnUnusedVar, "unused-var" }, + { &WarnReturnType, "return-type" }, }; Collection DiagnosticStrBufs; diff --git a/src/cc65/error.h b/src/cc65/error.h index a443aeff8..898793651 100644 --- a/src/cc65/error.h +++ b/src/cc65/error.h @@ -71,6 +71,7 @@ extern IntStack WarnUnknownPragma; /* - unknown #pragmas */ extern IntStack WarnUnusedLabel; /* - unused labels */ extern IntStack WarnUnusedParam; /* - unused parameters */ extern IntStack WarnUnusedVar; /* - unused variables */ +extern IntStack WarnReturnType; /* - control reaches end of non-void function */ /* Forward */ struct StrBuf; diff --git a/src/cc65/function.c b/src/cc65/function.c index 00755ae65..9269d4c62 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -654,8 +654,8 @@ void NewFunc (SymEntry* Func, FuncDesc* D) ** environment returning int, output a warning if we didn't see a return ** statement. */ - if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc) { - Warning ("Control reaches end of non-void function"); + if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc) && !C99MainFunc && IS_Get (&WarnReturnType)) { + Warning ("Control reaches end of non-void function [-Wreturn-type]"); } /* If this is the main function in a C99 environment returning an int, let From 96624699577c7497fda3592088f3928867dff381 Mon Sep 17 00:00:00 2001 From: Piotr Kaczorowski Date: Mon, 4 Jan 2021 22:05:55 +0100 Subject: [PATCH 494/806] Return-type warning and pseudo variable __A__ documentation added. --- doc/cc65.sgml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/cc65.sgml b/doc/cc65.sgml index e273ced9c..211aa21b3 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -549,6 +549,8 @@ Here is a description of all the command line options: Warn about a / that changes a character's code number from/to 0x00. + + Warn about no return statement in function returning non-void.. Warn when passing structs by value. @@ -726,6 +728,29 @@ This cc65 version has some extensions to the ISO C standard. will give the high byte of any unsigned value.

+ There is pseudo variable named