1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-06 16:29:30 +00:00

Merge branch 'master' into master

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

View File

@ -9,14 +9,14 @@ jobs:
- sudo apt-get update - sudo apt-get update
- sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass - sudo apt-get install linuxdoc-tools linuxdoc-tools-info binutils-mingw-w64-i686 gcc-mingw-w64-i686 sshpass
script: script:
- make bin USER_CFLAGS=-Werror - make -j2 bin USER_CFLAGS=-Werror
- make lib QUIET=1 - make -j2 lib QUIET=1
- make test QUIET=1 - make test QUIET=1
- make samples - make -j2 samples
- make -C src clean - 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 -C samples clean
- make doc zip - make -j2 doc zip
after_success: after_success:
- make -f Makefile.travis - make -f Makefile.travis

View File

@ -4,7 +4,7 @@
[Wiki](https://github.com/cc65/wiki/wiki) [Wiki](https://github.com/cc65/wiki/wiki)
[![Build Status](https://api.travis-ci.org/cc65/cc65.svg?branch=master)](https://travis-ci.org/cc65/cc65/builds) [![Build Status](https://app.travis-ci.com/cc65/cc65.svg?branch=master)](https://app.travis-ci.com/cc65/cc65)
cc65 is a complete cross development package for 65(C)02 systems, including cc65 is a complete cross development package for 65(C)02 systems, including
a powerful macro assembler, a C compiler, linker, librarian and several a powerful macro assembler, a C compiler, linker, librarian and several

View File

@ -924,7 +924,7 @@ KEY_QUESTIONMARK = KEY_SLASH | KEY_SHIFT
KEY_CLEAR = KEY_LESSTHAN | KEY_SHIFT KEY_CLEAR = KEY_LESSTHAN | KEY_SHIFT
KEY_INSERT = KEY_GREATERTHAN | 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_DOWN = KEY_EQUALS | KEY_CTRL
KEY_LEFT = KEY_PLUS | KEY_CTRL KEY_LEFT = KEY_PLUS | KEY_CTRL
KEY_RIGHT = KEY_ASTERISK | KEY_CTRL KEY_RIGHT = KEY_ASTERISK | KEY_CTRL

View File

@ -77,6 +77,8 @@ VIC_SPR_EXP_Y := $D017
VIC_SPR_EXP_X := $D01D VIC_SPR_EXP_X := $D01D
VIC_SPR_MCOLOR := $D01C VIC_SPR_MCOLOR := $D01C
VIC_SPR_BG_PRIO := $D01B VIC_SPR_BG_PRIO := $D01B
VIC_SPR_COLL := $D01E
VIC_SPR_BG_COLL := $D01F
VIC_SPR_MCOLOR0 := $D025 VIC_SPR_MCOLOR0 := $D025
VIC_SPR_MCOLOR1 := $D026 VIC_SPR_MCOLOR1 := $D026

View File

@ -5,21 +5,21 @@
;** Screen ;** Screen
SCREEN_ROWS = 24 SCREEN_ROWS = 24
SCREEN_COLS = 32 SCREEN_COLS = 32
SCREEN_PTR = $3A SCREEN_PTR := $3A
CURSOR_X = $3C CURSOR_X := $3C
CURSOR_Y = $3D CURSOR_Y := $3D
;** VDP ;** VDP
VDP_DATA_R = $2000 VDP_DATA_R := $2000
VDP_STATUS_R = $2001 VDP_STATUS_R := $2001
VDP_DATA_W = $3000 VDP_DATA_W := $3000
VDP_CONTROL_W = $3001 VDP_CONTROL_W := $3001
;** PIA ;** PIA
PIA0_DATA = $1000 PIA0_DATA := $1000
PIA0_STATUS = $1001 PIA0_STATUS := $1001
PIA1_DATA = $1002 PIA1_DATA := $1002
PIA1_STATUS = $1003 PIA1_STATUS := $1003
;** General ;** General
CH_VLINE = 33 CH_VLINE = 33
@ -30,11 +30,11 @@ CH_LLCORNER = 37
CH_LRCORNER = 38 CH_LRCORNER = 38
;** I/O (Zero-page variables) ;** I/O (Zero-page variables)
ZP_KEYBOARD = $10 ZP_KEYBOARD := $10
ZP_JOY0_DIR = $11 ZP_JOY0_DIR := $11
ZP_JOY1_DIR = $13 ZP_JOY1_DIR := $13
ZP_JOY0_BUTTONS = $16 ZP_JOY0_BUTTONS := $16
ZP_JOY1_BUTTONS = $17 ZP_JOY1_BUTTONS := $17
;** Joystick direction values (ZP_JOY0_DIR/ZP_JOY1_DIR) ;** Joystick direction values (ZP_JOY0_DIR/ZP_JOY1_DIR)
JOY_N = $49 JOY_N = $49
@ -54,8 +54,13 @@ JOY_WNW = $4C
JOY_NW = $4B JOY_NW = $4B
JOY_NNW = $4A JOY_NNW = $4A
;** BIOS ;** BIOS routines
BIOS_IRQ1_ADDR = $FF3F BIOS_NMI_RESET_ADDR := $F808
BIOS_IRQ2_ADDR = $FF52 BIOS_PLAY_TUNE1 := $FBD6
BIOS_NMI_RESET_ADDR = $F808 BIOS_PLAY_SONG := $FBED
BIOS_WRITE_VDP_REG = $FE1F BIOS_PLAY_TUNE2 := $FCE6
BIOS_WRITE_VDP_REG := $FE1F
BIOS_QUIET_PSG := $FE54
BIOS_POKE_PSG := $FE77
BIOS_IRQ1_ADDR := $FF3F
BIOS_IRQ2_ADDR := $FF52

View File

@ -44,7 +44,9 @@ EOF = -1
.if .defined(__APPLE2__) .if .defined(__APPLE2__)
FILENAME_MAX = 64+1 FILENAME_MAX = 64+1
.elseif .defined(__ATARI__) .elseif .defined(__ATARI__)
FILENAME_MAX = 12+1 FILENAME_MAX = 63+1
.elseif .defined(__CBM__)
FILENAME_MAX = 255
.elseif .defined(__LUNIX__) .elseif .defined(__LUNIX__)
FILENAME_MAX = 80+1 FILENAME_MAX = 80+1
.elseif .defined(__TELESTRAT__) .elseif .defined(__TELESTRAT__)

View File

@ -90,7 +90,7 @@ VIA1_T1LH := VIA1+$7 ; Timer 1 latch, high byte
VIA1_T2CL := VIA1+$8 ; Timer 2, low byte VIA1_T2CL := VIA1+$8 ; Timer 2, low byte
VIA1_T2CH := VIA1+$9 ; Timer 2, high byte VIA1_T2CH := VIA1+$9 ; Timer 2, high byte
VIA1_SR := VIA1+$A ; Shift register 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_PCR := VIA1+$C ; Peripheral control register
VIA1_IFR := VIA1+$D ; Interrupt flag register VIA1_IFR := VIA1+$D ; Interrupt flag register
VIA1_IER := VIA1+$E ; Interrupt enable 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_T2CL := VIA2+$8 ; Timer 2, low byte
VIA2_T2CH := VIA2+$9 ; Timer 2, high byte VIA2_T2CH := VIA2+$9 ; Timer 2, high byte
VIA2_SR := VIA2+$A ; Shift register 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_PCR := VIA2+$C ; Peripheral control register
VIA2_IFR := VIA2+$D ; Interrupt flag register VIA2_IFR := VIA2+$D ; Interrupt flag register
VIA2_IER := VIA2+$E ; Interrupt enable register VIA2_IER := VIA2+$E ; Interrupt enable register

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

@ -0,0 +1,20 @@
FEATURES {
STARTADDRESS: default = $1c01;
}
SYMBOLS {
__LOADADDR__: type = import;
}
MEMORY {
ZP: file = "", start = $0002, size = $00FE, define = yes;
LOADADDR: file = %O, start = %S - 2, size = $0002;
MAIN: file = %O, start = %S, size = $D000 - %S;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
LOADADDR: load = LOADADDR, type = ro;
EXEHDR: load = MAIN, type = ro, optional = yes;
CODE: load = MAIN, type = rw;
RODATA: load = MAIN, type = ro, optional = yes;
DATA: load = MAIN, type = rw, optional = yes;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
}

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

@ -0,0 +1,19 @@
FEATURES {
STARTADDRESS: default = $1001;
}
SYMBOLS {
__LOADADDR__: type = import;
}
MEMORY {
ZP: file = "", start = $0002, size = $001A, define = yes;
LOADADDR: file = %O, start = $1001, size = $0002;
MAIN: file = %O, start = %S, size = $0DF3 - %S;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
LOADADDR: load = LOADADDR, type = ro;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
}

View File

@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of
<tag><tt/a2.ssc.ser (a2_ssc_ser)/</tag> <tag><tt/a2.ssc.ser (a2_ssc_ser)/</tag>
Driver for the Apple&nbsp;II Super Serial Card. Supports up to 19200 baud, Driver for the Apple&nbsp;II Super Serial Card. Supports up to 19200 baud,
hardware flow control (RTS/CTS) and interrupt driven receives. Note requires hardware flow control (RTS/CTS) and does interrupt driven receives.
that because of the peculiarities of the 6551 chip transmits are not Note that because of the peculiarities of the 6551 chip transmits are not
interrupt driven, and the transceiver blocks if the receiver asserts interrupt driven, and the transceiver blocks if the receiver asserts
flow control because of a full buffer. flow control because of a full buffer.

View File

@ -428,8 +428,8 @@ The names in the parentheses denote the symbols to be used for static linking of
<tag><tt/a2e.ssc.ser (a2e_ssc_ser)/</tag> <tag><tt/a2e.ssc.ser (a2e_ssc_ser)/</tag>
Driver for the Apple&nbsp;II Super Serial Card. Supports up to 19200 baud, Driver for the Apple&nbsp;II Super Serial Card. Supports up to 19200 baud,
hardware flow control (RTS/CTS) and interrupt driven receives. Note requires hardware flow control (RTS/CTS) and does interrupt driven receives.
that because of the peculiarities of the 6551 chip transmits are not Note that because of the peculiarities of the 6551 chip transmits are not
interrupt driven, and the transceiver blocks if the receiver asserts interrupt driven, and the transceiver blocks if the receiver asserts
flow control because of a full buffer. flow control because of a full buffer.

View File

@ -675,9 +675,9 @@ The default callbacks definition (<tt/mouse_def_callbacks/) is an alias for the
<sect1>RS232 device drivers<p> <sect1>RS232 device drivers<p>
Currently there is one RS232 driver. It uses the R: device (therefore Currently there is one RS232 driver. It supports up to 9600 baud, requires hardware flow control
an R: driver needs to be installed) and was tested with the 850 (RTS/CTS) and uses the R: device (therefore an R: driver needs to be installed). It was tested
interface module. with the 850 interface module.
<table> <table>
<tabular ca="rr"> <tabular ca="rr">

View File

@ -176,10 +176,11 @@ No mouse drivers are currently available for the Atmos.
<tag><tt/atmos-acia.ser (atmos_acia_ser)/</tag> <tag><tt/atmos-acia.ser (atmos_acia_ser)/</tag>
Driver for the Telestrat integrated serial controller and the Atmos with a Driver for the Telestrat integrated serial controller and the Atmos with a
serial add-on. serial add-on. Supports up to 19200 baud, requires hardware flow control
Note that, because of the peculiarities of the 6551 chip, together with the (RTS/CTS) and does interrupt driven receives. Note that, because of the
use of the NMI, transmits are not interrupt driven; and, the transceiver peculiarities of the 6551 chip, together with the use of the NMI, transmits
blocks if the receiver asserts flow control because of a full buffer. are not interrupt driven; and, the transceiver blocks if the receiver
asserts flow control because of a full buffer.
</descrip><p> </descrip><p>

View File

@ -324,9 +324,9 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/c128-
<descrip> <descrip>
<tag><tt/c128-swlink.ser (c128_swlink_ser)/</tag> <tag><tt/c128-swlink.ser (c128_swlink_ser)/</tag>
Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware
control (RTS/CTS), and interrupt-driven receives. Note that, because of the flow control (RTS/CTS) and does interrupt driven receives. Note that, because of
peculiarities of the 6551 chip, together with the use of the NMI, transmits 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 are not interrupt driven; and, the transceiver blocks if the receiver asserts
flow control because of a full buffer. flow control because of a full buffer.

View File

@ -410,9 +410,9 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/c64-1
<descrip> <descrip>
<tag><tt/c64-swlink.ser (c64_swlink_ser)/</tag> <tag><tt/c64-swlink.ser (c64_swlink_ser)/</tag>
Driver for the SwiftLink cartridge. Supports up to 38400 BPS, hardware flow Driver for the SwiftLink cartridge. Supports up to 38400 baud, requires hardware
control (RTS/CTS), and interrupt-driven receives. Note that, because of the flow control (RTS/CTS) and does interrupt driven receives. Note that, because of
peculiarities of the 6551 chip, together with the use of the NMI, transmits 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 are not interrupt driven; and, the transceiver blocks if the receiver asserts
flow control because of a full buffer. flow control because of a full buffer.

View File

@ -231,10 +231,10 @@ The default drivers, <tt/mouse_stddrv (mouse_static_stddrv)/, point to <tt/cbm51
<tag><tt/cbm510-std.ser (cbm510_std_ser)/</tag> <tag><tt/cbm510-std.ser (cbm510_std_ser)/</tag>
Driver for the 6551 ACIA chip built into the Commodore 510. Supports up to 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. 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven
Note that, because of the peculiarities of the 6551 chip, transmits are not receives. Note that, because of the peculiarities of the 6551 chip, transmits
interrupt driven; and, the transceiver blocks if the receiver asserts flow are not interrupt driven; and, the transceiver blocks if the receiver asserts
control because of a full buffer. flow control because of a full buffer.
</descrip><p> </descrip><p>

View File

@ -212,10 +212,10 @@ No mouse drivers are currently available for the Commodore 610.
<tag><tt/cbm610-std.ser (cbm610_std_ser)/</tag> <tag><tt/cbm610-std.ser (cbm610_std_ser)/</tag>
Driver for the 6551 ACIA chip built into the Commodore 610. Supports up to 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. 19200 baud, requires hardware flow control (RTS/CTS) and does interrupt driven
Note that, because of the peculiarities of the 6551 chip, transmits are not receives. Note that, because of the peculiarities of the 6551 chip, transmits
interrupt driven; and, the transceiver blocks if the receiver asserts flow are not interrupt driven; and, the transceiver blocks if the receiver asserts
control because of a full buffer. flow control because of a full buffer.
</descrip><p> </descrip><p>

View File

@ -298,6 +298,16 @@ function.
</itemize> </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> <sect1><tt/ctype.h/<label id="ctype.h"><p>
<itemize> <itemize>
@ -1684,6 +1694,44 @@ used in presence of a prototype.
</quote> </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> <sect1>bordercolor<label id="bordercolor"><p>
<quote> <quote>
@ -1778,7 +1826,7 @@ be used in presence of a prototype.
<item>The function is specific to the C128. <item>The function is specific to the C128.
<item>The function will not return to the caller. <item>The function will not return to the caller.
</itemize> </itemize>
<tag/Availability/C128 <tag/Availability/cc65
<tag/Example/None. <tag/Example/None.
</descrip> </descrip>
</quote> </quote>
@ -3410,7 +3458,7 @@ loaded.
<descrip> <descrip>
<tag/Function/Install an already loaded extended memory driver. <tag/Function/Install an already loaded extended memory driver.
<tag/Header/<tt/<ref id="em.h" name="em.h">/ <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 <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 and returns an error code. The function may be used to install a driver linked
statically to the program. statically to the program.
@ -4733,7 +4781,7 @@ There's no way to check for the number of actually connected joysticks.
<descrip> <descrip>
<tag/Function/Install an already loaded driver and return an error code. <tag/Function/Install an already loaded driver and return an error code.
<tag/Header/<tt/<ref id="joystick.h" name="joystick.h">/ <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 <tag/Description/The function installs a driver that was already loaded into
memory (or linked statically to the program). It returns an error code memory (or linked statically to the program). It returns an error code
(<tt/JOY_ERR_OK/ in case of success). (<tt/JOY_ERR_OK/ in case of success).
@ -5769,6 +5817,78 @@ be used in presence of a prototype.
</quote> </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> <sect1>qsort<label id="qsort"><p>
<quote> <quote>
@ -6198,7 +6318,7 @@ while (ser_get(&amp;ch) == SER_ERR_NO_DATA)
<descrip> <descrip>
<tag/Function/Install an already loaded driver and return an error code. <tag/Function/Install an already loaded driver and return an error code.
<tag/Header/<tt/<ref id="serial.h" name="serial.h">/ <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 <tag/Description/The function installs a driver that was already loaded into
memory (or linked statically to the program). It returns an error code memory (or linked statically to the program). It returns an error code
(<tt/SER_ERR_OK/ in case of success). (<tt/SER_ERR_OK/ in case of success).

View File

@ -3,7 +3,7 @@
<article> <article>
<title>Gamate System specific information for cc65 <title>Gamate System specific information for cc65
<author> <author>
<url url="mailto:groepaz@gmx.net" name="Groepaz/Hitmen"> <url url="mailto:groepaz@gmx.net" name="Groepaz">
<abstract> <abstract>
An overview over the Gamate runtime system as it is implemented for the 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> <sect>Other hints<p>
<itemize> <itemize>
<item>The Gamate is emulated by MESS (<url url="http://www.mess.org/">), <item>some resources on the Gamate: <url url="http://en.wikipedia.org/wiki/Gamate">
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">
</itemize> </itemize>
<sect>License<p> <sect>License<p>

View File

@ -205,7 +205,7 @@ see them together in the filling box in GeoPaint.
<sect2>GraphicsString <sect2>GraphicsString
<p> <p>
<tt/void GraphicsString (char *myGString)/ <tt/void GraphicsString (const void *myGString)/
<p> <p>
One of the more powerfull routines of GEOS. This function calls other graphic functions depending 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. on the given command string. See the structures chapter for a more detailed description.

View File

@ -6,6 +6,7 @@
<url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline> <url url="mailto:uz@cc65.org" name="Ullrich von Bassewitz">,<newline>
<url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline> <url url="mailto:cbmnut@hushmail.com" name="CbmNut">,<newline>
<url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline> <url url="mailto:greg.king5@verizon.net" name="Greg King">,<newline>
<url url="mailto:groepaz@gmx.net" name="Groepaz">,<newline>
<url url="mailto:stephan.muehlstrasser@web.de" name="Stephan M&uuml;hlstrasser"> <url url="mailto:stephan.muehlstrasser@web.de" name="Stephan M&uuml;hlstrasser">
<abstract> <abstract>
@ -458,12 +459,8 @@ Substitute the name of a Commodore computer for that <tt/&lt;sys&gt;/:
Start the desired version of the emulator (CBM610 programs run on Start the desired version of the emulator (CBM610 programs run on
the CBM II &lsqb;<tt/xcbm2/&rsqb; emulator). the CBM II &lsqb;<tt/xcbm2/&rsqb; emulator).
In the Windows versions of VICE, choose <bf>File&gt;Autoboot disk/tape Choose <bf>File&gt;Autostart disk/tape image...</bf>, choose your executable,
image...</bf>, choose your executable, and click <bf/OK/. 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/.
The file has a 14-byte header which corresponds to a PRG-format BASIC program, The file has a 14-byte header which corresponds to a PRG-format BASIC program,
consisting of a single line, similar to this: 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. 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> <sect1>GEOS<p>
Available at <it/Click Here Software's/ <url Available at <it/Click Here Software's/ <url
url="http://cbmfiles.com/geos/index.html" name="GEOS download section">: url="http://cbmfiles.com/geos/index.html" name="GEOS download section">:
@ -535,17 +555,8 @@ feature on.
</quote> </quote>
<quote> <quote>
VICE even has different ways that depend on which operating system is running In VICE, got to <bf/Settings/ -> <bf/Settings/, then <bf/Peripherial devices/ ->
the emulator. <bf/Drive/. Then, you must enable the <bf/True drive emulation/ checkbox.
<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>
</quote> </quote>
Find the <bf/CONVERT/ program on the boot disk &lsqb;tap the 6-key; then, you Find the <bf/CONVERT/ program on the boot disk &lsqb;tap the 6-key; then, you
@ -572,6 +583,29 @@ directory notePad. Look at the eight file-positions on each page until you see
The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished The output is shown in a GEOS dialog box; click <bf/OK/ when you have finished
reading it. 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> <sect1>Ohio Scientific Challenger 1P<p>
The <tt/osic1p/ runtime library returns to the boot prompt when the main() 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. 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> <sect1>Contributions wanted<p>
We need your help! Recommended emulators and instructions for other targets We need your help! Recommended emulators and instructions for other targets

View File

@ -59,6 +59,9 @@ Functions that are <em/not/ available:
Functions not available on all supported systems: Functions not available on all supported systems:
<itemize> <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 <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 are built on open/read/write/close. Those latter functions are not available
on all systems. on all systems.

View File

@ -2,7 +2,7 @@
<article> <article>
<title>PC-Engine (TurboGrafx 16) System-specific information for cc65 <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"> <url url="mailto:greg.king5@verizon.net" name="Greg King">
<abstract> <abstract>
@ -206,11 +206,6 @@ following functions (and a few others):
<sect>Other hints<p> <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: Some useful resources on PCE coding:
<itemize> <itemize>

View File

@ -195,10 +195,10 @@ No mouse drivers are currently available for the Plus/4.
<tag><tt/plus4-stdser.ser (plus4_stdser_ser)/</tag> <tag><tt/plus4-stdser.ser (plus4_stdser_ser)/</tag>
Driver for the 6551 ACIA chip built into the Plus/4. Supports up to 19200 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 baud, requires hardware flow control (RTS/CTS) and does interrupt driven
that because of the peculiarities of the 6551 chip transmits are not receives. Note that because of the peculiarities of the 6551 chip transmits
interrupt driven, and the transceiver blocks if the receiver asserts flow are not interrupt driven, and the transceiver blocks if the receiver asserts
control because of a full buffer. flow control because of a full buffer.
You need an adapter to use the builtin port, since the output levels You need an adapter to use the builtin port, since the output levels
available at the user port don't follow the RS232 standard. available at the user port don't follow the RS232 standard.

View File

@ -555,7 +555,7 @@ tgi_init(); //Set up the default palette and clear the screen.
<descrip> <descrip>
<tag/Function/Install an already loaded driver and return an error code. <tag/Function/Install an already loaded driver and return an error code.
<tag/Header/<tt/<ref id="tgi.h" name="tgi.h">/ <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 <tag/Description/The function installs a driver that was already loaded into
memory (or linked statically to the program). It returns an error code memory (or linked statically to the program). It returns an error code
(<tt/TGI_ERR_OK/ in case of success). (<tt/TGI_ERR_OK/ in case of success).

View File

@ -70,9 +70,9 @@
#define COLOR_WHITE 15 #define COLOR_WHITE 15
/* Protos */ /* Protos */
void __fastcall__ psg_outb(unsigned char b); void __fastcall__ psg_outb (unsigned char b);
void __fastcall__ psg_delay(unsigned char b); void __fastcall__ psg_delay (unsigned char b);
void psg_silence(void); void psg_silence (void);
void __fastcall__ bios_playsound(void *a, unsigned char b); void __fastcall__ bios_playsound (const void *a, unsigned char b);
#endif /* #ifndef _CVISION_H */ #endif /* #ifndef _CVISION_H */

View File

@ -82,7 +82,7 @@ unsigned char __fastcall__ em_load_driver (const char* driver);
unsigned char em_unload (void); unsigned char em_unload (void);
/* Uninstall, then unload the currently loaded driver. */ /* 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. */ /* Install an already loaded driver. Return an error code. */
unsigned char em_uninstall (void); unsigned char em_uninstall (void);

View File

@ -43,7 +43,7 @@ void __fastcall__ BitOtherClip(void *proc1, void *proc2, char skipl,
char skipr, unsigned skiptop, char skipr, unsigned skiptop,
struct iconpic *myIcon); struct iconpic *myIcon);
void __fastcall__ GraphicsString(char *myGfxString); void __fastcall__ GraphicsString(const void *myGfxString);
#ifdef __GEOS_CBM__ #ifdef __GEOS_CBM__
void SetNewMode(void); void SetNewMode(void);

View File

@ -89,7 +89,7 @@ unsigned char __fastcall__ joy_load_driver (const char* driver);
unsigned char joy_unload (void); unsigned char joy_unload (void);
/* Uninstall, then unload the currently loaded driver. */ /* 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. */ /* Install an already loaded driver. Return an error code. */
unsigned char joy_uninstall (void); unsigned char joy_uninstall (void);

View File

@ -63,6 +63,20 @@
#define ULONG_MAX 4294967295UL #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 */ /* End of limits.h */

View File

@ -136,7 +136,7 @@ unsigned char __fastcall__ ser_load_driver (const char* driver);
unsigned char ser_unload (void); unsigned char ser_unload (void);
/* Uninstall, then unload the currently loaded driver. */ /* 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. */ /* Install an already loaded driver. Return an error code. */
unsigned char ser_uninstall (void); unsigned char ser_uninstall (void);

View File

@ -40,6 +40,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
#include <limits.h>
@ -64,18 +65,7 @@ extern FILE* stderr;
#define SEEK_SET 2 #define SEEK_SET 2
#define TMP_MAX 256 #define TMP_MAX 256
/* Standard defines that are platform dependent */ #define FILENAME_MAX PATH_MAX
#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 L_tmpnam FILENAME_MAX #define L_tmpnam FILENAME_MAX

View File

@ -82,7 +82,7 @@ void tgi_unload (void);
** necessary. ** necessary.
*/ */
void __fastcall__ tgi_install (void* driver); void __fastcall__ tgi_install (const void* driver);
/* Install an already loaded driver. */ /* Install an already loaded driver. */
void tgi_uninstall (void); void tgi_uninstall (void);

View File

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

View File

@ -93,9 +93,7 @@ joy1: lda #$7F
sta CIA1_PRA sta CIA1_PRA
lda CIA1_PRB lda CIA1_PRB
cli cli
and #$1F jmp end
eor #$1F
rts
; Read joystick 2 ; Read joystick 2
@ -107,8 +105,6 @@ joy2: ldx #0
lda CIA1_PRA lda CIA1_PRA
sty CIA1_DDRA sty CIA1_DDRA
cli cli
and #$1F end: and #$1F
eor #$1F eor #$1F
rts rts

View File

@ -1,11 +1,11 @@
; ;
; Standard joystick driver for the Creativision. ; 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 "zeropage.inc"
.include "joy-kernel.inc" .include "joy-kernel.inc"
.include "joy-error.inc" .include "joy-error.inc"
.include "creativision.inc" .include "creativision.inc"
@ -13,10 +13,12 @@
.macpack module .macpack module
buttons := tmp2
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; Header. Includes jump table ; Header. Includes jump table
module_header _creativisionstd_joy module_header _creativisionstd_joy
; Driver signature ; Driver signature
@ -39,16 +41,14 @@
JOY_COUNT = 2 ; Number of joysticks we support 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_UP = $10
JOY_DOWN = $04 JOY_DOWN = $04
JOY_LEFT = $20 JOY_LEFT = $20
JOY_RIGHT = $08 JOY_RIGHT = $08
; ------------------------------------------------------------------------
; Code
.code .code
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
@ -59,7 +59,7 @@ JOY_RIGHT = $08
; ;
INSTALL: lda #JOY_ERR_OK INSTALL: lda #JOY_ERR_OK
ldx #0 ldx #>$0000
; rts ; Fall through ; rts ; Fall through
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
@ -82,14 +82,14 @@ COUNT: lda #<JOY_COUNT
; READ: Read a particular joystick passed in A. ; READ: Read a particular joystick passed in A.
; ;
READJOY: and #1 ; fix joystick number READJOY: lsr a ; Get joystick number
bne READJOY_1 ; read right joystick bcs READJOY_1 ; Read right joystick
; Read left joystick ; Read left joystick
ldx ZP_JOY0_DIR ldx ZP_JOY0_DIR
lda ZP_JOY0_BUTTONS lda ZP_JOY0_BUTTONS
jmp convert ; convert joystick state to cc65 values bcc convert ; Convert joystick state to cc65 values
; Read right joystick ; Read right joystick
@ -97,11 +97,11 @@ READJOY_1: ldx ZP_JOY1_DIR
lda ZP_JOY1_BUTTONS lda ZP_JOY1_BUTTONS
lsr a lsr a
lsr a lsr a
;jmp convert ; convert joystick state to cc65 values ;jmp convert ; Convert joystick state to cc65 values
; fall thru... ; Fall thru...
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; convert: make runtime lib compatible values ; convert: make runtime lib-compatible values
; inputs: ; inputs:
; A - buttons ; A - buttons
; X - direction ; X - direction
@ -111,24 +111,24 @@ convert:
; ------ ; ------
; buttons: ; 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). ; values were shifted to the right to be identical).
; Why are there two bits indicating a pressed trigger? ; Why are there two bits indicating a pressed trigger?
; According to the "Second book of programs for the Dick Smith Wizard" ; According to the "Second book of programs for the Dick Smith Wizard"
; (pg. 88ff), the left hand button gives the value of ; (pg. 88ff), the left-hand button gives the value of
; %00010001 and the right hand button gives %00100010 ; %00010001 and the right-hand button gives %00100010
; Why two bits? Can there be cases that just one of those bits is set? ; 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 ; Until those questions have been answered, we only use the lower two
; bits and ignore the upper ones... ; bits, and ignore the upper ones.
and #%00000011 ; button status came in in A, strip high bits and #%00000011 ; Button status came in A, strip high bits
sta retval ; initialize 'retval' with button status sta buttons
; ------ ; ------
; direction: ; direction:
; CV has a 16-direction joystick ; CV has a 16-direction joystick.
; ;
; port values: (compass points) ; Port values: (compass points)
; N - $49 - %01001001 ; N - $49 - %01001001
; NNE - $48 - %01001000 ; NNE - $48 - %01001000
; NE - $47 - %01000111 ; NE - $47 - %01000111
@ -147,55 +147,51 @@ convert:
; NNW - $4A - %01001010 ; NNW - $4A - %01001010
; center - $00 - %00000000 ; 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 ; 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) ; 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: ; 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: ; NNE:
; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP | JOY_RIGHT
; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP
; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP | JOY_RIGHT
; call to READJOY: return JOY_UP ; call to READJOY: return JOY_UP
; call to READJOY: return JOY_UP | JOY_RIGHT ; call to READJOY: return JOY_UP | JOY_RIGHT
; etc... ; etc.
txa ; move direction status into A txa ; Move direction status into A
beq done ; center position (no bits are set), nothing to do beq done ; Center position (no bits are set), nothing to do
and #$0F ; get rid of the "$40" bit and #$0F ; Get rid of the "$40" bit
bit bit0 ; is it a "three letter" direction (NNE, ENE, etc.)? lsr a ; Is it "three-letter" direction (NNE, ENE, etc.)?
beq special ; yes (bit #0 is zero) tax ; Create index into table
bcc special ; Yes (bit #0 was zero)
lsr a ; create index into table
tax
lda dirtable,x lda dirtable,x
done: ora retval ; include "button" bits done: ora buttons ; Include button bits
ldx #0 ldx #>$0000
rts rts
; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW ; NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW
special: lsr a special: lda toggle ; Toggle the flag
tax
lda toggler ; toggle the toggler
eor #$01 eor #$01
sta toggler sta toggle
bne spec_1 ; toggler is 1, use spectable_1 entry bne spec_1 ; Flag is 1, use spectable_1 entry
lda spectable_0,x ; toggler is 0, use spectable_0 entry lda spectable_0,x
bne done ; jump always bne done ; Jump always
spec_1: lda spectable_1,x spec_1: lda spectable_1,x
bne done ; jump always bne done ; Jump always
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; ;
.rodata .rodata
; a mapping table of "port values" to "cc65 values" ; A mapping table of "port values" to "cc65 values"
; port value had been shifted one bit to the right (range 0..7) ; Port value had been shifted one bit to the right (range 0..7)
dirtable: .byte JOY_DOWN ; S dirtable: .byte JOY_DOWN ; S
.byte JOY_DOWN | JOY_RIGHT ; SE .byte JOY_DOWN | JOY_RIGHT ; SE
.byte JOY_RIGHT ; E .byte JOY_RIGHT ; E
@ -205,12 +201,12 @@ dirtable: .byte JOY_DOWN ; S
.byte JOY_LEFT ; W .byte JOY_LEFT ; W
.byte JOY_DOWN | JOY_LEFT ; SW .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 spectable_0: .byte JOY_DOWN ; SSW
.byte JOY_DOWN ; SSE .byte JOY_DOWN ; SSE
.byte JOY_RIGHT ; ESE .byte JOY_RIGHT ; ESE
.byte JOY_RIGHT ; ENE .byte JOY_RIGHT ; ENE
.byte JOY_RIGHT ; NNE .byte JOY_UP ; NNE
.byte JOY_UP ; NNW .byte JOY_UP ; NNW
.byte JOY_LEFT ; WNW .byte JOY_LEFT ; WNW
.byte JOY_LEFT ; WSW .byte JOY_LEFT ; WSW
@ -224,14 +220,10 @@ spectable_1: .byte JOY_DOWN | JOY_LEFT ; SSW
.byte JOY_UP | JOY_LEFT ; WNW .byte JOY_UP | JOY_LEFT ; WNW
.byte JOY_DOWN | JOY_LEFT ; WSW .byte JOY_DOWN | JOY_LEFT ; WSW
; ------------------------------------------------------------------------
;
bit0: .byte $01
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; ;
.bss .bss
toggler: .res 1
retval: .res 1 toggle: .res 1
.end .end

View File

@ -0,0 +1,40 @@
; void __fastcall__ bios_playsound (void *a, unsigned char b);
.export _bios_playsound
.import popax
.include "creativision.inc"
songptr := $00 ; Points to current tune data
volptr := $04 ; Points to current volume table
;* Creativision Sound Player
;* Based on BIOS song player.
;*
;* Pass a pointer to a set of note triples, terminated with a tempo byte;
;* and pass the length of the triples and tempo (max 255).
;*
;* Note: tune data must be stored backwards.
_bios_playsound:
php
pha ; Save tune length
sei
lda #<$FCD5 ; BIOS decreasing-volume table
ldx #>$FCD5
sta volptr
stx volptr+1
jsr popax ; Get tune array pointer
sta songptr
stx songptr+1
pla
tay
dey ; Point to tempo byte
jmp BIOS_PLAY_SONG ; Let BIOS do its thing

View File

@ -1,63 +1,30 @@
; void __fastcall__ psg_outb( unsigned char b ); ; void __fastcall__ psg_outb (unsigned char b);
; void __fastcall__ psg_delayms( unsigned char c); ; void __fastcall__ psg_delay (unsigned char b);
; void __fastcall__ bios_playsound( void *b, unsigned char c); ; void psg_silence (void);
; 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" .include "creativision.inc"
_psg_outb:
;* Let BIOS output the value ;* Let BIOS output the value.
jmp $FE77 _psg_outb := BIOS_POKE_PSG
_psg_silence:
jmp $FE54 _psg_silence := BIOS_QUIET_PSG
_psg_delay: _psg_delay:
tay tay
l1: lda #200 l1: lda #200
l2: sbc #1 l2: sbc #1
bne l2 bne l2
lda #200 lda #200
l3: sbc #1 l3: sbc #1
bne l3 bne l3
dey dey
bne l1 bne l1
rts rts
;* Creativision Sound Player
;*
;* Based on BIOS sound player.
;* Pass a pointer to a set of note triples, terminated with a tempo byte
;* and the len (max 255)
_bios_playsound:
pha ; Save Length Byte
sei
lda #$D5 ; BIOS volume table low
sta $4
lda #$FC ; BIOS volume table high
sta $5
jsr popa ; Get Sound table pointer low
sta $0
jsr popa ; Get Sound table pointer high
sta $1
pla
tay ; Put length in Y
dey
php
jmp $FBED ; Let BIOS do it's thing

View File

@ -36,7 +36,7 @@ emd_sig: .byte $65, $6d, $64, EMD_API_VERSION ; "emd", version
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
; unsigned char __fastcall__ em_install (void* driver); ; unsigned char __fastcall__ em_install (const void* driver);
; /* Install the driver once it is loaded */ ; /* Install the driver once it is loaded */

View File

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

View File

@ -33,7 +33,7 @@ joy_sig: .byte $6A, $6F, $79, JOY_API_VERSION ; "joy", version
.code .code
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
; unsigned char __fastcall__ joy_install (void* driver); ; unsigned char __fastcall__ joy_install (const void* driver);
; /* Install the driver once it is loaded */ ; /* Install the driver once it is loaded */

View File

@ -39,7 +39,7 @@ ser_sig: .byte $73, $65, $72, SER_API_VERSION ; "ser", version
.code .code
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
; unsigned char __fastcall__ ser_install (void* driver); ; unsigned char __fastcall__ ser_install (const void* driver);
; /* Install the driver once it is loaded */ ; /* Install the driver once it is loaded */

View File

@ -67,13 +67,13 @@ not_dma:
; Removing this segment gives only a warning. ; Removing this segment gives only a warning.
.segment "FFF0" .segment "FFF0"
.proc reset32kcode .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 sta sv_bank
; Now, the 32Kbyte image can reside in the top of 64Kbyte and 128Kbyte ROMs. ; Now, the 32Kbyte image can reside in the top of 64Kbyte and 128Kbyte ROMs.
jmp reset jmp reset
.endproc .endproc
.segment "VECTOR" .segment "VECTORS"
.word nmi .word nmi
.word reset32kcode .word reset32kcode

View File

@ -88,7 +88,7 @@ tgi_sig: .byte $74, $67, $69, TGI_API_VERSION ; "tgi", version
.code .code
;---------------------------------------------------------------------------- ;----------------------------------------------------------------------------
; void __fastcall__ tgi_install (void* driver); ; void __fastcall__ tgi_install (const void* driver);
; /* Install an already loaded driver. */ ; /* Install an already loaded driver. */

View File

@ -154,7 +154,7 @@ endif
# Lists of subdirectories # Lists of subdirectories
# disasm depends on cpp # disasm depends on cpp
DIRLIST = tutorial geos DIRLIST = tutorial geos atari2600 supervision cbm
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Lists of executables # Lists of executables
@ -190,6 +190,9 @@ EXELIST_atarixl = $(EXELIST_atari)
EXELIST_atari2600 = \ EXELIST_atari2600 = \
atari2600hello atari2600hello
EXELIST_atari5200 = \
notavailable
EXELIST_atmos = \ EXELIST_atmos = \
ascii \ ascii \
hello \ hello \
@ -202,28 +205,22 @@ EXELIST_bbc = \
EXELIST_c64 = \ EXELIST_c64 = \
ascii \ ascii \
enumdevdir \ enumdevdir \
fire \
gunzip65 \ gunzip65 \
hello \ hello \
mandelbrot \ mandelbrot \
mousedemo \ mousedemo \
multdemo \ multdemo \
nachtm \
ovrldemo \ ovrldemo \
plasma \
sieve \ sieve \
tgidemo tgidemo
EXELIST_c128 = \ EXELIST_c128 = \
ascii \ ascii \
enumdevdir \ enumdevdir \
fire \
gunzip65 \ gunzip65 \
hello \ hello \
mandelbrot \ mandelbrot \
mousedemo \ mousedemo \
nachtm \
plasma \
sieve \ sieve \
tgidemo tgidemo
@ -234,19 +231,15 @@ EXELIST_c16 = \
EXELIST_cbm510 = \ EXELIST_cbm510 = \
ascii \ ascii \
fire \
gunzip65 \ gunzip65 \
hello \ hello \
mousedemo \ mousedemo \
nachtm \
plasma \
sieve sieve
EXELIST_cbm610 = \ EXELIST_cbm610 = \
ascii \ ascii \
gunzip65 \ gunzip65 \
hello \ hello \
nachtm \
sieve sieve
EXELIST_creativision = \ EXELIST_creativision = \
@ -299,7 +292,6 @@ EXELIST_plus4 = \
enumdevdir \ enumdevdir \
gunzip65 \ gunzip65 \
hello \ hello \
plasma \
sieve sieve
EXELIST_sim6502 = \ EXELIST_sim6502 = \
@ -308,7 +300,7 @@ EXELIST_sim6502 = \
EXELIST_sim65c02 = $(EXELIST_sim6502) EXELIST_sim65c02 = $(EXELIST_sim6502)
EXELIST_supervision = \ EXELIST_supervision = \
supervisionhello notavailable
EXELIST_sym1 = \ EXELIST_sym1 = \
notavailable notavailable
@ -347,7 +339,9 @@ samples: $(EXELIST_$(SYS))
# empty target used to skip systems that will not work with any program in this dir # empty target used to skip systems that will not work with any program in this dir
notavailable: notavailable:
@echo "warning: generic samples not available for" $(SYS) ifeq ($(MAKELEVEL),0)
@echo "info: generic samples not available for" $(SYS)
endif
disk: $(DISK_$(SYS)) disk: $(DISK_$(SYS))
@ -396,7 +390,7 @@ $(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(
endef # D64_WRITE_SEQ_recipe endef # D64_WRITE_SEQ_recipe
samples.d64: samples 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,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe))
$(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe)) $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe))
$(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe)) $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe))
@ -458,11 +452,17 @@ install:
$(INSTALL) -d $(DESTDIR)$(samplesdir) $(INSTALL) -d $(DESTDIR)$(samplesdir)
$(INSTALL) -d $(DESTDIR)$(samplesdir)/geos $(INSTALL) -d $(DESTDIR)$(samplesdir)/geos
$(INSTALL) -d $(DESTDIR)$(samplesdir)/tutorial $(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 *.* $(DESTDIR)$(samplesdir)
$(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir)
$(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir) $(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir)
$(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos $(INSTALL) -m0644 geos/*.* $(DESTDIR)$(samplesdir)/geos
$(INSTALL) -m0644 tutorial/*.* $(DESTDIR)$(samplesdir)/tutorial $(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 # Packaging rules

View File

@ -0,0 +1,59 @@
# Run 'make SYS=<target>'; or, set a SYS env.
# var. to build for another target system.
SYS ?= atari2600
# Just the usual way to find out if we're
# using cmd.exe to execute make rules.
ifneq ($(shell echo),)
CMD_EXE = 1
endif
ifdef CMD_EXE
NULLDEV = nul:
DEL = -del /f
RMDIR = rmdir /s /q
else
NULLDEV = /dev/null
DEL = $(RM)
RMDIR = $(RM) -r
endif
ifdef CC65_HOME
AS = $(CC65_HOME)/bin/ca65
CC = $(CC65_HOME)/bin/cc65
CL = $(CC65_HOME)/bin/cl65
LD = $(CC65_HOME)/bin/ld65
SP = $(CC65_HOME)/bin/sp65
else
AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65)
CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65)
CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65)
LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65)
endif
EXELIST_atari2600 = \
hello
ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS))
else
samples: notavailable
endif
# empty target used to skip systems that will not work with any program in this dir
notavailable:
ifeq ($(MAKELEVEL),0)
@echo "info: atari 2600 samples not available for" $(SYS)
else
# suppress the "nothing to be done for 'samples' message
@echo > $(NULLDEV)
endif
hello: hello.c
$(CL) -t $(SYS) -O -o hello -m hello.map hello.c
clean:
@$(DEL) $(EXELIST_atari2600) 2>$(NULLDEV)
@$(DEL) *.map 2>$(NULLDEV)

164
samples/cbm/Makefile Normal file
View File

@ -0,0 +1,164 @@
# Run 'make SYS=<target>'; or, set a SYS env.
# var. to build for another target system.
SYS ?= c64
# Just the usual way to find out if we're
# using cmd.exe to execute make rules.
ifneq ($(shell echo),)
CMD_EXE = 1
endif
ifdef CMD_EXE
NULLDEV = nul:
DEL = -del /f
RMDIR = rmdir /s /q
else
NULLDEV = /dev/null
DEL = $(RM)
RMDIR = $(RM) -r
endif
ifdef CC65_HOME
AS = $(CC65_HOME)/bin/ca65
CC = $(CC65_HOME)/bin/cc65
CL = $(CC65_HOME)/bin/cl65
LD = $(CC65_HOME)/bin/ld65
SP = $(CC65_HOME)/bin/sp65
else
AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65)
CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65)
CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65)
LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65)
endif
ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),)
ifdef CC65_HOME
TARGET_PATH = $(CC65_HOME)/target
else
TARGET_PATH := $(if $(wildcard ../target),../target,$(shell $(CL) --print-target-path))
endif
# If TARGET_PATH contains spaces then it is presumed to contain escaped spaces. GNU make
# has very limited support for paths containing spaces. $(wildcard) is the only function
# that is aware of escaped spaces. However, $(wildcard) never returns paths with escaped
# spaces !!! So if it e.g. finds 4 files in a path with 2 spaces then one ends up with a
# return value consisting of 12 plain words :-((
#
# Fortunately we can work around that behaviour here because we know that the files we
# are looking for have known extensions. So we can $(filter) the in our example above 12
# words for file extensions so we come up with 4 path fragments. Then we remove those
# path fragments with $(notdir) from the file names.
#
# So far so good. But here we want to process files from different paths in a single
# recipe further down below and therefore want to prepend the paths to the files with
# $(addprefix). However, $(foreach) isn't aware of escaped spaces (only $(wildcard) is).
# Therefore, we need to replace the spaces with some other character temporarily in order
# to have $(foreach) generate one invocation per file. We use the character '?' for that
# purpose here, just because it is known to not be part of file names.
#
# Inside the recipe generated per file we then replace the '?' again with a space. As we
# want to be compatible with cmd.exe for execution we're not using an escaped space but
# rather double-quote the whole path.
#
# Note: The "strange" $(wildcard) further down below just serves the purpose to unescape
# spaces for cmd.exe. This could have as well been done with another $(subst).
SUBST_TARGET_PATH := $(subst \$(SPACE),?,$(TARGET_PATH))
EMD := $(wildcard $(TARGET_PATH)/$(SYS)/drv/emd/*)
MOU := $(wildcard $(TARGET_PATH)/$(SYS)/drv/mou/*)
TGI := $(wildcard $(TARGET_PATH)/$(SYS)/drv/tgi/*)
EMD := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/emd/,$(notdir $(filter %.emd,$(EMD))))
MOU := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/mou/,$(notdir $(filter %.mou,$(MOU))))
TGI := $(addprefix $(SUBST_TARGET_PATH)/$(SYS)/drv/tgi/,$(notdir $(filter %.tgi,$(TGI))))
# This one comes with the VICE emulator.
# See http://vice-emu.sourceforge.net/
C1541 ?= c1541
endif
DISK_c64 = samples.d64
EXELIST_c64 = \
fire \
plasma \
nachtm
EXELIST_c128 = \
fire \
plasma \
nachtm
EXELIST_cbm510 = \
fire \
plasma \
nachtm
EXELIST_cbm610 = \
nachtm
EXELIST_plus4 = \
plasma
EXELIST_c16 = \
notavailable
EXELIST_pet = \
notavailable
EXELIST_vic20 = \
notavailable
ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS))
else
samples: notavailable
endif
disk: $(DISK_$(SYS))
# empty target used to skip systems that will not work with any program in this dir
notavailable:
ifeq ($(MAKELEVEL),0)
@echo "info: cbm samples not available for" $(SYS)
else
# suppress the "nothing to be done for 'samples' message
@echo > $(NULLDEV)
endif
fire: fire.c
$(CL) -t $(SYS) -O -o fire -m fire.map fire.c
plasma: plasma.c
$(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c
nachtm: nachtm.c
$(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c
# --------------------------------------------------------------------------
# Rule to make a CBM disk with all samples. Needs the c1541 program that comes
# with the VICE emulator.
define D64_WRITE_PRG_recipe
$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),p >$(NULLDEV)
endef # D64_WRITE_PRG_recipe
define D64_WRITE_SEQ_recipe
$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV)
endef # D64_WRITE_SEQ_recipe
samples.d64: samples
@$(C1541) -format "samples,00" d64 $@ >$(NULLDEV)
$(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe))
# $(foreach file,$(OVERLAYLIST),$(D64_WRITE_PRG_recipe))
# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe))
clean:
@$(DEL) $(EXELIST_$(SYS)) 2>$(NULLDEV)
@$(DEL) *.map 2>$(NULLDEV)
@$(DEL) $(DISK_$(SYS)) 2>$(NULLDEV)

View File

@ -972,12 +972,12 @@ static void MakeNiceScreen (void)
/* Clear the screen hide the cursor, set colors */ /* Clear the screen hide the cursor, set colors */
#ifdef __CBM610__ #ifdef __CBM610__
textcolor (COLOR_WHITE); (void)textcolor (COLOR_WHITE);
#else #else
textcolor (COLOR_GRAY3); (void)textcolor (COLOR_GRAY3);
#endif #endif
bordercolor (COLOR_BLACK); (void)bordercolor (COLOR_BLACK);
bgcolor (COLOR_BLACK); (void)bgcolor (COLOR_BLACK);
clrscr (); clrscr ();
cursor (0); cursor (0);

View File

@ -11,36 +11,52 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <device.h> #include <device.h>
#include <dirent.h> #include <dirent.h>
#include <cc65.h> #include <cc65.h>
void printdir (char *newdir) /* returns true for error, false for OK */
bool printdir (char *newdir)
{ {
char olddir[FILENAME_MAX]; char *olddir;
char curdir[FILENAME_MAX]; char *curdir;
DIR *dir; DIR *dir;
struct dirent *ent; struct dirent *ent;
char *subdirs = NULL; char *subdirs = NULL;
unsigned dirnum = 0; unsigned dirnum = 0;
unsigned num; 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 (newdir)) {
/* If chdir() fails we just print the /* If chdir() fails we just print the
** directory name - as done for files. ** directory name - as done for files.
*/ */
printf (" Dir %s\n", newdir); 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 /* We call getcwd() in order to print the
** absolute pathname for a subdirectory. ** absolute pathname for a subdirectory.
*/ */
getcwd (curdir, sizeof (curdir)); getcwd (curdir, FILENAME_MAX);
printf (" Dir %s:\n", curdir); printf (" Dir %s:\n", curdir);
free (curdir);
/* Calling opendir() always with "." avoids /* Calling opendir() always with "." avoids
** fiddling around with pathname separators. ** fiddling around with pathname separators.
@ -65,18 +81,27 @@ void printdir (char *newdir)
closedir (dir); closedir (dir);
for (num = 0; num < dirnum; ++num) { for (num = 0; num < dirnum; ++num) {
printdir (subdirs + FILENAME_MAX * num); if (printdir (subdirs + FILENAME_MAX * num))
break;
} }
free (subdirs); free (subdirs);
chdir (olddir); chdir (olddir);
free (olddir);
return false;
} }
void main (void) void main (void)
{ {
unsigned char device; 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 /* Calling getfirstdevice()/getnextdevice() does _not_ turn on the motor
** of a drive-type device and does _not_ check for a disk in the drive. ** 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 /* Calling getdevicedir() _does_ check for a (formatted) disk in a
** floppy-disk-type device and returns NULL if that check fails. ** 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); printdir (devicedir);
} else { } else {
printf (" N/A\n"); printf (" N/A\n");
@ -100,4 +125,6 @@ void main (void)
if (doesclrscrafterexit ()) { if (doesclrscrafterexit ()) {
getchar (); getchar ();
} }
free (devicedir);
} }

View File

@ -3,6 +3,19 @@
# var. to build for another target system. # var. to build for another target system.
SYS ?= geos-cbm 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 # Just the usual way to find out if we're
# using cmd.exe to execute make rules. # using cmd.exe to execute make rules.
ifneq ($(shell echo),) ifneq ($(shell echo),)
@ -69,10 +82,14 @@ samples: $(EXELIST_$(SYS))
$(foreach dir,$(DIRLIST),$(SUBDIR_recipe)) $(foreach dir,$(DIRLIST),$(SUBDIR_recipe))
else else
samples: 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 endif
bitmap.c: logo.pcx bitmap.c: logo.pcx
$(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap $(SP) -r logo.pcx -c geos-bitmap -w bitmap.c,ident=bitmap

View File

@ -8,8 +8,8 @@ struct window wholeScreen = {0, SC_PIX_HEIGHT-1, 0, SC_PIX_WIDTH-1};
void main (void) void main (void)
{ {
unsigned char os = get_ostype(); unsigned char os = get_ostype();
unsigned char *machine = NULL; char *machine = NULL;
unsigned char *version = NULL; char *version = NULL;
unsigned char good = 1; unsigned char good = 1;
SetPattern(0); SetPattern(0);

View File

@ -41,7 +41,12 @@ ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS)) samples: $(EXELIST_$(SYS))
else else
samples: 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 endif
test.s: test.grc test.s: test.grc

View File

@ -10,11 +10,13 @@ Please note:
similar systems. If you're using Windows, then consider installing Cygwin similar systems. If you're using Windows, then consider installing Cygwin
or MSys2. or MSys2.
* The makefile specifies the C64 as the default target system because all * The makefile specifies the C64 as the default target system because most
but three of the programs run on that platform. When compiling for another of the programs run on that platform. When compiling for another system,
system, you will have to change the line that specifies the target system you will have to change the line that specifies the target system at the
at the top of the makefile, specify the system with SYS=<target> on the top of the makefile, specify the system with SYS=<target> on the make
make command line, or set a SYS environment variable. 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. * 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>. <greg.king5@verizon.com>.
Platforms: All platforms with conio or stdio (compile time configurable). 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 Name: diodemo
Description: A disc copy program written and contributed by Oliver 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, (currently the Commodore machines, the Commander X16,
and the Apple ][). 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 Name: gunzip65
Description: A gunzip utility for 6502-based machines, written by Piotr 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 Name: mandelbrot
Description: A mandelbrot demo using integer arithmetic. The demo was Description: A mandelbrot demo using integer arithmetic. The demo was
written by groepaz/hitmen, and converted to cc65 using TGI written by groepaz, and converted to cc65 using TGI graphics
graphics by Stephan Haubenthal. by Stephan Haubenthal.
Platforms: Runs on all platforms that have TGI support: Platforms: Runs on all platforms that have TGI support:
Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS,
NES, and Lynx. 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, access, and EMD support (currently the C64, the C128,
the Atari, and the Apple ][). 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 Name: overlaydemo
Description: Shows how to load overlay files from disk. Written and 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, Platforms: All systems with an overlay linker config. (currently the C64,
the C128, the Atari, and the Apple ][). 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 Name: sieve
Description: Implements the "Sieve of Eratosthenes" as a way to find all 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 Commander X16, Apple ][ (without timing due to missing clock
support). support).
-----------------------------------------------------------------------------
Name: supervisionhello
Description: A "Hello world" type program.
Platforms: Runs on only the Watara Supervision game console.
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
Name: tgidemo Name: tgidemo
Description: Shows some of the graphics capabilities of the "Tiny Graphics 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: Platforms: Runs on all platforms that have TGI support:
Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS, Apple ][, Atari, C64, C128, Oric Atmos and Telestrat, GEOS,
NES, and Lynx. NES, and Lynx.
=============================================================================
Platform specific samples follow:
atari 2600:
-----------
Name: hello
Description: A "Hello world" type program.
Platforms: Runs on only the Atari 2600 Video Console System.
-----------------------------------------------------------------------------
cbm:
----
Name: fire
Description: Another graphics demo written by groepaz.
Platforms: C64, C128, CBM510
-----------------------------------------------------------------------------
Name: nachtm
Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart.
Platforms: All systems that have the Commodore SID (Sound Interface
Device):
C64, C128, CBM510, CBM610.
-----------------------------------------------------------------------------
Name: plasma
Description: A fancy graphics demo written by groepaz.
Platforms: The program needs a VIC-II or a TED, so it runs on the following
systems:
C64, C128, CBM510, Plus/4.
-----------------------------------------------------------------------------
supervision:
------------
Name: hello
Description: A "Hello world" type program.
Platforms: Runs on only the Watara Supervision game console.
----------------------------------------------------------------------------

View File

@ -0,0 +1,59 @@
# Run 'make SYS=<target>'; or, set a SYS env.
# var. to build for another target system.
SYS ?= supervision
# Just the usual way to find out if we're
# using cmd.exe to execute make rules.
ifneq ($(shell echo),)
CMD_EXE = 1
endif
ifdef CMD_EXE
NULLDEV = nul:
DEL = -del /f
RMDIR = rmdir /s /q
else
NULLDEV = /dev/null
DEL = $(RM)
RMDIR = $(RM) -r
endif
ifdef CC65_HOME
AS = $(CC65_HOME)/bin/ca65
CC = $(CC65_HOME)/bin/cc65
CL = $(CC65_HOME)/bin/cl65
LD = $(CC65_HOME)/bin/ld65
SP = $(CC65_HOME)/bin/sp65
else
AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65)
CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65)
CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65)
LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
SP := $(if $(wildcard ../../bin/sp65*),../../bin/sp65,sp65)
endif
EXELIST_supervision = \
hello
ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS))
else
samples: notavailable
endif
# empty target used to skip systems that will not work with any program in this dir
notavailable:
ifeq ($(MAKELEVEL),0)
@echo "info: supervision samples not available for" $(SYS)
else
# suppress the "nothing to be done for 'samples' message
@echo > $(NULLDEV)
endif
hello: hello.c
$(CL) -t $(SYS) -O -o hello -m hello.map hello.c
clean:
@$(DEL) $(EXELIST_supervision) 2>$(NULLDEV)
@$(DEL) *.map 2>$(NULLDEV)

View File

@ -0,0 +1,95 @@
/*****************************************************************************/
/* */
/* Watara Supervision sample C program */
/* */
/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */
/* Greg King (greg.king5@verizon.net), 2021 */
/* */
/*****************************************************************************/
#include <supervision.h>
#include <string.h>
/* Number of words per screen line (Remark: Last 4 words aren't displayed) */
#define WORDS_PER_LINE (160/8+4)
struct sv_vram {
unsigned int v[160/8][8][WORDS_PER_LINE];
};
#define SV_VRAM ((*(struct sv_vram *)0x4000).v)
/* Character definitions in 8x8 format */
/* That format gives us a screen of 20 columns and 20 rows */
static const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00};
static const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00};
static const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00};
static const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00};
static const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00};
static const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00};
static const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00};
static void clear_screen(void)
{
memset(SV_VIDEO, 0, 0x2000);
}
/* Necessary conversion to have 2 bits per pixel with darkest hue */
/* Remark: The Supervision uses 2 bits per pixel, and bits are mapped into pixels in reversed order */
static unsigned int __fastcall__ double_reversed_bits(unsigned char)
{
__asm__("stz ptr2");
__asm__("stz ptr2+1");
__asm__("ldy #$08");
L1: __asm__("lsr a");
__asm__("php");
__asm__("rol ptr2");
__asm__("rol ptr2+1");
__asm__("plp");
__asm__("rol ptr2");
__asm__("rol ptr2+1");
__asm__("dey");
__asm__("bne %g", L1);
__asm__("lda ptr2");
__asm__("ldx ptr2+1");
return __AX__;
}
static void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch)
{
unsigned char k;
for(k=0;k<8;++k)
{
SV_VRAM[y][k][x] = double_reversed_bits(ch[k]);
}
}
static void init_lcd(void)
{
SV_LCD.width = 160;
SV_LCD.height = 160;
}
static void hello(unsigned char x, unsigned char y)
{
display_char(x+ 0,y,h_char);
display_char(x+ 1,y,e_char);
display_char(x+ 2,y,l_char);
display_char(x+ 3,y,l_char);
display_char(x+ 4,y,o_char);
display_char(x+ 6,y,w_char);
display_char(x+ 7,y,o_char);
display_char(x+ 8,y,r_char);
display_char(x+ 9,y,l_char);
display_char(x+10,y,d_char);
}
void main(void)
{
init_lcd();
clear_screen();
hello(2,3);
hello(7,16);
}

