mirror of
https://github.com/cc65/cc65.git
synced 2025-04-08 19:38:55 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
8b0de8a931
10
.travis.yml
10
.travis.yml
@ -9,14 +9,14 @@ jobs:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass
|
||||
script:
|
||||
- make bin USER_CFLAGS=-Werror
|
||||
- make lib QUIET=1
|
||||
- make -j2 bin USER_CFLAGS=-Werror
|
||||
- make -j2 lib QUIET=1
|
||||
- make test QUIET=1
|
||||
- make samples
|
||||
- make -j2 samples
|
||||
- make -C src clean
|
||||
- make bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32-
|
||||
- make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=i686-w64-mingw32-
|
||||
- make -C samples clean
|
||||
- make doc zip
|
||||
- make -j2 doc zip
|
||||
after_success:
|
||||
- make -f Makefile.travis
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
[Wiki](https://github.com/cc65/wiki/wiki)
|
||||
|
||||
[](https://travis-ci.org/cc65/cc65/builds)
|
||||
[](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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
|
@ -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
20
cfg/c128-asm.cfg
Normal 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
19
cfg/vic20-asm.cfg
Normal 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;
|
||||
}
|
@ -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 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.
|
||||
|
||||
|
@ -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 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.
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
128
doc/funcref.sgml
128
doc/funcref.sgml
@ -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(&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).
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -6,6 +6,7 @@
|
||||
<url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline>
|
||||
<url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline>
|
||||
<url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline>
|
||||
<url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline>
|
||||
<url url="mailto:stephan.muehlstrasser@web.de" name="Stephan Mühlstrasser">
|
||||
|
||||
<abstract>
|
||||
@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/<sys>/:
|
||||
Start the desired version of the emulator (CBM610 programs run on
|
||||
the CBM II [<tt/xcbm2/] emulator).
|
||||
|
||||
In the Windows versions of VICE, choose <bf>File>Autoboot disk/tape
|
||||
image...</bf>, choose your executable, and click <bf/OK/.
|
||||
|
||||
In the Unix versions, hold down the mouse's first button. Move the pointer to
|
||||
<bf>Smart-attach disk/tape...</bf>, and release the button. Choose your
|
||||
executable, and click <bf/Autostart/.
|
||||
Choose <bf>File>Autostart disk/tape image...</bf>, choose your executable,
|
||||
and click <bf/OK/.
|
||||
|
||||
The file has a 14-byte header which corresponds to a PRG-format BASIC program,
|
||||
consisting of a single line, similar to this:
|
||||
@ -499,6 +496,29 @@ The output will appear on a separate line, and you will be returned to a BASIC
|
||||
prompt.
|
||||
|
||||
|
||||
<sect1>Gamate<p>
|
||||
|
||||
Before you can run the cartridge image produced by the linker, the binary has to
|
||||
be patched using the <bf/gamate-fixcart/ tool that is included in the cc65
|
||||
package in the util/gamata directory.
|
||||
|
||||
<tscreen><verb>
|
||||
gamate-fixcart <image.bin>
|
||||
</verb></tscreen>
|
||||
|
||||
<sect2>MESS<p>
|
||||
Available at <url
|
||||
url="https://www.mamedev.org">:
|
||||
|
||||
MESS (Multiple Emulator Super System) is a multi system emulator that emulates
|
||||
various cc65 targets. It once started as a MAME fork, but was marged into MAME
|
||||
again at some point.
|
||||
|
||||
<tscreen><verb>
|
||||
mess gamate -debug -window -skip_gameinfo -cart <image.bin>
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1>GEOS<p>
|
||||
Available at <it/Click Here Software's/ <url
|
||||
url="http://cbmfiles.com/geos/index.html" name="GEOS download section">:
|
||||
@ -535,17 +555,8 @@ feature on.
|
||||
</quote>
|
||||
|
||||
<quote>
|
||||
VICE even has different ways that depend on which operating system is running
|
||||
the emulator.
|
||||
<itemize>
|
||||
<item>In Windows, you must click on <bf/Options/ (in an always visible menu).
|
||||
Then, you must click on <bf/True drive emulation/.
|
||||
<item>In Unix, you must <em/hold down/ the second button on your mouse. Move
|
||||
the pointer down to <bf/Drive settings/. Then, move the pointer over to
|
||||
<bf/Enable true drive emulation/. (If there is a check-mark in front of
|
||||
those words, that feature already is turned on -- then, move the pointer
|
||||
off of that menu.) Release the mouse button.
|
||||
</itemize>
|
||||
In VICE, got to <bf/Settings/ -> <bf/Settings/, then <bf/Peripherial devices/ ->
|
||||
<bf/Drive/. Then, you must enable the <bf/True drive emulation/ checkbox.
|
||||
</quote>
|
||||
|
||||
Find the <bf/CONVERT/ program on the boot disk [tap the 6-key; then, you
|
||||
@ -572,6 +583,29 @@ directory notePad. Look at the eight file-positions on each page until you see
|
||||
The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished
|
||||
reading it.
|
||||
|
||||
Alternatively you can use the <bf/c1541/ program that comes with VICE to write the
|
||||
file to a disk image directly in GEOS format, so it can be used in GEOS directly
|
||||
without having to use the <bf/CONVERT/ program.
|
||||
|
||||
<tscreen><verb>
|
||||
c1541 -attach geos.d64 -geoswrite hello1
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1>Nintendo Entertainment System<p>
|
||||
|
||||
<sect2>Mednafen (NES)<p>
|
||||
Available at <url
|
||||
url="https://mednafen.github.io/releases/">:
|
||||
|
||||
Mednafen is a multi system emulator that emulates a couple of the supported
|
||||
targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and
|
||||
PC Engine/TurboGrafx 16.
|
||||
|
||||
<tscreen><verb>
|
||||
mednafen -force_module nes <image.bin>
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1>Ohio Scientific Challenger 1P<p>
|
||||
The <tt/osic1p/ runtime library returns to the boot prompt when the main()
|
||||
@ -694,6 +728,32 @@ Press <RETURN>.
|
||||
After hitting the RETURN key, you should see the boot prompt again.
|
||||
|
||||
|
||||
<sect1>PC Engine/TurboGrafx 16<p>
|
||||
|
||||
For the cartridge image produced by the linker to work in emulators and on real
|
||||
hardware, its content must be rearranged so the first 8k block becomes the last
|
||||
8k block in the image.
|
||||
|
||||
For example, for a 32k image this can be done using <bf/dd/ as follows:
|
||||
|
||||
<tscreen><verb>
|
||||
dd if=infile.bin bs=8K skip=3 > outfile.pce
|
||||
dd if=infile.bin bs=8K count=3 >> outfile.pce
|
||||
</verb></tscreen>
|
||||
|
||||
<sect2>Mednafen<p>
|
||||
Available at <url
|
||||
url="https://mednafen.github.io/releases/">:
|
||||
|
||||
Mednafen is a multi system emulator that emulates a couple of the supported
|
||||
targets of cc65: Apple II/II+, Atari Lynx, Nintendo Entertainment System and
|
||||
PC Engine/TurboGrafx 16.
|
||||
|
||||
<tscreen><verb>
|
||||
mednafen -force_module pce <image.pce>
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1>Contributions wanted<p>
|
||||
|
||||
We need your help! Recommended emulators and instructions for other targets
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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).
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -44,4 +44,4 @@ _exit: jsr donelib ; Run module destructors
|
||||
|
||||
; A 5200 program isn't supposed to exit.
|
||||
|
||||
halt: jmp halt
|
||||
halt: jmp halt
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
40
libsrc/creativision/playsound.s
Normal file
40
libsrc/creativision/playsound.s
Normal 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
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
;
|
||||
; 25.12.99
|
||||
|
||||
; void GraphicsString (char *myString);
|
||||
; void GraphicsString (const void *myString);
|
||||
|
||||
.export _GraphicsString
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
59
samples/atari2600/Makefile
Normal file
59
samples/atari2600/Makefile
Normal 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
164
samples/cbm/Makefile
Normal 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)
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
----------------------------------------------------------------------------
|
||||
|
59
samples/supervision/Makefile
Normal file
59
samples/supervision/Makefile
Normal 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)
|
95
samples/supervision/hello.c
Normal file
95
samples/supervision/hello.c
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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) {
|
||||
|
804
src/cc65/expr.c
804
src/cc65/expr.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
177
src/cc65/stmt.c
177
src/cc65/stmt.c
@ -163,7 +163,7 @@ static int IfStatement (void)
|
||||
TestResult = TestInParens (Label1, 0);
|
||||
|
||||
/* Parse the if body */
|
||||
GotBreak = Statement (0);
|
||||
GotBreak = AnyStatement (0);
|
||||
|
||||
/* Else clause present? */
|
||||
if (CurTok.Tok != TOK_ELSE) {
|
||||
@ -195,7 +195,7 @@ static int IfStatement (void)
|
||||
g_defcodelabel (Label1);
|
||||
|
||||
/* Total break only if both branches had a break. */
|
||||
GotBreak &= Statement (0);
|
||||
GotBreak &= AnyStatement (0);
|
||||
|
||||
/* Generate the label for the else clause */
|
||||
g_defcodelabel (Label2);
|
||||
@ -225,7 +225,7 @@ static void DoStatement (void)
|
||||
g_defcodelabel (LoopLabel);
|
||||
|
||||
/* Parse the loop body */
|
||||
Statement (0);
|
||||
AnyStatement (0);
|
||||
|
||||
/* Output the label for a continue */
|
||||
g_defcodelabel (ContinueLabel);
|
||||
@ -283,7 +283,7 @@ static void WhileStatement (void)
|
||||
g_defcodelabel (LoopLabel);
|
||||
|
||||
/* Loop body */
|
||||
Statement (&PendingToken);
|
||||
AnyStatement (&PendingToken);
|
||||
|
||||
/* Emit the while condition label */
|
||||
g_defcodelabel (CondLabel);
|
||||
@ -509,7 +509,7 @@ static void ForStatement (void)
|
||||
|
||||
/* Loop body */
|
||||
g_defcodelabel (BodyLabel);
|
||||
Statement (&PendingToken);
|
||||
AnyStatement (&PendingToken);
|
||||
|
||||
/* If we had an increment expression, move the code to the bottom of
|
||||
** the loop. In this case we don't need to jump there at the end of
|
||||
@ -536,17 +536,20 @@ static void ForStatement (void)
|
||||
|
||||
|
||||
|
||||
static int CompoundStatement (void)
|
||||
static int CompoundStatement (int* PendingToken)
|
||||
/* Compound statement. Allow any number of statements inside braces. The
|
||||
** function returns true if the last statement was a break or return.
|
||||
*/
|
||||
{
|
||||
int GotBreak;
|
||||
int GotBreak = 0;
|
||||
|
||||
/* Remember the stack at block entry */
|
||||
int OldStack = StackPtr;
|
||||
unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack);
|
||||
|
||||
/* Skip '{' */
|
||||
NextToken ();
|
||||
|
||||
/* Enter a new lexical level */
|
||||
EnterBlockLevel ();
|
||||
|
||||
@ -554,16 +557,15 @@ static int CompoundStatement (void)
|
||||
DeclareLocals ();
|
||||
|
||||
/* Now process statements in this block */
|
||||
GotBreak = 0;
|
||||
while (CurTok.Tok != TOK_RCURLY) {
|
||||
if (CurTok.Tok != TOK_CEOF) {
|
||||
GotBreak = Statement (0);
|
||||
GotBreak = AnyStatement (0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up the stack. */
|
||||
/* Clean up the stack if the codeflow may reach the end */
|
||||
if (!GotBreak) {
|
||||
g_space (StackPtr - OldStack);
|
||||
}
|
||||
@ -583,12 +585,80 @@ static int CompoundStatement (void)
|
||||
/* Leave the lexical level */
|
||||
LeaveBlockLevel ();
|
||||
|
||||
/* Skip '}' */
|
||||
CheckTok (TOK_RCURLY, "'}' expected", PendingToken);
|
||||
|
||||
return GotBreak;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Statement (int* PendingToken)
|
||||
static void Statement (int* PendingToken)
|
||||
/* Single-line statement */
|
||||
{
|
||||
ExprDesc Expr;
|
||||
unsigned PrevErrorCount;
|
||||
CodeMark Start, End;
|
||||
|
||||
/* Remember the current error count and code position */
|
||||
PrevErrorCount = ErrorCount;
|
||||
GetCodePos (&Start);
|
||||
|
||||
/* Actual statement */
|
||||
ED_Init (&Expr);
|
||||
Expr.Flags |= E_NEED_NONE;
|
||||
Expression0 (&Expr);
|
||||
|
||||
/* If the statement didn't generate code, and is not of type
|
||||
** void, emit a warning.
|
||||
*/
|
||||
GetCodePos (&End);
|
||||
if (!ED_YetToLoad (&Expr) &&
|
||||
!ED_MayHaveNoEffect (&Expr) &&
|
||||
CodeRangeIsEmpty (&Start, &End) &&
|
||||
IS_Get (&WarnNoEffect) &&
|
||||
PrevErrorCount == ErrorCount) {
|
||||
Warning ("Expression result unused");
|
||||
}
|
||||
CheckSemi (PendingToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int ParseAnyLabels (void)
|
||||
/* Return -1 if there are any labels with a statement */
|
||||
{
|
||||
unsigned PrevErrorCount = ErrorCount;
|
||||
int HasLabels = 0;
|
||||
for (;;) {
|
||||
if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
|
||||
/* C 'goto' label */
|
||||
DoLabel ();
|
||||
} else if (CurTok.Tok == TOK_CASE) {
|
||||
/* C 'case' label */
|
||||
CaseLabel ();
|
||||
} else if (CurTok.Tok == TOK_DEFAULT) {
|
||||
/* C 'default' label */
|
||||
DefaultLabel ();
|
||||
} else {
|
||||
/* No labels */
|
||||
break;
|
||||
}
|
||||
HasLabels = 1;
|
||||
}
|
||||
|
||||
if (HasLabels) {
|
||||
if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AnyStatement (int* PendingToken)
|
||||
/* Statement parser. Returns 1 if the statement does a return/break, returns
|
||||
** 0 otherwise. If the PendingToken pointer is not NULL, the function will
|
||||
** not skip the terminating token of the statement (closing brace or
|
||||
@ -598,40 +668,27 @@ int Statement (int* PendingToken)
|
||||
** NULL, the function will skip the token.
|
||||
*/
|
||||
{
|
||||
ExprDesc Expr;
|
||||
int GotBreak;
|
||||
unsigned PrevErrorCount;
|
||||
CodeMark Start, End;
|
||||
|
||||
ED_Init (&Expr);
|
||||
|
||||
/* Assume no pending token */
|
||||
if (PendingToken) {
|
||||
*PendingToken = 0;
|
||||
}
|
||||
|
||||
/* Check for a label. A label is always part of a statement, it does not
|
||||
/* Handle any labels. A label is always part of a statement, it does not
|
||||
** replace one.
|
||||
*/
|
||||
while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
|
||||
/* Handle the label */
|
||||
DoLabel ();
|
||||
if (CheckLabelWithoutStatement ()) {
|
||||
return 0;
|
||||
}
|
||||
if (ParseAnyLabels ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (CurTok.Tok) {
|
||||
|
||||
case TOK_LCURLY:
|
||||
NextToken ();
|
||||
GotBreak = CompoundStatement ();
|
||||
CheckTok (TOK_RCURLY, "'{' expected", PendingToken);
|
||||
return GotBreak;
|
||||
|
||||
case TOK_IF:
|
||||
return IfStatement ();
|
||||
|
||||
case TOK_SWITCH:
|
||||
SwitchStatement ();
|
||||
break;
|
||||
|
||||
case TOK_WHILE:
|
||||
WhileStatement ();
|
||||
break;
|
||||
@ -640,10 +697,15 @@ int Statement (int* PendingToken)
|
||||
DoStatement ();
|
||||
break;
|
||||
|
||||
case TOK_SWITCH:
|
||||
SwitchStatement ();
|
||||
case TOK_FOR:
|
||||
ForStatement ();
|
||||
break;
|
||||
|
||||
case TOK_GOTO:
|
||||
GotoStatement ();
|
||||
CheckSemi (PendingToken);
|
||||
return 1;
|
||||
|
||||
case TOK_RETURN:
|
||||
ReturnStatement ();
|
||||
CheckSemi (PendingToken);
|
||||
@ -659,55 +721,22 @@ int Statement (int* PendingToken)
|
||||
CheckSemi (PendingToken);
|
||||
return 1;
|
||||
|
||||
case TOK_FOR:
|
||||
ForStatement ();
|
||||
break;
|
||||
|
||||
case TOK_GOTO:
|
||||
GotoStatement ();
|
||||
CheckSemi (PendingToken);
|
||||
return 1;
|
||||
|
||||
case TOK_SEMI:
|
||||
/* Ignore it */
|
||||
CheckSemi (PendingToken);
|
||||
break;
|
||||
|
||||
case TOK_PRAGMA:
|
||||
DoPragma ();
|
||||
break;
|
||||
|
||||
case TOK_CASE:
|
||||
CaseLabel ();
|
||||
CheckLabelWithoutStatement ();
|
||||
case TOK_SEMI:
|
||||
/* Empty statement. Ignore it */
|
||||
CheckSemi (PendingToken);
|
||||
break;
|
||||
|
||||
case TOK_DEFAULT:
|
||||
DefaultLabel ();
|
||||
CheckLabelWithoutStatement ();
|
||||
break;
|
||||
case TOK_LCURLY:
|
||||
return CompoundStatement (PendingToken);
|
||||
|
||||
default:
|
||||
/* Remember the current error count and code position */
|
||||
PrevErrorCount = ErrorCount;
|
||||
GetCodePos (&Start);
|
||||
|
||||
/* Actual statement */
|
||||
Expr.Flags |= E_NEED_NONE;
|
||||
Expression0 (&Expr);
|
||||
|
||||
/* If the statement didn't generate code, and is not of type
|
||||
** void, emit a warning.
|
||||
*/
|
||||
GetCodePos (&End);
|
||||
if (!ED_YetToLoad (&Expr) &&
|
||||
!ED_MayHaveNoEffect (&Expr) &&
|
||||
CodeRangeIsEmpty (&Start, &End) &&
|
||||
IS_Get (&WarnNoEffect) &&
|
||||
PrevErrorCount == ErrorCount) {
|
||||
Warning ("Expression result unused");
|
||||
}
|
||||
CheckSemi (PendingToken);
|
||||
/* Simple statement */
|
||||
Statement (PendingToken);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 %*
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
119
targettest/cbm/cbmdir-test.c
Normal file
119
targettest/cbm/cbmdir-test.c
Normal 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;
|
||||
}
|
@ -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)
|
||||
|
@ -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
30
test/misc/bug1357.c
Normal 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;
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user