diff --git a/.travis.yml b/.travis.yml index 0b25e5d16..4b0919a10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,14 +9,14 @@ jobs: - 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 -j2 bin USER_CFLAGS=-Werror + - make -j2 lib QUIET=1 - make test QUIET=1 - - make samples + - make -j2 samples - make -C src clean - - make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- + - make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32- - make -C samples clean - - make doc zip + - make -j2 doc zip after_success: - make -f Makefile.travis diff --git a/README.md b/README.md index 76b4cfcac..2a31d42be 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [Wiki](https://github.com/cc65/wiki/wiki) -[![Build Status](https://api.travis-ci.org/cc65/cc65.svg?branch=master)](https://travis-ci.org/cc65/cc65/builds) +[![Build Status](https://app.travis-ci.com/cc65/cc65.svg?branch=master)](https://app.travis-ci.com/cc65/cc65) cc65 is a complete cross development package for 65(C)02 systems, including a powerful macro assembler, a C compiler, linker, librarian and several diff --git a/asminc/atari.inc b/asminc/atari.inc index 7c46b3252..c2a210ea0 100644 --- a/asminc/atari.inc +++ b/asminc/atari.inc @@ -924,7 +924,7 @@ KEY_QUESTIONMARK = KEY_SLASH | KEY_SHIFT KEY_CLEAR = KEY_LESSTHAN | KEY_SHIFT KEY_INSERT = KEY_GREATERTHAN | KEY_SHIFT -KEY_UP = KEY_UNDERLINE | KEY_CTRL +KEY_UP = KEY_DASH | KEY_CTRL KEY_DOWN = KEY_EQUALS | KEY_CTRL KEY_LEFT = KEY_PLUS | KEY_CTRL KEY_RIGHT = KEY_ASTERISK | KEY_CTRL diff --git a/asminc/c64.inc b/asminc/c64.inc index 2cfc50db4..d131c7860 100644 --- a/asminc/c64.inc +++ b/asminc/c64.inc @@ -77,6 +77,8 @@ VIC_SPR_EXP_Y := $D017 VIC_SPR_EXP_X := $D01D VIC_SPR_MCOLOR := $D01C VIC_SPR_BG_PRIO := $D01B +VIC_SPR_COLL := $D01E +VIC_SPR_BG_COLL := $D01F VIC_SPR_MCOLOR0 := $D025 VIC_SPR_MCOLOR1 := $D026 diff --git a/asminc/creativision.inc b/asminc/creativision.inc index 49d55a342..a0259ecce 100644 --- a/asminc/creativision.inc +++ b/asminc/creativision.inc @@ -5,21 +5,21 @@ ;** Screen SCREEN_ROWS = 24 SCREEN_COLS = 32 -SCREEN_PTR = $3A -CURSOR_X = $3C -CURSOR_Y = $3D +SCREEN_PTR := $3A +CURSOR_X := $3C +CURSOR_Y := $3D ;** VDP -VDP_DATA_R = $2000 -VDP_STATUS_R = $2001 -VDP_DATA_W = $3000 -VDP_CONTROL_W = $3001 +VDP_DATA_R := $2000 +VDP_STATUS_R := $2001 +VDP_DATA_W := $3000 +VDP_CONTROL_W := $3001 ;** PIA -PIA0_DATA = $1000 -PIA0_STATUS = $1001 -PIA1_DATA = $1002 -PIA1_STATUS = $1003 +PIA0_DATA := $1000 +PIA0_STATUS := $1001 +PIA1_DATA := $1002 +PIA1_STATUS := $1003 ;** General CH_VLINE = 33 @@ -30,11 +30,11 @@ CH_LLCORNER = 37 CH_LRCORNER = 38 ;** I/O (Zero-page variables) -ZP_KEYBOARD = $10 -ZP_JOY0_DIR = $11 -ZP_JOY1_DIR = $13 -ZP_JOY0_BUTTONS = $16 -ZP_JOY1_BUTTONS = $17 +ZP_KEYBOARD := $10 +ZP_JOY0_DIR := $11 +ZP_JOY1_DIR := $13 +ZP_JOY0_BUTTONS := $16 +ZP_JOY1_BUTTONS := $17 ;** Joystick direction values (ZP_JOY0_DIR/ZP_JOY1_DIR) JOY_N = $49 @@ -54,8 +54,13 @@ JOY_WNW = $4C JOY_NW = $4B JOY_NNW = $4A -;** BIOS -BIOS_IRQ1_ADDR = $FF3F -BIOS_IRQ2_ADDR = $FF52 -BIOS_NMI_RESET_ADDR = $F808 -BIOS_WRITE_VDP_REG = $FE1F +;** BIOS routines +BIOS_NMI_RESET_ADDR := $F808 +BIOS_PLAY_TUNE1 := $FBD6 +BIOS_PLAY_SONG := $FBED +BIOS_PLAY_TUNE2 := $FCE6 +BIOS_WRITE_VDP_REG := $FE1F +BIOS_QUIET_PSG := $FE54 +BIOS_POKE_PSG := $FE77 +BIOS_IRQ1_ADDR := $FF3F +BIOS_IRQ2_ADDR := $FF52 diff --git a/asminc/stdio.inc b/asminc/stdio.inc index c727e8d0b..426389de3 100644 --- a/asminc/stdio.inc +++ b/asminc/stdio.inc @@ -44,7 +44,9 @@ EOF = -1 .if .defined(__APPLE2__) FILENAME_MAX = 64+1 .elseif .defined(__ATARI__) -FILENAME_MAX = 12+1 +FILENAME_MAX = 63+1 +.elseif .defined(__CBM__) +FILENAME_MAX = 255 .elseif .defined(__LUNIX__) FILENAME_MAX = 80+1 .elseif .defined(__TELESTRAT__) diff --git a/asminc/vic20.inc b/asminc/vic20.inc index 41a935238..7184ab6ee 100644 --- a/asminc/vic20.inc +++ b/asminc/vic20.inc @@ -90,7 +90,7 @@ VIA1_T1LH := VIA1+$7 ; Timer 1 latch, high byte VIA1_T2CL := VIA1+$8 ; Timer 2, low byte VIA1_T2CH := VIA1+$9 ; Timer 2, high byte VIA1_SR := VIA1+$A ; Shift register -VIA1_CR := VIA1+$B ; Auxiliary control register +VIA1_ACR := VIA1+$B ; Auxiliary control register VIA1_PCR := VIA1+$C ; Peripheral control register VIA1_IFR := VIA1+$D ; Interrupt flag register VIA1_IER := VIA1+$E ; Interrupt enable register @@ -112,7 +112,7 @@ VIA2_T1LH := VIA2+$7 ; Timer 1 latch, high byte VIA2_T2CL := VIA2+$8 ; Timer 2, low byte VIA2_T2CH := VIA2+$9 ; Timer 2, high byte VIA2_SR := VIA2+$A ; Shift register -VIA2_CR := VIA2+$B ; Auxiliary control register +VIA2_ACR := VIA2+$B ; Auxiliary control register VIA2_PCR := VIA2+$C ; Peripheral control register VIA2_IFR := VIA2+$D ; Interrupt flag register VIA2_IER := VIA2+$E ; Interrupt enable register diff --git a/cfg/c128-asm.cfg b/cfg/c128-asm.cfg new file mode 100644 index 000000000..0da296c71 --- /dev/null +++ b/cfg/c128-asm.cfg @@ -0,0 +1,20 @@ +FEATURES { + STARTADDRESS: default = $1c01; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $00FE, define = yes; + LOADADDR: file = %O, start = %S - 2, size = $0002; + MAIN: file = %O, start = %S, size = $D000 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = rw; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/cfg/vic20-asm.cfg b/cfg/vic20-asm.cfg new file mode 100644 index 000000000..7ab70888c --- /dev/null +++ b/cfg/vic20-asm.cfg @@ -0,0 +1,19 @@ +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0002, size = $001A, define = yes; + LOADADDR: file = %O, start = $1001, size = $0002; + MAIN: file = %O, start = %S, size = $0DF3 - %S; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + LOADADDR: load = LOADADDR, type = ro; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = MAIN, type = bss, optional = yes, define = yes; +} diff --git a/doc/apple2.sgml b/doc/apple2.sgml index f957e1247..bd01b68dc 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 4aafbc256..56fc05e31 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of Driver for the Apple II Super Serial Card. Supports up to 19200 baud, - hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not + requires hardware flow control (RTS/CTS) and does interrupt driven receives. + Note that because of the peculiarities of the 6551 chip transmits are not interrupt driven, and the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/atari.sgml b/doc/atari.sgml index f2ced13e1..903895d17 100644 --- a/doc/atari.sgml +++ b/doc/atari.sgml @@ -675,9 +675,9 @@ The default callbacks definition (RS232 device drivers

-Currently there is one RS232 driver. It uses the R: device (therefore -an R: driver needs to be installed) and was tested with the 850 -interface module. +Currently there is one RS232 driver. It supports up to 9600 baud, requires hardware flow control +(RTS/CTS) and uses the R: device (therefore an R: driver needs to be installed). It was tested +with the 850 interface module. diff --git a/doc/atmos.sgml b/doc/atmos.sgml index 3fd61abcf..cef7770e4 100644 --- a/doc/atmos.sgml +++ b/doc/atmos.sgml @@ -176,10 +176,11 @@ No mouse drivers are currently available for the Atmos. Driver for the Telestrat integrated serial controller and the Atmos with a - serial add-on. - Note that, because of the peculiarities of the 6551 chip, together with the - use of the NMI, transmits are not interrupt driven; and, the transceiver - blocks if the receiver asserts flow control because of a full buffer. + serial add-on. Supports up to 19200 baud, requires hardware flow control + (RTS/CTS) and does interrupt driven receives. Note that, because of the + peculiarities of the 6551 chip, together with the use of the NMI, transmits + are not interrupt driven; and, the transceiver blocks if the receiver + asserts flow control because of a full buffer.

diff --git a/doc/c128.sgml b/doc/c128.sgml index 8c62b6ad1..60306814c 100644 --- a/doc/c128.sgml +++ b/doc/c128.sgml @@ -324,9 +324,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/c64.sgml b/doc/c64.sgml index 7e00f3b93..de37ed4b7 100644 --- a/doc/c64.sgml +++ b/doc/c64.sgml @@ -410,9 +410,9 @@ The default drivers, - Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow - control (RTS/CTS), and interrupt-driven receives. Note that, because of the - peculiarities of the 6551 chip, together with the use of the NMI, transmits + Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware + flow control (RTS/CTS) and does interrupt driven receives. Note that, because of + the peculiarities of the 6551 chip, together with the use of the NMI, transmits are not interrupt driven; and, the transceiver blocks if the receiver asserts flow control because of a full buffer. diff --git a/doc/cbm510.sgml b/doc/cbm510.sgml index c208f3ead..86bed7607 100644 --- a/doc/cbm510.sgml +++ b/doc/cbm510.sgml @@ -231,10 +231,10 @@ The default drivers, Driver for the 6551 ACIA chip built into the Commodore 510. Supports up to - 19200 BPS, hardware flow control (RTS/CTS), and interrupt-driven receives. - Note that, because of the peculiarities of the 6551 chip, transmits are not - interrupt driven; and, the transceiver blocks if the receiver asserts flow - control because of a full buffer. + 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that, because of the peculiarities of the 6551 chip, transmits + are not interrupt driven; and, the transceiver blocks if the receiver asserts + flow control because of a full buffer.

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

diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 4d1a278b0..e6cb42a0f 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -298,6 +298,16 @@ function. +

+ + + + + + + + +

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

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

@@ -1778,7 +1826,7 @@ be used in presence of a prototype. The function is specific to the C128. The function will not return to the caller. - @@ -3410,7 +3458,7 @@ loaded. / - / - +psg_delay

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

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

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

@@ -6198,7 +6318,7 @@ while (ser_get(&ch) == SER_ERR_NO_DATA) / - Gamate System specific information for cc65 <author> -<url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen"> +<url url="mailto:groepaz@gmx.net" name="Groepaz"> <abstract> An overview over the Gamate runtime system as it is implemented for the @@ -117,14 +117,7 @@ following functions (and a few others): <sect>Other hints<p> <itemize> -<item>The Gamate is emulated by MESS (<url url="http://www.mess.org/">), -run like this: <tt>mess gamate -debug -window -skip_gameinfo -cart test.bin</tt> -</itemize> - -some resources on the Gamate: - -<itemize> -<item><url url="http://en.wikipedia.org/wiki/Gamate"> +<item>some resources on the Gamate: <url url="http://en.wikipedia.org/wiki/Gamate"> </itemize> <sect>License<p> diff --git a/doc/geos.sgml b/doc/geos.sgml index 11ff9c534..f7943f8cd 100644 --- a/doc/geos.sgml +++ b/doc/geos.sgml @@ -205,7 +205,7 @@ see them together in the filling box in GeoPaint. <sect2>GraphicsString <p> -<tt/void GraphicsString (char *myGString)/ +<tt/void GraphicsString (const void *myGString)/ <p> One of the more powerfull routines of GEOS. This function calls other graphic functions depending on the given command string. See the structures chapter for a more detailed description. diff --git a/doc/intro.sgml b/doc/intro.sgml index b2b141d10..0617b0ab3 100644 --- a/doc/intro.sgml +++ b/doc/intro.sgml @@ -6,6 +6,7 @@ <url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline> <url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline> +<url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:stephan.muehlstrasser@web.de" name="Stephan Mühlstrasser"> <abstract> @@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/<sys>/: Start the desired version of the emulator (CBM610 programs run on the CBM II [<tt/xcbm2/] emulator). -In the Windows versions of VICE, choose <bf>File>Autoboot disk/tape -image...</bf>, choose your executable, and click <bf/OK/. - -In the Unix versions, hold down the mouse's first button. Move the pointer to -<bf>Smart-attach disk/tape...</bf>, and release the button. Choose your -executable, and click <bf/Autostart/. +Choose <bf>File>Autostart disk/tape image...</bf>, choose your executable, +and click <bf/OK/. The file has a 14-byte header which corresponds to a PRG-format BASIC program, consisting of a single line, similar to this: @@ -499,6 +496,29 @@ The output will appear on a separate line, and you will be returned to a BASIC prompt. +<sect1>Gamate<p> + +Before you can run the cartridge image produced by the linker, the binary has to +be patched using the <bf/gamate-fixcart/ tool that is included in the cc65 +package in the util/gamata directory. + +<tscreen><verb> +gamate-fixcart <image.bin> +</verb></tscreen> + +<sect2>MESS<p> +Available at <url +url="https://www.mamedev.org">: + +MESS (Multiple Emulator Super System) is a multi system emulator that emulates +various cc65 targets. It once started as a MAME fork, but was marged into MAME +again at some point. + +<tscreen><verb> +mess gamate -debug -window -skip_gameinfo -cart <image.bin> +</verb></tscreen> + + <sect1>GEOS<p> Available at <it/Click Here Software's/ <url url="http://cbmfiles.com/geos/index.html" name="GEOS download section">: @@ -535,17 +555,8 @@ feature on. </quote> <quote> -VICE even has different ways that depend on which operating system is running -the emulator. -<itemize> -<item>In Windows, you must click on <bf/Options/ (in an always visible menu). - Then, you must click on <bf/True drive emulation/. -<item>In Unix, you must <em/hold down/ the second button on your mouse. Move - the pointer down to <bf/Drive settings/. Then, move the pointer over to - <bf/Enable true drive emulation/. (If there is a check-mark in front of - those words, that feature already is turned on -- then, move the pointer - off of that menu.) Release the mouse button. -</itemize> +In VICE, got to <bf/Settings/ -> <bf/Settings/, then <bf/Peripherial devices/ -> +<bf/Drive/. Then, you must enable the <bf/True drive emulation/ checkbox. </quote> Find the <bf/CONVERT/ program on the boot disk [tap the 6-key; then, you @@ -572,6 +583,29 @@ directory notePad. Look at the eight file-positions on each page until you see The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished reading it. +Alternatively you can use the <bf/c1541/ program that comes with VICE to write the +file to a disk image directly in GEOS format, so it can be used in GEOS directly +without having to use the <bf/CONVERT/ program. + +<tscreen><verb> +c1541 -attach geos.d64 -geoswrite hello1 +</verb></tscreen> + + +<sect1>Nintendo Entertainment System<p> + +<sect2>Mednafen (NES)<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module nes <image.bin> +</verb></tscreen> + <sect1>Ohio Scientific Challenger 1P<p> The <tt/osic1p/ runtime library returns to the boot prompt when the main() @@ -694,6 +728,32 @@ Press <RETURN>. After hitting the RETURN key, you should see the boot prompt again. +<sect1>PC Engine/TurboGrafx 16<p> + +For the cartridge image produced by the linker to work in emulators and on real +hardware, its content must be rearranged so the first 8k block becomes the last +8k block in the image. + +For example, for a 32k image this can be done using <bf/dd/ as follows: + +<tscreen><verb> +dd if=infile.bin bs=8K skip=3 > outfile.pce +dd if=infile.bin bs=8K count=3 >> outfile.pce +</verb></tscreen> + +<sect2>Mednafen<p> +Available at <url +url="https://mednafen.github.io/releases/">: + +Mednafen is a multi system emulator that emulates a couple of the supported +targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and +PC Engine/TurboGrafx 16. + +<tscreen><verb> +mednafen -force_module pce <image.pce> +</verb></tscreen> + + <sect1>Contributions wanted<p> We need your help! Recommended emulators and instructions for other targets diff --git a/doc/library.sgml b/doc/library.sgml index 303809c59..bcd5db86e 100644 --- a/doc/library.sgml +++ b/doc/library.sgml @@ -59,6 +59,9 @@ Functions that are <em/not/ available: Functions not available on all supported systems: <itemize> + <item><tt>clock</tt>: Support depends on the capabilities of the target + machine. + <p> <item><tt>fopen/fread/fwrite/fclose/fputs/fgets/fscanf</tt>: The functions are built on open/read/write/close. Those latter functions are not available on all systems. diff --git a/doc/pce.sgml b/doc/pce.sgml index 42a1ca9d3..47eeabcfd 100644 --- a/doc/pce.sgml +++ b/doc/pce.sgml @@ -2,7 +2,7 @@ <article> <title>PC-Engine (TurboGrafx 16) System-specific information for cc65 -<author><url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen">,<newline> +<author><url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King"> <abstract> @@ -206,11 +206,6 @@ following functions (and a few others): <sect>Other hints<p> -<itemize> -<item><url url="https://mednafen.github.io/" name= "Mednafen"> is a good -emulator to use for the PC-Engine. -</itemize> - Some useful resources on PCE coding: <itemize> diff --git a/doc/plus4.sgml b/doc/plus4.sgml index 645de5161..79a2597d0 100644 --- a/doc/plus4.sgml +++ b/doc/plus4.sgml @@ -195,10 +195,10 @@ No mouse drivers are currently available for the Plus/4. <tag><tt/plus4-stdser.ser (plus4_stdser_ser)/</tag> Driver for the 6551 ACIA chip built into the Plus/4. Supports up to 19200 - baud, hardware flow control (RTS/CTS) and interrupt driven receives. Note - that because of the peculiarities of the 6551 chip transmits are not - interrupt driven, and the transceiver blocks if the receiver asserts flow - control because of a full buffer. + baud, requires hardware flow control (RTS/CTS) and does interrupt driven + receives. Note that because of the peculiarities of the 6551 chip transmits + are not interrupt driven, and the transceiver blocks if the receiver asserts + flow control because of a full buffer. You need an adapter to use the builtin port, since the output levels available at the user port don't follow the RS232 standard. diff --git a/doc/tgi.sgml b/doc/tgi.sgml index b1497ca70..29acd8ce6 100644 --- a/doc/tgi.sgml +++ b/doc/tgi.sgml @@ -555,7 +555,7 @@ tgi_init(); //Set up the default palette and clear the screen. <descrip> <tag/Function/Install an already loaded driver and return an error code. <tag/Header/<tt/<ref id="tgi.h" name="tgi.h">/ -<tag/Declaration/<tt/unsigned char __fastcall__ tgi_install (void* driver);/ +<tag/Declaration/<tt/unsigned char __fastcall__ tgi_install (const void* driver);/ <tag/Description/The function installs a driver that was already loaded into memory (or linked statically to the program). It returns an error code (<tt/TGI_ERR_OK/ in case of success). diff --git a/include/creativision.h b/include/creativision.h index 40b9ee03a..6910ee0cf 100644 --- a/include/creativision.h +++ b/include/creativision.h @@ -70,9 +70,9 @@ #define COLOR_WHITE 15 /* Protos */ -void __fastcall__ psg_outb(unsigned char b); -void __fastcall__ psg_delay(unsigned char b); -void psg_silence(void); -void __fastcall__ bios_playsound(void *a, unsigned char b); +void __fastcall__ psg_outb (unsigned char b); +void __fastcall__ psg_delay (unsigned char b); +void psg_silence (void); +void __fastcall__ bios_playsound (const void *a, unsigned char b); #endif /* #ifndef _CVISION_H */ diff --git a/include/em.h b/include/em.h index b151800ae..47bd23222 100644 --- a/include/em.h +++ b/include/em.h @@ -82,7 +82,7 @@ unsigned char __fastcall__ em_load_driver (const char* driver); unsigned char em_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ em_install (void* driver); +unsigned char __fastcall__ em_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char em_uninstall (void); diff --git a/include/geos/ggraph.h b/include/geos/ggraph.h index ec9fb0fa1..b550d6ca1 100644 --- a/include/geos/ggraph.h +++ b/include/geos/ggraph.h @@ -43,7 +43,7 @@ void __fastcall__ BitOtherClip(void *proc1, void *proc2, char skipl, char skipr, unsigned skiptop, struct iconpic *myIcon); -void __fastcall__ GraphicsString(char *myGfxString); +void __fastcall__ GraphicsString(const void *myGfxString); #ifdef __GEOS_CBM__ void SetNewMode(void); diff --git a/include/joystick.h b/include/joystick.h index b6771c381..963c9ba95 100644 --- a/include/joystick.h +++ b/include/joystick.h @@ -89,7 +89,7 @@ unsigned char __fastcall__ joy_load_driver (const char* driver); unsigned char joy_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ joy_install (void* driver); +unsigned char __fastcall__ joy_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char joy_uninstall (void); diff --git a/include/limits.h b/include/limits.h index 23474c78c..531c6bef2 100644 --- a/include/limits.h +++ b/include/limits.h @@ -63,6 +63,20 @@ #define ULONG_MAX 4294967295UL +/* These defines that are platform dependent */ +#if defined(__APPLE2__) +# define PATH_MAX (64+1) +#elif defined(__ATARI__) +# define PATH_MAX (63+1) +#elif defined(__CBM__) +# define PATH_MAX (255) /* should be 256+1, see libsrc/common/_cmd.s why it's not */ +#elif defined(__LUNIX__) +# define PATH_MAX (80+1) +#elif defined(__TELESTRAT__) +# define PATH_MAX (50+1) +#else +# define PATH_MAX (16+1) +#endif /* End of limits.h */ diff --git a/include/serial.h b/include/serial.h index 990f66521..58943d507 100644 --- a/include/serial.h +++ b/include/serial.h @@ -136,7 +136,7 @@ unsigned char __fastcall__ ser_load_driver (const char* driver); unsigned char ser_unload (void); /* Uninstall, then unload the currently loaded driver. */ -unsigned char __fastcall__ ser_install (void* driver); +unsigned char __fastcall__ ser_install (const void* driver); /* Install an already loaded driver. Return an error code. */ unsigned char ser_uninstall (void); diff --git a/include/stdio.h b/include/stdio.h index 73dc05bdb..916affe71 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -40,6 +40,7 @@ #include <stddef.h> #include <stdarg.h> +#include <limits.h> @@ -64,18 +65,7 @@ extern FILE* stderr; #define SEEK_SET 2 #define TMP_MAX 256 -/* Standard defines that are platform dependent */ -#if defined(__APPLE2__) -# define FILENAME_MAX (64+1) -#elif defined(__ATARI__) -# define FILENAME_MAX (12+1) -#elif defined(__LUNIX__) -# define FILENAME_MAX (80+1) -#elif defined(__TELESTRAT__) -# define FILENAME_MAX (50+1) -#else -# define FILENAME_MAX (16+1) -#endif +#define FILENAME_MAX PATH_MAX #define L_tmpnam FILENAME_MAX diff --git a/include/tgi.h b/include/tgi.h index 135ef63f1..2ef65b856 100644 --- a/include/tgi.h +++ b/include/tgi.h @@ -82,7 +82,7 @@ void tgi_unload (void); ** necessary. */ -void __fastcall__ tgi_install (void* driver); +void __fastcall__ tgi_install (const void* driver); /* Install an already loaded driver. */ void tgi_uninstall (void); diff --git a/libsrc/atari5200/crt0.s b/libsrc/atari5200/crt0.s index 9538d3bb4..8f6e02273 100644 --- a/libsrc/atari5200/crt0.s +++ b/libsrc/atari5200/crt0.s @@ -44,4 +44,4 @@ _exit: jsr donelib ; Run module destructors ; A 5200 program isn't supposed to exit. -halt: jmp halt +halt: jmp halt diff --git a/libsrc/c64/joy/c64-stdjoy.s b/libsrc/c64/joy/c64-stdjoy.s index c983d81bb..d11093fba 100644 --- a/libsrc/c64/joy/c64-stdjoy.s +++ b/libsrc/c64/joy/c64-stdjoy.s @@ -93,9 +93,7 @@ joy1: lda #$7F sta CIA1_PRA lda CIA1_PRB cli - and #$1F - eor #$1F - rts + jmp end ; Read joystick 2 @@ -107,8 +105,6 @@ joy2: ldx #0 lda CIA1_PRA sty CIA1_DDRA cli - and #$1F +end: and #$1F eor #$1F rts - - diff --git a/libsrc/creativision/joy/creativision-stdjoy.s b/libsrc/creativision/joy/creativision-stdjoy.s index 5cf46c39f..73b0c249f 100644 --- a/libsrc/creativision/joy/creativision-stdjoy.s +++ b/libsrc/creativision/joy/creativision-stdjoy.s @@ -1,11 +1,11 @@ ; ; Standard joystick driver for the Creativision. ; -; Christian Groessler, 2017-03-08 +; 2017-03-08, Christian Groessler +; 2021-06-01, Greg King ; .include "zeropage.inc" - .include "joy-kernel.inc" .include "joy-error.inc" .include "creativision.inc" @@ -13,10 +13,12 @@ .macpack module +buttons := tmp2 + ; ------------------------------------------------------------------------ ; Header. Includes jump table - module_header _creativisionstd_joy + module_header _creativisionstd_joy ; Driver signature @@ -39,16 +41,14 @@ JOY_COUNT = 2 ; Number of joysticks we support -; Symbolic names for joystick masks (similar names like the defines in joystick.h, but not related to them) +; Symbolic names for joystick masks (similar names to the macros in joystick.h, +; with the same values as the masks in creativision.h) JOY_UP = $10 JOY_DOWN = $04 JOY_LEFT = $20 JOY_RIGHT = $08 -; ------------------------------------------------------------------------ -; Code - .code ; ------------------------------------------------------------------------ @@ -59,7 +59,7 @@ JOY_RIGHT = $08 ; INSTALL: lda #JOY_ERR_OK - ldx #0 + ldx #>$0000 ; rts ; Fall through ; ------------------------------------------------------------------------ @@ -82,14 +82,14 @@ COUNT: lda #<JOY_COUNT ; READ: Read a particular joystick passed in A. ; -READJOY: and #1 ; fix joystick number - bne READJOY_1 ; read right joystick +READJOY: lsr a ; Get joystick number + bcs READJOY_1 ; Read right joystick ; Read left joystick ldx ZP_JOY0_DIR lda ZP_JOY0_BUTTONS - jmp convert ; convert joystick state to cc65 values + bcc convert ; Convert joystick state to cc65 values ; Read right joystick @@ -97,11 +97,11 @@ READJOY_1: ldx ZP_JOY1_DIR lda ZP_JOY1_BUTTONS lsr a lsr a - ;jmp convert ; convert joystick state to cc65 values - ; fall thru... + ;jmp convert ; Convert joystick state to cc65 values + ; Fall thru... ; ------------------------------------------------------------------------ -; convert: make runtime lib compatible values +; convert: make runtime lib-compatible values ; inputs: ; A - buttons ; X - direction @@ -111,24 +111,24 @@ convert: ; ------ ; buttons: -; Port values are for the left hand joystick (right hand joystick +; Port values are for the left-hand joystick (right-hand joystick ; values were shifted to the right to be identical). ; Why are there two bits indicating a pressed trigger? ; According to the "Second book of programs for the Dick Smith Wizard" -; (pg. 88ff), the left hand button gives the value of -; %00010001 and the right hand button gives %00100010 +; (pg. 88ff), the left-hand button gives the value of +; %00010001 and the right-hand button gives %00100010 ; Why two bits? Can there be cases that just one of those bits is set? -; Until these questions have been answered, we only use the lower two -; bits and ignore the upper ones... +; Until those questions have been answered, we only use the lower two +; bits, and ignore the upper ones. - and #%00000011 ; button status came in in A, strip high bits - sta retval ; initialize 'retval' with button status + and #%00000011 ; Button status came in A, strip high bits + sta buttons ; ------ ; direction: -; CV has a 16-direction joystick +; CV has a 16-direction joystick. ; -; port values: (compass points) +; Port values: (compass points) ; N - $49 - %01001001 ; NNE - $48 - %01001000 ; NE - $47 - %01000111 @@ -147,55 +147,51 @@ convert: ; NNW - $4A - %01001010 ; center - $00 - %00000000 ; -; mapping to cc65 definitions (4-direction joystick with 8 possible directions thru combinations) +; Mapping to cc65 definitions (4-direction joystick with 8 possible directions thru combinations): ; N, E, S, W -> JOY_UP, JOY_RIGHT, JOY_DOWN, JOY_LEFT ; NE, SE, SW, NW -> (JOY_UP | JOY_RIGHT), (JOY_DOWN | JOY_RIGHT), (JOY_DOWN | JOY_LEFT), (JOY_UP | JOY_LEFT) ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW: -; toggle between straight and diagonal direction for every call, e.g. +; toggle between the straight and diagonal directions for each call, e.g., ; NNE: ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP | JOY_RIGHT -; etc... +; etc. - txa ; move direction status into A - beq done ; center position (no bits are set), nothing to do + txa ; Move direction status into A + beq done ; Center position (no bits are set), nothing to do - and #$0F ; get rid of the "$40" bit - bit bit0 ; is it a "three letter" direction (NNE, ENE, etc.)? - beq special ; yes (bit #0 is zero) + and #$0F ; Get rid of the "$40" bit + lsr a ; Is it "three-letter" direction (NNE, ENE, etc.)? + tax ; Create index into table + bcc special ; Yes (bit #0 was zero) - lsr a ; create index into table - tax lda dirtable,x -done: ora retval ; include "button" bits - ldx #0 +done: ora buttons ; Include button bits + ldx #>$0000 rts ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW -special: lsr a - tax - - lda toggler ; toggle the toggler +special: lda toggle ; Toggle the flag eor #$01 - sta toggler - bne spec_1 ; toggler is 1, use spectable_1 entry + sta toggle + bne spec_1 ; Flag is 1, use spectable_1 entry - lda spectable_0,x ; toggler is 0, use spectable_0 entry - bne done ; jump always + lda spectable_0,x + bne done ; Jump always spec_1: lda spectable_1,x - bne done ; jump always + bne done ; Jump always ; ------------------------------------------------------------------------ ; .rodata - ; a mapping table of "port values" to "cc65 values" - ; port value had been shifted one bit to the right (range 0..7) + ; A mapping table of "port values" to "cc65 values" + ; Port value had been shifted one bit to the right (range 0..7) dirtable: .byte JOY_DOWN ; S .byte JOY_DOWN | JOY_RIGHT ; SE .byte JOY_RIGHT ; E @@ -205,12 +201,12 @@ dirtable: .byte JOY_DOWN ; S .byte JOY_LEFT ; W .byte JOY_DOWN | JOY_LEFT ; SW - ; two "special" mapping tables for three-letter directions (NNE, etc.) + ; Two "special" mapping tables for three-letter directions (NNE, etc.) spectable_0: .byte JOY_DOWN ; SSW .byte JOY_DOWN ; SSE .byte JOY_RIGHT ; ESE .byte JOY_RIGHT ; ENE - .byte JOY_RIGHT ; NNE + .byte JOY_UP ; NNE .byte JOY_UP ; NNW .byte JOY_LEFT ; WNW .byte JOY_LEFT ; WSW @@ -224,14 +220,10 @@ spectable_1: .byte JOY_DOWN | JOY_LEFT ; SSW .byte JOY_UP | JOY_LEFT ; WNW .byte JOY_DOWN | JOY_LEFT ; WSW -; ------------------------------------------------------------------------ -; -bit0: .byte $01 - ; ------------------------------------------------------------------------ ; .bss -toggler: .res 1 -retval: .res 1 + +toggle: .res 1 .end diff --git a/libsrc/creativision/playsound.s b/libsrc/creativision/playsound.s new file mode 100644 index 000000000..55c7a3fed --- /dev/null +++ b/libsrc/creativision/playsound.s @@ -0,0 +1,40 @@ +; void __fastcall__ bios_playsound (void *a, unsigned char b); + + + .export _bios_playsound + + .import popax + + .include "creativision.inc" + + +songptr := $00 ; Points to current tune data +volptr := $04 ; Points to current volume table + + +;* Creativision Sound Player +;* Based on BIOS song player. +;* +;* Pass a pointer to a set of note triples, terminated with a tempo byte; +;* and pass the length of the triples and tempo (max 255). +;* +;* Note: tune data must be stored backwards. + +_bios_playsound: + php + pha ; Save tune length + sei + + lda #<$FCD5 ; BIOS decreasing-volume table + ldx #>$FCD5 + sta volptr + stx volptr+1 + + jsr popax ; Get tune array pointer + sta songptr + stx songptr+1 + + pla + tay + dey ; Point to tempo byte + jmp BIOS_PLAY_SONG ; Let BIOS do its thing diff --git a/libsrc/creativision/psg.s b/libsrc/creativision/psg.s index a32e87f02..ec878af31 100644 --- a/libsrc/creativision/psg.s +++ b/libsrc/creativision/psg.s @@ -1,63 +1,30 @@ -; void __fastcall__ psg_outb( unsigned char b ); -; void __fastcall__ psg_delayms( unsigned char c); -; void __fastcall__ bios_playsound( void *b, unsigned char c); -; void psg_silence( void ); +; void __fastcall__ psg_outb (unsigned char b); +; void __fastcall__ psg_delay (unsigned char b); +; void psg_silence (void); + + + .export _psg_outb, _psg_silence, _psg_delay - .export _psg_outb, _psg_silence, _psg_delay - .export _bios_playsound - .import popa .include "creativision.inc" -_psg_outb: - ;* Let BIOS output the value - jmp $FE77 +;* Let BIOS output the value. +_psg_outb := BIOS_POKE_PSG -_psg_silence: - jmp $FE54 +_psg_silence := BIOS_QUIET_PSG _psg_delay: - tay -l1: lda #200 -l2: sbc #1 - bne l2 +l1: lda #200 +l2: sbc #1 + bne l2 - lda #200 -l3: sbc #1 - bne l3 + lda #200 +l3: sbc #1 + bne l3 dey - bne l1 - + bne l1 rts - - -;* Creativision Sound Player -;* -;* Based on BIOS sound player. -;* Pass a pointer to a set of note triples, terminated with a tempo byte -;* and the len (max 255) - -_bios_playsound: - - pha ; Save Length Byte - sei - - lda #$D5 ; BIOS volume table low - sta $4 - lda #$FC ; BIOS volume table high - sta $5 - - jsr popa ; Get Sound table pointer low - sta $0 - jsr popa ; Get Sound table pointer high - sta $1 - - pla - tay ; Put length in Y - dey - php - jmp $FBED ; Let BIOS do it's thing diff --git a/libsrc/em/em-kernel.s b/libsrc/em/em-kernel.s index c982dac88..d5be0ae23 100644 --- a/libsrc/em/em-kernel.s +++ b/libsrc/em/em-kernel.s @@ -36,7 +36,7 @@ emd_sig: .byte $65, $6d, $64, EMD_API_VERSION ; "emd", version ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ em_install (void* driver); +; unsigned char __fastcall__ em_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/geos-common/graph/graphicsstring.s b/libsrc/geos-common/graph/graphicsstring.s index 86c74f1a6..282649284 100644 --- a/libsrc/geos-common/graph/graphicsstring.s +++ b/libsrc/geos-common/graph/graphicsstring.s @@ -3,7 +3,7 @@ ; ; 25.12.99 -; void GraphicsString (char *myString); +; void GraphicsString (const void *myString); .export _GraphicsString diff --git a/libsrc/joystick/joy-kernel.s b/libsrc/joystick/joy-kernel.s index c2d50c8d8..53d475c57 100644 --- a/libsrc/joystick/joy-kernel.s +++ b/libsrc/joystick/joy-kernel.s @@ -33,7 +33,7 @@ joy_sig: .byte $6A, $6F, $79, JOY_API_VERSION ; "joy", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ joy_install (void* driver); +; unsigned char __fastcall__ joy_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/serial/ser-kernel.s b/libsrc/serial/ser-kernel.s index 4c5b455b6..b6c57a3b5 100644 --- a/libsrc/serial/ser-kernel.s +++ b/libsrc/serial/ser-kernel.s @@ -39,7 +39,7 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version .code ;---------------------------------------------------------------------------- -; unsigned char __fastcall__ ser_install (void* driver); +; unsigned char __fastcall__ ser_install (const void* driver); ; /* Install the driver once it is loaded */ diff --git a/libsrc/supervision/crt0.s b/libsrc/supervision/crt0.s index 203c681b8..fbae1fc46 100644 --- a/libsrc/supervision/crt0.s +++ b/libsrc/supervision/crt0.s @@ -67,13 +67,13 @@ not_dma: ; Removing this segment gives only a warning. .segment "FFF0" .proc reset32kcode - lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON + lda #(6<<5) | SV_LCD_ON | SV_NMI_ENABLE_ON sta sv_bank ; Now, the 32Kbyte image can reside in the top of 64Kbyte and 128Kbyte ROMs. jmp reset .endproc - .segment "VECTOR" + .segment "VECTORS" .word nmi .word reset32kcode diff --git a/libsrc/tgi/tgi-kernel.s b/libsrc/tgi/tgi-kernel.s index 3a388b6dc..cbd655279 100644 --- a/libsrc/tgi/tgi-kernel.s +++ b/libsrc/tgi/tgi-kernel.s @@ -88,7 +88,7 @@ tgi_sig: .byte $74, $67, $69, TGI_API_VERSION ; "tgi", version .code ;---------------------------------------------------------------------------- -; void __fastcall__ tgi_install (void* driver); +; void __fastcall__ tgi_install (const void* driver); ; /* Install an already loaded driver. */ diff --git a/samples/Makefile b/samples/Makefile index 502748ea1..1dc3aef8a 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -154,7 +154,7 @@ endif # Lists of subdirectories # disasm depends on cpp -DIRLIST = tutorial geos +DIRLIST = tutorial geos atari2600 supervision cbm # -------------------------------------------------------------------------- # Lists of executables @@ -190,6 +190,9 @@ EXELIST_atarixl = $(EXELIST_atari) EXELIST_atari2600 = \ atari2600hello +EXELIST_atari5200 = \ + notavailable + EXELIST_atmos = \ ascii \ hello \ @@ -202,28 +205,22 @@ EXELIST_bbc = \ EXELIST_c64 = \ ascii \ enumdevdir \ - fire \ gunzip65 \ hello \ mandelbrot \ mousedemo \ multdemo \ - nachtm \ ovrldemo \ - plasma \ sieve \ tgidemo EXELIST_c128 = \ ascii \ enumdevdir \ - fire \ gunzip65 \ hello \ mandelbrot \ mousedemo \ - nachtm \ - plasma \ sieve \ tgidemo @@ -234,19 +231,15 @@ EXELIST_c16 = \ EXELIST_cbm510 = \ ascii \ - fire \ gunzip65 \ hello \ mousedemo \ - nachtm \ - plasma \ sieve EXELIST_cbm610 = \ ascii \ gunzip65 \ hello \ - nachtm \ sieve EXELIST_creativision = \ @@ -299,7 +292,6 @@ EXELIST_plus4 = \ enumdevdir \ gunzip65 \ hello \ - plasma \ sieve EXELIST_sim6502 = \ @@ -308,7 +300,7 @@ EXELIST_sim6502 = \ EXELIST_sim65c02 = $(EXELIST_sim6502) EXELIST_supervision = \ - supervisionhello + notavailable EXELIST_sym1 = \ notavailable @@ -347,7 +339,9 @@ samples: $(EXELIST_$(SYS)) # empty target used to skip systems that will not work with any program in this dir notavailable: - @echo "warning: generic samples not available for" $(SYS) +ifeq ($(MAKELEVEL),0) + @echo "info: generic samples not available for" $(SYS) +endif disk: $(DISK_$(SYS)) @@ -396,7 +390,7 @@ $(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$( endef # D64_WRITE_SEQ_recipe samples.d64: samples - @$(C1541) -format samples,AA d64 $@ >$(NULLDEV) + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) @@ -458,11 +452,17 @@ install: $(INSTALL) -d $(DESTDIR)$(samplesdir) $(INSTALL) -d $(DESTDIR)$(samplesdir)/geos $(INSTALL) -d $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -d $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -d $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -d $(DESTDIR)$(samplesdir)/supervision $(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos $(INSTALL) -m0644 tutorial/*.* $(DESTDIR)$(samplesdir)/tutorial + $(INSTALL) -m0644 atari2600/*.* $(DESTDIR)$(samplesdir)/atari2600 + $(INSTALL) -m0644 cbm/*.* $(DESTDIR)$(samplesdir)/cbm + $(INSTALL) -m0644 supervision/*.* $(DESTDIR)$(samplesdir)/supervision # -------------------------------------------------------------------------- # Packaging rules diff --git a/samples/atari2600/Makefile b/samples/atari2600/Makefile new file mode 100644 index 000000000..a02ec9e80 --- /dev/null +++ b/samples/atari2600/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= atari2600 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_atari2600 = \ + hello + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: atari 2600 samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_atari2600) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/atari2600hello.c b/samples/atari2600/hello.c similarity index 100% rename from samples/atari2600hello.c rename to samples/atari2600/hello.c diff --git a/samples/cbm/Makefile b/samples/cbm/Makefile new file mode 100644 index 000000000..989710932 --- /dev/null +++ b/samples/cbm/Makefile @@ -0,0 +1,164 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= c64 + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),) + ifdef CC65_HOME + TARGET_PATH = $(CC65_HOME)/target + else + TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path)) + endif + + # If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make + # has very limited support for paths containing spaces. $(wildcard) is the only function + # that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped + # spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a + # return value consisting of 12 plain words :-(( + # + # Fortunately we can work around that behaviour here because we know that the files we + # are looking for have known extensions. So we can $(filter) the in our example above 12 + # words for file extensions so we come up with 4 path fragments. Then we remove those + # path fragments with $(notdir) from the file names. + # + # So far so good. But here we want to process files from different paths in a single + # recipe further down below and therefore want to prepend the paths to the files with + # $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is). + # Therefore, we need to replace the spaces with some other character temporarily in order + # to have $(foreach) generate one invocation per file. We use the character '?' for that + # purpose here, just because it is known to not be part of file names. + # + # Inside the recipe generated per file we then replace the '?' again with a space. As we + # want to be compatible with cmd.exe for execution we're not using an escaped space but + # rather double-quote the whole path. + # + # Note: The "strange" $(wildcard) further down below just serves the purpose to unescape + # spaces for cmd.exe. This could have as well been done with another $(subst). + + SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH)) + + EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*) + MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*) + TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*) + + EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD)))) + MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU)))) + TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI)))) + + # This one comes with the VICE emulator. + # See http://vice-emu.sourceforge.net/ + C1541 ?= c1541 +endif + +DISK_c64 = samples.d64 + +EXELIST_c64 = \ + fire \ + plasma \ + nachtm + +EXELIST_c128 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm510 = \ + fire \ + plasma \ + nachtm + +EXELIST_cbm610 = \ + nachtm + +EXELIST_plus4 = \ + plasma + +EXELIST_c16 = \ + notavailable + +EXELIST_pet = \ + notavailable + +EXELIST_vic20 = \ + notavailable + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +disk: $(DISK_$(SYS)) + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: cbm samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +fire: fire.c + $(CL) -t $(SYS) -O -o fire -m fire.map fire.c +plasma: plasma.c + $(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c +nachtm: nachtm.c + $(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c + +# -------------------------------------------------------------------------- +# Rule to make a CBM disk with all samples. Needs the c1541 program that comes +# with the VICE emulator. + +define D64_WRITE_PRG_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV) + +endef # D64_WRITE_PRG_recipe + +define D64_WRITE_SEQ_recipe + +$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV) + +endef # D64_WRITE_SEQ_recipe + +samples.d64: samples + @$(C1541) -format "samples,00" d64 $@ >$(NULLDEV) + $(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) +# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) + +clean: + @$(DEL) $(EXELIST_$(SYS)) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) + @$(DEL) $(DISK_$(SYS)) 2>$(NULLDEV) diff --git a/samples/fire.c b/samples/cbm/fire.c similarity index 100% rename from samples/fire.c rename to samples/cbm/fire.c diff --git a/samples/nachtm.c b/samples/cbm/nachtm.c similarity index 99% rename from samples/nachtm.c rename to samples/cbm/nachtm.c index 0b962fa5d..02801da24 100644 --- a/samples/nachtm.c +++ b/samples/cbm/nachtm.c @@ -972,12 +972,12 @@ static void MakeNiceScreen (void) /* Clear the screen hide the cursor, set colors */ #ifdef __CBM610__ - textcolor (COLOR_WHITE); + (void)textcolor (COLOR_WHITE); #else - textcolor (COLOR_GRAY3); + (void)textcolor (COLOR_GRAY3); #endif - bordercolor (COLOR_BLACK); - bgcolor (COLOR_BLACK); + (void)bordercolor (COLOR_BLACK); + (void)bgcolor (COLOR_BLACK); clrscr (); cursor (0); diff --git a/samples/plasma.c b/samples/cbm/plasma.c similarity index 100% rename from samples/plasma.c rename to samples/cbm/plasma.c diff --git a/samples/enumdevdir.c b/samples/enumdevdir.c index ce2dc99ec..bf1ff3bbb 100644 --- a/samples/enumdevdir.c +++ b/samples/enumdevdir.c @@ -11,36 +11,52 @@ #include <string.h> #include <unistd.h> #include <stdlib.h> +#include <stdbool.h> #include <device.h> #include <dirent.h> #include <cc65.h> -void printdir (char *newdir) +/* returns true for error, false for OK */ +bool printdir (char *newdir) { - char olddir[FILENAME_MAX]; - char curdir[FILENAME_MAX]; + char *olddir; + char *curdir; DIR *dir; struct dirent *ent; char *subdirs = NULL; unsigned dirnum = 0; unsigned num; - getcwd (olddir, sizeof (olddir)); + olddir = malloc (FILENAME_MAX); + if (olddir == NULL) { + perror ("cannot allocate memory"); + return true; + } + + getcwd (olddir, FILENAME_MAX); if (chdir (newdir)) { /* If chdir() fails we just print the ** directory name - as done for files. */ printf (" Dir %s\n", newdir); - return; + free (olddir); + return false; + } + + curdir = malloc (FILENAME_MAX); + if (curdir == NULL) { + perror ("cannot allocate memory"); + return true; } /* We call getcwd() in order to print the ** absolute pathname for a subdirectory. */ - getcwd (curdir, sizeof (curdir)); + getcwd (curdir, FILENAME_MAX); printf (" Dir %s:\n", curdir); + free (curdir); /* Calling opendir() always with "." avoids ** fiddling around with pathname separators. @@ -65,18 +81,27 @@ void printdir (char *newdir) closedir (dir); for (num = 0; num < dirnum; ++num) { - printdir (subdirs + FILENAME_MAX * num); + if (printdir (subdirs + FILENAME_MAX * num)) + break; } free (subdirs); chdir (olddir); + free (olddir); + return false; } void main (void) { unsigned char device; - char devicedir[FILENAME_MAX]; + char *devicedir; + + devicedir = malloc (FILENAME_MAX); + if (devicedir == NULL) { + perror ("cannot allocate memory"); + return; + } /* Calling getfirstdevice()/getnextdevice() does _not_ turn on the motor ** of a drive-type device and does _not_ check for a disk in the drive. @@ -88,7 +113,7 @@ void main (void) /* Calling getdevicedir() _does_ check for a (formatted) disk in a ** floppy-disk-type device and returns NULL if that check fails. */ - if (getdevicedir (device, devicedir, sizeof (devicedir))) { + if (getdevicedir (device, devicedir, FILENAME_MAX)) { printdir (devicedir); } else { printf (" N/A\n"); @@ -100,4 +125,6 @@ void main (void) if (doesclrscrafterexit ()) { getchar (); } + + free (devicedir); } diff --git a/samples/geos/Makefile b/samples/geos/Makefile index 9ad555565..e792c52f1 100644 --- a/samples/geos/Makefile +++ b/samples/geos/Makefile @@ -3,6 +3,19 @@ # var. to build for another target system. SYS ?= geos-cbm +# If SYS was given on the commandline, redirect "c64" to "geos-cbm" and +# "apple2enh" to "geos-apple" +ifeq ($(origin SYS),command line) + ifeq ($(SYS),c64) + override SYS = geos-cbm + $(info GEOS: c64 -> geos-cbm) + endif + ifeq ($(SYS),apple2enh) + override SYS = geos-apple + $(info GEOS: apple2enh -> geos-apple) + endif +endif + # Just the usual way to find out if we're # using cmd.exe to execute make rules. ifneq ($(shell echo),) @@ -69,10 +82,14 @@ samples: $(EXELIST_$(SYS)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) else samples: - @echo "warning: geos samples not available for" $(SYS) + ifeq ($(MAKELEVEL),0) + @echo "info: geos samples not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) + endif endif - bitmap.c: logo.pcx $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap diff --git a/samples/geos/geosver.c b/samples/geos/geosver.c index 1402d148e..3d68798a2 100644 --- a/samples/geos/geosver.c +++ b/samples/geos/geosver.c @@ -8,8 +8,8 @@ struct window wholeScreen = {0, SC_PIX_HEIGHT-1, 0, SC_PIX_WIDTH-1}; void main (void) { unsigned char os = get_ostype(); - unsigned char *machine = NULL; - unsigned char *version = NULL; + char *machine = NULL; + char *version = NULL; unsigned char good = 1; SetPattern(0); diff --git a/samples/geos/grc/Makefile b/samples/geos/grc/Makefile index 0a432fd89..9dd9ebc5e 100644 --- a/samples/geos/grc/Makefile +++ b/samples/geos/grc/Makefile @@ -41,7 +41,12 @@ ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) else samples: - @echo "warning: grc sample not available for" $(SYS) + ifeq ($(MAKELEVEL),0) + @echo "info: grc sample not available for" $(SYS) + else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) + endif endif test.s: test.grc diff --git a/samples/readme.txt b/samples/readme.txt index 3c9247c39..56b275764 100644 --- a/samples/readme.txt +++ b/samples/readme.txt @@ -10,11 +10,13 @@ Please note: similar systems. If you're using Windows, then consider installing Cygwin or MSys2. - * The makefile specifies the C64 as the default target system because all - but three of the programs run on that platform. When compiling for another - system, you will have to change the line that specifies the target system - at the top of the makefile, specify the system with SYS=<target> on the - make command line, or set a SYS environment variable. + * The makefile specifies the C64 as the default target system because most + of the programs run on that platform. When compiling for another system, + you will have to change the line that specifies the target system at the + top of the makefile, specify the system with SYS=<target> on the make + command line, or set a SYS environment variable. For example: + + make SYS=apple2 * Use "make disk" to build a disk image with all sample programs. @@ -31,11 +33,6 @@ Description: Shows the ASCII (or ATASCII, PETSCII) codes of typed <greg.king5@verizon.com>. Platforms: All platforms with conio or stdio (compile time configurable). ------------------------------------------------------------------------------ -Name: atari2600hello -Description: A "Hello world" type program. -Platforms: Runs on only the Atari 2600 Video Console System. - ----------------------------------------------------------------------------- Name: diodemo Description: A disc copy program written and contributed by Oliver @@ -52,12 +49,6 @@ Platforms: All systems with device enumeration and directory access (currently the Commodore machines, the Commander X16, and the Apple ][). ------------------------------------------------------------------------------ -Name: fire -Description: Another graphics demo written by groepaz/hitmen. -Platforms: The program currently is running on only the C64, but should - be portable to the C128 and CBM510 (and maybe more machines). - ----------------------------------------------------------------------------- Name: gunzip65 Description: A gunzip utility for 6502-based machines, written by Piotr @@ -76,8 +67,8 @@ Platforms: Runs on all platforms that support conio, which means: ----------------------------------------------------------------------------- Name: mandelbrot Description: A mandelbrot demo using integer arithmetic. The demo was - written by groepaz/hitmen, and converted to cc65 using TGI - graphics by Stephan Haubenthal. + written by groepaz, and converted to cc65 using TGI graphics + by Stephan Haubenthal. Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. @@ -97,13 +88,6 @@ Platforms: All systems with an overlay linker config., disk directory access, and EMD support (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: nachtm -Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. -Platforms: All systems that have the Commodore SID (Sound Interface - Device): - C64, C128, CBM510, CBM610. - ----------------------------------------------------------------------------- Name: overlaydemo Description: Shows how to load overlay files from disk. Written and @@ -111,13 +95,6 @@ Description: Shows how to load overlay files from disk. Written and Platforms: All systems with an overlay linker config. (currently the C64, the C128, the Atari, and the Apple ][). ------------------------------------------------------------------------------ -Name: plasma -Description: A fancy graphics demo written by groepaz/hitmen. -Platforms: The program needs a VIC-II or a TED, so it runs on the following - systems: - C64, C128, CBM510, Plus/4. - ----------------------------------------------------------------------------- Name: sieve Description: Implements the "Sieve of Eratosthenes" as a way to find all @@ -128,11 +105,6 @@ Platforms: All systems with conio and clock support: Commander X16, Apple ][ (without timing due to missing clock support). ------------------------------------------------------------------------------ -Name: supervisionhello -Description: A "Hello world" type program. -Platforms: Runs on only the Watara Supervision game console. - ----------------------------------------------------------------------------- Name: tgidemo Description: Shows some of the graphics capabilities of the "Tiny Graphics @@ -140,3 +112,46 @@ Description: Shows some of the graphics capabilities of the "Tiny Graphics Platforms: Runs on all platforms that have TGI support: Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, NES, and Lynx. + +============================================================================= + +Platform specific samples follow: + +atari 2600: +----------- + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Atari 2600 Video Console System. +----------------------------------------------------------------------------- + +cbm: +---- + +Name: fire +Description: Another graphics demo written by groepaz. +Platforms: C64, C128, CBM510 + +----------------------------------------------------------------------------- +Name: nachtm +Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart. +Platforms: All systems that have the Commodore SID (Sound Interface + Device): + C64, C128, CBM510, CBM610. + +----------------------------------------------------------------------------- +Name: plasma +Description: A fancy graphics demo written by groepaz. +Platforms: The program needs a VIC-II or a TED, so it runs on the following + systems: + C64, C128, CBM510, Plus/4. +----------------------------------------------------------------------------- + + +supervision: +------------ + +Name: hello +Description: A "Hello world" type program. +Platforms: Runs on only the Watara Supervision game console. +---------------------------------------------------------------------------- diff --git a/samples/supervision/Makefile b/samples/supervision/Makefile new file mode 100644 index 000000000..5829b3f01 --- /dev/null +++ b/samples/supervision/Makefile @@ -0,0 +1,59 @@ + +# Run 'make SYS=<target>'; or, set a SYS env. +# var. to build for another target system. +SYS ?= supervision + +# Just the usual way to find out if we're +# using cmd.exe to execute make rules. +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + NULLDEV = nul: + DEL = -del /f + RMDIR = rmdir /s /q +else + NULLDEV = /dev/null + DEL = $(RM) + RMDIR = $(RM) -r +endif + +ifdef CC65_HOME + AS = $(CC65_HOME)/bin/ca65 + CC = $(CC65_HOME)/bin/cc65 + CL = $(CC65_HOME)/bin/cl65 + LD = $(CC65_HOME)/bin/ld65 + SP = $(CC65_HOME)/bin/sp65 +else + AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65) + CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65) + CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65) + LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) + SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65) +endif + +EXELIST_supervision = \ + hello + +ifneq ($(EXELIST_$(SYS)),) +samples: $(EXELIST_$(SYS)) +else +samples: notavailable +endif + +# empty target used to skip systems that will not work with any program in this dir +notavailable: +ifeq ($(MAKELEVEL),0) + @echo "info: supervision samples not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + +hello: hello.c + $(CL) -t $(SYS) -O -o hello -m hello.map hello.c + +clean: + @$(DEL) $(EXELIST_supervision) 2>$(NULLDEV) + @$(DEL) *.map 2>$(NULLDEV) diff --git a/samples/supervision/hello.c b/samples/supervision/hello.c new file mode 100644 index 000000000..ed03e82f3 --- /dev/null +++ b/samples/supervision/hello.c @@ -0,0 +1,95 @@ +/*****************************************************************************/ +/* */ +/* Watara Supervision sample C program */ +/* */ +/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ +/* Greg King (greg.king5@verizon.net), 2021 */ +/* */ +/*****************************************************************************/ + +#include <supervision.h> +#include <string.h> + +/* Number of words per screen line (Remark: Last 4 words aren't displayed) */ +#define WORDS_PER_LINE (160/8+4) + +struct sv_vram { + unsigned int v[160/8][8][WORDS_PER_LINE]; +}; +#define SV_VRAM ((*(struct sv_vram *)0x4000).v) + +/* Character definitions in 8x8 format */ +/* That format gives us a screen of 20 columns and 20 rows */ +static const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; +static const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; +static const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; +static const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; +static const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; +static const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; +static const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; + +static void clear_screen(void) +{ + memset(SV_VIDEO, 0, 0x2000); +} + +/* Necessary conversion to have 2 bits per pixel with darkest hue */ +/* Remark: The Supervision uses 2 bits per pixel, and bits are mapped into pixels in reversed order */ +static unsigned int __fastcall__ double_reversed_bits(unsigned char) +{ + __asm__("stz ptr2"); + __asm__("stz ptr2+1"); + __asm__("ldy #$08"); +L1: __asm__("lsr a"); + __asm__("php"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("plp"); + __asm__("rol ptr2"); + __asm__("rol ptr2+1"); + __asm__("dey"); + __asm__("bne %g", L1); + __asm__("lda ptr2"); + __asm__("ldx ptr2+1"); + return __AX__; +} + +static void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) +{ + unsigned char k; + + for(k=0;k<8;++k) + { + SV_VRAM[y][k][x] = double_reversed_bits(ch[k]); + } +} + +static void init_lcd(void) +{ + SV_LCD.width = 160; + SV_LCD.height = 160; +} + +static void hello(unsigned char x, unsigned char y) +{ + display_char(x+ 0,y,h_char); + display_char(x+ 1,y,e_char); + display_char(x+ 2,y,l_char); + display_char(x+ 3,y,l_char); + display_char(x+ 4,y,o_char); + + display_char(x+ 6,y,w_char); + display_char(x+ 7,y,o_char); + display_char(x+ 8,y,r_char); + display_char(x+ 9,y,l_char); + display_char(x+10,y,d_char); +} + +void main(void) +{ + init_lcd(); + clear_screen(); + + hello(2,3); + hello(7,16); +} diff --git a/samples/supervisionhello.c b/samples/supervisionhello.c deleted file mode 100644 index db2b5f66c..000000000 --- a/samples/supervisionhello.c +++ /dev/null @@ -1,91 +0,0 @@ -/*****************************************************************************/ -/* */ -/* Watara Supervision sample C program */ -/* */ -/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ -/* */ -/*****************************************************************************/ - -#include <supervision.h> -#include <peekpoke.h> - -// Number of bytes per screen line (Remark: Last 8 bytes are not displayed) -#define BYTES_PER_LINE 48 - -// Character definitions in 8x8 format -const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; -const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00}; -const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}; -const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}; -const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}; -const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00}; -const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}; - -void clear_screen(void) -{ - unsigned short i; - - for(i=0;i<0x2000;++i) - { - POKE(SV_VIDEO+i,0); - } -} - -// Necessary conversion to have 2 bits per pixel with darkest hue -// Remark: The Supervision uses 2 bits per pixel and bits are mapped into pixel in reversed order -unsigned char reversed_map_one_to_two_lookup[16] = -{ - 0x00, 0xC0, 0x30, 0xF0, 0x0C, 0xCC, 0x3C, 0xFC, - 0x03, 0xC3, 0x33, 0xF3, 0x0F, 0xCF, 0x3F, 0xFF -}; - -unsigned char left_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n >> 4]; -} - -unsigned char right_map_one_to_two(unsigned char n) -{ - return reversed_map_one_to_two_lookup[n&0x0F]; -} - -void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch) -{ - unsigned char k; - - for(k=0;k<8;++k) - { \ - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)] = left_map_one_to_two(ch[k]); - SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)+1] = right_map_one_to_two(ch[k]); - } -} - -void init_lcd(void) -{ - SV_LCD.width = 160; - SV_LCD.height = 160; -} - -int main() -{ - init_lcd(); - clear_screen(); - - display_char(3,2, h_char); - display_char(3,3, e_char); - display_char(3,4, l_char); - display_char(3,5, l_char); - display_char(3,6, o_char); - - display_char(3,8, w_char); - display_char(3,9, o_char); - display_char(3,10,r_char); - display_char(3,11,l_char); - display_char(3,12,d_char); - - while(1) {}; - - return 0; -} - - diff --git a/samples/tutorial/Makefile b/samples/tutorial/Makefile index 39b2d6e8c..af5062588 100644 --- a/samples/tutorial/Makefile +++ b/samples/tutorial/Makefile @@ -81,6 +81,12 @@ hello: hello.c text.s # empty target used to skip systems that will not work with any program in this dir notavailable: - @echo "warning: tutorial sample not available for" $(SYS) +ifeq ($(MAKELEVEL),0) + @echo "info: tutorial sample not available for" $(SYS) +else +# suppress the "nothing to be done for 'samples' message + @echo > $(NULLDEV) +endif + clean: @$(DEL) hello 2>$(NULLDEV) diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 34d87e65f..d6c807035 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -438,9 +438,7 @@ void MacDef (unsigned Style) /* Parse the parameter list */ if (HaveParams) { - while (CurTok.Tok == TOK_IDENT) { - /* Create a struct holding the identifier */ IdDesc* I = NewIdDesc (&CurTok.SVal); @@ -449,6 +447,7 @@ void MacDef (unsigned Style) M->Params = I; } else { IdDesc* List = M->Params; + while (1) { if (SB_Compare (&List->Id, &CurTok.SVal) == 0) { Error ("Duplicate symbol '%m%p'", &CurTok.SVal); @@ -490,9 +489,8 @@ void MacDef (unsigned Style) ** the .LOCAL command is detected and removed, at this time. */ while (1) { - /* Check for include */ - if (CurTok.Tok == TOK_INCLUDE) { + if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) { /* Include another file */ NextTok (); /* Name must follow */ @@ -529,9 +527,7 @@ void MacDef (unsigned Style) /* Check for a .LOCAL declaration */ if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) { - while (1) { - IdDesc* I; /* Skip .local or comma */ @@ -570,6 +566,7 @@ void MacDef (unsigned Style) if (CurTok.Tok == TOK_IDENT) { unsigned Count = 0; IdDesc* I = M->Params; + while (I) { if (SB_Compare (&I->Id, &CurTok.SVal) == 0) { /* Local param name, replace it */ diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 7ebd2c4e1..05a6d9a96 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -42,19 +42,35 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "stackptr.h" #include "stdnames.h" #include "typecmp.h" #include "typeconv.h" +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Map a generator function and its attributes to a token */ +typedef struct GenDesc { + token_t Tok; /* Token to map to */ + unsigned Flags; /* Flags for generator function */ + void (*Func) (unsigned, unsigned long); /* Generator func */ +} GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) +static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Copy the struct/union represented by RExpr to the one represented by LExpr */ { /* If the size is that of a basic type (char, int, long), we will copy @@ -127,14 +143,440 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) ** to a boolean in C, but there is no harm to be future-proof. */ ED_MarkAsUntested (LExpr); - - return 1; } -void Assignment (ExprDesc* Expr) -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) +/* Process inc/dec for bit-field */ +{ + int AddrSP; + unsigned Flags; /* Internal codegen flags */ + unsigned Mask; + unsigned ChunkFlags; + const Type* ChunkType; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + /* Handle for add and sub */ + if (Val > 0) { + g_inc (Flags | CF_CONST, Val); + } else if (Val < 0) { + g_dec (Flags | CF_CONST, -Val); + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + if (KeepResult == OA_NEED_OLD) { + /* Save the original expression value */ + g_save (ChunkFlags | CF_FORCECHAR); + } + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + if (KeepResult == OA_NEED_OLD) { + /* Restore the original expression value */ + g_restore (ChunkFlags | CF_FORCECHAR); + } +} + + + +static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for bit-field lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + int AddrSP; + unsigned Mask; + unsigned Flags; + unsigned ChunkFlags; + const Type* ChunkType; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + if (Gen != 0) { + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + /* Backup them on stack */ + GetCodePos (&PushPos); + g_push (Flags & ~CF_FORCECHAR, 0); + + } + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + if (Gen == 0) { + + /* Get the value and apply the mask */ + unsigned Val = (unsigned)(Expr2.IVal & Mask); + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* If the value is equal to the mask now, all bits are one, and we + ** can skip the mask operation. + */ + if (Val != Mask) { + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + } + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags | CF_CONST, Val << Expr->Type->A.B.Offs); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Done */ + goto Done; + + } else { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); + } else { + Gen->Func ((Flags & ~CF_FORCECHAR) | CF_CONST, Expr2.IVal); + } + } + + } + + } else { + + /* Do 'op' if provided */ + if (Gen != 0) { + + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); + } else { + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + } + + } else { + + /* Do type conversion if necessary */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load rhs into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } + + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + +Done: + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for arithmetic lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + + unsigned Flags; + int MustScale; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type); + + /* Determine the type of the lhs */ + MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && + IsTypePtr (Expr->Type); + + /* Get the address on stack for the store */ + PushAddr (Expr); + + if (Gen == 0) { + + /* Read the expression on the right side of the '=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* Do type conversion if necessary. Beware: Do not use char type + ** here! + */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } else { + + /* Load the original value if necessary */ + LoadExpr (CF_NONE, Expr); + + /* Push lhs on stack */ + GetCodePos (&PushPos); + g_push (Flags, 0); + + /* Read the expression on the right side of the '=' or 'op=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + if (MustScale) { + /* lhs is a pointer, scale rhs */ + Expr2.IVal *= CheckedSizeOf (Expr->Type+1); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + Gen->Func (Flags | CF_CONST, Expr2.IVal); + } + + } else { + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Adjust the types of the operands if needed */ + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + + } + } + + /* Generate a store instruction */ + Store (Expr, 0); + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ { const Type* ltype = Expr->Type; @@ -142,28 +584,32 @@ void Assignment (ExprDesc* Expr) ED_Init (&Expr2); Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - /* We must have an lvalue for an assignment */ - if (ED_IsRVal (Expr)) { - 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"); + /* Only "=" accept struct/union */ + if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) { + Error ("Invalid left operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } else { + /* Check for assignment to incomplete type */ + if (IsIncompleteESUType (ltype)) { + Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); + } else if (ED_IsRVal (Expr)) { + /* Assignment can only be used with lvalues */ + if (IsTypeArray (ltype)) { + Error ("Array type '%s' is not assignable", GetFullTypeName (ltype)); + } else if (IsTypeFunc (ltype)) { + Error ("Function type '%s' is not assignable", GetFullTypeName (ltype)); + } else { + Error ("Assignment to rvalue"); + } + } else if (IsQualConst (ltype)) { + /* Check for assignment to const */ + Error ("Assignment to const"); } } - /* Check for assignment to const */ - if (IsQualConst (ltype)) { - Error ("Assignment to const"); - } - - /* Check for assignment to incomplete type */ - if (IsIncompleteESUType (ltype)) { - Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); - } - - /* Skip the '=' token */ + /* Skip the '=' or 'op=' token */ NextToken (); /* cc65 does not have full support for handling structs or unions. Since @@ -174,114 +620,131 @@ void Assignment (ExprDesc* Expr) if (IsClassStruct (ltype)) { /* Copy the struct or union by value */ CopyStruct (Expr, &Expr2); - - } else if (ED_IsBitField (Expr)) { - - CodeMark AndPos; - CodeMark PushPos; - - unsigned Mask; - unsigned Flags; - - /* If the bit-field fits within one byte, do the following operations - ** with bytes. - */ - if (Expr->BitOffs / CHAR_BITS == (Expr->BitOffs + Expr->BitWidth - 1) / CHAR_BITS) { - Expr->Type = type_uchar; - } - - /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); - - /* Assignment to a bit field. Get the address on stack for the store. */ - PushAddr (Expr); - - /* Load the value from the location */ - Expr->Flags &= ~E_BITFIELD; - LoadExpr (CF_NONE, Expr); - - /* Mask unwanted bits */ - Mask = (0x0001U << Expr->BitWidth) - 1U; - GetCodePos (&AndPos); - g_and (Flags | CF_CONST, ~(Mask << Expr->BitOffs)); - - /* Push it on stack */ - GetCodePos (&PushPos); - g_push (Flags, 0); - - /* Read the expression on the right side of the '=' */ - MarkedExprWithCheck (hie1, &Expr2); - - /* Do type conversion if necessary. Beware: Do not use char type - ** here! - */ - TypeConversion (&Expr2, ltype); - - /* Special treatment if the value is constant. */ - /* Beware: Expr2 may contain side effects, so there must not be - ** code generated for Expr2. - */ - if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - - /* Get the value and apply the mask */ - unsigned Val = (unsigned) (Expr2.IVal & Mask); - - /* Since we will do the OR with a constant, we can remove the push */ - RemoveCode (&PushPos); - - /* If the value is equal to the mask now, all bits are one, and we - ** can remove the mask operation from above. - */ - if (Val == Mask) { - RemoveCode (&AndPos); - } - - /* Generate the or operation */ - g_or (Flags | CF_CONST, Val << Expr->BitOffs); - - } else { - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Apply the mask */ - g_and (Flags | CF_CONST, Mask); - - /* Shift it into the right position */ - g_asl (Flags | CF_CONST, Expr->BitOffs); - - /* Or both values */ - g_or (Flags, 0); - } - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Restore the expression type */ - Expr->Type = ltype; - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + } else if (IsTypeBitField (ltype)) { + /* Special care is needed for bit-field 'op=' */ + OpAssignBitField (Gen, Expr, Op); } else { - - /* Get the address on stack if needed */ - PushAddr (Expr); - - /* Read the expression on the right side of the '=' */ - hie1 (&Expr2); - - /* Do type conversion if necessary */ - TypeConversion (&Expr2, ltype); - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + /* Normal straight 'op=' */ + OpAssignArithmetic (Gen, Expr, Op); } } + + + +void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) +/* Parse a "+=" or "-=" operation */ +{ + ExprDesc Expr2; + unsigned lflags; + unsigned rflags; + int MustScale; + + /* We currently only handle non-bit-fields in some addressing modes here */ + if (IsTypeBitField (Expr->Type) || ED_IsLocPrimaryOrExpr (Expr)) { + /* Use generic routine instead */ + OpAssign (Gen, Expr, Op); + return; + } + + /* There must be an integer or pointer on the left side */ + if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { + Error ("Invalid left operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } else { + /* We must have an lvalue */ + if (ED_IsRVal (Expr)) { + Error ("Invalid lvalue in assignment"); + } else if (IsQualConst (Expr->Type)) { + /* The left side must not be const qualified */ + Error ("Assignment to const"); + } + } + + /* Skip the operator */ + NextToken (); + + /* Check if we have a pointer expression and must scale rhs */ + MustScale = IsTypePtr (Expr->Type); + + /* Initialize the code generator flags */ + lflags = 0; + rflags = 0; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Evaluate the rhs. We expect an integer here, since float is not + ** supported + */ + hie1 (&Expr2); + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator '%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + ** break, so this is the best error recovery. + */ + } + + /* Setup the code generator flags */ + lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; + rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + + if (ED_IsConstAbs (&Expr2)) { + /* The resulting value is a constant */ + rflags |= CF_CONST; + lflags |= CF_CONST; + + /* Scale it */ + if (MustScale) { + Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); + } + } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Convert the type of the rhs to that of the lhs */ + g_typecast (lflags, rflags & ~CF_FORCECHAR); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + } + } + + /* Output apropriate code depending on the location */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Absolute numeric addressed variable, global variable, local + ** static variable, register variable, pooled literal or code + ** label location. + */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } else { + g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } + break; + + case E_LOC_STACK: + /* Value on the stack */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); + } else { + g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); + } + break; + + default: + Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); + } + + /* Expression is an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); +} diff --git a/src/cc65/assignment.h b/src/cc65/assignment.h index b2cc1548b..6098118b4 100644 --- a/src/cc65/assignment.h +++ b/src/cc65/assignment.h @@ -43,14 +43,38 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Whether to save/restore the original lhs or result value */ +enum { + OA_NEED_NONE, + OA_NEED_OLD, + OA_NEED_NEW, +}; + +/* Forward */ +struct GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -void Assignment (ExprDesc* lval); -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult); +/* Process inc/dec for bit-field */ + +void OpAssign (const struct GenDesc* Gen, ExprDesc* lval, const char* Op); +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ + +void OpAddSubAssign (const struct GenDesc* Gen, ExprDesc *Expr, const char* Op); +/* Parse a "+=" or "-=" operation */ diff --git a/src/cc65/codeent.c b/src/cc65/codeent.c index dd2000db0..0a1b917db 100644 --- a/src/cc65/codeent.c +++ b/src/cc65/codeent.c @@ -172,6 +172,10 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D) if (Info && Info->ByteUse != REG_NONE) { /* These addressing modes will never change the zp loc */ E->Use |= Info->WordUse; + + if ((E->Use & REG_SP) != 0) { + E->Use |= SLV_IND; + } } break; @@ -1777,6 +1781,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs) if (RegValIsKnown (In->RegX)) { Out->RegX = (In->RegX ^ 0xFF); } + } else if (strncmp (E->Arg, "asrax", 5) == 0 || + strncmp (E->Arg, "shrax", 5) == 0) { + if (RegValIsKnown (In->RegX)) { + if (In->RegX == 0x00 || In->RegX == 0xFF) { + Out->RegX = In->RegX; + } + } } else if (strcmp (E->Arg, "tosandax") == 0) { if (RegValIsKnown (In->RegA) && In->RegA == 0) { Out->RegA = 0; diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index f8a1dcdf6..cf10314b9 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -1166,7 +1166,9 @@ void g_putind (unsigned Flags, unsigned Offs) /* Overflow - we need to add the low byte also */ AddCodeLine ("ldy #$00"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", Offs & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); @@ -1174,7 +1176,9 @@ void g_putind (unsigned Flags, unsigned Offs) AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Complete address is on stack, new offset is zero */ Offs = 0; @@ -1184,12 +1188,15 @@ void g_putind (unsigned Flags, unsigned Offs) /* We can just add the high byte */ AddCodeLine ("ldy #$01"); AddCodeLine ("clc"); - AddCodeLine ("pha"); + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pha"); + } AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("pla"); - + if ((Flags & CF_NOKEEP) == 0) { + AddCodeLine ("pla"); + } /* Offset is now just the low byte */ Offs &= 0x00FF; } @@ -1696,7 +1703,9 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("inc %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1726,8 +1735,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bne %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("lda %s", lbuf); /* Hmmm... */ - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); /* Hmmm... */ + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("clc"); @@ -1738,13 +1749,17 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcc %s", LocalLabelName (L)); AddCodeLine ("inc %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } } else { @@ -1754,8 +1769,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("txa"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; @@ -1837,9 +1854,11 @@ void g_addeqlocal (unsigned flags, int Offs, unsigned long val) AddCodeLine ("lda #$%02X", (int) ((val >> 8) & 0xFF)); AddCodeLine ("adc (sp),y"); AddCodeLine ("sta (sp),y"); - AddCodeLine ("tax"); - AddCodeLine ("dey"); - AddCodeLine ("lda (sp),y"); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("dey"); + AddCodeLine ("lda (sp),y"); + } } else { g_getimmed (flags, val, 0); AddCodeLine ("jsr addeqysp"); @@ -1919,7 +1938,9 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, if (flags & CF_CONST) { if (val == 1) { AddCodeLine ("dec %s", lbuf); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("lda %s", lbuf); + } } else { AddCodeLine ("lda %s", lbuf); AddCodeLine ("sec"); @@ -1953,13 +1974,17 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("bcs %s", LocalLabelName (L)); AddCodeLine ("dec %s+1", lbuf); g_defcodelabel (L); - AddCodeLine ("ldx %s+1", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("ldx %s+1", lbuf); + } } else { AddCodeLine ("lda %s+1", lbuf); AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } } else { AddCodeLine ("eor #$FF"); @@ -1970,8 +1995,10 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs, AddCodeLine ("eor #$FF"); AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf); - AddCodeLine ("tax"); - AddCodeLine ("lda %s", lbuf); + if ((flags & CF_NOKEEP) == 0) { + AddCodeLine ("tax"); + AddCodeLine ("lda %s", lbuf); + } } break; diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 46a7d76c6..88f8a5138 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -82,269 +82,296 @@ struct FuncInfo { unsigned Chg; /* Changed/destroyed registers */ }; -/* Note for the shift functions: Shifts are done modulo 32, so all shift +/* Functions that change the SP are regarded as using the SP as well. +** The callax/jmpvec functions may call a function that uses/changes more +** registers, so we should further check the info of the called function +** or just play it safe. +** Note for the shift functions: Shifts are done modulo 32, so all shift ** routines are marked to use only the A register. The remainder is ignored ** anyway. */ static const FuncInfo FuncInfoTable[] = { - { "addeq0sp", REG_AX, 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 }, + { "addeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "addeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "addysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP }, + { "along", REG_A, PSTATE_ALL | REG_X | REG_SREG }, + { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "aulong", REG_NONE, PSTATE_ALL | REG_X | REG_SREG }, + { "axlong", REG_X, PSTATE_ALL | REG_Y | REG_SREG }, + { "axulong", REG_NONE, PSTATE_ALL | REG_Y | REG_SREG }, + { "bcasta", REG_A, PSTATE_ALL | REG_AX }, + { "bcastax", REG_AX, PSTATE_ALL | REG_AX }, + { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "bnega", REG_A, PSTATE_ALL | REG_AX }, + { "bnegax", REG_AX, PSTATE_ALL | REG_AX }, + { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, + { "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, + { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, + { "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, + { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, + { "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, + { "callax", REG_AX, PSTATE_ALL | REG_ALL }, /* PSTATE_ZN | REG_PTR1 */ + { "complax", REG_AX, PSTATE_ALL | REG_AX }, + { "decax1", REG_AX, PSTATE_ALL | REG_AX }, + { "decax2", REG_AX, PSTATE_ALL | REG_AX }, + { "decax3", REG_AX, PSTATE_ALL | REG_AX }, + { "decax4", REG_AX, PSTATE_ALL | REG_AX }, + { "decax5", REG_AX, PSTATE_ALL | REG_AX }, + { "decax6", REG_AX, PSTATE_ALL | REG_AX }, + { "decax7", REG_AX, PSTATE_ALL | REG_AX }, + { "decax8", REG_AX, PSTATE_ALL | REG_AX }, + { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, + { "decsp1", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "decsp2", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp3", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp4", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp5", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp6", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp7", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "decsp8", REG_SP, PSTATE_ALL | REG_SP | REG_A }, + { "enter", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "incax1", REG_AX, PSTATE_ALL | REG_AX }, + { "incax2", REG_AX, PSTATE_ALL | REG_AX }, + { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "incsp1", REG_SP, PSTATE_ALL | REG_SP }, + { "incsp2", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp3", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp4", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp5", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp6", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp7", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "incsp8", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "jmpvec", REG_EVERYTHING, PSTATE_ALL | REG_ALL }, /* NONE */ + { "laddeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "laddeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "ldax0sp", SLV_TOP, PSTATE_ALL | REG_AXY }, + { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, + { "ldaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_AXY }, + { "ldeax0sp", SLV_TOP, PSTATE_ALL | REG_EAXY }, + { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, + { "ldeaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_EAXY }, + { "leaa0sp", REG_SP | REG_A, PSTATE_ALL | REG_AX }, + { "leaaxsp", REG_SP | REG_AX, PSTATE_ALL | REG_AX }, + { "leave00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leave0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leave", REG_SP, PSTATE_ALL | REG_SP | REG_Y }, + { "leavey00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "leavey0", REG_SP, PSTATE_ALL | REG_SP | REG_XY }, + { "leavey", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_Y }, + { "lsubeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY }, + { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, + { "lsubeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY }, + { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, + { "negax", REG_AX, PSTATE_ALL | REG_AX }, + { "negeax", REG_EAX, PSTATE_ALL | REG_EAX }, + { "popa", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "popax", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "popeax", SLV_TOP, PSTATE_ALL | REG_SP | REG_EAXY }, + { "push0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push0ax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y | REG_SREG }, + { "push1", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push2", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push3", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push4", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push5", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push6", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "push7", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pusha", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pusha0", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_XY }, + { "pusha0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY }, + { "pushaFF", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y }, + { "pushax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushaysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "pushc0", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc1", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pushc2", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y }, + { "pusheax", REG_SP | REG_EAX, PSTATE_ALL | REG_SP | REG_Y }, + { "pushl0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushw", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushw0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY }, + { "pushwidx", REG_SP | REG_AXY, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "pushwysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AXY }, + { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "regswap1", REG_XY, PSTATE_ALL | REG_A }, + { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, + { "resteax", REG_SAVE, PSTATE_ZN | REG_EAX }, /* also uses regsave+2/+3 */ + { "return0", REG_NONE, PSTATE_ALL | REG_AX }, + { "return1", REG_NONE, PSTATE_ALL | REG_AX }, + { "saveeax", REG_EAX, PSTATE_ZN | REG_Y | REG_SAVE }, /* also regsave+2/+3 */ + { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, + { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, + { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, + { "staspidx", SLV_TOP | REG_AY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, + { "stax0sp", REG_SP | REG_AX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "staxspidx", SLV_TOP | REG_AXY, PSTATE_ALL | REG_SP | REG_TMP1 | REG_PTR1 }, + { "staxysp", REG_SP | REG_AXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "steax0sp", REG_SP | REG_EAX, PSTATE_ALL | SLV_TOP | REG_Y }, + { "steaxspidx", SLV_TOP | REG_EAXY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, /* also tmp2, tmp3 */ + { "steaxysp", REG_SP | REG_EAXY, PSTATE_ALL | SLV_IND | REG_Y }, + { "subeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY }, + { "subeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY }, + { "subysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY }, + { "swapstk", SLV_TOP | REG_AX, PSTATE_ALL | SLV_TOP | REG_AXY }, /* also ptr4 */ + { "tosadd0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosadda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosaddeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosand0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosanda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tosandeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosaslax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosasrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosasreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosdiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL }, + { "tosdiveax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_ALL }, + { "toseq00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqa0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toseqeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosgt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosgteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosicmp", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosicmp0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosint", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslcmp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_A | REG_Y | REG_PTR1 }, + { "tosle00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "toslong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "toslt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosltax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "toslteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosmod0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmodeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosmul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosmulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosmuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosne00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosnea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosneeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosrsuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosrsubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshlax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosshrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosshreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tossub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tossuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY }, + { "tossubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY }, + { "tosudiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosudiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosudiveax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosuge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosugt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosugteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosule00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosuleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosulong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y }, + { "tosult00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosultax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG }, + { "tosulteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 }, + { "tosumod0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumoda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */ + { "tosumodeax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, + { "tosumul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL }, + { "tosumulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL }, + { "tosumuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL }, + { "tosxor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tosxora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 }, + { "tosxoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 }, + { "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, + { "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, }; #define FuncInfoCount (sizeof(FuncInfoTable) / sizeof(FuncInfoTable[0])) @@ -481,6 +508,7 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Did we find it in the top-level table? */ if (E && IsTypeFunc (E->Type)) { FuncDesc* D = GetFuncDesc (E->Type); + *Use = REG_NONE; /* A variadic function will use the Y register (the parameter list ** size is passed there). A fastcall function will use the A or A/X @@ -488,31 +516,40 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) ** we assume that any function will destroy all registers. */ if ((D->Flags & FD_VARIADIC) != 0) { - *Use = REG_Y; + *Use = REG_Y | REG_SP | SLV_TOP; } else if (D->Flags & FD_CALL_WRAPPER) { /* Wrappers may go to any functions, so mark them as using all ** registers. */ *Use = REG_EAXY; - } else if ((D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) && - IsFastcallFunc (E->Type)) { + } else if (D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) { /* Will use registers depending on the last param. If the last ** param has incomplete type, or if the function has not been ** prototyped yet, just assume __EAX__. */ - if (D->LastParam != 0) { - switch (SizeOf (D->LastParam->Type)) { - case 1u: - *Use = REG_A; - break; - case 2u: - *Use = REG_AX; - break; - default: - *Use = REG_EAX; + if (IsFastcallFunc (E->Type)) { + if (D->LastParam != 0) { + switch (SizeOf (D->LastParam->Type)) { + case 1u: + *Use = REG_A; + break; + case 2u: + *Use = REG_AX; + break; + default: + *Use = REG_EAX; + } + if (D->ParamCount > 1) { + /* Passes other params on the stack */ + *Use |= REG_SP | SLV_TOP; + } + } else { + /* We'll assume all */ + *Use = REG_EAX | REG_SP | SLV_TOP; } } else { - *Use = REG_EAX; + /* Passes all params on the stack */ + *Use = REG_SP | SLV_TOP; } } else { /* Will not use any registers */ @@ -551,6 +588,9 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg) /* Use the information we have */ *Use = Info->Use; *Chg = Info->Chg; + if ((*Use & (SLV_TOP | SLV_IND)) != 0) { + *Use |= REG_SP; + } } else { /* It's an internal function we have no information for. If in ** debug mode, output an additional warning, so we have a chance diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index 88e26cdf4..14ef54d8f 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -75,6 +75,8 @@ struct RegContents; #define REG_SP_HI 0x2000U /* Defines for some special register usage */ +#define SLV_IND 0x00010000U /* Accesses (sp),y */ +#define SLV_TOP 0x00020000U /* Accesses (sp),0 */ #define SLV_SP65 0x00200000U /* Accesses 6502 stack pointer */ #define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ #define SLV_PL65 0x00800000U /* Pops from 6502 stack */ @@ -104,6 +106,7 @@ 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) diff --git a/src/cc65/codeoptutil.c b/src/cc65/codeoptutil.c index 16c41162a..173d5185f 100644 --- a/src/cc65/codeoptutil.c +++ b/src/cc65/codeoptutil.c @@ -827,21 +827,28 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) CodeEntry* E = CS_GetEntry (D->Code, I); + /* Check against some things that should not happen */ + CHECK ((E->Use & SLV_TOP) != SLV_TOP); + /* Check if this entry does a stack access, and if so, if it's a plain ** load from stack, since this is needed later. */ int Correction = 0; - if ((E->Use & REG_SP) != 0) { + if ((E->Use & SLV_IND) == SLV_IND) { - /* 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; + if (E->OPC != OP65_JSR) { + /* Check against some things that should not happen */ + CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs); + CHECK (strcmp (E->Arg, "sp") == 0); + + /* We need to correct this one */ + Correction = 2; + + } else { + /* We need to correct this one */ + Correction = 1; + } - } else if (CE_IsCallTo (E, "ldaxysp")) { - /* We need to correct this one */ - Correction = 1; } if (Correction) { @@ -849,7 +856,7 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) ** value. */ CodeEntry* P = CS_GetPrevEntry (D->Code, I); - if (P && P->OPC == OP65_LDY && CE_IsConstImm (P)) { + if (P && P->OPC == OP65_LDY && CE_IsConstImm (P) && !CE_HasLabel (E)) { /* The Y load is just before the stack access, adjust it */ CE_SetNumArg (P, P->Num - Offs); } else { @@ -860,39 +867,59 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs) } /* If we need the value of Y later, be sure to reload it */ - if (RegYUsed (D->Code, I+1)) { - CodeEntry* N; + unsigned R = REG_Y | (E->Chg & ~REG_A); + R = GetRegInfo (D->Code, I + 1, R) & R; + if ((R & REG_Y) != 0) { const char* Arg = MakeHexArg (E->RI->In.RegY); - if (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. - */ + if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) { + CodeEntry* N; + if ((N = CS_GetNextEntry (D->Code, I)) != 0 && + ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { + /* The Y register is used but the load instruction loads A + ** and is followed by a branch that evaluates the zero flag. + ** This means that we cannot just insert the load insn + ** for the Y register at this place, because it would + ** destroy the Z flag. Instead place load insns at the + ** target of the branch and after it. + ** Note: There is a chance that this code won't work. The + ** jump may be a backwards jump (in which case the stack + ** offset has already been adjusted) or there may be other + ** instructions between the load and the conditional jump. + ** Currently the compiler does not generate such code, but + ** it is possible to force the optimizer into something + ** invalid by use of inline assembler. + ** Note: In reality, this route is never taken as all + ** callers of this function will just give up with + ** optimization whenever they detect a branch. + */ - /* Add load insn after the branch */ - CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - InsertEntry (D, X, I+2); + /* Add load insn 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); + /* 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); + /* Move the label to the new insn */ + CodeLabel* L = CS_GenLabel (D->Code, Y); + CS_MoveLabelRef (D->Code, N, L); + + /* Skip the next two instructions in the next round */ + I += 2; + } else { + /* This could be suboptimal but it will always work (unless stack overflows) */ + CodeEntry* X = NewCodeEntry (OP65_PHP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, 0, 0, E->LI); + InsertEntry (D, X, I+2); + X = NewCodeEntry (OP65_PLP, AM65_IMP, 0, 0, E->LI); + InsertEntry (D, X, I+3); + /* Skip the three inserted instructions in the next round */ + I += 3; + } } else { CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); InsertEntry (D, X, I+1); @@ -1197,66 +1224,43 @@ static int CmpHarmless (const void* Key, const void* Entry) -int HarmlessCall (const char* Name) +int HarmlessCall (const CodeEntry* E, int PushedBytes) /* Check if this is a call to a harmless subroutine that will not interrupt ** the pushax/op sequence when encountered. */ { - 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", - "ldaidx", - "ldauidx", - "ldaxidx", - "ldaxysp", - "negax", - "shlax1", - "shlax2", - "shlax3", - "shlax4", - "shlaxy", - "shrax1", - "shrax2", - "shrax3", - "shrax4", - "shraxy", - }; + unsigned Use = 0, Chg = 0; + if (GetFuncInfo (E->Arg, &Use, &Chg) == FNCLS_BUILTIN) { + if ((Chg & REG_SP) != 0) { + return 0; + } + if ((Use & REG_SP) != 0 && + ((Use & (SLV_IND | SLV_TOP)) != SLV_IND || + RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < PushedBytes)) { + /* If we are using the stack, and we don't have "indirect" + ** addressing mode, or the value of Y is unknown, or less + ** than two, we cannot cope with this piece of code. Having + ** an unknown value of Y means that we cannot correct the + ** stack offset, while having an offset less than PushedBytes + ** means that the code works with the value on stack which + ** is to be removed. + */ + return 0; + } + return 1; + } else { + static const char* const Tab[] = { + "_abs", + }; - void* R = bsearch (Name, - Tab, - sizeof (Tab) / sizeof (Tab[0]), - sizeof (Tab[0]), - CmpHarmless); - return (R != 0); + void* R = bsearch (E->Arg, + 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 index 70aa5f462..140b11236 100644 --- a/src/cc65/codeoptutil.h +++ b/src/cc65/codeoptutil.h @@ -261,7 +261,7 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI); void RemoveRemainders (StackOpData* D); /* Remove the code that is unnecessary after translation of the sequence */ -int HarmlessCall (const char* Name); +int HarmlessCall (const CodeEntry* E, int PushedBytes); /* Check if this is a call to a harmless subroutine that will not interrupt ** the pushax/op sequence when encountered. */ diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 01d0b039c..08f6c820e 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1113,9 +1113,9 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer) D->IP = D->OpIndex + 1; - if (!D->RhsMultiChg && - (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0 && - (D->Rhs.A.Flags & LI_DIRECT) != 0) { + if (!D->RhsMultiChg && + (D->Rhs.A.Flags & LI_DIRECT) != 0 && + (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) { /* cmp */ AddOpLow (D, OP65_CMP, &D->Rhs); @@ -1820,20 +1820,18 @@ unsigned OptStackOps (CodeSeg* S) Data.OpEntry = E; State = FoundOp; break; - } else if (!HarmlessCall (E->Arg)) { - /* A call to an unkown subroutine: We need to start - ** over after the last pushax. Note: This will also - ** happen if we encounter a call to pushax! + } else if (!HarmlessCall (E, 2)) { + /* The call might use or change the content that we are + ** going to access later via the stack pointer. In any + ** case, we need to start over after the last pushax. + ** Note: This will also happen if we encounter a call + ** to pushax! */ I = Data.PushIndex; State = Initialize; break; } - - } else if ((E->Use & REG_SP) != 0 && - (E->AM != AM65_ZP_INDY || - RegValIsUnknown (E->RI->In.RegY) || - E->RI->In.RegY < 2)) { + } else if (((E->Chg | E->Use) & REG_SP) != 0) { /* If we are using the stack, and we don't have "indirect Y" ** addressing mode, or the value of Y is unknown, or less @@ -1843,9 +1841,14 @@ unsigned OptStackOps (CodeSeg* S) ** that the code works with the value on stack which is to ** be removed. */ - I = Data.PushIndex; - State = Initialize; - break; + if (E->AM == AM65_ZPX_IND || + ((E->Chg | E->Use) & SLV_IND) == 0 || + (RegValIsUnknown (E->RI->In.RegY) || + E->RI->In.RegY < 2)) { + I = Data.PushIndex; + State = Initialize; + break; + } } diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 90bf892ba..e5d3f8d96 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -207,7 +207,11 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } } - SB_AppendStr (&Buf, GetSymTypeName (T)); + if (!IsTypeBitField (T)) { + SB_AppendStr (&Buf, GetSymTypeName (T)); + } else { + SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + } if (!SB_IsEmpty (West)) { SB_AppendChar (&Buf, ' '); @@ -231,6 +235,7 @@ const char* GetBasicTypeName (const Type* T) { switch (GetRawType (T)) { case T_TYPE_ENUM: return "enum"; + case T_TYPE_BITFIELD: return "bit-field"; case T_TYPE_FLOAT: return "float"; case T_TYPE_DOUBLE: return "double"; case T_TYPE_VOID: return "void"; @@ -546,14 +551,14 @@ unsigned long GetIntegerTypeMax (const Type* Type) -static unsigned TypeOfBySize (const Type* Type) +static unsigned TypeOfBySize (unsigned Size) /* Get the code generator replacement type of the object by its size */ { unsigned NewType; /* If the size is less than or equal to that of a a long, we will copy ** the struct using the primary register, otherwise we use memcpy. */ - switch (SizeOf (Type)) { + switch (Size) { case 1: NewType = CF_CHAR; break; case 2: NewType = CF_INT; break; case 3: /* FALLTHROUGH */ @@ -566,125 +571,6 @@ static unsigned TypeOfBySize (const Type* Type) -Type* NewPointerTo (const Type* T) -/* Return a type string that is "pointer to T". The type string is allocated -** on the heap and may be freed after use. -*/ -{ - /* Get the size of the type string including the terminator */ - unsigned Size = TypeLen (T) + 1; - - /* Allocate the new type string */ - Type* P = TypeAlloc (Size + 1); - - /* Create the return type... */ - P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); - memcpy (P+1, T, Size * sizeof (Type)); - - /* ...and return it */ - return P; -} - - - -void PrintType (FILE* F, const Type* T) -/* Print fulle name of the type */ -{ - StrBuf Buf = AUTO_STRBUF_INITIALIZER; - fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); - SB_Done (&Buf); -} - - - -void PrintFuncSig (FILE* F, const char* Name, const Type* T) -/* Print a function signature */ -{ - StrBuf Buf = AUTO_STRBUF_INITIALIZER; - StrBuf ParamList = AUTO_STRBUF_INITIALIZER; - StrBuf East = AUTO_STRBUF_INITIALIZER; - StrBuf West = AUTO_STRBUF_INITIALIZER; - - /* Get the function descriptor used in definition */ - const FuncDesc* D = GetFuncDefinitionDesc (T); - - /* Get the parameter list string. Start from the first parameter */ - SymEntry* Param = D->SymTab->SymHead; - unsigned I; - for (I = 0; I < D->ParamCount; ++I) { - CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); - if (I > 0) { - SB_AppendStr (&ParamList, ", "); - } - if (SymIsRegVar (Param)) { - SB_AppendStr (&ParamList, "register "); - } - if (!HasAnonName (Param)) { - SB_AppendStr (&Buf, Param->Name); - } - SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); - SB_Clear (&Buf); - /* Next argument */ - Param = Param->NextSym; - } - if ((D->Flags & FD_VARIADIC) == 0) { - if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { - SB_AppendStr (&ParamList, "void"); - } - } else { - if (D->ParamCount > 0) { - SB_AppendStr (&ParamList, ", ..."); - } else { - SB_AppendStr (&ParamList, "..."); - } - } - SB_Terminate (&ParamList); - - /* Get the function qualifiers */ - if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { - /* Append a space between the qualifiers and the name */ - SB_AppendChar (&Buf, ' '); - } - SB_Terminate (&Buf); - - /* Get the signature string without the return type */ - SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); - SB_Done (&Buf); - SB_Done (&ParamList); - - /* Complete with the return type */ - GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); - SB_Append (&West, &East); - SB_Terminate (&West); - - /* Output */ - fprintf (F, "%s", SB_GetConstBuf (&West)); - SB_Done (&East); - SB_Done (&West); -} - - - -void PrintRawType (FILE* F, const Type* T) -/* Print a type string in raw hex format (for debugging) */ -{ - while (T->C != T_END) { - fprintf (F, "%04lX ", T->C); - ++T; - } - fprintf (F, "\n"); -} - - - -int TypeHasAttr (const Type* T) -/* Return true if the given type has attribute data */ -{ - return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); -} - - - const Type* GetUnderlyingType (const Type* Type) /* Get the underlying type of an enum or other integer class type */ { @@ -700,6 +586,18 @@ const Type* GetUnderlyingType (const Type* Type) if (Type->A.S->V.E.Type != 0) { return Type->A.S->V.E.Type; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break; + case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break; + case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break; + default: Type = IsSignSigned (Type) ? type_int : type_uint; break; + } } return Type; @@ -713,13 +611,14 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) */ { TypeCode Underlying = UnqualifiedType (Type->C); - TypeCode TCode; if (IsISOChar (Type)) { return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; } else if (IsTypeEnum (Type)) { + TypeCode TCode; + /* This should not happen, but just in case */ if (Type->A.S == 0) { Internal ("Enum tag type error in GetUnderlyingTypeCode"); @@ -742,6 +641,21 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; default: Underlying |= T_TYPE_INT; break; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Underlying = T_CHAR; break; + case SIZEOF_INT: Underlying = T_INT; break; + case SIZEOF_LONG: Underlying = T_LONG; break; + case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break; + default: Underlying = T_INT; break; + } + Underlying &= ~T_MASK_SIGN; + Underlying |= Type->C & T_MASK_SIGN; } return Underlying; @@ -906,7 +820,7 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: - NewType = TypeOfBySize (T); + NewType = TypeOfBySize (SizeOf (T)); if (NewType != CF_NONE) { return NewType; } @@ -968,6 +882,48 @@ Type* IndirectModifiable (Type* T) +Type* NewPointerTo (const Type* T) +/* Return a type string that is "pointer to T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE); + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + +const Type* AddressOf (const Type* T) +/* Return a type string that is "address of T". The type string is allocated +** on the heap and may be freed after use. +*/ +{ + /* Get the size of the type string including the terminator */ + unsigned Size = TypeLen (T) + 1; + + /* Allocate the new type string */ + Type* P = TypeAlloc (Size + 1); + + /* Create the return type... */ + P[0].C = T_PTR | (T[0].C & T_QUAL_ADDRSIZE) | T_QUAL_CONST; + memcpy (P+1, T, Size * sizeof (Type)); + + /* ...and return it */ + return P; +} + + + Type* ArrayToPtr (const Type* T) /* Convert an array to a pointer to it's first element */ { @@ -977,6 +933,200 @@ Type* ArrayToPtr (const Type* T) +const Type* PtrConversion (const Type* T) +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. Otherwise +** return T. +*/ +{ + if (IsTypeFunc (T)) { + return AddressOf (T); + } else if (IsTypeArray (T)) { + return AddressOf (GetElementType (T)); + } else { + return T; + } +} + + + +const Type* IntPromotion (const Type* T) +/* Apply the integer promotions to T and return the result. The returned type +** string may be T if there is no need to change it. +*/ +{ + /* We must have an int to apply int promotions */ + PRECONDITION (IsClassInt (T)); + + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 + ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or + ** an object that has enumeration type, may be used in an expression wherever an int or + ** unsigned int may be used. If an int can represent all values of the original type, + ** the value is converted to an int; otherwise it is converted to an unsigned int. + ** These are called the integral promotions. + */ + + if (IsTypeBitField (T)) { + /* The standard rule is OK for now as we don't support bit-fields with widths > 16. + */ + return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; + } else if (IsTypeChar (T)) { + /* An integer can represent all values from either signed or unsigned char, so convert + ** chars to int. + */ + return type_int; + } else if (IsTypeShort (T)) { + /* An integer cannot represent all values from unsigned short, so convert unsigned short + ** to unsigned int. + */ + return IsSignUnsigned (T) ? type_uint : type_int; + } else if (!IsIncompleteESUType (T)) { + /* The type is a complete type not smaller than int, so leave it alone. */ + return T; + } else { + /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. + ** Assume int to avoid further errors. + */ + return type_int; + } +} + + + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) +/* Perform the usual arithmetic conversions for binary operators. */ +{ + /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 + ** Many binary operators that expect operands of arithmetic type cause conversions and yield + ** result types in a similar way. The purpose is to yield a common type, which is also the type + ** of the result. This pattern is called the usual arithmetic conversions. + */ + + /* There are additional rules for floating point types that we don't bother with, since + ** floating point types are not (yet) supported. + ** The integral promotions are performed on both operands. + */ + lhst = IntPromotion (lhst); + rhst = IntPromotion (rhst); + + /* If either operand has type unsigned long int, the other operand is converted to + ** unsigned long int. + */ + if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || + (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { + return type_ulong; + } + + /* Otherwise, if one operand has type long int and the other has type unsigned int, + ** if a long int can represent all values of an unsigned int, the operand of type unsigned int + ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, + ** both operands are converted to unsigned long int. + */ + if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || + (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { + /* long can represent all unsigneds, so we are in the first sub-case. */ + return type_long; + } + + /* Otherwise, if either operand has type long int, the other operand is converted to long int. + */ + if (IsTypeLong (lhst) || IsTypeLong (rhst)) { + return type_long; + } + + /* Otherwise, if either operand has type unsigned int, the other operand is converted to + ** unsigned int. + */ + if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || + (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { + return type_uint; + } + + /* Otherwise, both operands have type int. */ + CHECK (IsTypeInt (lhst)); + CHECK (IsSignSigned (lhst)); + CHECK (IsTypeInt (rhst)); + CHECK (IsSignSigned (rhst)); + return type_int; +} + + + +const Type* SignedType (const Type* T) +/* Get signed counterpart of the integral type */ +{ + switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { + case T_TYPE_CHAR: + return type_schar; + + case T_TYPE_INT: + case T_TYPE_SHORT: + return type_int; + + case T_TYPE_LONG: + return type_long; + + default: + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + return T; + } +} + + + +const Type* UnsignedType (const Type* T) +/* Get unsigned counterpart of the integral type */ +{ + switch (GetUnderlyingTypeCode (T) & T_MASK_TYPE) { + case T_TYPE_CHAR: + return type_uchar; + + case T_TYPE_INT: + case T_TYPE_SHORT: + return type_uint; + + case T_TYPE_LONG: + return type_ulong; + + default: + Internal ("Unknown type code: %lX", GetUnderlyingTypeCode (T)); + return T; + } +} + + + +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth) +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ +{ + Type* P; + + /* The type specifier must be integeral */ + CHECK (IsClassInt (T)); + + /* Allocate the new type string */ + P = TypeAlloc (3); + + /* Create the return type... */ + P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD; + P[0].C |= (T[0].C & T_QUAL_ADDRSIZE); + P[0].A.B.Offs = BitOffs; + P[0].A.B.Width = BitWidth; + + /* Get the declaration type */ + memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1])); + + /* Get done... */ + P[2].C = T_END; + + /* ...and return it */ + return P; +} + + + int IsClassObject (const Type* T) /* Return true if this is a fully described object type */ { @@ -1266,62 +1416,6 @@ void SetESUSymEntry (Type* T, struct SymEntry* S) -const Type* IntPromotion (const Type* T) -/* Apply the integer promotions to T and return the result. The returned type -** string may be T if there is no need to change it. -*/ -{ - /* We must have an int to apply int promotions */ - PRECONDITION (IsClassInt (T)); - - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 - ** A char, a short int, or an int bit-field, or their signed or unsigned varieties, or an - ** object that has enumeration type, may be used in an expression wherever an int or - ** unsigned int may be used. If an int can represent all values of the original type, the value - ** is converted to an int; otherwise it is converted to an unsigned int. - ** These are called the integral promotions. - */ - - if (IsTypeChar (T)) { - /* An integer can represent all values from either signed or unsigned char, so convert - ** chars to int. - */ - return type_int; - } else if (IsTypeShort (T)) { - /* An integer cannot represent all values from unsigned short, so convert unsigned short - ** to unsigned int. - */ - return IsSignUnsigned (T) ? type_uint : type_int; - } else if (!IsIncompleteESUType (T)) { - /* The type is a complete type not smaller than int, so leave it alone. */ - return T; - } else { - /* Otherwise, this is an incomplete enum, and there is expceted to be an error already. - ** Assume int to avoid further errors. - */ - return type_int; - } -} - - - -const Type* PtrConversion (const Type* T) -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. Otherwise -** return T. -*/ -{ - if (IsTypeFunc (T)) { - return NewPointerTo (T); - } else if (IsTypeArray (T)) { - return ArrayToPtr (T); - } else { - return T; - } -} - - - TypeCode AddrSizeQualifier (unsigned AddrSize) /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ { @@ -1339,3 +1433,101 @@ TypeCode AddrSizeQualifier (unsigned AddrSize) } } + + + +int TypeHasAttr (const Type* T) +/* Return true if the given type has attribute data */ +{ + return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T); +} + + + +void PrintType (FILE* F, const Type* T) +/* Print fulle name of the type */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T))); + SB_Done (&Buf); +} + + + +void PrintFuncSig (FILE* F, const char* Name, const Type* T) +/* Print a function signature */ +{ + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + StrBuf ParamList = AUTO_STRBUF_INITIALIZER; + StrBuf East = AUTO_STRBUF_INITIALIZER; + StrBuf West = AUTO_STRBUF_INITIALIZER; + + /* Get the function descriptor used in definition */ + const FuncDesc* D = GetFuncDefinitionDesc (T); + + /* Get the parameter list string. Start from the first parameter */ + SymEntry* Param = D->SymTab->SymHead; + unsigned I; + for (I = 0; I < D->ParamCount; ++I) { + CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0); + if (I > 0) { + SB_AppendStr (&ParamList, ", "); + } + if (SymIsRegVar (Param)) { + SB_AppendStr (&ParamList, "register "); + } + if (!HasAnonName (Param)) { + SB_AppendStr (&Buf, Param->Name); + } + SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type))); + SB_Clear (&Buf); + /* Next argument */ + Param = Param->NextSym; + } + if ((D->Flags & FD_VARIADIC) == 0) { + if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) { + SB_AppendStr (&ParamList, "void"); + } + } else { + if (D->ParamCount > 0) { + SB_AppendStr (&ParamList, ", ..."); + } else { + SB_AppendStr (&ParamList, "..."); + } + } + SB_Terminate (&ParamList); + + /* Get the function qualifiers */ + if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) { + /* Append a space between the qualifiers and the name */ + SB_AppendChar (&Buf, ' '); + } + SB_Terminate (&Buf); + + /* Get the signature string without the return type */ + SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList)); + SB_Done (&Buf); + SB_Done (&ParamList); + + /* Complete with the return type */ + GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T)); + SB_Append (&West, &East); + SB_Terminate (&West); + + /* Output */ + fprintf (F, "%s", SB_GetConstBuf (&West)); + SB_Done (&East); + SB_Done (&West); +} + + + +void PrintRawType (FILE* F, const Type* T) +/* Print a type string in raw hex format (for debugging) */ +{ + while (T->C != T_END) { + fprintf (F, "%04lX ", T->C); + ++T; + } + fprintf (F, "\n"); +} diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index af1c6b8e4..e36d7c82e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -78,54 +78,55 @@ enum { T_TYPE_INT = 0x000003, T_TYPE_LONG = 0x000004, T_TYPE_LONGLONG = 0x000005, - T_TYPE_ENUM = 0x000006, - T_TYPE_FLOAT = 0x000007, - T_TYPE_DOUBLE = 0x000008, - T_TYPE_VOID = 0x000009, - T_TYPE_STRUCT = 0x00000A, - T_TYPE_UNION = 0x00000B, - T_TYPE_ARRAY = 0x00000C, - T_TYPE_PTR = 0x00000D, - T_TYPE_FUNC = 0x00000E, - T_MASK_TYPE = 0x00000F, + T_TYPE_ENUM = 0x000008, + T_TYPE_BITFIELD = 0x000009, + T_TYPE_FLOAT = 0x00000A, + T_TYPE_DOUBLE = 0x00000B, + T_TYPE_VOID = 0x000010, + T_TYPE_STRUCT = 0x000011, + T_TYPE_UNION = 0x000012, + T_TYPE_ARRAY = 0x000018, + T_TYPE_PTR = 0x000019, + T_TYPE_FUNC = 0x00001A, + T_MASK_TYPE = 0x00001F, /* Type classes */ T_CLASS_NONE = 0x000000, - T_CLASS_INT = 0x000010, - T_CLASS_FLOAT = 0x000020, - T_CLASS_PTR = 0x000030, - T_CLASS_STRUCT = 0x000040, - T_CLASS_FUNC = 0x000050, - T_MASK_CLASS = 0x000070, + T_CLASS_INT = 0x000020, + T_CLASS_FLOAT = 0x000040, + T_CLASS_PTR = 0x000060, + T_CLASS_STRUCT = 0x000080, + T_CLASS_FUNC = 0x0000A0, + T_MASK_CLASS = 0x0000E0, /* Type signedness */ T_SIGN_NONE = 0x000000, - T_SIGN_UNSIGNED = 0x000080, - T_SIGN_SIGNED = 0x000100, - T_MASK_SIGN = 0x000180, + T_SIGN_UNSIGNED = 0x000100, + T_SIGN_SIGNED = 0x000200, + T_MASK_SIGN = 0x000300, /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_CHAR = 0x000200, - T_SIZE_SHORT = 0x000400, - T_SIZE_INT = 0x000600, - T_SIZE_LONG = 0x000800, - T_SIZE_LONGLONG = 0x000A00, - T_MASK_SIZE = 0x000E00, + T_SIZE_CHAR = 0x001000, + T_SIZE_SHORT = 0x002000, + T_SIZE_INT = 0x003000, + T_SIZE_LONG = 0x004000, + T_SIZE_LONGLONG = 0x005000, + T_MASK_SIZE = 0x00F000, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x001000, - T_QUAL_VOLATILE = 0x002000, - T_QUAL_RESTRICT = 0x004000, + T_QUAL_CONST = 0x010000, + T_QUAL_VOLATILE = 0x020000, + T_QUAL_RESTRICT = 0x040000, T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, - T_QUAL_NEAR = 0x008000, - T_QUAL_FAR = 0x010000, + T_QUAL_NEAR = 0x080000, + T_QUAL_FAR = 0x100000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x020000, - T_QUAL_CDECL = 0x040000, + T_QUAL_FASTCALL = 0x200000, + T_QUAL_CDECL = 0x400000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x07F000, + T_MASK_QUAL = 0x7F0000, /* Types */ T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, @@ -140,6 +141,8 @@ enum { T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, + T_SBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_UBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, @@ -171,6 +174,10 @@ struct Type { struct SymEntry* S; /* Enum/struct/union tag symbol entry pointer */ long L; /* Numeric attribute value */ unsigned long U; /* Dito, unsigned */ + struct { + unsigned Offs; /* Bit offset into storage unit */ + unsigned Width; /* Width in bits */ + } B; /* Data for bit fields */ } A; /* Type attribute if necessary */ }; @@ -288,33 +295,6 @@ unsigned long GetIntegerTypeMax (const Type* Type); ** The type must have a known size. */ -Type* NewPointerTo (const Type* T); -/* Return a type string that is "pointer to T". The type string is allocated -** on the heap and may be freed after use. -*/ - -void PrintType (FILE* F, const Type* T); -/* Print fulle name of the type */ - -void PrintFuncSig (FILE* F, const char* Name, const Type* T); -/* Print a function signature */ - -void PrintRawType (FILE* F, const Type* T); -/* Print a type string in raw hex format (for debugging) */ - -int TypeHasAttr (const Type* T); -/* Return true if the given type has attribute data */ - -#if defined(HAVE_INLINE) -INLINE void CopyTypeAttr (const Type* Src, Type* Dest) -/* Copy attribute data from Src to Dest */ -{ - Dest->A = Src->A; -} -#else -# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) -#endif - #if defined(HAVE_INLINE) INLINE TypeCode UnqualifiedType (TypeCode T) /* Return the unqalified type code */ @@ -366,9 +346,44 @@ Type* IndirectModifiable (Type* T); ** given type points to. */ +Type* NewPointerTo (const Type* T); +/* Return a type string that is "pointer to T". The type string is allocated +** on the heap and may be freed after use. +*/ + +const Type* AddressOf (const Type* T); +/* Return a type string that is "address of T". The type string is allocated +** on the heap and may be freed after use. +*/ + Type* ArrayToPtr (const Type* T); /* Convert an array to a pointer to it's first element */ +const Type* PtrConversion (const Type* T); +/* If the type is a function, convert it to pointer to function. If the +** expression is an array, convert it to pointer to first element. Otherwise +** return T. +*/ + +const Type* IntPromotion (const Type* T); +/* Apply the integer promotions to T and return the result. The returned type +** string may be T if there is no need to change it. +*/ + +const Type* ArithmeticConvert (const Type* lhst, const Type* rhst); +/* Perform the usual arithmetic conversions for binary operators. */ + +const Type* SignedType (const Type* T); +/* Get signed counterpart of the integral type */ + +const Type* UnsignedType (const Type* T); +/* Get unsigned counterpart of the integral type */ + +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth); +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ @@ -511,6 +526,36 @@ INLINE int IsTypeEnum (const Type* T) # define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeSignedBitField (const Type* T) +/* Return true if this is a signed bit-field */ +{ + return (UnqualifiedType (T->C) == T_SBITFIELD); +} +#else +# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == T_SBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeUnsignedBitField (const Type* T) +/* Return true if this is an unsigned bit-field */ +{ + return (UnqualifiedType (T->C) == T_UBITFIELD); +} +#else +# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeBitField (const Type* T) +/* Return true if this is a bit-field (either signed or unsigned) */ +{ + return IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T); +} +#else +# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T)) +#endif + #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ @@ -892,17 +937,6 @@ struct SymEntry* GetESUSymEntry (const Type* T) attribute ((const)); void SetESUSymEntry (Type* T, struct SymEntry* S); /* Set the SymEntry pointer for an enum/struct/union type */ -const Type* IntPromotion (const Type* T); -/* Apply the integer promotions to T and return the result. The returned type -** string may be T if there is no need to change it. -*/ - -const Type* PtrConversion (const Type* T); -/* If the type is a function, convert it to pointer to function. If the -** expression is an array, convert it to pointer to first element. Otherwise -** return T. -*/ - TypeCode AddrSizeQualifier (unsigned AddrSize); /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ @@ -926,6 +960,28 @@ INLINE TypeCode DataAddrSizeQualifier (void) # define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize)) #endif +int TypeHasAttr (const Type* T); +/* Return true if the given type has attribute data */ + +#if defined(HAVE_INLINE) +INLINE void CopyTypeAttr (const Type* Src, Type* Dest) +/* Copy attribute data from Src to Dest */ +{ + Dest->A = Src->A; +} +#else +# define CopyTypeAttr(Src, Dest) ((Dest)->A = (Src)->A) +#endif + +void PrintType (FILE* F, const Type* T); +/* Print fulle name of the type */ + +void PrintFuncSig (FILE* F, const char* Name, const Type* T); +/* Print a function signature */ + +void PrintRawType (FILE* F, const Type* T); +/* Print a type string in raw hex format (for debugging) */ + /* End of datatype.h */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index a18c837b9..c1346e872 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2243,7 +2243,7 @@ static void DefineData (ExprDesc* Expr) -static void OutputBitFieldData (StructInitData* SI) +static void DefineBitFieldData (StructInitData* SI) /* Output bit field data */ { /* Ignore if we have no data */ @@ -2266,7 +2266,18 @@ static void OutputBitFieldData (StructInitData* SI) -static ExprDesc ParseScalarInitInternal (Type* T) +static void DefineStrData (Literal* Lit, unsigned Count) +{ + /* Translate into target charset */ + TranslateLiteral (Lit); + + /* Output the data */ + g_defbytes (GetLiteralStr (Lit), Count); +} + + + +static ExprDesc ParseScalarInitInternal (const Type* T) /* Parse initializaton for scalar data types. This function will not output the ** data but return it in ED. */ @@ -2293,7 +2304,7 @@ static ExprDesc ParseScalarInitInternal (Type* T) -static unsigned ParseScalarInit (Type* T) +static unsigned ParseScalarInit (const Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { /* Parse initialization */ @@ -2311,7 +2322,7 @@ static unsigned ParseScalarInit (Type* T) -static unsigned ParsePointerInit (Type* T) +static unsigned ParsePointerInit (const Type* T) /* Parse initializaton for pointer data types. Return the number of data bytes. */ { /* Optional opening brace */ @@ -2364,9 +2375,6 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) NextToken (); } - /* Translate into target charset */ - TranslateLiteral (CurTok.SVal); - /* If the array is one too small for the string literal, omit the ** trailing zero. */ @@ -2379,7 +2387,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Output the data */ - g_defbytes (GetLiteralStr (CurTok.SVal), Count); + DefineStrData (CurTok.SVal, Count); /* Skip the string */ NextToken (); @@ -2453,7 +2461,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers) static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of a struct or union. Return the number of data bytes. */ { - SymEntry* Entry; + SymEntry* Sym; SymTable* Tab; StructInitData SI; int HasCurly = 0; @@ -2468,15 +2476,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the struct entry from the type */ - Entry = GetESUSymEntry (T); + Sym = GetESUSymEntry (T); /* Get the size of the struct from the symbol table entry */ - SI.Size = Entry->V.S.Size; + SI.Size = Sym->V.S.Size; /* Check if this struct definition has a field table. If it doesn't, it ** is an incomplete definition. */ - Tab = Entry->V.S.SymTab; + Tab = Sym->V.S.SymTab; if (Tab == 0) { Error ("Cannot initialize variables with incomplete type"); /* Try error recovery */ @@ -2486,7 +2494,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Get a pointer to the list of symbols */ - Entry = Tab->SymHead; + Sym = Tab->SymHead; /* Initialize fields */ SI.Offs = 0; @@ -2495,7 +2503,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) while (CurTok.Tok != TOK_RCURLY) { /* Check for excess elements */ - if (Entry == 0) { + if (Sym == 0) { /* Is there just one trailing comma before a closing curly? */ if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) { /* Skip comma and exit scope */ @@ -2511,7 +2519,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) } /* Check for special members that don't consume the initializer */ - if ((Entry->Flags & SC_ALIAS) == SC_ALIAS) { + if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) { /* Just skip */ goto NextMember; } @@ -2519,17 +2527,17 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* This may be an anonymous bit-field, in which case it doesn't ** have an initializer. */ - if (SymIsBitField (Entry) && (IsAnonName (Entry->Name))) { + if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) { /* Account for the data and output it if we have at least a full ** word. We may have more if there was storage unit overlap, for ** example two consecutive 10 bit fields. These will be packed ** into 3 bytes. */ - SI.ValBits += Entry->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); } /* Avoid consuming the comma if any */ goto NextMember; @@ -2541,7 +2549,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SkipComma = 0; } - if (SymIsBitField (Entry)) { + if (SymIsBitField (Sym)) { /* Parse initialization of one field. Bit-fields need a special ** handling. @@ -2552,14 +2560,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Entry->V.B.BitWidth) - 1U; + unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; /* Safety ... */ - CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == - SI.Offs * CHAR_BITS + SI.ValBits); + CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == + SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ED = ParseScalarInitInternal (Entry->Type); + ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2569,31 +2577,31 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** any useful bits. */ Val = (unsigned) ED.IVal & Mask; - if (IsSignUnsigned (Entry->Type)) { + if (IsSignUnsigned (Sym->Type)) { if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %u", - GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), - Entry->V.B.BitWidth, ED.IVal, Val); + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, ED.IVal, Val); } } else { /* Sign extend back to full width of host long. */ - unsigned ShiftBits = sizeof (long) * CHAR_BIT - Entry->V.B.BitWidth; + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); if (ED.IVal != RestoredVal) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " "changes value from %ld to %ld", - GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), - Entry->V.B.BitWidth, ED.IVal, RestoredVal); + GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, ED.IVal, RestoredVal); } } /* Add the value to the currently stored bit-field value */ - Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; + Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Entry->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* Make sure unsigned is big enough to hold the value, 22 bits. ** This is 22 bits because the most we can have is 7 bits left ** over from the previous OutputBitField call, plus 15 bits @@ -2604,7 +2612,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); } } else { @@ -2618,7 +2626,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, 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, Braces, AllowFlexibleMembers && Entry->NextSym == 0); + SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0); } /* More initializers? */ @@ -2633,10 +2641,10 @@ NextMember: /* Next member. For unions, only the first one can be initialized */ if (IsTypeUnion (T)) { /* Union */ - Entry = 0; + Sym = 0; } else { /* Struct */ - Entry = Entry->NextSym; + Sym = Sym->NextSym; } } @@ -2647,7 +2655,7 @@ NextMember: /* If we have data from a bit-field left, output it now */ CHECK (SI.ValBits < CHAR_BITS); - OutputBitFieldData (&SI); + DefineBitFieldData (&SI); /* If there are struct fields left, reserve additional storage */ if (SI.Offs < SI.Size) { diff --git a/src/cc65/expr.c b/src/cc65/expr.c index a63214f49..3b9307a37 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -47,13 +47,8 @@ -/* Generator attributes */ -#define GEN_NOPUSH 0x01 /* Don't push lhs */ -#define GEN_COMM 0x02 /* Operator is commutative */ -#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ - /* Map a generator function and its attributes to a token */ -typedef struct { +typedef struct GenDesc { token_t Tok; /* Token to map to */ unsigned Flags; /* Flags for generator function */ void (*Func) (unsigned, unsigned long); /* Generator func */ @@ -80,6 +75,8 @@ static GenDesc GenOASGN = { TOK_OR_ASSIGN, GEN_NOPUSH, g_or }; static void parseadd (ExprDesc* Expr, int DoArrayRef); +static void PostInc (ExprDesc* Expr); +static void PostDec (ExprDesc* Expr); @@ -88,7 +85,8 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef); /*****************************************************************************/ -static unsigned GlobalModeFlags (const ExprDesc* Expr) + +unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { @@ -150,65 +148,6 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) -static const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) -/* Perform the usual arithmetic conversions for binary operators. */ -{ - /* https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 - ** Many binary operators that expect operands of arithmetic type cause conversions and yield - ** result types in a similar way. The purpose is to yield a common type, which is also the type - ** of the result. This pattern is called the usual arithmetic conversions. - */ - - /* There are additional rules for floating point types that we don't bother with, since - ** floating point types are not (yet) supported. - ** The integral promotions are performed on both operands. - */ - lhst = IntPromotion (lhst); - rhst = IntPromotion (rhst); - - /* If either operand has type unsigned long int, the other operand is converted to - ** unsigned long int. - */ - if ((IsTypeLong (lhst) && IsSignUnsigned (lhst)) || - (IsTypeLong (rhst) && IsSignUnsigned (rhst))) { - return type_ulong; - } - - /* Otherwise, if one operand has type long int and the other has type unsigned int, - ** if a long int can represent all values of an unsigned int, the operand of type unsigned int - ** is converted to long int ; if a long int cannot represent all the values of an unsigned int, - ** both operands are converted to unsigned long int. - */ - if ((IsTypeLong (lhst) && IsTypeInt (rhst) && IsSignUnsigned (rhst)) || - (IsTypeLong (rhst) && IsTypeInt (lhst) && IsSignUnsigned (lhst))) { - /* long can represent all unsigneds, so we are in the first sub-case. */ - return type_long; - } - - /* Otherwise, if either operand has type long int, the other operand is converted to long int. - */ - if (IsTypeLong (lhst) || IsTypeLong (rhst)) { - return type_long; - } - - /* Otherwise, if either operand has type unsigned int, the other operand is converted to - ** unsigned int. - */ - if ((IsTypeInt (lhst) && IsSignUnsigned (lhst)) || - (IsTypeInt (rhst) && IsSignUnsigned (rhst))) { - return type_uint; - } - - /* Otherwise, both operands have type int. */ - CHECK (IsTypeInt (lhst)); - CHECK (IsSignSigned (lhst)); - CHECK (IsTypeInt (rhst)); - CHECK (IsSignSigned (rhst)); - return type_int; -} - - - static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) /* Adjust the two values for a binary operation. lhs is expected on stack or ** to be constant, rhs is expected to be in the primary register or constant. @@ -260,7 +199,7 @@ static unsigned typeadjust (ExprDesc* lhs, const ExprDesc* rhs, int NoPush) -static void LimitExprValue (ExprDesc* Expr) +void LimitExprValue (ExprDesc* Expr) /* Limit the constant value of the expression to the range of its type */ { switch (GetUnderlyingTypeCode (Expr->Type)) { @@ -446,117 +385,175 @@ static void DeferDec (const ExprDesc* Expr) -static void DeferredInc (ExprDesc* Expr) -/* Do the deferred post-inc */ +static void DoInc (ExprDesc* Expr, unsigned KeepResult) +/* Do increment */ { unsigned Flags; - unsigned long Val; - - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; + long Val; /* 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)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - 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; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; + /* Do the increment */ + g_inc (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_addeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_addeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_inc (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_addeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoInc(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredInc(): 0x%04X", ED_GetLoc (Expr)); } } -static void DeferredDec (ExprDesc* Expr) -/* Do the deferred post-dec */ +static void DoDec (ExprDesc* Expr, unsigned KeepResult) +/* Do decrement */ { unsigned Flags; - unsigned long Val; + long Val; - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; - - /* Get the increment value in bytes */ + /* Get the decrement value in bytes */ Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, -Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - 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; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; + /* Do the decrement */ + g_dec (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_subeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_subeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_dec (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_subeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoDec(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredDec(): 0x%04X", ED_GetLoc (Expr)); } } @@ -620,11 +617,11 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) switch (Op->OpType) { case DOT_INC: - DeferredInc (&Op->Expr); + DoInc (&Op->Expr, OA_NEED_NONE); break; case DOT_DEC: - DeferredDec (&Op->Expr); + DoDec (&Op->Expr, OA_NEED_NONE); break; } xfree (&Op->Expr); @@ -651,6 +648,7 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) } + static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Parse the argument list of the called function and pass the arguments to it. ** Depending on several criteria, this may be done by just pushing into each @@ -780,14 +778,17 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); } else { + /* Load the value into the primary if it is not already there */ + LoadExpr (CF_NONE, &Expr); + /* Use the type of the argument for the push */ Flags |= TypeOf (Expr.Type); } - /* 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); @@ -1079,7 +1080,7 @@ static void Primary (ExprDesc* E) /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { - E->FVal = CurTok.FVal; + E->V.FVal = CurTok.FVal; E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; E->Type = CurTok.Type; NextToken (); @@ -1254,9 +1255,9 @@ static void Primary (ExprDesc* E) case TOK_WCSCONST: /* String literal */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - E->LVal = UseLiteral (CurTok.SVal); + E->V.LVal = UseLiteral (CurTok.SVal); } else { - E->LVal = CurTok.SVal; + E->V.LVal = CurTok.SVal; } E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; @@ -1456,14 +1457,14 @@ static void StructRef (ExprDesc* Expr) */ BitOffs = Field.V.Offs * CHAR_BITS; if (SymIsBitField (&Field)) { - BitOffs += Field.V.B.BitOffs; + BitOffs += Field.Type->A.B.Offs; g_asr (Flags, BitOffs); /* Mask the value. This is unnecessary if the shift executed above ** moved only zeroes into the value. */ - if (BitOffs + Field.V.B.BitWidth != FieldSize * CHAR_BITS) { + if (BitOffs + Field.Type->A.B.Width != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, - (0x0001U << Field.V.B.BitWidth) - 1U); + (0x0001U << Field.Type->A.B.Width) - 1U); } } else { g_asr (Flags, BitOffs); @@ -1490,12 +1491,7 @@ static void StructRef (ExprDesc* Expr) ED_AddrExpr (Expr); } - /* Make the expression a bit field if necessary */ - if (SymIsBitField (&Field)) { - ED_MakeBitField (Expr, Field.V.B.BitOffs, Field.V.B.BitWidth); - } } - } @@ -1510,7 +1506,8 @@ static void hie11 (ExprDesc *Expr) Primary (Expr); /* Check for a rhs */ - while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || + while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC || + CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN || CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) { switch (CurTok.Tok) { @@ -1554,6 +1551,14 @@ static void hie11 (ExprDesc *Expr) StructRef (Expr); break; + case TOK_INC: + PostInc (Expr); + break; + + case TOK_DEC: + PostDec (Expr); + break; + default: Internal ("Invalid token in hie11: %d", CurTok.Tok); @@ -1629,9 +1634,6 @@ void Store (ExprDesc* Expr, const Type* StoreType) static void PreInc (ExprDesc* Expr) /* Handle the preincrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1647,49 +1649,8 @@ static void PreInc (ExprDesc* Expr) Error ("Increment of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression referenced in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the increment */ + DoInc (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1700,9 +1661,6 @@ static void PreInc (ExprDesc* Expr) static void PreDec (ExprDesc* Expr) /* Handle the predecrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1718,49 +1676,8 @@ static void PreDec (ExprDesc* Expr) Error ("Decrement of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the decrement */ + DoDec (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1771,7 +1688,7 @@ static void PreDec (ExprDesc* Expr) static void PostInc (ExprDesc* Expr) /* Handle the postincrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); @@ -1795,42 +1712,25 @@ static void PostInc (ExprDesc* Expr) */ /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* 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); + /* Do the increment */ + DoInc (Expr, OA_NEED_OLD); } else { - /* Fetch the value and use it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - /* Defer the increment until after the value of this expression is used */ DeferInc (Expr); + + /* Just return */ + return; } } @@ -1843,11 +1743,11 @@ static void PostInc (ExprDesc* Expr) static void PostDec (ExprDesc* Expr) /* Handle the postdecrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); - /* The expression to increment must be an lvalue */ + /* The expression to decrement must be an lvalue */ if (!ED_IsLVal (Expr)) { Error ("Invalid lvalue"); return; @@ -1862,42 +1762,25 @@ static void PostDec (ExprDesc* Expr) Flags = TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* 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); + /* Do the decrement */ + DoDec (Expr, OA_NEED_OLD); } else { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - /* Defer the decrement until after the value of this expression is used */ DeferDec (Expr); + + /* Just return */ + return; } } @@ -1910,8 +1793,6 @@ static void PostDec (ExprDesc* Expr) static void UnaryOp (ExprDesc* Expr) /* Handle unary -/+ and ~ */ { - unsigned Flags; - /* Remember the operator token and skip it */ token_t Tok = CurTok.Tok; NextToken (); @@ -1935,15 +1816,24 @@ static void UnaryOp (ExprDesc* Expr) default: Internal ("Unexpected token: %d", Tok); } + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + /* Limit the calculated value to the range of its type */ LimitExprValue (Expr); } else { + unsigned Flags; + /* Value is not constant */ LoadExpr (CF_NONE, Expr); - /* Adjust the type of the value */ - Flags = g_typeadjust (TypeOf (Expr->Type), TypeOf (type_int) | CF_CONST); + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + TypeConversion (Expr, Expr->Type); + + /* Get code generation flags */ + Flags = TypeOf (Expr->Type); /* Handle the operation */ switch (Tok) { @@ -1956,9 +1846,6 @@ 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); } @@ -2050,13 +1937,13 @@ void hie10 (ExprDesc* Expr) if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { if (ED_IsRVal (Expr)) { Error ("Illegal address"); - break; + /* Continue anyway, just to avoid further warnings */ } - if (ED_IsBitField (Expr)) { + if (IsTypeBitField (Expr->Type)) { Error ("Cannot take address of bit-field"); - /* Do it anyway, just to avoid further warnings */ - ED_DisBitField (Expr); + /* Continue anyway, just to avoid further warnings */ + Expr->Type = GetUnderlyingType (Expr->Type); } /* The & operator yields an rvalue address */ ED_AddrExpr (Expr); @@ -2081,7 +1968,7 @@ void hie10 (ExprDesc* Expr) ED_Init (&Uneval); ED_MarkForUneval (&Uneval); hie10 (&Uneval); - if (ED_IsBitField (&Uneval)) { + if (IsTypeBitField (Uneval.Type)) { Error ("Cannot apply 'sizeof' to bit-field"); Size = 0; } else { @@ -2106,13 +1993,6 @@ void hie10 (ExprDesc* Expr) /* An expression */ hie11 (Expr); - /* Handle post increment */ - switch (CurTok.Tok) { - case TOK_INC: PostInc (Expr); break; - case TOK_DEC: PostDec (Expr); break; - default: break; - } - } break; } @@ -2590,6 +2470,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Determine the signedness of the operands */ int LeftSigned = IsSignSigned (Expr->Type); int RightSigned = IsSignSigned (Expr2.Type); + int CmpSigned = IsClassInt (Expr->Type) && IsClassInt (Expr2.Type) && + IsSignSigned (ArithmeticConvert (Expr->Type, Expr2.Type)); /* If the right hand side is constant, and the generator function ** expects the lhs in the primary, remove the push of the primary @@ -2684,6 +2566,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ */ flags |= (CF_CHAR | CF_FORCECHAR); if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } @@ -2698,10 +2581,15 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_FORCECHAR; } if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } } else { unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + if (CmpSigned) { + ltype &= ~CF_UNSIGNED; + rtype &= ~CF_UNSIGNED; + } flags |= g_typeadjust (ltype, rtype); } @@ -2709,7 +2597,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ ** constant, we may be able to change the compares to something more ** effective. */ - if ((!LeftSigned || !RightSigned) && rconst) { + if (!CmpSigned && rconst) { switch (Tok) { @@ -4175,248 +4063,6 @@ static void hieQuest (ExprDesc* Expr) -static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) -/* Process "op=" operators. */ -{ - unsigned flags; - CodeMark Mark; - int MustScale; - - /* op= can only be used with lvalues */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator token */ - NextToken (); - - /* Determine the type of the lhs */ - flags = TypeOf (Expr->Type); - MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type); - - /* Get the lhs address on stack (if needed) */ - PushAddr (Expr); - - /* Fetch the lhs into the primary register if needed */ - LoadExpr (CF_NONE, Expr); - - /* Bring the lhs on stack */ - GetCodePos (&Mark); - g_push (flags, 0); - - ExprDesc Expr2; - ED_Init (&Expr2); - Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - - /* Evaluate the rhs */ - MarkedExprWithCheck (hie1, &Expr2); - - /* The rhs must be an integer (or a float, but we don't support that yet */ - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Check for a constant expression */ - if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - /* The resulting value is a constant. If the generator has the NOPUSH - ** flag set, don't push the lhs. - */ - if (Gen->Flags & GEN_NOPUSH) { - RemoveCode (&Mark); - } - if (MustScale) { - /* lhs is a pointer, scale rhs */ - Expr2.IVal *= CheckedSizeOf (Expr->Type+1); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Special handling for add and sub - some sort of a hack, but short code */ - if (Gen->Func == g_add) { - g_inc (flags | CF_CONST, Expr2.IVal); - } else if (Gen->Func == g_sub) { - g_dec (flags | CF_CONST, Expr2.IVal); - } else { - if (Expr2.IVal == 0) { - /* Check for div by zero/mod by zero */ - if (Gen->Func == g_div) { - Error ("Division by zero"); - } else if (Gen->Func == g_mod) { - Error ("Modulo operation with zero"); - } - } - Gen->Func (flags | CF_CONST, Expr2.IVal); - } - } else { - - /* rhs is not constant. Load into the primary */ - LoadExpr (CF_NONE, &Expr2); - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0); - } - Store (Expr, 0); - ED_FinalizeRValLoad (Expr); -} - - - -static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) -/* Process the += and -= operators */ -{ - ExprDesc Expr2; - unsigned lflags; - 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) { - /* Use generic routine */ - opeq (Gen, Expr, Op); - return; - } - - /* We must have an lvalue */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Skip the operator */ - NextToken (); - - /* Check if we have a pointer expression and must scale rhs */ - MustScale = IsTypePtr (Expr->Type); - - /* Initialize the code generator flags */ - lflags = 0; - rflags = 0; - - /* Evaluate the rhs. We expect an integer here, since float is not - ** supported - */ - hie1 (&Expr2); - if (!IsClassInt (Expr2.Type)) { - Error ("Invalid right operand for binary operator '%s'", Op); - /* Continue. Wrong code will be generated, but the compiler won't - ** break, so this is the best error recovery. - */ - } - - /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; - - if (ED_IsConstAbs (&Expr2)) { - /* The resulting value is a constant */ - rflags |= CF_CONST; - lflags |= CF_CONST; - - /* Scale it */ - if (MustScale) { - Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); - } - } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); - - /* Convert the type of the rhs to that of the lhs */ - g_typecast (lflags, rflags & ~CF_FORCECHAR); - - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); - } - } - - /* Output apropriate code depending on the location */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Absolute numeric addressed variable, global variable, local - ** static variable, register variable, pooled literal or code - ** label location. - */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_STACK: - /* Value on the stack */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); - } else { - g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); - } - break; - - default: - Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); - } - - /* Expression is an rvalue in the primary now */ - ED_FinalizeRValLoad (Expr); -} - - - void hie1 (ExprDesc* Expr) /* Parse first level of expression hierarchy. */ { @@ -4424,47 +4070,47 @@ void hie1 (ExprDesc* Expr) switch (CurTok.Tok) { case TOK_ASSIGN: - Assignment (Expr); + OpAssign (0, Expr, "="); break; case TOK_PLUS_ASSIGN: - addsubeq (&GenPASGN, Expr, "+="); + OpAddSubAssign (&GenPASGN, Expr, "+="); break; case TOK_MINUS_ASSIGN: - addsubeq (&GenSASGN, Expr, "-="); + OpAddSubAssign (&GenSASGN, Expr, "-="); break; case TOK_MUL_ASSIGN: - opeq (&GenMASGN, Expr, "*="); + OpAssign (&GenMASGN, Expr, "*="); break; case TOK_DIV_ASSIGN: - opeq (&GenDASGN, Expr, "/="); + OpAssign (&GenDASGN, Expr, "/="); break; case TOK_MOD_ASSIGN: - opeq (&GenMOASGN, Expr, "%="); + OpAssign (&GenMOASGN, Expr, "%="); break; case TOK_SHL_ASSIGN: - opeq (&GenSLASGN, Expr, "<<="); + OpAssign (&GenSLASGN, Expr, "<<="); break; case TOK_SHR_ASSIGN: - opeq (&GenSRASGN, Expr, ">>="); + OpAssign (&GenSRASGN, Expr, ">>="); break; case TOK_AND_ASSIGN: - opeq (&GenAASGN, Expr, "&="); + OpAssign (&GenAASGN, Expr, "&="); break; case TOK_XOR_ASSIGN: - opeq (&GenXOASGN, Expr, "^="); + OpAssign (&GenXOASGN, Expr, "^="); break; case TOK_OR_ASSIGN: - opeq (&GenOASGN, Expr, "|="); + OpAssign (&GenOASGN, Expr, "|="); break; default: diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 02f9f7a5f..841edcb62 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -28,6 +28,11 @@ #define SQP_KEEP_EAX 0x02U #define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ +/* Generator attributes */ +#define GEN_NOPUSH 0x01 /* Don't push lhs */ +#define GEN_COMM 0x02 /* Operator is commutative */ +#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ + /*****************************************************************************/ @@ -36,6 +41,9 @@ +unsigned GlobalModeFlags (const ExprDesc* Expr); +/* Return the addressing mode flags for the given expression */ + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ @@ -44,6 +52,9 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); ** generated code. */ +void LimitExprValue (ExprDesc* Expr); +/* Limit the constant value of the expression to the range of its type */ + void PushAddr (const ExprDesc* Expr); /* If the expression contains an address that was somehow evaluated, ** push this address on the stack. This is a helper function for all diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 1d4fd6872..3d7b7c384 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -56,30 +56,17 @@ ExprDesc* ED_Init (ExprDesc* Expr) /* Initialize an ExprDesc */ { - Expr->Sym = 0; Expr->Type = 0; Expr->Flags = E_NEED_EAX; Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = 0; - Expr->FVal = FP_D_Make (0.0); - Expr->LVal = 0; - Expr->BitOffs = 0; - Expr->BitWidth = 0; + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) -/* Make this expression a bit field expression */ -{ - Expr->Flags |= E_BITFIELD; - Expr->BitOffs = BitOffs; - Expr->BitWidth = 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 @@ -231,12 +218,12 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) /* Replace Expr with an absolute const with the given value and type */ { - Expr->Sym = 0; Expr->Type = Type; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -245,12 +232,12 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) /* Replace Expr with a constant integer expression with the given value */ { - Expr->Sym = 0; Expr->Type = type_int; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -264,7 +251,7 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -273,13 +260,13 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) 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_ADDRESS_OF); + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF); Expr->Flags &= ~E_CC_SET; Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); + Expr->Sym = 0; Expr->Name = 0; Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -464,8 +451,8 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == - (E_LOC_NONE|E_RTYPE_RVAL) && + return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == + (E_LOC_NONE|E_RTYPE_RVAL) && Expr->IVal == 0 && IsClassInt (Expr->Type); } @@ -503,7 +490,7 @@ void PrintExprDesc (FILE* F, ExprDesc* E) "Raw type: (unknown)\n"); } fprintf (F, "IVal: 0x%08lX\n", E->IVal); - fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->FVal)); + fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->V.FVal)); Flags = E->Flags; Sep = '('; @@ -558,11 +545,6 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_LOC_CODE; Sep = ','; } - if (Flags & E_BITFIELD) { - fprintf (F, "%cE_BITFIELD", Sep); - Flags &= ~E_BITFIELD; - Sep = ','; - } if (Flags & E_NEED_TEST) { fprintf (F, "%cE_NEED_TEST", Sep); Flags &= ~E_NEED_TEST; diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index a46685b59..a1487a0bd 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -114,7 +114,6 @@ enum { 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 */ @@ -198,17 +197,15 @@ struct Literal; /* Describe the result of an expression */ typedef struct ExprDesc ExprDesc; struct ExprDesc { - struct SymEntry* Sym; /* Symbol table entry if known */ - const Type* Type; /* Type array of expression */ - unsigned Flags; + const Type* Type; /* C type of the expression */ + unsigned Flags; /* Properties of the expression */ uintptr_t Name; /* Name pointer or label number */ + struct SymEntry* Sym; /* Symbol table entry if any */ long IVal; /* Integer value if expression constant */ - Double FVal; /* Floating point value */ - struct Literal* LVal; /* Literal value */ - - /* Bit field stuff */ - unsigned BitOffs; /* Bit offset for bit fields */ - unsigned BitWidth; /* Bit width for bit fields */ + union { + Double FVal; /* Floating point value */ + struct Literal* LVal; /* Literal value */ + } V; /* Start and end of generated code */ CodeMark Start; @@ -331,29 +328,6 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); */ #endif -#if defined(HAVE_INLINE) -INLINE int ED_IsBitField (const ExprDesc* Expr) -/* Return true if the expression is a bit field */ -{ - return (Expr->Flags & E_BITFIELD) != 0; -} -#else -# define ED_IsBitField(Expr) (((Expr)->Flags & E_BITFIELD) != 0) -#endif - -#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 */ - #if defined(HAVE_INLINE) INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 4e61cc1d3..452181af9 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -644,7 +644,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Now process statements in this block */ while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { - Statement (0); + AnyStatement (0); } /* If this is not a void function, and not the main function in a C99 diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 95617f596..a742087b7 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -124,38 +124,40 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ int AdjustBitField = 0; unsigned BitFieldFullWidthFlags = 0; - if (ED_IsBitField (Expr)) { - unsigned EndBit = Expr->BitOffs + Expr->BitWidth; - AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + if ((Flags & CF_TYPEMASK) == 0) { + if (IsTypeBitField (Expr->Type)) { + unsigned EndBit = Expr->Type->A.B.Offs + Expr->Type->A.B.Width; + AdjustBitField = Expr->Type->A.B.Offs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); - /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are - ** supported. - */ - Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; - if (IsSignUnsigned (Expr->Type)) { - Flags |= CF_UNSIGNED; - } - - /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ - BitFieldFullWidthFlags = Flags; - - /* If we're adjusting, then only load a char (not an int) and do only char ops; - ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the - ** type is not CF_CHAR. - */ - if (AdjustBitField) { - /* If adjusting, then we're sign extending manually, so do everything unsigned - ** to make shifts faster. + /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are + ** supported. */ - Flags |= CF_UNSIGNED | CF_FORCECHAR; - BitFieldFullWidthFlags |= CF_UNSIGNED; + Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; + if (IsSignUnsigned (Expr->Type)) { + Flags |= CF_UNSIGNED; + } + + /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ + BitFieldFullWidthFlags = Flags; + + /* If we're adjusting, then only load a char (not an int) and do only char ops; + ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the + ** type is not CF_CHAR. + */ + if (AdjustBitField) { + /* If adjusting, then we're sign extending manually, so do everything unsigned + ** to make shifts faster. + */ + Flags |= CF_UNSIGNED | CF_FORCECHAR; + BitFieldFullWidthFlags |= CF_UNSIGNED; + } + } else { + /* If Expr is an incomplete ESY type, bail out */ + if (IsIncompleteESUType (Expr->Type)) { + return; + } + Flags |= TypeOf (Expr->Type); } - } else if ((Flags & CF_TYPEMASK) == 0) { - /* If Expr is an incomplete ESY type, bail out */ - if (IsIncompleteESUType (Expr->Type)) { - return; - } - Flags |= TypeOf (Expr->Type); } if (ED_YetToTest (Expr)) { @@ -254,13 +256,13 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) /* 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); + CHECK (Expr->Type->A.B.Offs < CHAR_BITS); if (ED_YetToTest (Expr)) { - g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); + g_testbitfield (Flags, Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } else { g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), - Expr->BitOffs, Expr->BitWidth); + Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } } diff --git a/src/cc65/main.c b/src/cc65/main.c index 89c1b190e..f95d89bd9 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -295,7 +295,7 @@ static void SetSys (const char* Sys) break; default: - AbEnd ("Unknown target system type %d", Target); + AbEnd ("Unknown target system '%s'", Sys); } /* Initialize the translation tables for the target system */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 37073e784..a607e3217 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -71,7 +71,7 @@ unsigned char Preprocessing = 0; /* Management data for #if */ -#define MAX_IFS 64 +#define MAX_IFS 256 #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index bdc7be006..37566a455 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -832,8 +832,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.LVal) == 1 && - GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { + GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') { /* Drop the generated code so we have the first argument in the ** primary @@ -841,7 +841,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) RemoveCode (&Arg1.Push); /* We don't need the literal any longer */ - ReleaseLiteral (Arg2.Expr.LVal); + ReleaseLiteral (Arg2.Expr.V.LVal); /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. @@ -1232,10 +1232,10 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); /* We don't need the literal any longer */ - ReleaseLiteral (Arg.LVal); + ReleaseLiteral (Arg.V.LVal); /* Bail out, no need for further improvements */ goto ExitPoint; diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index ccb113978..022a8475c 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -163,7 +163,7 @@ static int IfStatement (void) TestResult = TestInParens (Label1, 0); /* Parse the if body */ - GotBreak = Statement (0); + GotBreak = AnyStatement (0); /* Else clause present? */ if (CurTok.Tok != TOK_ELSE) { @@ -195,7 +195,7 @@ static int IfStatement (void) g_defcodelabel (Label1); /* Total break only if both branches had a break. */ - GotBreak &= Statement (0); + GotBreak &= AnyStatement (0); /* Generate the label for the else clause */ g_defcodelabel (Label2); @@ -225,7 +225,7 @@ static void DoStatement (void) g_defcodelabel (LoopLabel); /* Parse the loop body */ - Statement (0); + AnyStatement (0); /* Output the label for a continue */ g_defcodelabel (ContinueLabel); @@ -283,7 +283,7 @@ static void WhileStatement (void) g_defcodelabel (LoopLabel); /* Loop body */ - Statement (&PendingToken); + AnyStatement (&PendingToken); /* Emit the while condition label */ g_defcodelabel (CondLabel); @@ -509,7 +509,7 @@ static void ForStatement (void) /* Loop body */ g_defcodelabel (BodyLabel); - Statement (&PendingToken); + AnyStatement (&PendingToken); /* If we had an increment expression, move the code to the bottom of ** the loop. In this case we don't need to jump there at the end of @@ -536,17 +536,20 @@ static void ForStatement (void) -static int CompoundStatement (void) +static int CompoundStatement (int* PendingToken) /* Compound statement. Allow any number of statements inside braces. The ** function returns true if the last statement was a break or return. */ { - int GotBreak; + int GotBreak = 0; /* Remember the stack at block entry */ int OldStack = StackPtr; unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); + /* Skip '{' */ + NextToken (); + /* Enter a new lexical level */ EnterBlockLevel (); @@ -554,16 +557,15 @@ static int CompoundStatement (void) DeclareLocals (); /* Now process statements in this block */ - GotBreak = 0; while (CurTok.Tok != TOK_RCURLY) { if (CurTok.Tok != TOK_CEOF) { - GotBreak = Statement (0); + GotBreak = AnyStatement (0); } else { break; } } - /* Clean up the stack. */ + /* Clean up the stack if the codeflow may reach the end */ if (!GotBreak) { g_space (StackPtr - OldStack); } @@ -583,12 +585,80 @@ static int CompoundStatement (void) /* Leave the lexical level */ LeaveBlockLevel (); + /* Skip '}' */ + CheckTok (TOK_RCURLY, "'}' expected", PendingToken); + return GotBreak; } -int Statement (int* PendingToken) +static void Statement (int* PendingToken) +/* Single-line statement */ +{ + ExprDesc Expr; + unsigned PrevErrorCount; + CodeMark Start, End; + + /* Remember the current error count and code position */ + PrevErrorCount = ErrorCount; + GetCodePos (&Start); + + /* Actual statement */ + ED_Init (&Expr); + Expr.Flags |= E_NEED_NONE; + Expression0 (&Expr); + + /* If the statement didn't generate code, and is not of type + ** void, emit a warning. + */ + GetCodePos (&End); + if (!ED_YetToLoad (&Expr) && + !ED_MayHaveNoEffect (&Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + CheckSemi (PendingToken); +} + + + +static int ParseAnyLabels (void) +/* Return -1 if there are any labels with a statement */ +{ + unsigned PrevErrorCount = ErrorCount; + int HasLabels = 0; + for (;;) { + if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { + /* C 'goto' label */ + DoLabel (); + } else if (CurTok.Tok == TOK_CASE) { + /* C 'case' label */ + CaseLabel (); + } else if (CurTok.Tok == TOK_DEFAULT) { + /* C 'default' label */ + DefaultLabel (); + } else { + /* No labels */ + break; + } + HasLabels = 1; + } + + if (HasLabels) { + if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) { + return -1; + } + } + + return 0; +} + + + +int AnyStatement (int* PendingToken) /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or @@ -598,40 +668,27 @@ int Statement (int* PendingToken) ** NULL, the function will skip the token. */ { - ExprDesc Expr; - int GotBreak; - unsigned PrevErrorCount; - CodeMark Start, End; - - ED_Init (&Expr); - /* Assume no pending token */ if (PendingToken) { *PendingToken = 0; } - /* Check for a label. A label is always part of a statement, it does not + /* Handle any labels. A label is always part of a statement, it does not ** replace one. */ - while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { - /* Handle the label */ - DoLabel (); - if (CheckLabelWithoutStatement ()) { - return 0; - } + if (ParseAnyLabels ()) { + return 0; } switch (CurTok.Tok) { - case TOK_LCURLY: - NextToken (); - GotBreak = CompoundStatement (); - CheckTok (TOK_RCURLY, "'{' expected", PendingToken); - return GotBreak; - case TOK_IF: return IfStatement (); + case TOK_SWITCH: + SwitchStatement (); + break; + case TOK_WHILE: WhileStatement (); break; @@ -640,10 +697,15 @@ int Statement (int* PendingToken) DoStatement (); break; - case TOK_SWITCH: - SwitchStatement (); + case TOK_FOR: + ForStatement (); break; + case TOK_GOTO: + GotoStatement (); + CheckSemi (PendingToken); + return 1; + case TOK_RETURN: ReturnStatement (); CheckSemi (PendingToken); @@ -659,55 +721,22 @@ int Statement (int* PendingToken) CheckSemi (PendingToken); return 1; - case TOK_FOR: - ForStatement (); - break; - - case TOK_GOTO: - GotoStatement (); - CheckSemi (PendingToken); - return 1; - - case TOK_SEMI: - /* Ignore it */ - CheckSemi (PendingToken); - break; - case TOK_PRAGMA: DoPragma (); break; - case TOK_CASE: - CaseLabel (); - CheckLabelWithoutStatement (); + case TOK_SEMI: + /* Empty statement. Ignore it */ + CheckSemi (PendingToken); break; - case TOK_DEFAULT: - DefaultLabel (); - CheckLabelWithoutStatement (); - break; + case TOK_LCURLY: + return CompoundStatement (PendingToken); default: - /* Remember the current error count and code position */ - PrevErrorCount = ErrorCount; - GetCodePos (&Start); - - /* Actual statement */ - Expr.Flags |= E_NEED_NONE; - Expression0 (&Expr); - - /* If the statement didn't generate code, and is not of type - ** void, emit a warning. - */ - GetCodePos (&End); - if (!ED_YetToLoad (&Expr) && - !ED_MayHaveNoEffect (&Expr) && - CodeRangeIsEmpty (&Start, &End) && - IS_Get (&WarnNoEffect) && - PrevErrorCount == ErrorCount) { - Warning ("Expression result unused"); - } - CheckSemi (PendingToken); + /* Simple statement */ + Statement (PendingToken); + break; } return 0; } diff --git a/src/cc65/stmt.h b/src/cc65/stmt.h index 04ef728a3..8cff99d92 100644 --- a/src/cc65/stmt.h +++ b/src/cc65/stmt.h @@ -44,7 +44,7 @@ -int Statement (int* PendingToken); +int AnyStatement (int* PendingToken); /* Statement parser. Returns 1 if the statement does a return/break, returns ** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** not skip the terminating token of the statement (closing brace or diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 3878f7b67..ee0bd1a85 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -148,7 +148,7 @@ void SwitchStatement (void) /* Parse the following statement, which may actually be a compound ** statement if there is a curly brace at the current input position */ - HaveBreak = Statement (&RCurlyBrace); + HaveBreak = AnyStatement (&RCurlyBrace); /* Check if we had any labels */ if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 56d884bb6..bb87c7472 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -183,13 +183,6 @@ struct SymEntry { const Type* Type; /* Underlying type */ } E; - /* Data for bit fields */ - struct { - unsigned Offs; /* Byte offset into struct */ - unsigned BitOffs; /* Bit offset into storage unit */ - unsigned BitWidth; /* Width in bits */ - } B; - /* Data for functions */ struct { struct Segments* Seg; /* Segments for this function */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 4073a38bc..5d7bd1436 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -881,10 +881,8 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, Entry = NewSymEntry (Name, SC_BITFIELD); /* Set the symbol attributes. Bit-fields are always integral types. */ - Entry->Type = TypeDup (T); - Entry->V.B.Offs = Offs; - Entry->V.B.BitOffs = BitOffs; - Entry->V.B.BitWidth = BitWidth; + Entry->Type = NewBitFieldType (T, BitOffs, BitWidth); + Entry->V.Offs = Offs; if (!SignednessSpecified) { /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, @@ -896,8 +894,10 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, */ 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; + Entry->Type[0].C &= ~T_MASK_SIGN; + Entry->Type[0].C |= T_SIGN_UNSIGNED; + Entry->Type[1].C &= ~T_MASK_SIGN; + Entry->Type[1].C |= T_SIGN_UNSIGNED; } /* Add the entry to the symbol table */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 8c9da3445..6052f4a84 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -278,6 +278,21 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_STRICT_COMPATIBLE); } + /* Bit-fields are considered compatible if they have the same + ** signedness, bit-offset and bit-width. + */ + if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { + if (!IsTypeBitField (lhs) || + !IsTypeBitField (rhs) || + lhs->A.B.Offs != rhs->A.B.Offs || + lhs->A.B.Width != rhs->A.B.Width) { + SetResult (Result, TC_INCOMPATIBLE); + } + if (LeftType != RightType) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 16f173cc4..a7528a2f8 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -83,11 +83,16 @@ 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. */ - if (ED_IsBitField (Expr)) { - OldBits = Expr->BitWidth; + if (IsTypeBitField (OldType)) { + OldBits = OldType->A.B.Width; } else { OldBits = CheckedSizeOf (OldType) * CHAR_BITS; } + + /* If the new type is a bit-field, we use its underlying type instead */ + if (IsTypeBitField (NewType)) { + NewType = GetUnderlyingType (NewType); + } NewBits = CheckedSizeOf (NewType) * CHAR_BITS; /* lvalue? */ @@ -167,9 +172,6 @@ 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 */ - ED_DisBitField (Expr); } diff --git a/src/ld65/expr.c b/src/ld65/expr.c index bc3d7941c..ff210e315 100644 --- a/src/ld65/expr.c +++ b/src/ld65/expr.c @@ -321,20 +321,18 @@ long GetExprVal (ExprNode* Expr) return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); case EXPR_DIV: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Division by zero"); } - return Left / Right; + return GetExprVal (Expr->Left) / Right; case EXPR_MOD: - Left = GetExprVal (Expr->Left); Right = GetExprVal (Expr->Right); if (Right == 0) { Error ("Modulo operation with zero"); } - return Left % Right; + return GetExprVal (Expr->Left) % Right; case EXPR_OR: return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); @@ -403,17 +401,20 @@ long GetExprVal (ExprNode* Expr) case EXPR_BANK: GetSegExprVal (Expr->Left, &D); - if (D.TooComplex || D.Seg == 0) { - Error ("Argument for .BANK is not segment relative or too complex"); + if (D.TooComplex) { + Error ("Argument of .BANK() is too complex"); + } + if (D.Seg == 0) { + Error ("Argument of .BANK() isn't a label attached to a segment"); } if (D.Seg->MemArea == 0) { - Error ("Segment '%s' is referenced by .BANK but " - "not assigned to a memory area", + Error ("Segment '%s' is referenced by .BANK()," + " but not assigned to a memory area", GetString (D.Seg->Name)); } if (D.Seg->MemArea->BankExpr == 0) { - Error ("Memory area '%s' is referenced by .BANK but " - "has no BANK attribute", + Error ("Memory area '%s' is referenced by .BANK()," + " but has no BANK attribute", GetString (D.Seg->MemArea->Name)); } return GetExprVal (D.Seg->MemArea->BankExpr); @@ -457,13 +458,15 @@ long GetExprVal (ExprNode* Expr) static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) /* Check if the given expression consists of a segment reference and only -** constant values, additions and subtractions. If anything else is found, +** constant values, additions, and subtractions. If anything else is found, ** set D->TooComplex to true. ** Internal, recursive routine. */ { Export* E; + CHECK (Expr != 0); + switch (Expr->Op) { case EXPR_LITERAL: @@ -479,7 +482,7 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) */ if (ExportHasMark (E)) { CircularRefError (E); - } else { + } else if (E->Expr != 0) { MarkExport (E); GetSegExprValInternal (E->Expr, D, Sign); UnmarkExport (E); diff --git a/src/msbuild.cmd b/src/msbuild.cmd index 5736846da..2e1821f0a 100644 --- a/src/msbuild.cmd +++ b/src/msbuild.cmd @@ -1,2 +1,18 @@ +@echo off + +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" goto vs2017 +if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" goto vs2019 + +echo Error: VsDevCmd.bat not found! +goto:eof + +:vs2017 call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" +goto run + +:vs2019 +call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" +goto run + +:run msbuild.exe %* diff --git a/targettest/atari/multi-xex.cfg b/targettest/atari/multi-xex.cfg index 7558aa895..f13a9eabe 100644 --- a/targettest/atari/multi-xex.cfg +++ b/targettest/atari/multi-xex.cfg @@ -5,13 +5,13 @@ MEMORY { ZP: file = "", define = yes, start = $0082, size = $007E; # First memory segment in file, show message LOADER: file = %O, start = $680, size = 128; - # First memory segment in file, load over COLOR registers: + # Second memory segment in file, load over COLOR registers: COLOR: file = %O, start = $2C4, size = 5; - # Second memory segment, load at page 6: + # Third memory segment, load at page 6: PAGE6: file = %O, start = $600, size = 128; - # Third memory segment in file, load over SDLST register: + # Fourth memory segment in file, load over SDLST register: SDLST: file = %O, start = $230, size = 2; - # Main segment, load at "STARTADDRESS" + # Fifth/Main segment, load at "STARTADDRESS" MAIN: file = %O, start = %S, size = $BC20 - %S; } FILES { diff --git a/targettest/atari/ostype.c b/targettest/atari/ostype.c index 552735ac8..5561f64fd 100644 --- a/targettest/atari/ostype.c +++ b/targettest/atari/ostype.c @@ -9,9 +9,9 @@ int main(void) { + char *rev; unsigned int t, v; unsigned char palntsc; - unsigned char *rev; unsigned char minor; unsigned char c; diff --git a/targettest/cbm/Makefile b/targettest/cbm/Makefile index fb7af1a9a..298f80d62 100644 --- a/targettest/cbm/Makefile +++ b/targettest/cbm/Makefile @@ -30,10 +30,13 @@ else LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) endif -all: petscii.prg +all: petscii.prg cbmdir-test.prg petscii.prg: petscii.c $(CL) -t $(SYS) -O -o petscii.prg petscii.c +cbmdir-test.prg: cbmdir-test.c + $(CL) -t $(SYS) -Oris -o $@ $< + clean: - @$(DEL) petscii.prg 2>$(NULLDEV) + @$(DEL) petscii.prg cbmdir-test.prg 2>$(NULLDEV) diff --git a/targettest/cbm/cbmdir-test.c b/targettest/cbm/cbmdir-test.c new file mode 100644 index 000000000..0db9856b7 --- /dev/null +++ b/targettest/cbm/cbmdir-test.c @@ -0,0 +1,119 @@ +/* +** Tests the CBM-specific directory functions. +** +** 2021-08-12, Greg King +*/ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <conio.h> +#include <cbm.h> + + +/* device number */ +#define UNIT 8 + +/* directory patterm */ +static const char name[] = "$"; + + +static const char* const type[] = { + "DEL", + "CBM", + "DIR", + "LNK", + "???", + "Directory header", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SEQ", + "PRG", + "USR", + "REL", + "VRP" +}; + +static const char* const access[] = { + "unknown", + "read-only", + "write-only", + "read/write" +}; + +static const char* const error[] = { + "", + "couldn't read it", + "", + "couldn't find start of file-name", + "couldn't find end of file-name", + "couldn't read file-type", + "premature end of file" +}; + + +int main(void) +{ + unsigned char go = 0; + unsigned char rc; + struct cbm_dirent E; + + /* Explain use, and wait for a key. */ + printf ("use the following keys:\n" + " g -> go ahead without stopping\n" + " q -> quit directory lister\n" + "tap any key to start ...\n\n"); + cgetc (); + + /* Open the directory. */ + if (cbm_opendir (1, UNIT, name) != 0) { + printf("error opening %s:\n %s\n", name, _stroserror (_oserror)); + return 1; + } + + /* Output the directory. */ + printf("contents of \"%s\":\n", name); + while ((rc = cbm_readdir (1, &E)) != 2) { + if (rc != 0) { + goto oops; + } + + printf (" name[]: \"%s\"\n", E.name); + printf (" size :%6u\n", E.size); + printf (" type : %s\n", type[E.type]); + printf (" access: %s\n", access[E.access > 3 ? 0 : E.access]); + printf ("----\n"); + + if (!go) { + switch (cgetc ()) { + case 'q': + goto done; + + case 'g': + go = 1; + } + } + } + + printf (" size :%6u free.\n", E.size); + +done: + /* Close the directory. */ + cbm_closedir (1); + return 0; + +oops: + if (rc <= 6) { + printf ("\ndirectory error:\n %s.\n", error[rc]); + } + cbm_closedir (1); + return 1; +} diff --git a/targettest/pce/Makefile b/targettest/pce/Makefile index 1ecc0566f..f91f0eed8 100644 --- a/targettest/pce/Makefile +++ b/targettest/pce/Makefile @@ -42,14 +42,15 @@ else COUNT := 1 endif -all: conio.pce +all: conio.bin + +%.bin: %.c + $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ + @echo "use 'make conio.pce' to produce a .pce file using dd" %.pce: %.bin dd if=$< bs=8K skip=${COUNT} > $@ dd if=$< bs=8K count=${COUNT} >> $@ -%.bin: %.c - $(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@ - clean: @$(DEL) conio.o conio.??? 2>$(NULLDEV) diff --git a/test/misc/Makefile b/test/misc/Makefile index 397635afd..e6c58c5a4 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -64,6 +64,7 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) $(if $(QUIET),echo misc/bug760.$1.$2.prg) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) +# should compile, but gives an error $(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." $(if $(QUIET),echo misc/bug1437.$1.$2.prg) @@ -105,6 +106,18 @@ $(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)/bug1357.$1.$2.prg: bug1357.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1357.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + +# should compile, but compiler exits with internal error +$(WORKDIR)/bug1211-ice-move-refs-2.$1.$2.prg: bug1211-ice-move-refs-2.c | $(WORKDIR) + @echo "FIXME: " $$@ "currently does not compile." + $(if $(QUIET),echo misc/bug1211-ice-move-refs-2.$1.$2.prg) + $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) + # this one requires --std=c89, it fails with --std=c99 $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(if $(QUIET),echo misc/bug1265.$1.$2.prg) diff --git a/test/err/bug1211-ice-move-refs-2.c b/test/misc/bug1211-ice-move-refs-2.c similarity index 100% rename from test/err/bug1211-ice-move-refs-2.c rename to test/misc/bug1211-ice-move-refs-2.c diff --git a/test/misc/bug1357.c b/test/misc/bug1357.c new file mode 100644 index 000000000..40415f868 --- /dev/null +++ b/test/misc/bug1357.c @@ -0,0 +1,30 @@ + +/* issue #1357 - X Macros don't work with C preprocessor */ + +#define OPCODES(X) \ + X(PUSHNIL) \ + X(PUSHTRUE) \ + X(PUSHFALSE) + +enum { +#define X(op) op, +OPCODES(X) +#undef X + N_OPS +}; + +/* cc65 -E bug1357.c -o bug1357.c.pre + should produce something like this: + +enum { +PUSHNIL, +PUSHTRUE, +PUSHFALSE, + N_OPS +}; +*/ + +int main(void) +{ + return 0; +} diff --git a/test/readme.txt b/test/readme.txt index 0523482fd..49ae363cc 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -1,6 +1,13 @@ This directory contains test code for automatic regression testing of the CC65 -compiler. +compiler and tools. +/asm - contains the assembler regression tests + +/dasm - contains the disassembler regression tests + + +/val, /ref and /err generally contain the tests that are used to verify that the +compiler is working as expected (when the tests behave as described): /val - The bulk of tests are contained here, individual tests should exit with an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error. @@ -9,6 +16,9 @@ compiler. /err - contains tests that MUST NOT compile + +/todo and /misc generally contain the tests that fail because of known bugs: + /todo - These tests fail due to open compiler issues. The makefile in this directory _expects_ the tests to fail, because of @@ -16,9 +26,6 @@ compiler. 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 - -/dasm - contains the disassembler regression tests /misc - a few tests that need special care of some sort diff --git a/test/val/bug1462-2.c b/test/val/bug1462-2.c new file mode 100644 index 000000000..df94cfc59 --- /dev/null +++ b/test/val/bug1462-2.c @@ -0,0 +1,51 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* even the = operation is buggy in certain ways */ + +#include <stdio.h> + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; +} T; + +int failures = 0; + +T *f(T *t) +{ + t->a = 0; + t->c = 0; + return t; +} + +void test(void) +{ + T a = { 7, 0, 7 }; + T *p = &a; + + a.b = f(p)->a; + + if (a.a != 0) { + ++failures; + } + printf("%d\n", a.a); + + if (p->b != 0) { + ++failures; + } + printf("%d\n", p->b); + + if ((&a)->c != 0) { + ++failures; + } + printf("%d\n", (&a)->c); + + printf("Failures: %d\n", failures); +} + +int main(void) +{ + test(); + return failures; +} diff --git a/test/val/bug1462-3.c b/test/val/bug1462-3.c new file mode 100644 index 000000000..12559b94f --- /dev/null +++ b/test/val/bug1462-3.c @@ -0,0 +1,95 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More tests on "op= expression result value" that a naive fix might fail with */ + +#include <stdio.h> + +typedef struct { + signed int a : 3; + unsigned int b : 3; + signed int c : 3; + unsigned int d : 3; +} T1; + +typedef struct { + signed int a : 3; + signed int b : 3; + signed int c : 3; + signed int d : 3; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 3, 3, 3, 3 }; + int i; + + i = a.a -= a.b + a.c; + if (i != -3 || a.a != -3) { + ++failures1; + } + printf("i = %d, a.a = %d\n", i, a.a); + + a.b = i = a.b / -1; + if (i != -3 || a.b != 5) { + ++failures1; + } + printf("i = %d, a.b = %d\n", i, a.b); + + i = a.c = 0; + if (i != 0 || a.c != 0) { + ++failures1; + } + printf("i = %d, a.c = %d\n", i, a.c); + + i = a.d /= -1; + if (i != 5 || a.d != 5) { + ++failures1; + } + printf("i = %d, a.d = %d\n", i, a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T2 b = { 3, 3, 4, 4 }; + int i; + + i = b.a++; + if (i != 3 || b.a != -4) { + ++failures2; + } + printf("i = %d, b.a = %d\n", i, b.a); + + i = ++b.b; + if (i != -4 || b.b != -4) { + ++failures2; + } + printf("i = %d, b.b = %d\n", i, b.b); + + i = b.c--; + if (i != -4 || b.c != 3) { + ++failures2; + } + printf("i = %d, b.c = %d\n", i, b.c); + + i = --b.d; + if (i != 3 || b.d != 3) { + ++failures2; + } + printf("i = %d, b.d = %d\n", i, b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + diff --git a/test/val/bug1462-4.c b/test/val/bug1462-4.c new file mode 100644 index 000000000..f811ddbd6 --- /dev/null +++ b/test/val/bug1462-4.c @@ -0,0 +1,85 @@ + +/* issue #1462 - Bit-fields are still broken */ +/* More tests on "op= expression result value" that a naive fix might fail with */ + +#include <stdio.h> +#include <limits.h> + +#define SMALL_WIDTH 4 +#define LARGE_WIDTH (CHAR_BIT * sizeof (unsigned int)) + +typedef struct { + unsigned int a : SMALL_WIDTH; + unsigned int b : SMALL_WIDTH; + unsigned int c : SMALL_WIDTH; + unsigned int d : SMALL_WIDTH; +} T1; + +typedef struct { + unsigned int a : LARGE_WIDTH; + unsigned int b : LARGE_WIDTH; + unsigned int c : LARGE_WIDTH; + unsigned int d : LARGE_WIDTH; +} T2; + + +int failures1 = 0; +int failures2 = 0; + +void test1(void) +{ + T1 a = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", SMALL_WIDTH); + if (!(~a.a < 0)) { + ++failures1; + } + printf("~a.a < 0 : %d\n", ~a.a < 0); + if (!(0 > ~a.b)) { + ++failures1; + } + printf("0 > ~a.b : %d\n",0 > ~a.b); + if (!(a.c > -1)) { + ++failures1; + } + printf("a.c > -1 : %d\n", a.c > -1); + if (!(-1 < a.d)) { + ++failures1; + } + printf("-1 < a.d : %d\n", -1 < a.d); + + printf("Failures: %d\n", failures1); +} + +void test2(void) +{ + T1 b = { 0, 0, 0, 0 }; + + printf("\nunsigned int : %d\n", LARGE_WIDTH); + if (!(~b.a < 0)) { + ++failures2; + } + printf("~b.a < 0 : %d\n", ~b.a < 0); + if (!(0 > ~b.b)) { + ++failures2; + } + printf("0 > ~b.b : %d\n", 0 > ~b.b); + if (!(b.c > -1)) { + ++failures2; + } + printf("b.c > -1 : %d\n", b.c > -1); + if (!(-1 < b.d)) { + ++failures2; + } + printf("-1 < b.d : %d\n", -1 < b.d); + + printf("Failures: %d\n", failures2); +} + +int main(void) +{ + test1(); + test2(); + return failures1 + failures2; +} + diff --git a/test/todo/bug1462.c b/test/val/bug1462.c similarity index 100% rename from test/todo/bug1462.c rename to test/val/bug1462.c diff --git a/test/val/bug1504.c b/test/val/bug1504.c new file mode 100644 index 000000000..bd93c7387 --- /dev/null +++ b/test/val/bug1504.c @@ -0,0 +1,13 @@ + +/* bug #1504 - Some compilation failures */ + +#include <stdio.h> + +int main(void) +{ + int i = 0, *p = &i; + switch (i) case 0: case 1: i = 21; /* Should be OK but fails */ + p++[0] += 21; /* Should be OK but fails */ + printf("%d\n", i); + return i != 42; +} diff --git a/test/val/bug1552.c b/test/val/bug1552.c new file mode 100644 index 000000000..42f39eec6 --- /dev/null +++ b/test/val/bug1552.c @@ -0,0 +1,42 @@ + +/* + bug #1552 - crash in fuzix xec.c + + cc65 -t none -O bug1552.c +*/ + +#include <stdio.h> + +typedef struct trenod *TREPTR; +typedef struct whnod *WHPTR; + +struct trenod { + int tretyp; +}; + +struct whnod { + int whtyp; + TREPTR whtre; +}; + +int execute(TREPTR argt, int execflg, int *pf1, int *pf2) +{ + register TREPTR t; + int type; + switch (type) + { + case 6: + { + while ((execute(((WHPTR) t)->whtre, 0, NULL, NULL) == 0) == (type == 5)) { + + } + break; + } + } + return 0; +} + +int main(void) +{ + return execute((TREPTR)42, 2, (int *)3, (int *)4); +} diff --git a/test/val/bug1562.c b/test/val/bug1562.c new file mode 100644 index 000000000..7e6c1751e --- /dev/null +++ b/test/val/bug1562.c @@ -0,0 +1,30 @@ + +/* bug 1562: cc65 generates incorrect code for logical expression with -O */ + +#include <stdio.h> +#include <string.h> + +int failures = 0; + +char input[256]; + +#define DEBUGTRUE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 0 : 1 + +#define DEBUGFALSE(x) printf("%s=%d\n", #x, (x)); failures += (x) ? 1 : 0 + +int main(void) { + char* r; + strcpy(input, "\"XYZ\""); + r = input+4; + DEBUGFALSE(*r != '"'); // = false + DEBUGTRUE(*r == '"'); // = true + DEBUGFALSE(*(r+1) == '"'); // = false + // Next answer should be false because + // (false || true && false) is false, but it is true with -O. + DEBUGFALSE(*r != '"' || *r == '"' && *(r+1) == '"'); + // Adding parens fixes it even with -O. + DEBUGFALSE(*r != '"' || (*r == '"' && *(r+1) == '"')); + + printf("failures: %d\n", failures); + return failures; +} diff --git a/test/val/bug263.c b/test/val/bug263.c new file mode 100644 index 000000000..6f3f8f57e --- /dev/null +++ b/test/val/bug263.c @@ -0,0 +1,49 @@ + +/* issue #263 - cc65 miscompiles w/ a static variable and -O */ + +#include <stdint.h> +#include <stdio.h> + +int failures = 0; + +void __fastcall__ set_vram_update(unsigned char *ptr) +{ + printf("set_vram_update: %04x\n", ptr); + if (ptr != NULL) { + failures++; + } +} + +unsigned char __fastcall__ ppu_wait_nmi(void) +{ + // we need to make sure somehow the akku is not zero before the break + return 0x1234; +} + +unsigned char ctrl, ret, i; + +unsigned char gameloop (void) +{ + ctrl = 0; + ret = 0; + while(1) { + if (ctrl & 1) { + while (--i) { + ppu_wait_nmi(); + } + break; + } + ctrl = 1; + } + // This will pass garbage, not NULL. + set_vram_update(NULL); + return ret; +} + +int main(void) +{ + gameloop(); + printf("failures: %d\n", failures); + return failures; +} + diff --git a/test/val/bug897.c b/test/val/bug897.c new file mode 100644 index 000000000..eaf751441 --- /dev/null +++ b/test/val/bug897.c @@ -0,0 +1,52 @@ + +/* issue #897 - __asm__()-referenced code-labels are generated for only branches and jumps */ + +#include <stdlib.h> +#include <stdio.h> + +static unsigned char *srcptr, *dstptr; + +#define COPY_LEN 16 + +void test(void) +{ + asm("lda %v", srcptr); + asm("sta %g+1", s2b_copy_from); + asm("lda %v+1", srcptr); + asm("sta %g+2", s2b_copy_from); + + asm("lda %v", dstptr); + asm("sta %g+1", s2b_copy_to); + asm("lda %v+1", dstptr); + asm("sta %g+2", s2b_copy_to); + + asm("ldy #%b", COPY_LEN-1); +s2b_copy_from: + asm("lda $FFFF,y"); +s2b_copy_to: + asm("sta $FFFF,y"); + asm("dey"); + asm("bpl %g", s2b_copy_from); +} + +unsigned char src[16] = "0123456789abcdef"; +unsigned char dest[16]; + +int failures = 0; + +unsigned char i; + +int main(void) +{ + srcptr = src; + dstptr = dest; + test(); + for (i = 0; i < COPY_LEN; i++) { + printf("%d %02x %02x\n", i, src[i], dest[i]); + if (src[i] != dest[i]) { + failures++; + } + } + printf("failures: %d\n", failures); + return failures; +} diff --git a/test/val/enum-bitfield.c b/test/val/enum-bitfield.c index a942091c2..5669978c9 100644 --- a/test/val/enum-bitfield.c +++ b/test/val/enum-bitfield.c @@ -23,6 +23,7 @@ */ #include <stdio.h> +#include <limits.h> static unsigned char failures = 0; @@ -35,7 +36,7 @@ enum e10u { static struct enum_bitfield_uint { enum e10u x : 1; enum e10u y : 8; - enum e10u z : 16; + enum e10u z : CHAR_BIT * sizeof (enum e10u); } e10ubf = {0, E10U_200, E10U_1000}; static void test_enum_bitfield_uint(void) @@ -68,11 +69,11 @@ static void test_enum_bitfield_uint(void) failures++; } - /* Check signedness, should be unsigned. */ + /* Check signedness, should be signed. */ { long v = e10ubf.x - 2; - if (v < 0) { - printf ("Got negative v = %ld, expected large positive.\n", v); + if (v >= 0) { + printf ("Got non-negative v (= e10ubf.x - 2) = %ld, expected negative.\n", v); failures++; } } @@ -85,6 +86,15 @@ static void test_enum_bitfield_uint(void) printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z); failures++; } + + /* Check signedness, should be unsigned. */ + { + long v = e10ubf.z - 1024; + if (v < 0) { + printf ("Got negative v (= e10ubf.z - 1024) = %ld, expected positive.\n", v); + failures++; + } + } } /* Enum with underlying type signed int. */ @@ -97,7 +107,7 @@ enum e11i { static struct enum_bitfield_int { enum e11i x : 2; enum e11i y : 8; - enum e11i z : 16; + enum e11i z : CHAR_BIT * sizeof (enum e11i); } e11ibf = {E11I_M1, E11I_100, E11I_1000}; static void test_enum_bitfield_int(void) @@ -133,8 +143,8 @@ static void test_enum_bitfield_int(void) /* Check signedness, should be signed. */ { long v = e11ibf.x - 2; - if (v > 0) { - printf ("Got positive v = %ld, expected negative.\n", v); + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.x - 2) = %ld, expected negative.\n", v); failures++; } } @@ -147,6 +157,15 @@ static void test_enum_bitfield_int(void) printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z); failures++; } + + /* Check signedness, should be signed. */ + { + long v = e11ibf.z - 1024; + if (v >= 0) { + printf ("Got non-negative v (= e11ibf.z - 1024) = %ld, expected negative.\n", v); + failures++; + } + } } /* Enum with underlying type unsigned char. */ @@ -157,7 +176,7 @@ enum e7uc { static struct enum_bitfield_uchar { enum e7uc x : 1; enum e7uc y : 4; - enum e7uc z : 8; + enum e7uc z : CHAR_BIT; } e7ucbf = {0, 10, E7UC_100}; static void test_enum_bitfield_uchar(void) @@ -212,7 +231,7 @@ enum e8sc { static struct enum_bitfield_char { enum e8sc x : 1; enum e8sc y : 4; - enum e8sc z : 8; + enum e8sc z : CHAR_BIT; } e8scbf = {0, 5, E8SC_100}; static void test_enum_bitfield_char(void) diff --git a/test/val/opsize.c b/test/val/opsize.c new file mode 100644 index 000000000..20c7f0511 --- /dev/null +++ b/test/val/opsize.c @@ -0,0 +1,33 @@ + +/* Test for result types of certain unary operations */ + +#include <stdio.h> + +signed char x; +struct S { + unsigned char a : 3; + unsigned int b : 3; +} s; + +int main(void) +{ + _Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type"); + _Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type"); + _Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type"); + _Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type"); + _Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type"); + + _Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type"); + _Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type"); + _Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type"); + + _Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type"); + _Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type"); + _Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type"); + + _Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type"); + _Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type"); + _Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type"); + + return 0; +}