View File

@ -1,91 +0,0 @@
/*****************************************************************************/
/* */
/* Watara Supervision sample C program */
/* */
/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */
/* */
/*****************************************************************************/
#include <supervision.h>
#include <peekpoke.h>
// Number of bytes per screen line (Remark: Last 8 bytes are not displayed)
#define BYTES_PER_LINE 48
// Character definitions in 8x8 format
const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00};
const unsigned char e_char[] = {0x7E,0x60,0x60,0x78,0x60,0x60,0x7E,0x00};
const unsigned char l_char[] = {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00};
const unsigned char o_char[] = {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00};
const unsigned char w_char[] = {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00};
const unsigned char r_char[] = {0x7C,0x66,0x66,0x7C,0x78,0x6C,0x66,0x00};
const unsigned char d_char[] = {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00};
void clear_screen(void)
{
unsigned short i;
for(i=0;i<0x2000;++i)
{
POKE(SV_VIDEO+i,0);
}
}
// Necessary conversion to have 2 bits per pixel with darkest hue
// Remark: The Supervision uses 2 bits per pixel and bits are mapped into pixel in reversed order
unsigned char reversed_map_one_to_two_lookup[16] =
{
0x00, 0xC0, 0x30, 0xF0, 0x0C, 0xCC, 0x3C, 0xFC,
0x03, 0xC3, 0x33, 0xF3, 0x0F, 0xCF, 0x3F, 0xFF
};
unsigned char left_map_one_to_two(unsigned char n)
{
return reversed_map_one_to_two_lookup[n >> 4];
}
unsigned char right_map_one_to_two(unsigned char n)
{
return reversed_map_one_to_two_lookup[n&0x0F];
}
void display_char(const unsigned char x, const unsigned char y, const unsigned char *ch)
{
unsigned char k;
for(k=0;k<8;++k)
{ \
SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)] = left_map_one_to_two(ch[k]);
SV_VIDEO[2*(y)+BYTES_PER_LINE*k+BYTES_PER_LINE*(x<<3)+1] = right_map_one_to_two(ch[k]);
}
}
void init_lcd(void)
{
SV_LCD.width = 160;
SV_LCD.height = 160;
}
int main()
{
init_lcd();
clear_screen();
display_char(3,2, h_char);
display_char(3,3, e_char);
display_char(3,4, l_char);
display_char(3,5, l_char);
display_char(3,6, o_char);
display_char(3,8, w_char);
display_char(3,9, o_char);
display_char(3,10,r_char);
display_char(3,11,l_char);
display_char(3,12,d_char);
while(1) {};
return 0;
}

