Merge branch 'master' into master

This commit is contained in:
Oliver Schmidt 2021-11-23 22:56:51 +01:00 committed by GitHub
commit 8b0de8a931
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 3761 additions and 2043 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__)

View File

@ -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

20
cfg/c128-asm.cfg Normal file
View File

@ -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;
}

19
cfg/vic20-asm.cfg Normal file
View File

@ -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;
}

View File

@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of
<tag><tt/a2.ssc.ser (a2_ssc_ser)/</tag>
Driver for the Apple&nbsp;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.

View File

@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of
<tag><tt/a2e.ssc.ser (a2e_ssc_ser)/</tag>
Driver for the Apple&nbsp;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.

View File

@ -675,9 +675,9 @@ The default callbacks definition (<tt/mouse_def_callbacks/) is an alias for the
<sect1>RS232 device drivers<p>
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.
<table>
<tabular ca="rr">

View File

@ -176,10 +176,11 @@ No mouse drivers are currently available for the Atmos.
<tag><tt/atmos-acia.ser (atmos_acia_ser)/</tag>
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.
</descrip><p>

View File

@ -324,9 +324,9 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/c128-
<descrip>
<tag><tt/c128-swlink.ser (c128_swlink_ser)/</tag>
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.

View File

@ -410,9 +410,9 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/c64-1
<descrip>
<tag><tt/c64-swlink.ser (c64_swlink_ser)/</tag>
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.

View File

@ -231,10 +231,10 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/cbm51
<tag><tt/cbm510-std.ser (cbm510_std_ser)/</tag>
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.
</descrip><p>

View File

@ -212,10 +212,10 @@ No mouse drivers are currently available for the Commodore 610.
<tag><tt/cbm610-std.ser (cbm610_std_ser)/</tag>
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.
</descrip><p>

View File

@ -298,6 +298,16 @@ function.
</itemize>
<sect1><tt/creativision.h/<label id="creativision.h"><p>
<itemize>
<item><ref id="bios_playsound" name="bios_playsound">
<item><ref id="psg_delay" name="psg_delay">
<item><ref id="psg_outb" name="psg_outb">
<item><ref id="psg_silence" name="psg_silence">
</itemize>
<sect1><tt/ctype.h/<label id="ctype.h"><p>
<itemize>
@ -1684,6 +1694,44 @@ used in presence of a prototype.
</quote>
<sect1>bios_playsound<label id="bios_playsound"><p>
<quote>
<descrip>
<tag/Function/Play a sequence of musical notes.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ bios_playsound (const void *a, unsigned char b);/
<tag/Description/The function plays chords based on a BASIC statement. Notes and
durations are defined in the BASIC manual, chapter 13 on pages 102 resp. 103.
<tag/Notes/<itemize>
<item>BASIC has a fixed tempo of 18.
<item>The function is only available as fastcall function, so it may only be
used in presence of a prototype.
</itemize>
<tag/Availability/cc65
<tag/See also/
<ref id="psg_delay" name="psg_delay">,
<ref id="psg_outb" name="psg_outb">
<tag/Example/<verb>
#include <creativision.h>
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);
}
</verb>
</descrip>
</quote>
<sect1>bordercolor<label id="bordercolor"><p>
<quote>
@ -1778,7 +1826,7 @@ be used in presence of a prototype.
<item>The function is specific to the C128.
<item>The function will not return to the caller.
</itemize>
<tag/Availability/C128
<tag/Availability/cc65
<tag/Example/None.
</descrip>
</quote>
@ -3410,7 +3458,7 @@ loaded.
<descrip>
<tag/Function/Install an already loaded extended memory driver.
<tag/Header/<tt/<ref id="em.h" name="em.h">/
<tag/Declaration/<tt/unsigned char _fastcall__ em_install (void* driver);/
<tag/Declaration/<tt/unsigned char _fastcall__ em_install (const void* driver);/
<tag/Description/The function installs an already loaded extended memory driver
and returns an error code. The function may be used to install a driver linked
statically to the program.
@ -4733,7 +4781,7 @@ There's no way to check for the number of actually connected joysticks.
<descrip>
<tag/Function/Install an already loaded driver and return an error code.
<tag/Header/<tt/<ref id="joystick.h" name="joystick.h">/
<tag/Declaration/<tt/unsigned char __fastcall__ joy_install (void* driver);/
<tag/Declaration/<tt/unsigned char __fastcall__ joy_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/JOY_ERR_OK/ in case of success).
@ -5769,6 +5817,78 @@ be used in presence of a prototype.
</quote>
<sect1>psg_delay<label id="psg_delay"><p>
<quote>
<descrip>
<tag/Function/Delay for a short period of time.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ psg_delay (unsigned char b);/
<tag/Description/The function specifies how long each note or pause between
notes should last.
<tag/Notes/<itemize>
<item>The function is only available as fastcall function, so it may only be
used in presence of a prototype.
</itemize>
<tag/Availability/cc65
<tag/See also/
<ref id="psg_outb" name="psg_outb">,
<ref id="psg_silence" name="psg_silence">
<tag/Example/None.
</descrip>
</quote>
<sect1>psg_outb<label id="psg_outb"><p>
<quote>
<descrip>
<tag/Function/Output a byte to the PSG.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ psg_outb (unsigned char b);/
<tag/Description/The function sends a byte to the Programmable Sound
Generator, then waits for the PSG to acknowledge.
<tag/Notes/<itemize>
<item>The function is only available as fastcall function, so it may only be
used in presence of a prototype.
</itemize>
<tag/Availability/cc65
<tag/See also/
<ref id="psg_delay" name="psg_delay">,
<ref id="psg_silence" name="psg_silence">
<tag/Example/<verb>
#include <creativision.h>
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 ();
}
</verb>
</descrip>
</quote>
<sect1>psg_silence<label id="psg_silence"><p>
<quote>
<descrip>
<tag/Function/Set volume off on each PSG channel.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void psg_silence (void);/
<tag/Description/The function resets the Programmable Sound Generator,
then sends $9F, $BF, $DF, $FF to the PSG.
<tag/Availability/cc65
<tag/See also/
<ref id="psg_delay" name="psg_delay">,
<ref id="psg_outb" name="psg_outb">
<tag/Example/None.
</descrip>
</quote>
<sect1>qsort<label id="qsort"><p>
<quote>
@ -6198,7 +6318,7 @@ while (ser_get(&amp;ch) == SER_ERR_NO_DATA)
<descrip>
<tag/Function/Install an already loaded driver and return an error code.
<tag/Header/<tt/<ref id="serial.h" name="serial.h">/
<tag/Declaration/<tt/unsigned char __fastcall__ ser_install (void* driver);/
<tag/Declaration/<tt/unsigned char __fastcall__ ser_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/SER_ERR_OK/ in case of success).

View File

@ -3,7 +3,7 @@
<article>
<title>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>

View File

@ -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.

View File

@ -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&uuml;hlstrasser">
<abstract>
@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/&lt;sys&gt;/:
Start the desired version of the emulator (CBM610 programs run on
the CBM II &lsqb;<tt/xcbm2/&rsqb; emulator).
In the Windows versions of VICE, choose <bf>File&gt;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&gt;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 &lsqb;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

View File

@ -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.

View File

@ -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>

View File

@ -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.

View File

@ -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).

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -44,4 +44,4 @@ _exit: jsr donelib ; Run module destructors
; A 5200 program isn't supposed to exit.
halt: jmp halt
halt: jmp halt

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -3,7 +3,7 @@
;
; 25.12.99
; void GraphicsString (char *myString);
; void GraphicsString (const void *myString);
.export _GraphicsString

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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)

164
samples/cbm/Makefile Normal file
View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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.
----------------------------------------------------------------------------

View File

@ -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)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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");
}

View File

@ -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 */

View File

@ -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) {

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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;

View File

@ -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. */

View File

@ -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

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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 %*

View File

@ -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 {

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

30
test/misc/bug1357.c Normal file
View File

@ -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;
}

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More