View File

@ -81,6 +81,12 @@ hello: hello.c text.s
# empty target used to skip systems that will not work with any program in this dir # empty target used to skip systems that will not work with any program in this dir
notavailable: 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: clean:
@$(DEL) hello 2>$(NULLDEV) @$(DEL) hello 2>$(NULLDEV)

View File

@ -438,9 +438,7 @@ void MacDef (unsigned Style)
/* Parse the parameter list */ /* Parse the parameter list */
if (HaveParams) { if (HaveParams) {
while (CurTok.Tok == TOK_IDENT) { while (CurTok.Tok == TOK_IDENT) {
/* Create a struct holding the identifier */ /* Create a struct holding the identifier */
IdDesc* I = NewIdDesc (&CurTok.SVal); IdDesc* I = NewIdDesc (&CurTok.SVal);
@ -449,6 +447,7 @@ void MacDef (unsigned Style)
M->Params = I; M->Params = I;
} else { } else {
IdDesc* List = M->Params; IdDesc* List = M->Params;
while (1) { while (1) {
if (SB_Compare (&List->Id, &CurTok.SVal) == 0) { if (SB_Compare (&List->Id, &CurTok.SVal) == 0) {
Error ("Duplicate symbol '%m%p'", &CurTok.SVal); 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. ** the .LOCAL command is detected and removed, at this time.
*/ */
while (1) { while (1) {
/* Check for include */ /* Check for include */
if (CurTok.Tok == TOK_INCLUDE) { if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) {
/* Include another file */ /* Include another file */
NextTok (); NextTok ();
/* Name must follow */ /* Name must follow */
@ -529,9 +527,7 @@ void MacDef (unsigned Style)
/* Check for a .LOCAL declaration */ /* Check for a .LOCAL declaration */
if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) { if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) {
while (1) { while (1) {
IdDesc* I; IdDesc* I;
/* Skip .local or comma */ /* Skip .local or comma */
@ -570,6 +566,7 @@ void MacDef (unsigned Style)
if (CurTok.Tok == TOK_IDENT) { if (CurTok.Tok == TOK_IDENT) {
unsigned Count = 0; unsigned Count = 0;
IdDesc* I = M->Params; IdDesc* I = M->Params;
while (I) { while (I) {
if (SB_Compare (&I->Id, &CurTok.SVal) == 0) { if (SB_Compare (&I->Id, &CurTok.SVal) == 0) {
/* Local param name, replace it */ /* Local param name, replace it */

View File

@ -42,19 +42,35 @@
#include "expr.h" #include "expr.h"
#include "loadexpr.h" #include "loadexpr.h"
#include "scanner.h" #include "scanner.h"
#include "stackptr.h"
#include "stdnames.h" #include "stdnames.h"
#include "typecmp.h" #include "typecmp.h"
#include "typeconv.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 */ /* 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 */ /* 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 /* 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. ** to a boolean in C, but there is no harm to be future-proof.
*/ */
ED_MarkAsUntested (LExpr); ED_MarkAsUntested (LExpr);
return 1;
} }
void Assignment (ExprDesc* Expr) void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult)
/* Parse an assignment */ /* 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; const Type* ltype = Expr->Type;
@ -142,28 +584,32 @@ void Assignment (ExprDesc* Expr)
ED_Init (&Expr2); ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* We must have an lvalue for an assignment */ /* Only "=" accept struct/union */
if (ED_IsRVal (Expr)) { if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) {
if (IsTypeArray (Expr->Type)) { Error ("Invalid left operand for binary operator '%s'", Op);
Error ("Array type '%s' is not assignable", GetFullTypeName (Expr->Type)); /* Continue. Wrong code will be generated, but the compiler won't
} else if (IsTypeFunc (Expr->Type)) { ** break, so this is the best error recovery.
Error ("Function type '%s' is not assignable", GetFullTypeName (Expr->Type)); */
} else { } else {
Error ("Assignment to rvalue"); /* 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 */ /* Skip the '=' or 'op=' token */
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 */
NextToken (); NextToken ();
/* cc65 does not have full support for handling structs or unions. Since /* cc65 does not have full support for handling structs or unions. Since
@ -174,114 +620,131 @@ void Assignment (ExprDesc* Expr)
if (IsClassStruct (ltype)) { if (IsClassStruct (ltype)) {
/* Copy the struct or union by value */ /* Copy the struct or union by value */
CopyStruct (Expr, &Expr2); CopyStruct (Expr, &Expr2);
} else if (IsTypeBitField (ltype)) {
} else if (ED_IsBitField (Expr)) { /* Special care is needed for bit-field 'op=' */
OpAssignBitField (Gen, Expr, Op);
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 { } else {
/* Normal straight 'op=' */
/* Get the address on stack if needed */ OpAssignArithmetic (Gen, Expr, Op);
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);
} }
} }
void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
/* Parse a "+=" or "-=" operation */
{
ExprDesc Expr2;
unsigned lflags;
unsigned rflags;
int MustScale;
/* We currently only handle non-bit-fields in some addressing modes here */
if (IsTypeBitField (Expr->Type) || ED_IsLocPrimaryOrExpr (Expr)) {
/* Use generic routine instead */
OpAssign (Gen, Expr, Op);
return;
}
/* There must be an integer or pointer on the left side */
if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) {
Error ("Invalid left operand for binary operator '%s'", Op);
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
} else {
/* We must have an lvalue */
if (ED_IsRVal (Expr)) {
Error ("Invalid lvalue in assignment");
} else if (IsQualConst (Expr->Type)) {
/* The left side must not be const qualified */
Error ("Assignment to const");
}
}
/* Skip the operator */
NextToken ();
/* Check if we have a pointer expression and must scale rhs */
MustScale = IsTypePtr (Expr->Type);
/* Initialize the code generator flags */
lflags = 0;
rflags = 0;
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* Evaluate the rhs. We expect an integer here, since float is not
** supported
*/
hie1 (&Expr2);
if (!IsClassInt (Expr2.Type)) {
Error ("Invalid right operand for binary operator '%s'", Op);
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
}
/* Setup the code generator flags */
lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR;
rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR;
if (ED_IsConstAbs (&Expr2)) {
/* The resulting value is a constant */
rflags |= CF_CONST;
lflags |= CF_CONST;
/* Scale it */
if (MustScale) {
Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type));
}
} else {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
/* Convert the type of the rhs to that of the lhs */
g_typecast (lflags, rflags & ~CF_FORCECHAR);
if (MustScale) {
/* lhs is a pointer, scale rhs */
g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type)));
}
}
/* Output apropriate code depending on the location */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
case E_LOC_GLOBAL:
case E_LOC_STATIC:
case E_LOC_REGISTER:
case E_LOC_LITERAL:
case E_LOC_CODE:
/* Absolute numeric addressed variable, global variable, local
** static variable, register variable, pooled literal or code
** label location.
*/
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
} else {
g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
}
break;
case E_LOC_STACK:
/* Value on the stack */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqlocal (lflags, Expr->IVal, Expr2.IVal);
} else {
g_subeqlocal (lflags, Expr->IVal, Expr2.IVal);
}
break;
default:
Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr));
}
/* Expression is an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
}

View File

@ -43,14 +43,38 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Whether to save/restore the original lhs or result value */
enum {
OA_NEED_NONE,
OA_NEED_OLD,
OA_NEED_NEW,
};
/* Forward */
struct GenDesc;
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Code */
/*****************************************************************************/ /*****************************************************************************/
void Assignment (ExprDesc* lval); void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult);
/* Parse an assignment */ /* Process inc/dec for bit-field */
void OpAssign (const struct GenDesc* Gen, ExprDesc* lval, const char* Op);
/* Parse an "=" (if 'Gen' is 0) or "op=" operation */
void OpAddSubAssign (const struct GenDesc* Gen, ExprDesc *Expr, const char* Op);
/* Parse a "+=" or "-=" operation */

View File

@ -172,6 +172,10 @@ static void SetUseChgInfo (CodeEntry* E, const OPCDesc* D)
if (Info && Info->ByteUse != REG_NONE) { if (Info && Info->ByteUse != REG_NONE) {
/* These addressing modes will never change the zp loc */ /* These addressing modes will never change the zp loc */
E->Use |= Info->WordUse; E->Use |= Info->WordUse;
if ((E->Use & REG_SP) != 0) {
E->Use |= SLV_IND;
}
} }
break; break;
@ -1777,6 +1781,13 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
if (RegValIsKnown (In->RegX)) { if (RegValIsKnown (In->RegX)) {
Out->RegX = (In->RegX ^ 0xFF); 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) { } else if (strcmp (E->Arg, "tosandax") == 0) {
if (RegValIsKnown (In->RegA) && In->RegA == 0) { if (RegValIsKnown (In->RegA) && In->RegA == 0) {
Out->RegA = 0; Out->RegA = 0;

View File

@ -1166,7 +1166,9 @@ void g_putind (unsigned Flags, unsigned Offs)
/* Overflow - we need to add the low byte also */ /* Overflow - we need to add the low byte also */
AddCodeLine ("ldy #$00"); AddCodeLine ("ldy #$00");
AddCodeLine ("clc"); AddCodeLine ("clc");
AddCodeLine ("pha"); if ((Flags & CF_NOKEEP) == 0) {
AddCodeLine ("pha");
}
AddCodeLine ("lda #$%02X", Offs & 0xFF); AddCodeLine ("lda #$%02X", Offs & 0xFF);
AddCodeLine ("adc (sp),y"); AddCodeLine ("adc (sp),y");
AddCodeLine ("sta (sp),y"); AddCodeLine ("sta (sp),y");
@ -1174,7 +1176,9 @@ void g_putind (unsigned Flags, unsigned Offs)
AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF);
AddCodeLine ("adc (sp),y"); AddCodeLine ("adc (sp),y");
AddCodeLine ("sta (sp),y"); AddCodeLine ("sta (sp),y");
AddCodeLine ("pla"); if ((Flags & CF_NOKEEP) == 0) {
AddCodeLine ("pla");
}
/* Complete address is on stack, new offset is zero */ /* Complete address is on stack, new offset is zero */
Offs = 0; Offs = 0;
@ -1184,12 +1188,15 @@ void g_putind (unsigned Flags, unsigned Offs)
/* We can just add the high byte */ /* We can just add the high byte */
AddCodeLine ("ldy #$01"); AddCodeLine ("ldy #$01");
AddCodeLine ("clc"); AddCodeLine ("clc");
AddCodeLine ("pha"); if ((Flags & CF_NOKEEP) == 0) {
AddCodeLine ("pha");
}
AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF); AddCodeLine ("lda #$%02X", (Offs >> 8) & 0xFF);
AddCodeLine ("adc (sp),y"); AddCodeLine ("adc (sp),y");
AddCodeLine ("sta (sp),y"); AddCodeLine ("sta (sp),y");
AddCodeLine ("pla"); if ((Flags & CF_NOKEEP) == 0) {
AddCodeLine ("pla");
}
/* Offset is now just the low byte */ /* Offset is now just the low byte */
Offs &= 0x00FF; Offs &= 0x00FF;
} }
@ -1696,7 +1703,9 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs,
if (flags & CF_CONST) { if (flags & CF_CONST) {
if (val == 1) { if (val == 1) {
AddCodeLine ("inc %s", lbuf); AddCodeLine ("inc %s", lbuf);
AddCodeLine ("lda %s", lbuf); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf);
}
} else { } else {
AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
AddCodeLine ("clc"); AddCodeLine ("clc");
@ -1726,8 +1735,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs,
AddCodeLine ("bne %s", LocalLabelName (L)); AddCodeLine ("bne %s", LocalLabelName (L));
AddCodeLine ("inc %s+1", lbuf); AddCodeLine ("inc %s+1", lbuf);
g_defcodelabel (L); g_defcodelabel (L);
AddCodeLine ("lda %s", lbuf); /* Hmmm... */ if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("ldx %s+1", lbuf); AddCodeLine ("lda %s", lbuf); /* Hmmm... */
AddCodeLine ("ldx %s+1", lbuf);
}
} else { } else {
AddCodeLine ("lda #$%02X", (int)(val & 0xFF)); AddCodeLine ("lda #$%02X", (int)(val & 0xFF));
AddCodeLine ("clc"); AddCodeLine ("clc");
@ -1738,13 +1749,17 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs,
AddCodeLine ("bcc %s", LocalLabelName (L)); AddCodeLine ("bcc %s", LocalLabelName (L));
AddCodeLine ("inc %s+1", lbuf); AddCodeLine ("inc %s+1", lbuf);
g_defcodelabel (L); g_defcodelabel (L);
AddCodeLine ("ldx %s+1", lbuf); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("ldx %s+1", lbuf);
}
} else { } else {
AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("lda #$%02X", (unsigned char)(val >> 8));
AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("adc %s+1", lbuf);
AddCodeLine ("sta %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf);
AddCodeLine ("tax"); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf); AddCodeLine ("tax");
AddCodeLine ("lda %s", lbuf);
}
} }
} }
} else { } else {
@ -1754,8 +1769,10 @@ void g_addeqstatic (unsigned flags, uintptr_t label, long offs,
AddCodeLine ("txa"); AddCodeLine ("txa");
AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("adc %s+1", lbuf);
AddCodeLine ("sta %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf);
AddCodeLine ("tax"); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf); AddCodeLine ("tax");
AddCodeLine ("lda %s", lbuf);
}
} }
break; break;
@ -1837,9 +1854,11 @@ void g_addeqlocal (unsigned flags, int Offs, unsigned long val)
AddCodeLine ("lda #$%02X", (int) ((val >> 8) & 0xFF)); AddCodeLine ("lda #$%02X", (int) ((val >> 8) & 0xFF));
AddCodeLine ("adc (sp),y"); AddCodeLine ("adc (sp),y");
AddCodeLine ("sta (sp),y"); AddCodeLine ("sta (sp),y");
AddCodeLine ("tax"); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("dey"); AddCodeLine ("tax");
AddCodeLine ("lda (sp),y"); AddCodeLine ("dey");
AddCodeLine ("lda (sp),y");
}
} else { } else {
g_getimmed (flags, val, 0); g_getimmed (flags, val, 0);
AddCodeLine ("jsr addeqysp"); AddCodeLine ("jsr addeqysp");
@ -1919,7 +1938,9 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs,
if (flags & CF_CONST) { if (flags & CF_CONST) {
if (val == 1) { if (val == 1) {
AddCodeLine ("dec %s", lbuf); AddCodeLine ("dec %s", lbuf);
AddCodeLine ("lda %s", lbuf); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf);
}
} else { } else {
AddCodeLine ("lda %s", lbuf); AddCodeLine ("lda %s", lbuf);
AddCodeLine ("sec"); AddCodeLine ("sec");
@ -1953,13 +1974,17 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs,
AddCodeLine ("bcs %s", LocalLabelName (L)); AddCodeLine ("bcs %s", LocalLabelName (L));
AddCodeLine ("dec %s+1", lbuf); AddCodeLine ("dec %s+1", lbuf);
g_defcodelabel (L); g_defcodelabel (L);
AddCodeLine ("ldx %s+1", lbuf); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("ldx %s+1", lbuf);
}
} else { } else {
AddCodeLine ("lda %s+1", lbuf); AddCodeLine ("lda %s+1", lbuf);
AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8)); AddCodeLine ("sbc #$%02X", (unsigned char)(val >> 8));
AddCodeLine ("sta %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf);
AddCodeLine ("tax"); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf); AddCodeLine ("tax");
AddCodeLine ("lda %s", lbuf);
}
} }
} else { } else {
AddCodeLine ("eor #$FF"); AddCodeLine ("eor #$FF");
@ -1970,8 +1995,10 @@ void g_subeqstatic (unsigned flags, uintptr_t label, long offs,
AddCodeLine ("eor #$FF"); AddCodeLine ("eor #$FF");
AddCodeLine ("adc %s+1", lbuf); AddCodeLine ("adc %s+1", lbuf);
AddCodeLine ("sta %s+1", lbuf); AddCodeLine ("sta %s+1", lbuf);
AddCodeLine ("tax"); if ((flags & CF_NOKEEP) == 0) {
AddCodeLine ("lda %s", lbuf); AddCodeLine ("tax");
AddCodeLine ("lda %s", lbuf);
}
} }
break; break;

View File

@ -82,269 +82,296 @@ struct FuncInfo {
unsigned Chg; /* Changed/destroyed registers */ 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 ** routines are marked to use only the A register. The remainder is ignored
** anyway. ** anyway.
*/ */
static const FuncInfo FuncInfoTable[] = { static const FuncInfo FuncInfoTable[] = {
{ "addeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, { "addeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY },
{ "addeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, { "addeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY },
{ "addysp", REG_Y, PSTATE_ALL | REG_NONE }, { "addysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP },
{ "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "along", REG_A, PSTATE_ALL | REG_X | REG_SREG },
{ "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "asleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 },
{ "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "asreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "bcasta", REG_A, PSTATE_ALL | REG_AX }, { "asreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 },
{ "bcastax", REG_AX, PSTATE_ALL | REG_AX }, { "aulong", REG_NONE, PSTATE_ALL | REG_X | REG_SREG },
{ "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "axlong", REG_X, PSTATE_ALL | REG_Y | REG_SREG },
{ "bnega", REG_A, PSTATE_ALL | REG_AX }, { "axulong", REG_NONE, PSTATE_ALL | REG_Y | REG_SREG },
{ "bnegax", REG_AX, PSTATE_ALL | REG_AX }, { "bcasta", REG_A, PSTATE_ALL | REG_AX },
{ "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "bcastax", REG_AX, PSTATE_ALL | REG_AX },
{ "booleq", PSTATE_Z, PSTATE_ALL | REG_AX }, { "bcasteax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "boolge", PSTATE_N, PSTATE_ALL | REG_AX }, { "bnega", REG_A, PSTATE_ALL | REG_AX },
{ "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX }, { "bnegax", REG_AX, PSTATE_ALL | REG_AX },
{ "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX }, { "bnegeax", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "boollt", PSTATE_N, PSTATE_ALL | REG_AX }, { "booleq", PSTATE_Z, PSTATE_ALL | REG_AX },
{ "boolne", PSTATE_Z, PSTATE_ALL | REG_AX }, { "boolge", PSTATE_N, PSTATE_ALL | REG_AX },
{ "booluge", PSTATE_C, PSTATE_ALL | REG_AX }, { "boolgt", PSTATE_ZN, PSTATE_ALL | REG_AX },
{ "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX }, { "boolle", PSTATE_ZN, PSTATE_ALL | REG_AX },
{ "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX }, { "boollt", PSTATE_N, PSTATE_ALL | REG_AX },
{ "boolult", PSTATE_C, PSTATE_ALL | REG_AX }, { "boolne", PSTATE_Z, PSTATE_ALL | REG_AX },
{ "callax", REG_AX, PSTATE_ALL | REG_ALL }, { "booluge", PSTATE_C, PSTATE_ALL | REG_AX },
{ "complax", REG_AX, PSTATE_ALL | REG_AX }, { "boolugt", PSTATE_CZ, PSTATE_ALL | REG_AX },
{ "decax1", REG_AX, PSTATE_ALL | REG_AX }, { "boolule", PSTATE_CZ, PSTATE_ALL | REG_AX },
{ "decax2", REG_AX, PSTATE_ALL | REG_AX }, { "boolult", PSTATE_C, PSTATE_ALL | REG_AX },
{ "decax3", REG_AX, PSTATE_ALL | REG_AX }, { "callax", REG_AX, PSTATE_ALL | REG_ALL }, /* PSTATE_ZN | REG_PTR1 */
{ "decax4", REG_AX, PSTATE_ALL | REG_AX }, { "complax", REG_AX, PSTATE_ALL | REG_AX },
{ "decax5", REG_AX, PSTATE_ALL | REG_AX }, { "decax1", REG_AX, PSTATE_ALL | REG_AX },
{ "decax6", REG_AX, PSTATE_ALL | REG_AX }, { "decax2", REG_AX, PSTATE_ALL | REG_AX },
{ "decax7", REG_AX, PSTATE_ALL | REG_AX }, { "decax3", REG_AX, PSTATE_ALL | REG_AX },
{ "decax8", REG_AX, PSTATE_ALL | REG_AX }, { "decax4", REG_AX, PSTATE_ALL | REG_AX },
{ "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 }, { "decax5", REG_AX, PSTATE_ALL | REG_AX },
{ "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX }, { "decax6", REG_AX, PSTATE_ALL | REG_AX },
{ "decsp1", REG_NONE, PSTATE_ALL | REG_Y }, { "decax7", REG_AX, PSTATE_ALL | REG_AX },
{ "decsp2", REG_NONE, PSTATE_ALL | REG_A }, { "decax8", REG_AX, PSTATE_ALL | REG_AX },
{ "decsp3", REG_NONE, PSTATE_ALL | REG_A }, { "decaxy", REG_AXY, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "decsp4", REG_NONE, PSTATE_ALL | REG_A }, { "deceaxy", REG_EAXY, PSTATE_ALL | REG_EAX },
{ "decsp5", REG_NONE, PSTATE_ALL | REG_A }, { "decsp1", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "decsp6", REG_NONE, PSTATE_ALL | REG_A }, { "decsp2", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "decsp7", REG_NONE, PSTATE_ALL | REG_A }, { "decsp3", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "decsp8", REG_NONE, PSTATE_ALL | REG_A }, { "decsp4", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "incax1", REG_AX, PSTATE_ALL | REG_AX }, { "decsp5", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "incax2", REG_AX, PSTATE_ALL | REG_AX }, { "decsp6", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "decsp7", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "decsp8", REG_SP, PSTATE_ALL | REG_SP | REG_A },
{ "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "enter", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY },
{ "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "incax1", REG_AX, PSTATE_ALL | REG_AX },
{ "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "incax2", REG_AX, PSTATE_ALL | REG_AX },
{ "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "incax3", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "incax4", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp1", REG_NONE, PSTATE_ALL | REG_NONE }, { "incax5", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp2", REG_NONE, PSTATE_ALL | REG_Y }, { "incax6", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp3", REG_NONE, PSTATE_ALL | REG_Y }, { "incax7", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp4", REG_NONE, PSTATE_ALL | REG_Y }, { "incax8", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp5", REG_NONE, PSTATE_ALL | REG_Y }, { "incaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "incsp6", REG_NONE, PSTATE_ALL | REG_Y }, { "incsp1", REG_SP, PSTATE_ALL | REG_SP },
{ "incsp7", REG_NONE, PSTATE_ALL | REG_Y }, { "incsp2", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "incsp8", REG_NONE, PSTATE_ALL | REG_Y }, { "incsp3", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "laddeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "incsp4", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "laddeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, { "incsp5", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "incsp6", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "incsp7", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "laddeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, { "incsp8", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, { "jmpvec", REG_EVERYTHING, PSTATE_ALL | REG_ALL }, /* NONE */
{ "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 }, { "laddeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "ldax0sp", REG_NONE, PSTATE_ALL | REG_AXY }, { "laddeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY },
{ "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "laddeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "laddeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "ldaxysp", REG_Y, PSTATE_ALL | REG_AXY }, { "laddeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY },
{ "ldeax0sp", REG_NONE, PSTATE_ALL | REG_EAXY }, { "ldaidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, { "ldauidx", REG_AXY, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 }, { "ldax0sp", SLV_TOP, PSTATE_ALL | REG_AXY },
{ "ldeaxysp", REG_Y, PSTATE_ALL | REG_EAXY }, { "ldaxi", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 },
{ "leaa0sp", REG_A, PSTATE_ALL | REG_AX }, { "ldaxidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 },
{ "leaaxsp", REG_AX, PSTATE_ALL | REG_AX }, { "ldaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_AXY },
{ "lsubeq", REG_EAXY|REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "ldeax0sp", SLV_TOP, PSTATE_ALL | REG_EAXY },
{ "lsubeq0sp", REG_EAX, PSTATE_ALL | REG_EAXY }, { "ldeaxi", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 },
{ "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "ldeaxidx", REG_AXY, PSTATE_ALL | REG_EAXY | REG_PTR1 },
{ "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI }, { "ldeaxysp", SLV_IND | REG_Y, PSTATE_ALL | REG_EAXY },
{ "lsubeqysp", REG_EAXY, PSTATE_ALL | REG_EAXY }, { "leaa0sp", REG_SP | REG_A, PSTATE_ALL | REG_AX },
{ "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leaaxsp", REG_SP | REG_AX, PSTATE_ALL | REG_AX },
{ "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leave00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leave0", REG_SP, PSTATE_ALL | REG_SP | REG_XY },
{ "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leave", REG_SP, PSTATE_ALL | REG_SP | REG_Y },
{ "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leavey00", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 }, { "leavey0", REG_SP, PSTATE_ALL | REG_SP | REG_XY },
{ "negax", REG_AX, PSTATE_ALL | REG_AX }, { "leavey", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_Y },
{ "push0", REG_NONE, PSTATE_ALL | REG_AXY }, { "lsubeq", REG_EAXY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "push0ax", REG_AX, PSTATE_ALL | REG_Y | REG_SREG }, { "lsubeq0sp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_EAXY },
{ "push1", REG_NONE, PSTATE_ALL | REG_AXY }, { "lsubeq1", REG_Y | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "push2", REG_NONE, PSTATE_ALL | REG_AXY }, { "lsubeqa", REG_AY | REG_PTR1_LO, PSTATE_ALL | REG_EAXY | REG_PTR1_HI },
{ "push3", REG_NONE, PSTATE_ALL | REG_AXY }, { "lsubeqysp", SLV_IND | REG_EAXY, PSTATE_ALL | REG_EAXY },
{ "push4", REG_NONE, PSTATE_ALL | REG_AXY }, { "mulax10", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "push5", REG_NONE, PSTATE_ALL | REG_AXY }, { "mulax3", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "push6", REG_NONE, PSTATE_ALL | REG_AXY }, { "mulax5", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "push7", REG_NONE, PSTATE_ALL | REG_AXY }, { "mulax6", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "pusha", REG_A, PSTATE_ALL | REG_Y }, { "mulax7", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "pusha0", REG_A, PSTATE_ALL | REG_XY }, { "mulax9", REG_AX, PSTATE_ALL | REG_AX | REG_PTR1 },
{ "pusha0sp", REG_NONE, PSTATE_ALL | REG_AY }, { "negax", REG_AX, PSTATE_ALL | REG_AX },
{ "pushaFF", REG_A, PSTATE_ALL | REG_Y }, { "negeax", REG_EAX, PSTATE_ALL | REG_EAX },
{ "pushax", REG_AX, PSTATE_ALL | REG_Y }, { "popa", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY },
{ "pushaysp", REG_Y, PSTATE_ALL | REG_AY }, { "popax", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushc0", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, { "popeax", SLV_TOP, PSTATE_ALL | REG_SP | REG_EAXY },
{ "pushc1", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, { "push0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushc2", REG_NONE, PSTATE_ALL | REG_A | REG_Y }, { "push0ax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y | REG_SREG },
{ "pusheax", REG_EAX, PSTATE_ALL | REG_Y }, { "push1", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushl0", REG_NONE, PSTATE_ALL | REG_AXY }, { "push2", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushw", REG_AX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "push3", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushw0sp", REG_NONE, PSTATE_ALL | REG_AXY }, { "push4", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushwidx", REG_AXY, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "push5", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "pushwysp", REG_Y, PSTATE_ALL | REG_AXY }, { "push6", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "push7", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "regswap1", REG_XY, PSTATE_ALL | REG_A }, { "pusha", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y },
{ "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y }, { "pusha0", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_XY },
{ "return0", REG_NONE, PSTATE_ALL | REG_AX }, { "pusha0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AY },
{ "return1", REG_NONE, PSTATE_ALL | REG_AX }, { "pushaFF", REG_SP | REG_A, PSTATE_ALL | REG_SP | REG_Y },
{ "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushax", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_Y },
{ "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushaysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AY },
{ "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushc0", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y },
{ "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushc1", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y },
{ "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "pushc2", REG_SP, PSTATE_ALL | REG_SP | REG_A | REG_Y },
{ "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "pusheax", REG_SP | REG_EAX, PSTATE_ALL | REG_SP | REG_Y },
{ "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "pushl0", REG_SP, PSTATE_ALL | REG_SP | REG_AXY },
{ "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "pushw", REG_SP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "pushw0sp", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY },
{ "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushwidx", REG_SP | REG_AXY, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "pushwysp", SLV_IND | REG_Y, PSTATE_ALL | REG_SP | REG_AXY },
{ "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "regswap", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "regswap1", REG_XY, PSTATE_ALL | REG_A },
{ "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "regswap2", REG_XY, PSTATE_ALL | REG_A | REG_Y },
{ "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "resteax", REG_SAVE, PSTATE_ZN | REG_EAX }, /* also uses regsave+2/+3 */
{ "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "return0", REG_NONE, PSTATE_ALL | REG_AX },
{ "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "return1", REG_NONE, PSTATE_ALL | REG_AX },
{ "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "saveeax", REG_EAX, PSTATE_ZN | REG_Y | REG_SAVE }, /* also regsave+2/+3 */
{ "staspidx", REG_A | REG_Y, PSTATE_ALL | REG_Y | REG_TMP1 | REG_PTR1 }, { "shlax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "stax0sp", REG_AX, PSTATE_ALL | REG_Y }, { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "staxspidx", REG_AXY, PSTATE_ALL | REG_TMP1 | REG_PTR1 }, { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "staxysp", REG_AXY, PSTATE_ALL | REG_Y }, { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "steax0sp", REG_EAX, PSTATE_ALL | REG_Y }, { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "steaxysp", REG_EAXY, PSTATE_ALL | REG_Y }, { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "subeq0sp", REG_AX, PSTATE_ALL | REG_AXY }, { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "subeqysp", REG_AXY, PSTATE_ALL | REG_AXY }, { "shleax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "subysp", REG_Y, PSTATE_ALL | REG_AY }, { "shleax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 },
{ "tosadd0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "shrax1", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "tosadda0", REG_A, PSTATE_ALL | REG_AXY }, { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "tosaddax", REG_AX, PSTATE_ALL | REG_AXY }, { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "tosaddeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "tosand0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "tosanda0", REG_A, PSTATE_ALL | REG_AXY }, { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "tosandax", REG_AX, PSTATE_ALL | REG_AXY }, { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "tosandeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "shreax3", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "tosaslax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shreax4", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 },
{ "tosasleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "staspidx", SLV_TOP | REG_AY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 },
{ "tosasrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "stax0sp", REG_SP | REG_AX, PSTATE_ALL | SLV_TOP | REG_Y },
{ "tosasreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "staxspidx", SLV_TOP | REG_AXY, PSTATE_ALL | REG_SP | REG_TMP1 | REG_PTR1 },
{ "tosdiv0ax", REG_AX, PSTATE_ALL | REG_ALL }, { "staxysp", REG_SP | REG_AXY, PSTATE_ALL | SLV_IND | REG_Y },
{ "tosdiva0", REG_A, PSTATE_ALL | REG_ALL }, { "steax0sp", REG_SP | REG_EAX, PSTATE_ALL | SLV_TOP | REG_Y },
{ "tosdivax", REG_AX, PSTATE_ALL | REG_ALL }, { "steaxspidx", SLV_TOP | REG_EAXY, PSTATE_ALL | REG_SP | REG_Y | REG_TMP1 | REG_PTR1 }, /* also tmp2, tmp3 */
{ "tosdiveax", REG_EAX, PSTATE_ALL | REG_ALL }, { "steaxysp", REG_SP | REG_EAXY, PSTATE_ALL | SLV_IND | REG_Y },
{ "toseq00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "subeq0sp", SLV_TOP | REG_AX, PSTATE_ALL | REG_AXY },
{ "toseqa0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "subeqysp", SLV_IND | REG_AXY, PSTATE_ALL | REG_AXY },
{ "toseqax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "subysp", REG_SP | REG_Y, PSTATE_ALL | REG_SP | REG_AY },
{ "toseqeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "swapstk", SLV_TOP | REG_AX, PSTATE_ALL | SLV_TOP | REG_AXY }, /* also ptr4 */
{ "tosge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosadd0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosgea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosadda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosgeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosaddax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosgeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosaddeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosgt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosand0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosgta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosanda0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosgtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosandax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosgteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosandeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosicmp", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosaslax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosicmp0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosasleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "toslcmp", REG_EAX, PSTATE_ALL | REG_A | REG_Y | REG_PTR1 }, { "tosasrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosle00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosasreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "toslea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosdiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL },
{ "tosleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosdiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_ALL },
{ "tosleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosdivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_ALL },
{ "toslt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosdiveax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_ALL },
{ "toslta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "toseq00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosltax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "toseqa0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "toslteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "toseqax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosmod0ax", REG_AX, PSTATE_ALL | REG_ALL }, { "toseqeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosmodeax", REG_EAX, PSTATE_ALL | REG_ALL }, { "tosge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosmul0ax", REG_AX, PSTATE_ALL | REG_ALL }, { "tosgea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosmula0", REG_A, PSTATE_ALL | REG_ALL }, { "tosgeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosmulax", REG_AX, PSTATE_ALL | REG_ALL }, { "tosgeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosmuleax", REG_EAX, PSTATE_ALL | REG_ALL }, { "tosgt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosne00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosgta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosnea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosgtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosneax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosgteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosneeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosicmp", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "tosicmp0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosint", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y },
{ "tosorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "toslcmp", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_A | REG_Y | REG_PTR1 },
{ "tosoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "tosle00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosrsub0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "toslea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosrsuba0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosleax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosrsubax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosleeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosrsubeax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "toslong", SLV_TOP, PSTATE_ALL | REG_SP | REG_Y },
{ "tosshlax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "toslt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosshleax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "toslta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosshrax", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosltax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosshreax", REG_A, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "toslteax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tossub0ax", REG_AX, PSTATE_ALL | REG_EAXY }, { "tosmod0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL },
{ "tossuba0", REG_A, PSTATE_ALL | REG_AXY }, { "tosmodeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL },
{ "tossubax", REG_AX, PSTATE_ALL | REG_AXY }, { "tosmul0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL },
{ "tossubeax", REG_EAX, PSTATE_ALL | REG_EAXY }, { "tosmula0", SLV_TOP | REG_A, PSTATE_ALL | REG_ALL },
{ "tosudiv0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, { "tosmulax", SLV_TOP | REG_AX, PSTATE_ALL | REG_ALL },
{ "tosudiva0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ { "tosmuleax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_ALL },
{ "tosudivax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ { "tosne00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosudiveax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, { "tosnea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosuge00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosneax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosugea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosneeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosugeax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosor0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosugeeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosora0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosugt00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosorax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosugta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosoreax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosugtax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosrsub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosugteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosrsuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosule00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosrsubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosulea0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosrsubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosuleax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosshlax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosuleeax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tosshleax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosult00", REG_NONE, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosshrax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_TMP1 },
{ "tosulta0", REG_A, PSTATE_ALL | REG_AXY | REG_SREG }, { "tosshreax", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_TMP1 },
{ "tosultax", REG_AX, PSTATE_ALL | REG_AXY | REG_SREG }, { "tossub0ax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY },
{ "tosulteax", REG_EAX, PSTATE_ALL | REG_AXY | REG_PTR1 }, { "tossuba0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosumod0ax", REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, { "tossubax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY },
{ "tosumoda0", REG_A, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ { "tossubeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_EAXY },
{ "tosumodax", REG_AX, PSTATE_ALL | REG_EAXY | REG_PTR1 }, /* also ptr4 */ { "tosudiv0ax", SLV_TOP | REG_AX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) },
{ "tosumodeax", REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) }, { "tosudiva0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */
{ "tosumul0ax", REG_AX, PSTATE_ALL | REG_ALL }, { "tosudivax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_EAXY | REG_PTR1 }, /* also ptr4 */
{ "tosumula0", REG_A, PSTATE_ALL | REG_ALL }, { "tosudiveax", SLV_TOP | REG_EAX, PSTATE_ALL | (REG_ALL & ~REG_SAVE) },
{ "tosumulax", REG_AX, PSTATE_ALL | REG_ALL }, { "tosuge00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosumuleax", REG_EAX, PSTATE_ALL | REG_ALL }, { "tosugea0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosxor0ax", REG_AX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "tosugeax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosxora0", REG_A, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosugeeax", SLV_TOP | REG_EAX, PSTATE_ALL | REG_SP | REG_AXY | REG_PTR1 },
{ "tosxorax", REG_AX, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "tosugt00", SLV_TOP, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tosxoreax", REG_EAX, PSTATE_ALL | REG_EAXY | REG_TMP1 }, { "tosugta0", SLV_TOP | REG_A, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "tsteax", REG_EAX, PSTATE_ALL | REG_Y }, { "tosugtax", SLV_TOP | REG_AX, PSTATE_ALL | REG_SP | REG_AXY | REG_SREG },
{ "utsteax", REG_EAX, PSTATE_ALL | REG_Y }, { "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])) #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? */ /* Did we find it in the top-level table? */
if (E && IsTypeFunc (E->Type)) { if (E && IsTypeFunc (E->Type)) {
FuncDesc* D = GetFuncDesc (E->Type); FuncDesc* D = GetFuncDesc (E->Type);
*Use = REG_NONE;
/* A variadic function will use the Y register (the parameter list /* 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 ** 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. ** we assume that any function will destroy all registers.
*/ */
if ((D->Flags & FD_VARIADIC) != 0) { if ((D->Flags & FD_VARIADIC) != 0) {
*Use = REG_Y; *Use = REG_Y | REG_SP | SLV_TOP;
} else if (D->Flags & FD_CALL_WRAPPER) { } else if (D->Flags & FD_CALL_WRAPPER) {
/* Wrappers may go to any functions, so mark them as using all /* Wrappers may go to any functions, so mark them as using all
** registers. ** registers.
*/ */
*Use = REG_EAXY; *Use = REG_EAXY;
} else if ((D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) && } else if (D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) {
IsFastcallFunc (E->Type)) {
/* Will use registers depending on the last param. If the last /* Will use registers depending on the last param. If the last
** param has incomplete type, or if the function has not been ** param has incomplete type, or if the function has not been
** prototyped yet, just assume __EAX__. ** prototyped yet, just assume __EAX__.
*/ */
if (D->LastParam != 0) { if (IsFastcallFunc (E->Type)) {
switch (SizeOf (D->LastParam->Type)) { if (D->LastParam != 0) {
case 1u: switch (SizeOf (D->LastParam->Type)) {
*Use = REG_A; case 1u:
break; *Use = REG_A;
case 2u: break;
*Use = REG_AX; case 2u:
break; *Use = REG_AX;
default: break;
*Use = REG_EAX; 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 { } else {
*Use = REG_EAX; /* Passes all params on the stack */
*Use = REG_SP | SLV_TOP;
} }
} else { } else {
/* Will not use any registers */ /* 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 the information we have */
*Use = Info->Use; *Use = Info->Use;
*Chg = Info->Chg; *Chg = Info->Chg;
if ((*Use & (SLV_TOP | SLV_IND)) != 0) {
*Use |= REG_SP;
}
} else { } else {
/* It's an internal function we have no information for. If in /* It's an internal function we have no information for. If in
** debug mode, output an additional warning, so we have a chance ** debug mode, output an additional warning, so we have a chance

View File

@ -75,6 +75,8 @@ struct RegContents;
#define REG_SP_HI 0x2000U #define REG_SP_HI 0x2000U
/* Defines for some special register usage */ /* 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_SP65 0x00200000U /* Accesses 6502 stack pointer */
#define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */ #define SLV_PH65 0x00400000U /* Pushes onto 6502 stack */
#define SLV_PL65 0x00800000U /* Pops from 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_EAXY (REG_EAX | REG_Y)
#define REG_ZP 0xFFF8U #define REG_ZP 0xFFF8U
#define REG_ALL 0xFFFFU #define REG_ALL 0xFFFFU
#define PSTATE_CZ (PSTATE_C | PSTATE_Z) #define PSTATE_CZ (PSTATE_C | PSTATE_Z)
#define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N) #define PSTATE_CZN (PSTATE_C | PSTATE_Z | PSTATE_N)
#define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N) #define PSTATE_CZVN (PSTATE_C | PSTATE_Z | PSTATE_V | PSTATE_N)

View File

@ -827,21 +827,28 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs)
CodeEntry* E = CS_GetEntry (D->Code, I); 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 /* Check if this entry does a stack access, and if so, if it's a plain
** load from stack, since this is needed later. ** load from stack, since this is needed later.
*/ */
int Correction = 0; int Correction = 0;
if ((E->Use & REG_SP) != 0) { if ((E->Use & SLV_IND) == SLV_IND) {
/* Check for some things that should not happen */ if (E->OPC != OP65_JSR) {
CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs); /* Check against some things that should not happen */
CHECK (strcmp (E->Arg, "sp") == 0); CHECK (E->AM == AM65_ZP_INDY && E->RI->In.RegY >= (short) Offs);
/* We need to correct this one */ CHECK (strcmp (E->Arg, "sp") == 0);
Correction = (E->OPC == OP65_LDA)? 2 : 1;
/* 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) { if (Correction) {
@ -849,7 +856,7 @@ void AdjustStackOffset (StackOpData* D, unsigned Offs)
** value. ** value.
*/ */
CodeEntry* P = CS_GetPrevEntry (D->Code, I); 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 */ /* The Y load is just before the stack access, adjust it */
CE_SetNumArg (P, P->Num - Offs); CE_SetNumArg (P, P->Num - Offs);
} else { } 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 we need the value of Y later, be sure to reload it */
if (RegYUsed (D->Code, I+1)) { unsigned R = REG_Y | (E->Chg & ~REG_A);
CodeEntry* N; R = GetRegInfo (D->Code, I + 1, R) & R;
if ((R & REG_Y) != 0) {
const char* Arg = MakeHexArg (E->RI->In.RegY); const char* Arg = MakeHexArg (E->RI->In.RegY);
if (Correction == 2 && (N = CS_GetNextEntry(D->Code, I)) != 0 && if ((R & PSTATE_ZN) != 0 && (R & ~(REG_Y | PSTATE_ZN)) == 0) {
((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) { CodeEntry* N;
/* The Y register is used but the load instruction loads A if ((N = CS_GetNextEntry (D->Code, I)) != 0 &&
** and is followed by a branch that evaluates the zero flag. ((N->Info & OF_ZBRA) != 0) && N->JumpTo != 0) {
** This means that we cannot just insert the load insn /* The Y register is used but the load instruction loads A
** for the Y register at this place, because it would ** and is followed by a branch that evaluates the zero flag.
** destroy the Z flag. Instead place load insns at the ** This means that we cannot just insert the load insn
** target of the branch and after it. ** for the Y register at this place, because it would
** Note: There is a chance that this code won't work. The ** destroy the Z flag. Instead place load insns at the
** jump may be a backwards jump (in which case the stack ** target of the branch and after it.
** offset has already been adjusted) or there may be other ** Note: There is a chance that this code won't work. The
** instructions between the load and the conditional jump. ** jump may be a backwards jump (in which case the stack
** Currently the compiler does not generate such code, but ** offset has already been adjusted) or there may be other
** it is possible to force the optimizer into something ** instructions between the load and the conditional jump.
** invalid by use of inline assembler. ** 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 */ /* Add load insn after the branch */
CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
InsertEntry (D, X, I+2); InsertEntry (D, X, I+2);
/* Add load insn before branch target */ /* Add load insn before branch target */
CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); CodeEntry* Y = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner); int J = CS_GetEntryIndex (D->Code, N->JumpTo->Owner);
CHECK (J > I); /* Must not happen */ CHECK (J > I); /* Must not happen */
InsertEntry (D, Y, J); InsertEntry (D, Y, J);
/* Move the label to the new insn */ /* Move the label to the new insn */
CodeLabel* L = CS_GenLabel (D->Code, Y); CodeLabel* L = CS_GenLabel (D->Code, Y);
CS_MoveLabelRef (D->Code, N, L); 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 { } else {
CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); CodeEntry* X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI);
InsertEntry (D, X, I+1); 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 /* Check if this is a call to a harmless subroutine that will not interrupt
** the pushax/op sequence when encountered. ** the pushax/op sequence when encountered.
*/ */
{ {
static const char* const Tab[] = { unsigned Use = 0, Chg = 0;
"aslax1", if (GetFuncInfo (E->Arg, &Use, &Chg) == FNCLS_BUILTIN) {
"aslax2", if ((Chg & REG_SP) != 0) {
"aslax3", return 0;
"aslax4", }
"aslaxy", if ((Use & REG_SP) != 0 &&
"asrax1", ((Use & (SLV_IND | SLV_TOP)) != SLV_IND ||
"asrax2", RegValIsUnknown (E->RI->In.RegY) ||
"asrax3", E->RI->In.RegY < PushedBytes)) {
"asrax4", /* If we are using the stack, and we don't have "indirect"
"asraxy", ** addressing mode, or the value of Y is unknown, or less
"bcastax", ** than two, we cannot cope with this piece of code. Having
"bnegax", ** an unknown value of Y means that we cannot correct the
"complax", ** stack offset, while having an offset less than PushedBytes
"decax1", ** means that the code works with the value on stack which
"decax2", ** is to be removed.
"decax3", */
"decax4", return 0;
"decax5", }
"decax6", return 1;
"decax7", } else {
"decax8", static const char* const Tab[] = {
"decaxy", "_abs",
"incax1", };
"incax2",
"incax3",
"incax4",
"incax5",
"incax6",
"incax7",
"incax8",
"incaxy",
"ldaidx",
"ldauidx",
"ldaxidx",
"ldaxysp",
"negax",
"shlax1",
"shlax2",
"shlax3",
"shlax4",
"shlaxy",
"shrax1",
"shrax2",
"shrax3",
"shrax4",
"shraxy",
};
void* R = bsearch (Name, void* R = bsearch (E->Arg,
Tab, Tab,
sizeof (Tab) / sizeof (Tab[0]), sizeof (Tab) / sizeof (Tab[0]),
sizeof (Tab[0]), sizeof (Tab[0]),
CmpHarmless); CmpHarmless);
return (R != 0); return (R != 0);
}
} }

View File

@ -261,7 +261,7 @@ void RemoveRegLoads (StackOpData* D, LoadInfo* LI);
void RemoveRemainders (StackOpData* D); void RemoveRemainders (StackOpData* D);
/* Remove the code that is unnecessary after translation of the sequence */ /* 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 /* Check if this is a call to a harmless subroutine that will not interrupt
** the pushax/op sequence when encountered. ** the pushax/op sequence when encountered.
*/ */

View File

@ -1113,9 +1113,9 @@ static unsigned Opt_a_toscmpbool (StackOpData* D, const char* BoolTransformer)
D->IP = D->OpIndex + 1; D->IP = D->OpIndex + 1;
if (!D->RhsMultiChg && if (!D->RhsMultiChg &&
(D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0 && (D->Rhs.A.Flags & LI_DIRECT) != 0 &&
(D->Rhs.A.Flags & LI_DIRECT) != 0) { (D->Rhs.A.LoadEntry->Flags & CEF_DONT_REMOVE) == 0) {
/* cmp */ /* cmp */
AddOpLow (D, OP65_CMP, &D->Rhs); AddOpLow (D, OP65_CMP, &D->Rhs);
@ -1820,20 +1820,18 @@ unsigned OptStackOps (CodeSeg* S)
Data.OpEntry = E; Data.OpEntry = E;
State = FoundOp; State = FoundOp;
break; break;
} else if (!HarmlessCall (E->Arg)) { } else if (!HarmlessCall (E, 2)) {
/* A call to an unkown subroutine: We need to start /* The call might use or change the content that we are
** over after the last pushax. Note: This will also ** going to access later via the stack pointer. In any
** happen if we encounter a call to pushax! ** 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; I = Data.PushIndex;
State = Initialize; State = Initialize;
break; break;
} }
} else if (((E->Chg | E->Use) & REG_SP) != 0) {
} else if ((E->Use & REG_SP) != 0 &&
(E->AM != AM65_ZP_INDY ||
RegValIsUnknown (E->RI->In.RegY) ||
E->RI->In.RegY < 2)) {
/* If we are using the stack, and we don't have "indirect Y" /* If we are using the stack, and we don't have "indirect Y"
** addressing mode, or the value of Y is unknown, or less ** 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 ** that the code works with the value on stack which is to
** be removed. ** be removed.
*/ */
I = Data.PushIndex; if (E->AM == AM65_ZPX_IND ||
State = Initialize; ((E->Chg | E->Use) & SLV_IND) == 0 ||
break; (RegValIsUnknown (E->RI->In.RegY) ||
E->RI->In.RegY < 2)) {
I = Data.PushIndex;
State = Initialize;
break;
}
} }

View File

@ -207,7 +207,11 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu
} }
} }
SB_AppendStr (&Buf, GetSymTypeName (T)); if (!IsTypeBitField (T)) {
SB_AppendStr (&Buf, GetSymTypeName (T));
} else {
SB_AppendStr (&Buf, GetBasicTypeName (T + 1));
}
if (!SB_IsEmpty (West)) { if (!SB_IsEmpty (West)) {
SB_AppendChar (&Buf, ' '); SB_AppendChar (&Buf, ' ');
@ -231,6 +235,7 @@ const char* GetBasicTypeName (const Type* T)
{ {
switch (GetRawType (T)) { switch (GetRawType (T)) {
case T_TYPE_ENUM: return "enum"; case T_TYPE_ENUM: return "enum";
case T_TYPE_BITFIELD: return "bit-field";
case T_TYPE_FLOAT: return "float"; case T_TYPE_FLOAT: return "float";
case T_TYPE_DOUBLE: return "double"; case T_TYPE_DOUBLE: return "double";
case T_TYPE_VOID: return "void"; 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 */ /* Get the code generator replacement type of the object by its size */
{ {
unsigned NewType; unsigned NewType;
/* If the size is less than or equal to that of a a long, we will copy /* 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. ** the struct using the primary register, otherwise we use memcpy.
*/ */
switch (SizeOf (Type)) { switch (Size) {
case 1: NewType = CF_CHAR; break; case 1: NewType = CF_CHAR; break;
case 2: NewType = CF_INT; break; case 2: NewType = CF_INT; break;
case 3: /* FALLTHROUGH */ 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) const Type* GetUnderlyingType (const Type* Type)
/* Get the underlying type of an enum or other integer class 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) { if (Type->A.S->V.E.Type != 0) {
return Type->A.S->V.E.Type; 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; return Type;
@ -713,13 +611,14 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
*/ */
{ {
TypeCode Underlying = UnqualifiedType (Type->C); TypeCode Underlying = UnqualifiedType (Type->C);
TypeCode TCode;
if (IsISOChar (Type)) { if (IsISOChar (Type)) {
return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR;
} else if (IsTypeEnum (Type)) { } else if (IsTypeEnum (Type)) {
TypeCode TCode;
/* This should not happen, but just in case */ /* This should not happen, but just in case */
if (Type->A.S == 0) { if (Type->A.S == 0) {
Internal ("Enum tag type error in GetUnderlyingTypeCode"); 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; case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break;
default: Underlying |= T_TYPE_INT; 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; return Underlying;
@ -906,7 +820,7 @@ unsigned TypeOf (const Type* T)
case T_STRUCT: case T_STRUCT:
case T_UNION: case T_UNION:
NewType = TypeOfBySize (T); NewType = TypeOfBySize (SizeOf (T));
if (NewType != CF_NONE) { if (NewType != CF_NONE) {
return NewType; 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) Type* ArrayToPtr (const Type* T)
/* Convert an array to a pointer to it's first element */ /* 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) int IsClassObject (const Type* T)
/* Return true if this is a fully described object type */ /* 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) TypeCode AddrSizeQualifier (unsigned AddrSize)
/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ /* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */
{ {
@ -1339,3 +1433,101 @@ TypeCode AddrSizeQualifier (unsigned AddrSize)
} }
} }
int TypeHasAttr (const Type* T)
/* Return true if the given type has attribute data */
{
return IsClassStruct (T) || IsTypeArray (T) || IsClassFunc (T);
}
void PrintType (FILE* F, const Type* T)
/* Print fulle name of the type */
{
StrBuf Buf = AUTO_STRBUF_INITIALIZER;
fprintf (F, "%s", SB_GetConstBuf (GetFullTypeNameBuf (&Buf, T)));
SB_Done (&Buf);
}
void PrintFuncSig (FILE* F, const char* Name, const Type* T)
/* Print a function signature */
{
StrBuf Buf = AUTO_STRBUF_INITIALIZER;
StrBuf ParamList = AUTO_STRBUF_INITIALIZER;
StrBuf East = AUTO_STRBUF_INITIALIZER;
StrBuf West = AUTO_STRBUF_INITIALIZER;
/* Get the function descriptor used in definition */
const FuncDesc* D = GetFuncDefinitionDesc (T);
/* Get the parameter list string. Start from the first parameter */
SymEntry* Param = D->SymTab->SymHead;
unsigned I;
for (I = 0; I < D->ParamCount; ++I) {
CHECK (Param != 0 && (Param->Flags & SC_PARAM) != 0);
if (I > 0) {
SB_AppendStr (&ParamList, ", ");
}
if (SymIsRegVar (Param)) {
SB_AppendStr (&ParamList, "register ");
}
if (!HasAnonName (Param)) {
SB_AppendStr (&Buf, Param->Name);
}
SB_AppendStr (&ParamList, SB_GetConstBuf (GetFullTypeNameBuf (&Buf, Param->Type)));
SB_Clear (&Buf);
/* Next argument */
Param = Param->NextSym;
}
if ((D->Flags & FD_VARIADIC) == 0) {
if (D->ParamCount == 0 && (D->Flags & FD_EMPTY) == 0) {
SB_AppendStr (&ParamList, "void");
}
} else {
if (D->ParamCount > 0) {
SB_AppendStr (&ParamList, ", ...");
} else {
SB_AppendStr (&ParamList, "...");
}
}
SB_Terminate (&ParamList);
/* Get the function qualifiers */
if (GetQualifierTypeCodeNameBuf (&Buf, T->C, T_QUAL_NONE) > 0) {
/* Append a space between the qualifiers and the name */
SB_AppendChar (&Buf, ' ');
}
SB_Terminate (&Buf);
/* Get the signature string without the return type */
SB_Printf (&West, "%s%s (%s)", SB_GetConstBuf (&Buf), Name, SB_GetConstBuf (&ParamList));
SB_Done (&Buf);
SB_Done (&ParamList);
/* Complete with the return type */
GetFullTypeNameWestEast (&West, &East, GetFuncReturn (T));
SB_Append (&West, &East);
SB_Terminate (&West);
/* Output */
fprintf (F, "%s", SB_GetConstBuf (&West));
SB_Done (&East);
SB_Done (&West);
}
void PrintRawType (FILE* F, const Type* T)
/* Print a type string in raw hex format (for debugging) */
{
while (T->C != T_END) {
fprintf (F, "%04lX ", T->C);
++T;
}
fprintf (F, "\n");
}

View File

@ -78,54 +78,55 @@ enum {
T_TYPE_INT = 0x000003, T_TYPE_INT = 0x000003,
T_TYPE_LONG = 0x000004, T_TYPE_LONG = 0x000004,
T_TYPE_LONGLONG = 0x000005, T_TYPE_LONGLONG = 0x000005,
T_TYPE_ENUM = 0x000006, T_TYPE_ENUM = 0x000008,
T_TYPE_FLOAT = 0x000007, T_TYPE_BITFIELD = 0x000009,
T_TYPE_DOUBLE = 0x000008, T_TYPE_FLOAT = 0x00000A,
T_TYPE_VOID = 0x000009, T_TYPE_DOUBLE = 0x00000B,
T_TYPE_STRUCT = 0x00000A, T_TYPE_VOID = 0x000010,
T_TYPE_UNION = 0x00000B, T_TYPE_STRUCT = 0x000011,
T_TYPE_ARRAY = 0x00000C, T_TYPE_UNION = 0x000012,
T_TYPE_PTR = 0x00000D, T_TYPE_ARRAY = 0x000018,
T_TYPE_FUNC = 0x00000E, T_TYPE_PTR = 0x000019,
T_MASK_TYPE = 0x00000F, T_TYPE_FUNC = 0x00001A,
T_MASK_TYPE = 0x00001F,
/* Type classes */ /* Type classes */
T_CLASS_NONE = 0x000000, T_CLASS_NONE = 0x000000,
T_CLASS_INT = 0x000010, T_CLASS_INT = 0x000020,
T_CLASS_FLOAT = 0x000020, T_CLASS_FLOAT = 0x000040,
T_CLASS_PTR = 0x000030, T_CLASS_PTR = 0x000060,
T_CLASS_STRUCT = 0x000040, T_CLASS_STRUCT = 0x000080,
T_CLASS_FUNC = 0x000050, T_CLASS_FUNC = 0x0000A0,
T_MASK_CLASS = 0x000070, T_MASK_CLASS = 0x0000E0,
/* Type signedness */ /* Type signedness */
T_SIGN_NONE = 0x000000, T_SIGN_NONE = 0x000000,
T_SIGN_UNSIGNED = 0x000080, T_SIGN_UNSIGNED = 0x000100,
T_SIGN_SIGNED = 0x000100, T_SIGN_SIGNED = 0x000200,
T_MASK_SIGN = 0x000180, T_MASK_SIGN = 0x000300,
/* Type size modifiers */ /* Type size modifiers */
T_SIZE_NONE = 0x000000, T_SIZE_NONE = 0x000000,
T_SIZE_CHAR = 0x000200, T_SIZE_CHAR = 0x001000,
T_SIZE_SHORT = 0x000400, T_SIZE_SHORT = 0x002000,
T_SIZE_INT = 0x000600, T_SIZE_INT = 0x003000,
T_SIZE_LONG = 0x000800, T_SIZE_LONG = 0x004000,
T_SIZE_LONGLONG = 0x000A00, T_SIZE_LONGLONG = 0x005000,
T_MASK_SIZE = 0x000E00, T_MASK_SIZE = 0x00F000,
/* Type qualifiers */ /* Type qualifiers */
T_QUAL_NONE = 0x000000, T_QUAL_NONE = 0x000000,
T_QUAL_CONST = 0x001000, T_QUAL_CONST = 0x010000,
T_QUAL_VOLATILE = 0x002000, T_QUAL_VOLATILE = 0x020000,
T_QUAL_RESTRICT = 0x004000, T_QUAL_RESTRICT = 0x040000,
T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT,
T_QUAL_NEAR = 0x008000, T_QUAL_NEAR = 0x080000,
T_QUAL_FAR = 0x010000, T_QUAL_FAR = 0x100000,
T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR,
T_QUAL_FASTCALL = 0x020000, T_QUAL_FASTCALL = 0x200000,
T_QUAL_CDECL = 0x040000, T_QUAL_CDECL = 0x400000,
T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL,
T_MASK_QUAL = 0x07F000, T_MASK_QUAL = 0x7F0000,
/* Types */ /* Types */
T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, 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_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_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_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_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_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, 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 */ struct SymEntry* S; /* Enum/struct/union tag symbol entry pointer */
long L; /* Numeric attribute value */ long L; /* Numeric attribute value */
unsigned long U; /* Dito, unsigned */ 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 */ } A; /* Type attribute if necessary */
}; };
@ -288,33 +295,6 @@ unsigned long GetIntegerTypeMax (const Type* Type);
** The type must have a known size. ** 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) #if defined(HAVE_INLINE)
INLINE TypeCode UnqualifiedType (TypeCode T) INLINE TypeCode UnqualifiedType (TypeCode T)
/* Return the unqalified type code */ /* Return the unqalified type code */
@ -366,9 +346,44 @@ Type* IndirectModifiable (Type* T);
** given type points to. ** 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); Type* ArrayToPtr (const Type* T);
/* Convert an array to a pointer to it's first element */ /* 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) #if defined(HAVE_INLINE)
INLINE TypeCode GetRawType (const Type* T) INLINE TypeCode GetRawType (const Type* T)
/* Get the raw type */ /* Get the raw type */
@ -511,6 +526,36 @@ INLINE int IsTypeEnum (const Type* T)
# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) # define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM)
#endif #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) #if defined(HAVE_INLINE)
INLINE int IsTypeStruct (const Type* T) INLINE int IsTypeStruct (const Type* T)
/* Return true if this is a struct type */ /* 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); void SetESUSymEntry (Type* T, struct SymEntry* S);
/* Set the SymEntry pointer for an enum/struct/union type */ /* 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); TypeCode AddrSizeQualifier (unsigned AddrSize);
/* Return T_QUAL_NEAR or T_QUAL_FAR depending on the address size */ /* 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)) # define DataAddrSizeQualifier() (AddrSizeQualifier (DataAddrSize))
#endif #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 */ /* End of datatype.h */

View File

@ -2243,7 +2243,7 @@ static void DefineData (ExprDesc* Expr)
static void OutputBitFieldData (StructInitData* SI) static void DefineBitFieldData (StructInitData* SI)
/* Output bit field data */ /* Output bit field data */
{ {
/* Ignore if we have no 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 /* Parse initializaton for scalar data types. This function will not output the
** data but return it in ED. ** 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 initializaton for scalar data types. Return the number of data bytes. */
{ {
/* Parse initialization */ /* 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. */ /* Parse initializaton for pointer data types. Return the number of data bytes. */
{ {
/* Optional opening brace */ /* Optional opening brace */
@ -2364,9 +2375,6 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
NextToken (); NextToken ();
} }
/* Translate into target charset */
TranslateLiteral (CurTok.SVal);
/* If the array is one too small for the string literal, omit the /* If the array is one too small for the string literal, omit the
** trailing zero. ** trailing zero.
*/ */
@ -2379,7 +2387,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
} }
/* Output the data */ /* Output the data */
g_defbytes (GetLiteralStr (CurTok.SVal), Count); DefineStrData (CurTok.SVal, Count);
/* Skip the string */ /* Skip the string */
NextToken (); NextToken ();
@ -2453,7 +2461,7 @@ static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
static unsigned ParseStructInit (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. */ /* Parse initialization of a struct or union. Return the number of data bytes. */
{ {
SymEntry* Entry; SymEntry* Sym;
SymTable* Tab; SymTable* Tab;
StructInitData SI; StructInitData SI;
int HasCurly = 0; 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 */ /* 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 */ /* 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 /* Check if this struct definition has a field table. If it doesn't, it
** is an incomplete definition. ** is an incomplete definition.
*/ */
Tab = Entry->V.S.SymTab; Tab = Sym->V.S.SymTab;
if (Tab == 0) { if (Tab == 0) {
Error ("Cannot initialize variables with incomplete type"); Error ("Cannot initialize variables with incomplete type");
/* Try error recovery */ /* Try error recovery */
@ -2486,7 +2494,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
} }
/* Get a pointer to the list of symbols */ /* Get a pointer to the list of symbols */
Entry = Tab->SymHead; Sym = Tab->SymHead;
/* Initialize fields */ /* Initialize fields */
SI.Offs = 0; SI.Offs = 0;
@ -2495,7 +2503,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
while (CurTok.Tok != TOK_RCURLY) { while (CurTok.Tok != TOK_RCURLY) {
/* Check for excess elements */ /* Check for excess elements */
if (Entry == 0) { if (Sym == 0) {
/* Is there just one trailing comma before a closing curly? */ /* Is there just one trailing comma before a closing curly? */
if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) { if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
/* Skip comma and exit scope */ /* 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 */ /* 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 */ /* Just skip */
goto NextMember; 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 /* This may be an anonymous bit-field, in which case it doesn't
** have an initializer. ** 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 /* 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 ** word. We may have more if there was storage unit overlap, for
** example two consecutive 10 bit fields. These will be packed ** example two consecutive 10 bit fields. These will be packed
** into 3 bytes. ** 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. */ /* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
while (SI.ValBits >= CHAR_BITS) { while (SI.ValBits >= CHAR_BITS) {
OutputBitFieldData (&SI); DefineBitFieldData (&SI);
} }
/* Avoid consuming the comma if any */ /* Avoid consuming the comma if any */
goto NextMember; goto NextMember;
@ -2541,7 +2549,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
SkipComma = 0; SkipComma = 0;
} }
if (SymIsBitField (Entry)) { if (SymIsBitField (Sym)) {
/* Parse initialization of one field. Bit-fields need a special /* Parse initialization of one field. Bit-fields need a special
** handling. ** handling.
@ -2552,14 +2560,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
unsigned Shift; unsigned Shift;
/* Calculate the bitmask from the bit-field data */ /* 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 ... */ /* Safety ... */
CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
SI.Offs * CHAR_BITS + SI.ValBits); SI.Offs * CHAR_BITS + SI.ValBits);
/* Read the data, check for a constant integer, do a range check */ /* 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)) { if (!ED_IsConstAbsInt (&ED)) {
Error ("Constant initializer expected"); Error ("Constant initializer expected");
ED_MakeConstAbsInt (&ED, 1); ED_MakeConstAbsInt (&ED, 1);
@ -2569,31 +2577,31 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
** any useful bits. ** any useful bits.
*/ */
Val = (unsigned) ED.IVal & Mask; Val = (unsigned) ED.IVal & Mask;
if (IsSignUnsigned (Entry->Type)) { if (IsSignUnsigned (Sym->Type)) {
if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) {
Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %u", " changes value from %ld to %u",
GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
Entry->V.B.BitWidth, ED.IVal, Val); Sym->Type->A.B.Width, ED.IVal, Val);
} }
} else { } else {
/* Sign extend back to full width of host long. */ /* 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); long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits);
if (ED.IVal != RestoredVal) { if (ED.IVal != RestoredVal) {
Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer "
"changes value from %ld to %ld", "changes value from %ld to %ld",
GetFullTypeName (ED.Type), GetFullTypeName (Entry->Type), GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
Entry->V.B.BitWidth, ED.IVal, RestoredVal); Sym->Type->A.B.Width, ED.IVal, RestoredVal);
} }
} }
/* Add the value to the currently stored bit-field value */ /* 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); SI.BitVal |= (Val << Shift);
/* Account for the data and output any full bytes we have. */ /* 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. /* 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 ** This is 22 bits because the most we can have is 7 bits left
** over from the previous OutputBitField call, plus 15 bits ** 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. */ /* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
while (SI.ValBits >= CHAR_BITS) { while (SI.ValBits >= CHAR_BITS) {
OutputBitFieldData (&SI); DefineBitFieldData (&SI);
} }
} else { } else {
@ -2618,7 +2626,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Flexible array members may only be initialized if they are /* Flexible array members may only be initialized if they are
** the last field (or part of the last struct field). ** 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? */ /* More initializers? */
@ -2633,10 +2641,10 @@ NextMember:
/* Next member. For unions, only the first one can be initialized */ /* Next member. For unions, only the first one can be initialized */
if (IsTypeUnion (T)) { if (IsTypeUnion (T)) {
/* Union */ /* Union */
Entry = 0; Sym = 0;
} else { } else {
/* Struct */ /* Struct */
Entry = Entry->NextSym; Sym = Sym->NextSym;
} }
} }
@ -2647,7 +2655,7 @@ NextMember:
/* If we have data from a bit-field left, output it now */ /* If we have data from a bit-field left, output it now */
CHECK (SI.ValBits < CHAR_BITS); CHECK (SI.ValBits < CHAR_BITS);
OutputBitFieldData (&SI); DefineBitFieldData (&SI);
/* If there are struct fields left, reserve additional storage */ /* If there are struct fields left, reserve additional storage */
if (SI.Offs < SI.Size) { if (SI.Offs < SI.Size) {

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,11 @@
#define SQP_KEEP_EAX 0x02U #define SQP_KEEP_EAX 0x02U
#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ #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); void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr);
/* Call an expression function with checks. */ /* Call an expression function with checks. */
@ -44,6 +52,9 @@ void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr);
** generated code. ** generated code.
*/ */
void LimitExprValue (ExprDesc* Expr);
/* Limit the constant value of the expression to the range of its type */
void PushAddr (const ExprDesc* Expr); void PushAddr (const ExprDesc* Expr);
/* If the expression contains an address that was somehow evaluated, /* If the expression contains an address that was somehow evaluated,
** push this address on the stack. This is a helper function for all ** push this address on the stack. This is a helper function for all

View File

@ -56,30 +56,17 @@
ExprDesc* ED_Init (ExprDesc* Expr) ExprDesc* ED_Init (ExprDesc* Expr)
/* Initialize an ExprDesc */ /* Initialize an ExprDesc */
{ {
Expr->Sym = 0;
Expr->Type = 0; Expr->Type = 0;
Expr->Flags = E_NEED_EAX; Expr->Flags = E_NEED_EAX;
Expr->Name = 0; Expr->Name = 0;
Expr->Sym = 0;
Expr->IVal = 0; Expr->IVal = 0;
Expr->FVal = FP_D_Make (0.0); memset (&Expr->V, 0, sizeof (Expr->V));
Expr->LVal = 0;
Expr->BitOffs = 0;
Expr->BitWidth = 0;
return Expr; 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) #if !defined(HAVE_INLINE)
int ED_IsLocQuasiConst (const ExprDesc* Expr) int ED_IsLocQuasiConst (const ExprDesc* Expr)
/* Return true if the expression is a constant location of some sort or on the /* 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) ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type)
/* Replace Expr with an absolute const with the given value and type */ /* Replace Expr with an absolute const with the given value and type */
{ {
Expr->Sym = 0;
Expr->Type = Type; Expr->Type = Type;
Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
Expr->Name = 0; Expr->Name = 0;
Expr->Sym = 0;
Expr->IVal = Value; Expr->IVal = Value;
Expr->FVal = FP_D_Make (0.0); memset (&Expr->V, 0, sizeof (Expr->V));
return Expr; return Expr;
} }
@ -245,12 +232,12 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type)
ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value)
/* Replace Expr with a constant integer expression with the given value */ /* Replace Expr with a constant integer expression with the given value */
{ {
Expr->Sym = 0;
Expr->Type = type_int; Expr->Type = type_int;
Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
Expr->Name = 0; Expr->Name = 0;
Expr->Sym = 0;
Expr->IVal = Value; Expr->IVal = Value;
Expr->FVal = FP_D_Make (0.0); memset (&Expr->V, 0, sizeof (Expr->V));
return Expr; 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->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE);
Expr->Name = 0; Expr->Name = 0;
Expr->IVal = Value; Expr->IVal = Value;
Expr->FVal = FP_D_Make (0.0); memset (&Expr->V, 0, sizeof (Expr->V));
return Expr; return Expr;
} }
@ -273,13 +260,13 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value)
ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr)
/* Finalize the result of LoadExpr to be an rvalue in the primary register */ /* 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_ADDRESS_OF);
Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF);
Expr->Flags &= ~E_CC_SET; Expr->Flags &= ~E_CC_SET;
Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL);
Expr->Sym = 0;
Expr->Name = 0; Expr->Name = 0;
Expr->IVal = 0; /* No offset */ Expr->IVal = 0; /* No offset */
Expr->FVal = FP_D_Make (0.0); memset (&Expr->V, 0, sizeof (Expr->V));
return Expr; return Expr;
} }
@ -464,8 +451,8 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr)
int ED_IsNullPtr (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr)
/* Return true if the given expression is a NULL pointer constant */ /* Return true if the given expression is a NULL pointer constant */
{ {
return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) ==
(E_LOC_NONE|E_RTYPE_RVAL) && (E_LOC_NONE|E_RTYPE_RVAL) &&
Expr->IVal == 0 && Expr->IVal == 0 &&
IsClassInt (Expr->Type); IsClassInt (Expr->Type);
} }
@ -503,7 +490,7 @@ void PrintExprDesc (FILE* F, ExprDesc* E)
"Raw type: (unknown)\n"); "Raw type: (unknown)\n");
} }
fprintf (F, "IVal: 0x%08lX\n", E->IVal); 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; Flags = E->Flags;
Sep = '('; Sep = '(';
@ -558,11 +545,6 @@ void PrintExprDesc (FILE* F, ExprDesc* E)
Flags &= ~E_LOC_CODE; Flags &= ~E_LOC_CODE;
Sep = ','; Sep = ',';
} }
if (Flags & E_BITFIELD) {
fprintf (F, "%cE_BITFIELD", Sep);
Flags &= ~E_BITFIELD;
Sep = ',';
}
if (Flags & E_NEED_TEST) { if (Flags & E_NEED_TEST) {
fprintf (F, "%cE_NEED_TEST", Sep); fprintf (F, "%cE_NEED_TEST", Sep);
Flags &= ~E_NEED_TEST; Flags &= ~E_NEED_TEST;

View File

@ -114,7 +114,6 @@ enum {
E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK,
/* Expression type modifiers */ /* Expression type modifiers */
E_BITFIELD = 0x0200, /* Expression is a bit-field */
E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */ E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */
/* lvalue/rvalue in C language's sense */ /* lvalue/rvalue in C language's sense */
@ -198,17 +197,15 @@ struct Literal;
/* Describe the result of an expression */ /* Describe the result of an expression */
typedef struct ExprDesc ExprDesc; typedef struct ExprDesc ExprDesc;
struct ExprDesc { struct ExprDesc {
struct SymEntry* Sym; /* Symbol table entry if known */ const Type* Type; /* C type of the expression */
const Type* Type; /* Type array of expression */ unsigned Flags; /* Properties of the expression */
unsigned Flags;
uintptr_t Name; /* Name pointer or label number */ uintptr_t Name; /* Name pointer or label number */
struct SymEntry* Sym; /* Symbol table entry if any */
long IVal; /* Integer value if expression constant */ long IVal; /* Integer value if expression constant */
Double FVal; /* Floating point value */ union {
struct Literal* LVal; /* Literal value */ Double FVal; /* Floating point value */
struct Literal* LVal; /* Literal value */
/* Bit field stuff */ } V;
unsigned BitOffs; /* Bit offset for bit fields */
unsigned BitWidth; /* Bit width for bit fields */
/* Start and end of generated code */ /* Start and end of generated code */
CodeMark Start; CodeMark Start;
@ -331,29 +328,6 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr);
*/ */
#endif #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) #if defined(HAVE_INLINE)
INLINE void ED_RequireTest (ExprDesc* Expr) INLINE void ED_RequireTest (ExprDesc* Expr)
/* Mark the expression for a test. */ /* Mark the expression for a test. */

View File

@ -644,7 +644,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Now process statements in this block */ /* Now process statements in this block */
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) { 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 /* If this is not a void function, and not the main function in a C99

View File

@ -124,38 +124,40 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr)
*/ */
int AdjustBitField = 0; int AdjustBitField = 0;
unsigned BitFieldFullWidthFlags = 0; unsigned BitFieldFullWidthFlags = 0;
if (ED_IsBitField (Expr)) { if ((Flags & CF_TYPEMASK) == 0) {
unsigned EndBit = Expr->BitOffs + Expr->BitWidth; if (IsTypeBitField (Expr->Type)) {
AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); 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 /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are
** supported. ** 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.
*/ */
Flags |= CF_UNSIGNED | CF_FORCECHAR; Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT;
BitFieldFullWidthFlags |= CF_UNSIGNED; 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)) { 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 /* We always need to do something with the low byte, so there is no opportunity
** for optimization by skipping it. ** for optimization by skipping it.
*/ */
CHECK (Expr->BitOffs < CHAR_BITS); CHECK (Expr->Type->A.B.Offs < CHAR_BITS);
if (ED_YetToTest (Expr)) { 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 { } else {
g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type),
Expr->BitOffs, Expr->BitWidth); Expr->Type->A.B.Offs, Expr->Type->A.B.Width);
} }
} }

View File

@ -295,7 +295,7 @@ static void SetSys (const char* Sys)
break; break;
default: default:
AbEnd ("Unknown target system type %d", Target); AbEnd ("Unknown target system '%s'", Sys);
} }
/* Initialize the translation tables for the target system */ /* Initialize the translation tables for the target system */

View File

@ -71,7 +71,7 @@
unsigned char Preprocessing = 0; unsigned char Preprocessing = 0;
/* Management data for #if */ /* Management data for #if */
#define MAX_IFS 64 #define MAX_IFS 256
#define IFCOND_NONE 0x00U #define IFCOND_NONE 0x00U
#define IFCOND_SKIP 0x01U #define IFCOND_SKIP 0x01U
#define IFCOND_ELSE 0x02U #define IFCOND_ELSE 0x02U

View File

@ -832,8 +832,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
*/ */
if (ED_IsLocLiteral (&Arg2.Expr) && if (ED_IsLocLiteral (&Arg2.Expr) &&
IS_Get (&WritableStrings) == 0 && IS_Get (&WritableStrings) == 0 &&
GetLiteralSize (Arg2.Expr.LVal) == 1 && GetLiteralSize (Arg2.Expr.V.LVal) == 1 &&
GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') {
/* Drop the generated code so we have the first argument in the /* Drop the generated code so we have the first argument in the
** primary ** primary
@ -841,7 +841,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
RemoveCode (&Arg1.Push); RemoveCode (&Arg1.Push);
/* We don't need the literal any longer */ /* 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 /* We do now have Arg1 in the primary. Load the first character from
** this string and cast to int. This is the function result. ** 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) { if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
/* Constant string literal */ /* 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 */ /* We don't need the literal any longer */
ReleaseLiteral (Arg.LVal); ReleaseLiteral (Arg.V.LVal);
/* Bail out, no need for further improvements */ /* Bail out, no need for further improvements */
goto ExitPoint; goto ExitPoint;

View File

@ -163,7 +163,7 @@ static int IfStatement (void)
TestResult = TestInParens (Label1, 0); TestResult = TestInParens (Label1, 0);
/* Parse the if body */ /* Parse the if body */
GotBreak = Statement (0); GotBreak = AnyStatement (0);
/* Else clause present? */ /* Else clause present? */
if (CurTok.Tok != TOK_ELSE) { if (CurTok.Tok != TOK_ELSE) {
@ -195,7 +195,7 @@ static int IfStatement (void)
g_defcodelabel (Label1); g_defcodelabel (Label1);
/* Total break only if both branches had a break. */ /* Total break only if both branches had a break. */
GotBreak &= Statement (0); GotBreak &= AnyStatement (0);
/* Generate the label for the else clause */ /* Generate the label for the else clause */
g_defcodelabel (Label2); g_defcodelabel (Label2);
@ -225,7 +225,7 @@ static void DoStatement (void)
g_defcodelabel (LoopLabel); g_defcodelabel (LoopLabel);
/* Parse the loop body */ /* Parse the loop body */
Statement (0); AnyStatement (0);
/* Output the label for a continue */ /* Output the label for a continue */
g_defcodelabel (ContinueLabel); g_defcodelabel (ContinueLabel);
@ -283,7 +283,7 @@ static void WhileStatement (void)
g_defcodelabel (LoopLabel); g_defcodelabel (LoopLabel);
/* Loop body */ /* Loop body */
Statement (&PendingToken); AnyStatement (&PendingToken);
/* Emit the while condition label */ /* Emit the while condition label */
g_defcodelabel (CondLabel); g_defcodelabel (CondLabel);
@ -509,7 +509,7 @@ static void ForStatement (void)
/* Loop body */ /* Loop body */
g_defcodelabel (BodyLabel); g_defcodelabel (BodyLabel);
Statement (&PendingToken); AnyStatement (&PendingToken);
/* If we had an increment expression, move the code to the bottom of /* 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 ** 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 /* Compound statement. Allow any number of statements inside braces. The
** function returns true if the last statement was a break or return. ** function returns true if the last statement was a break or return.
*/ */
{ {
int GotBreak; int GotBreak = 0;
/* Remember the stack at block entry */ /* Remember the stack at block entry */
int OldStack = StackPtr; int OldStack = StackPtr;
unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack); unsigned OldBlockStackSize = CollCount (&CurrentFunc->LocalsBlockStack);
/* Skip '{' */
NextToken ();
/* Enter a new lexical level */ /* Enter a new lexical level */
EnterBlockLevel (); EnterBlockLevel ();
@ -554,16 +557,15 @@ static int CompoundStatement (void)
DeclareLocals (); DeclareLocals ();
/* Now process statements in this block */ /* Now process statements in this block */
GotBreak = 0;
while (CurTok.Tok != TOK_RCURLY) { while (CurTok.Tok != TOK_RCURLY) {
if (CurTok.Tok != TOK_CEOF) { if (CurTok.Tok != TOK_CEOF) {
GotBreak = Statement (0); GotBreak = AnyStatement (0);
} else { } else {
break; break;
} }
} }
/* Clean up the stack. */ /* Clean up the stack if the codeflow may reach the end */
if (!GotBreak) { if (!GotBreak) {
g_space (StackPtr - OldStack); g_space (StackPtr - OldStack);
} }
@ -583,12 +585,80 @@ static int CompoundStatement (void)
/* Leave the lexical level */ /* Leave the lexical level */
LeaveBlockLevel (); LeaveBlockLevel ();
/* Skip '}' */
CheckTok (TOK_RCURLY, "'}' expected", PendingToken);
return GotBreak; 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 /* Statement parser. Returns 1 if the statement does a return/break, returns
** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** 0 otherwise. If the PendingToken pointer is not NULL, the function will
** not skip the terminating token of the statement (closing brace or ** 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. ** NULL, the function will skip the token.
*/ */
{ {
ExprDesc Expr;
int GotBreak;
unsigned PrevErrorCount;
CodeMark Start, End;
ED_Init (&Expr);
/* Assume no pending token */ /* Assume no pending token */
if (PendingToken) { if (PendingToken) {
*PendingToken = 0; *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. ** replace one.
*/ */
while (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) { if (ParseAnyLabels ()) {
/* Handle the label */ return 0;
DoLabel ();
if (CheckLabelWithoutStatement ()) {
return 0;
}
} }
switch (CurTok.Tok) { switch (CurTok.Tok) {
case TOK_LCURLY:
NextToken ();
GotBreak = CompoundStatement ();
CheckTok (TOK_RCURLY, "'{' expected", PendingToken);
return GotBreak;
case TOK_IF: case TOK_IF:
return IfStatement (); return IfStatement ();
case TOK_SWITCH:
SwitchStatement ();
break;
case TOK_WHILE: case TOK_WHILE:
WhileStatement (); WhileStatement ();
break; break;
@ -640,10 +697,15 @@ int Statement (int* PendingToken)
DoStatement (); DoStatement ();
break; break;
case TOK_SWITCH: case TOK_FOR:
SwitchStatement (); ForStatement ();
break; break;
case TOK_GOTO:
GotoStatement ();
CheckSemi (PendingToken);
return 1;
case TOK_RETURN: case TOK_RETURN:
ReturnStatement (); ReturnStatement ();
CheckSemi (PendingToken); CheckSemi (PendingToken);
@ -659,55 +721,22 @@ int Statement (int* PendingToken)
CheckSemi (PendingToken); CheckSemi (PendingToken);
return 1; 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: case TOK_PRAGMA:
DoPragma (); DoPragma ();
break; break;
case TOK_CASE: case TOK_SEMI:
CaseLabel (); /* Empty statement. Ignore it */
CheckLabelWithoutStatement (); CheckSemi (PendingToken);
break; break;
case TOK_DEFAULT: case TOK_LCURLY:
DefaultLabel (); return CompoundStatement (PendingToken);
CheckLabelWithoutStatement ();
break;
default: default:
/* Remember the current error count and code position */ /* Simple statement */
PrevErrorCount = ErrorCount; Statement (PendingToken);
GetCodePos (&Start); break;
/* 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);
} }
return 0; return 0;
} }

View File

@ -44,7 +44,7 @@
int Statement (int* PendingToken); int AnyStatement (int* PendingToken);
/* Statement parser. Returns 1 if the statement does a return/break, returns /* Statement parser. Returns 1 if the statement does a return/break, returns
** 0 otherwise. If the PendingToken pointer is not NULL, the function will ** 0 otherwise. If the PendingToken pointer is not NULL, the function will
** not skip the terminating token of the statement (closing brace or ** not skip the terminating token of the statement (closing brace or

View File

@ -148,7 +148,7 @@ void SwitchStatement (void)
/* Parse the following statement, which may actually be a compound /* Parse the following statement, which may actually be a compound
** statement if there is a curly brace at the current input position ** statement if there is a curly brace at the current input position
*/ */
HaveBreak = Statement (&RCurlyBrace); HaveBreak = AnyStatement (&RCurlyBrace);
/* Check if we had any labels */ /* Check if we had any labels */
if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) { if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {

View File

@ -183,13 +183,6 @@ struct SymEntry {
const Type* Type; /* Underlying type */ const Type* Type; /* Underlying type */
} E; } 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 */ /* Data for functions */
struct { struct {
struct Segments* Seg; /* Segments for this function */ struct Segments* Seg; /* Segments for this function */

View File

@ -881,10 +881,8 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs,
Entry = NewSymEntry (Name, SC_BITFIELD); Entry = NewSymEntry (Name, SC_BITFIELD);
/* Set the symbol attributes. Bit-fields are always integral types. */ /* Set the symbol attributes. Bit-fields are always integral types. */
Entry->Type = TypeDup (T); Entry->Type = NewBitFieldType (T, BitOffs, BitWidth);
Entry->V.B.Offs = Offs; Entry->V.Offs = Offs;
Entry->V.B.BitOffs = BitOffs;
Entry->V.B.BitWidth = BitWidth;
if (!SignednessSpecified) { if (!SignednessSpecified) {
/* int is treated as signed int everywhere except bit-fields; switch it to unsigned, /* 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 || CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED ||
IsTypeChar (Entry->Type)); IsTypeChar (Entry->Type));
Entry->Type->C &= ~T_MASK_SIGN; Entry->Type[0].C &= ~T_MASK_SIGN;
Entry->Type->C |= T_SIGN_UNSIGNED; 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 */ /* Add the entry to the symbol table */

View File

@ -278,6 +278,21 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
SetResult (Result, TC_STRICT_COMPATIBLE); 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 the underlying types are not identical, the types are incompatible */
if (LeftType != RightType) { if (LeftType != RightType) {
SetResult (Result, TC_INCOMPATIBLE); SetResult (Result, TC_INCOMPATIBLE);

View File

@ -83,11 +83,16 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType)
/* Get the sizes of the types. Since we've excluded void types, checking /* Get the sizes of the types. Since we've excluded void types, checking
** for known sizes makes sense here. ** for known sizes makes sense here.
*/ */
if (ED_IsBitField (Expr)) { if (IsTypeBitField (OldType)) {
OldBits = Expr->BitWidth; OldBits = OldType->A.B.Width;
} else { } else {
OldBits = CheckedSizeOf (OldType) * CHAR_BITS; 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; NewBits = CheckedSizeOf (NewType) * CHAR_BITS;
/* lvalue? */ /* lvalue? */
@ -167,9 +172,6 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType)
ExitPoint: ExitPoint:
/* The expression has always the new type */ /* The expression has always the new type */
ReplaceType (Expr, NewType); ReplaceType (Expr, NewType);
/* Bit-fields are converted to integers */
ED_DisBitField (Expr);
} }

View File

@ -321,20 +321,18 @@ long GetExprVal (ExprNode* Expr)
return GetExprVal (Expr->Left) * GetExprVal (Expr->Right); return GetExprVal (Expr->Left) * GetExprVal (Expr->Right);
case EXPR_DIV: case EXPR_DIV:
Left = GetExprVal (Expr->Left);
Right = GetExprVal (Expr->Right); Right = GetExprVal (Expr->Right);
if (Right == 0) { if (Right == 0) {
Error ("Division by zero"); Error ("Division by zero");
} }
return Left / Right; return GetExprVal (Expr->Left) / Right;
case EXPR_MOD: case EXPR_MOD:
Left = GetExprVal (Expr->Left);
Right = GetExprVal (Expr->Right); Right = GetExprVal (Expr->Right);
if (Right == 0) { if (Right == 0) {
Error ("Modulo operation with zero"); Error ("Modulo operation with zero");
} }
return Left % Right; return GetExprVal (Expr->Left) % Right;
case EXPR_OR: case EXPR_OR:
return GetExprVal (Expr->Left) | GetExprVal (Expr->Right); return GetExprVal (Expr->Left) | GetExprVal (Expr->Right);
@ -403,17 +401,20 @@ long GetExprVal (ExprNode* Expr)
case EXPR_BANK: case EXPR_BANK:
GetSegExprVal (Expr->Left, &D); GetSegExprVal (Expr->Left, &D);
if (D.TooComplex || D.Seg == 0) { if (D.TooComplex) {
Error ("Argument for .BANK is not segment relative or too complex"); 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) { if (D.Seg->MemArea == 0) {
Error ("Segment '%s' is referenced by .BANK but " Error ("Segment '%s' is referenced by .BANK(),"
"not assigned to a memory area", " but not assigned to a memory area",
GetString (D.Seg->Name)); GetString (D.Seg->Name));
} }
if (D.Seg->MemArea->BankExpr == 0) { if (D.Seg->MemArea->BankExpr == 0) {
Error ("Memory area '%s' is referenced by .BANK but " Error ("Memory area '%s' is referenced by .BANK(),"
"has no BANK attribute", " but has no BANK attribute",
GetString (D.Seg->MemArea->Name)); GetString (D.Seg->MemArea->Name));
} }
return GetExprVal (D.Seg->MemArea->BankExpr); return GetExprVal (D.Seg->MemArea->BankExpr);
@ -457,13 +458,15 @@ long GetExprVal (ExprNode* Expr)
static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign) static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign)
/* Check if the given expression consists of a segment reference and only /* 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. ** set D->TooComplex to true.
** Internal, recursive routine. ** Internal, recursive routine.
*/ */
{ {
Export* E; Export* E;
CHECK (Expr != 0);
switch (Expr->Op) { switch (Expr->Op) {
case EXPR_LITERAL: case EXPR_LITERAL:
@ -479,7 +482,7 @@ static void GetSegExprValInternal (ExprNode* Expr, SegExprDesc* D, int Sign)
*/ */
if (ExportHasMark (E)) { if (ExportHasMark (E)) {
CircularRefError (E); CircularRefError (E);
} else { } else if (E->Expr != 0) {
MarkExport (E); MarkExport (E);
GetSegExprValInternal (E->Expr, D, Sign); GetSegExprValInternal (E->Expr, D, Sign);
UnmarkExport (E); UnmarkExport (E);

View File

@ -1,2 +1,18 @@
@echo off
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" goto vs2017
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" goto vs2019
echo Error: VsDevCmd.bat not found!
goto:eof
:vs2017
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" 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 %* msbuild.exe %*

View File

@ -5,13 +5,13 @@ MEMORY {
ZP: file = "", define = yes, start = $0082, size = $007E; ZP: file = "", define = yes, start = $0082, size = $007E;
# First memory segment in file, show message # First memory segment in file, show message
LOADER: file = %O, start = $680, size = 128; 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; 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; 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; 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; MAIN: file = %O, start = %S, size = $BC20 - %S;
} }
FILES { FILES {

View File

@ -9,9 +9,9 @@
int main(void) int main(void)
{ {
char *rev;
unsigned int t, v; unsigned int t, v;
unsigned char palntsc; unsigned char palntsc;
unsigned char *rev;
unsigned char minor; unsigned char minor;
unsigned char c; unsigned char c;

View File

@ -30,10 +30,13 @@ else
LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65) LD := $(if $(wildcard ../../../bin/ld65*),../../../bin/ld65,ld65)
endif endif
all: petscii.prg all: petscii.prg cbmdir-test.prg
petscii.prg: petscii.c petscii.prg: petscii.c
$(CL) -t $(SYS) -O -o 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: clean:
@$(DEL) petscii.prg 2>$(NULLDEV) @$(DEL) petscii.prg cbmdir-test.prg 2>$(NULLDEV)

View File

@ -0,0 +1,119 @@
/*
** Tests the CBM-specific directory functions.
**
** 2021-08-12, Greg King
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <conio.h>
#include <cbm.h>
/* device number */
#define UNIT 8
/* directory patterm */
static const char name[] = "$";
static const char* const type[] = {
"DEL",
"CBM",
"DIR",
"LNK",
"???",
"Directory header",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"SEQ",
"PRG",
"USR",
"REL",
"VRP"
};
static const char* const access[] = {
"unknown",
"read-only",
"write-only",
"read/write"
};
static const char* const error[] = {
"",
"couldn't read it",
"",
"couldn't find start of file-name",
"couldn't find end of file-name",
"couldn't read file-type",
"premature end of file"
};
int main(void)
{
unsigned char go = 0;
unsigned char rc;
struct cbm_dirent E;
/* Explain use, and wait for a key. */
printf ("use the following keys:\n"
" g -> go ahead without stopping\n"
" q -> quit directory lister\n"
"tap any key to start ...\n\n");
cgetc ();
/* Open the directory. */
if (cbm_opendir (1, UNIT, name) != 0) {
printf("error opening %s:\n %s\n", name, _stroserror (_oserror));
return 1;
}
/* Output the directory. */
printf("contents of \"%s\":\n", name);
while ((rc = cbm_readdir (1, &E)) != 2) {
if (rc != 0) {
goto oops;
}
printf (" name[]: \"%s\"\n", E.name);
printf (" size :%6u\n", E.size);
printf (" type : %s\n", type[E.type]);
printf (" access: %s\n", access[E.access > 3 ? 0 : E.access]);
printf ("----\n");
if (!go) {
switch (cgetc ()) {
case 'q':
goto done;
case 'g':
go = 1;
}
}
}
printf (" size :%6u free.\n", E.size);
done:
/* Close the directory. */
cbm_closedir (1);
return 0;
oops:
if (rc <= 6) {
printf ("\ndirectory error:\n %s.\n", error[rc]);
}
cbm_closedir (1);
return 1;
}

View File

@ -42,14 +42,15 @@ else
COUNT := 1 COUNT := 1
endif 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 %.pce: %.bin
dd if=$< bs=8K skip=${COUNT} > $@ dd if=$< bs=8K skip=${COUNT} > $@
dd if=$< bs=8K count=${COUNT} >> $@ dd if=$< bs=8K count=${COUNT} >> $@
%.bin: %.c
$(CL) -t pce $< -Wl -D__CARTSIZE__=${CARTSIZE} -m $*.map -o $@
clean: clean:
@$(DEL) conio.o conio.??? 2>$(NULLDEV) @$(DEL) conio.o conio.??? 2>$(NULLDEV)

View File

@ -64,6 +64,7 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR)
$(if $(QUIET),echo misc/bug760.$1.$2.prg) $(if $(QUIET),echo misc/bug760.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
# should compile, but gives an error
$(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR) $(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR)
@echo "FIXME: " $$@ "currently does not compile." @echo "FIXME: " $$@ "currently does not compile."
$(if $(QUIET),echo misc/bug1437.$1.$2.prg) $(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) $(if $(QUIET),echo misc/bug1263.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) $(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 # this one requires --std=c89, it fails with --std=c99
$(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR) $(WORKDIR)/bug1265.$1.$2.prg: bug1265.c | $(WORKDIR)
$(if $(QUIET),echo misc/bug1265.$1.$2.prg) $(if $(QUIET),echo misc/bug1265.$1.$2.prg)

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

@ -0,0 +1,30 @@
/* issue #1357 - X Macros don't work with C preprocessor */
#define OPCODES(X) \
X(PUSHNIL) \
X(PUSHTRUE) \
X(PUSHFALSE)
enum {
#define X(op) op,
OPCODES(X)
#undef X
N_OPS
};
/* cc65 -E bug1357.c -o bug1357.c.pre
should produce something like this:
enum {
PUSHNIL,
PUSHTRUE,
PUSHFALSE,
N_OPS
};
*/
int main(void)
{
return 0;
}

View File

@ -1,6 +1,13 @@
This directory contains test code for automatic regression testing of the CC65 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 /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. 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 /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. /todo - These tests fail due to open compiler issues.
The makefile in this directory _expects_ the tests to fail, because of 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. moved to /val in the PR fixing the issue, which will make CI pass again.
No changes to makefiles are required! 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 /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