1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-24 11:31:31 +00:00

Merge branch 'cc65:master' into master

This commit is contained in:
polluks2 2021-08-30 13:56:54 +02:00 committed by GitHub
commit bd23ad4d6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 2594 additions and 1816 deletions

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

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

@ -1669,41 +1669,6 @@ used in presence of a prototype.
</quote> </quote>
<sect1>bios_playsound<label id="bios_playsound"><p>
<quote>
<descrip>
<tag/Function/Play sound sequence.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ bios_playsound(void *a, unsigned char b);/
<tag/Description/The function may play chords based on a BASIC statement,
note and duration are defined in the manual chapter 13 on page 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/Creativision
<tag/See also/
<ref id="psg_delay" name="psg_delay">,
<ref id="psg_outb" name="psg_outb">
<tag/Example/
<verb>
static const unsigned char notes[] = {
0x77, 0x4F, 0x37,
0x4B, 0x05, 0xBB,
0x4F, 0x27, 0x83,
0x93, 0x9B, 0x93,
0x17, 0x4F, 0x96,
0xAB, 0x17, 0x4F,
0x0E
};
bios_playsound (notes, sizeof notes);
</verb>
</descrip>
</quote>
<sect1>bgcolor<label id="bgcolor"><p> <sect1>bgcolor<label id="bgcolor"><p>
<quote> <quote>
@ -1729,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>
@ -1823,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>
@ -5820,14 +5823,14 @@ be used in presence of a prototype.
<descrip> <descrip>
<tag/Function/Delay for a short period of time. <tag/Function/Delay for a short period of time.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/ <tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ psg_delay(unsigned char b);/ <tag/Declaration/<tt/void __fastcall__ psg_delay (unsigned char b);/
<tag/Description/The function specifies how long each note or pause between <tag/Description/The function specifies how long each note or pause between
notes should last. notes should last.
<tag/Notes/<itemize> <tag/Notes/<itemize>
<item>The function is only available as fastcall function, so it may only be <item>The function is only available as fastcall function, so it may only be
used in presence of a prototype. used in presence of a prototype.
</itemize> </itemize>
<tag/Availability/Creativision <tag/Availability/cc65
<tag/See also/ <tag/See also/
<ref id="psg_outb" name="psg_outb">, <ref id="psg_outb" name="psg_outb">,
<ref id="psg_silence" name="psg_silence"> <ref id="psg_silence" name="psg_silence">
@ -5840,23 +5843,29 @@ used in presence of a prototype.
<quote> <quote>
<descrip> <descrip>
<tag/Function/Output a byte. <tag/Function/Output a byte to the PSG.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/ <tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void __fastcall__ psg_outb(unsigned char b);/ <tag/Declaration/<tt/void __fastcall__ psg_outb (unsigned char b);/
<tag/Description/The function will send a PSG byte and wait for acknowledge. <tag/Description/The function sends a byte to the Programmable Sound
Generator, then waits for the PSG to acknowledge.
<tag/Notes/<itemize> <tag/Notes/<itemize>
<item>The function is only available as fastcall function, so it may only be <item>The function is only available as fastcall function, so it may only be
used in presence of a prototype. used in presence of a prototype.
</itemize> </itemize>
<tag/Availability/Creativision <tag/Availability/cc65
<tag/See also/ <tag/See also/
<ref id="psg_delay" name="psg_delay">, <ref id="psg_delay" name="psg_delay">,
<ref id="psg_silence" name="psg_silence"> <ref id="psg_silence" name="psg_silence">
<tag/Example/ <tag/Example/<verb>
<verb> #include <creativision.h>
psg_outb (0x80); // Latch frequency void main (void)
psg_outb (0x07); // Frequency byte 2 {
psg_outb (0x90); // Channel 0 full volume psg_outb (0x80); // Latch frequency
psg_outb (0x07); // Frequency byte 2
psg_outb (0x90); // Channel 0 full volume
psg_delay (100);
psg_silence ();
}
</verb> </verb>
</descrip> </descrip>
</quote> </quote>
@ -5866,13 +5875,12 @@ psg_outb (0x90); // Channel 0 full volume
<quote> <quote>
<descrip> <descrip>
<tag/Function/Set volume off on each channel. <tag/Function/Set volume off on each PSG channel.
<tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/ <tag/Header/<tt/<ref id="creativision.h" name="creativision.h">/
<tag/Declaration/<tt/void psg_silence(void);/ <tag/Declaration/<tt/void psg_silence (void);/
<tag/Description/The function sends $9F, $BF, $DF, $FF to the PSG. <tag/Description/The function resets the Programmable Sound Generator,
<tag/Notes/<itemize> then sends $9F, $BF, $DF, $FF to the PSG.
</itemize> <tag/Availability/cc65
<tag/Availability/Creativision
<tag/See also/ <tag/See also/
<ref id="psg_delay" name="psg_delay">, <ref id="psg_delay" name="psg_delay">,
<ref id="psg_outb" name="psg_outb"> <ref id="psg_outb" name="psg_outb">

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

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

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

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

@ -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,89 +3,93 @@
/* Watara Supervision sample C program */ /* Watara Supervision sample C program */
/* */ /* */
/* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */ /* Fabrizio Caruso (fabrizio_caruso@hotmail.com), 2019 */
/* Greg King (greg.king5@verizon.net), 2021 */
/* */ /* */
/*****************************************************************************/ /*****************************************************************************/
#include <supervision.h> #include <supervision.h>
#include <peekpoke.h> #include <string.h>
// Number of bytes per screen line (Remark: Last 8 bytes are not displayed) /* Number of words per screen line (Remark: Last 4 words aren't displayed) */
#define BYTES_PER_LINE 48 #define WORDS_PER_LINE (160/8+4)
// Character definitions in 8x8 format struct sv_vram {
const unsigned char h_char[] = {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}; unsigned int v[160/8][8][WORDS_PER_LINE];
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}; #define SV_VRAM ((*(struct sv_vram *)0x4000).v)
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) /* 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)
{ {
unsigned short i; memset(SV_VIDEO, 0, 0x2000);
}
for(i=0;i<0x2000;++i)
/* 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)
{ {
POKE(SV_VIDEO+i,0); SV_VRAM[y][k][x] = double_reversed_bits(ch[k]);
} }
} }
// Necessary conversion to have 2 bits per pixel with darkest hue static void init_lcd(void)
// 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.width = 160;
SV_LCD.height = 160; SV_LCD.height = 160;
} }
int main() static void hello(unsigned char x, unsigned char y)
{ {
init_lcd(); display_char(x+ 0,y,h_char);
clear_screen(); display_char(x+ 1,y,e_char);
display_char(x+ 2,y,l_char);
display_char(3,2, h_char); display_char(x+ 3,y,l_char);
display_char(3,3, e_char); display_char(x+ 4,y,o_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) {}; display_char(x+ 6,y,w_char);
display_char(x+ 7,y,o_char);
return 0; 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

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

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

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

@ -291,7 +291,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

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

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

@ -1,6 +1,6 @@
/* issue #1462 - Bit-fields are still broken */ /* issue #1462 - Bit-fields are still broken */
/* More testson "op= expression result value" that a naive fix might fail with */ /* More tests on "op= expression result value" that a naive fix might fail with */
#include <stdio.h> #include <stdio.h>
@ -27,26 +27,26 @@ void test1(void)
T1 a = { 3, 3, 3, 3 }; T1 a = { 3, 3, 3, 3 };
int i; int i;
i = a.a += a.b + a.c; i = a.a -= a.b + a.c;
if (i != 1) { if (i != -3 || a.a != -3) {
++failures1; ++failures1;
} }
printf("i = %d, a.a = %d\n", i, a.a); printf("i = %d, a.a = %d\n", i, a.a);
i = a.b *= -1; a.b = i = a.b / -1;
if (i != 5 || a.b != 5) { if (i != -3 || a.b != 5) {
++failures1; ++failures1;
} }
printf("i = %d, a.b = %d\n", i, a.b); printf("i = %d, a.b = %d\n", i, a.b);
i = a.c * -1; i = a.c = 0;
if (i != -3) { if (i != 0 || a.c != 0) {
++failures1; ++failures1;
} }
printf("i = %d, a.c = %d\n", i, a.c); printf("i = %d, a.c = %d\n", i, a.c);
i = a.d ^= -1; i = a.d /= -1;
if (i != 4 || a.d != 4) { if (i != 5 || a.d != 5) {
++failures1; ++failures1;
} }
printf("i = %d, a.d = %d\n", i, a.d); printf("i = %d, a.d = %d\n", i, a.d);

85
test/val/bug1462-4.c Normal file
View File

@ -0,0 +1,85 @@
/* issue #1462 - Bit-fields are still broken */
/* More tests on "op= expression result value" that a naive fix might fail with */
#include <stdio.h>
#include <limits.h>
#define SMALL_WIDTH 4
#define LARGE_WIDTH (CHAR_BIT * sizeof (unsigned int))
typedef struct {
unsigned int a : SMALL_WIDTH;
unsigned int b : SMALL_WIDTH;
unsigned int c : SMALL_WIDTH;
unsigned int d : SMALL_WIDTH;
} T1;
typedef struct {
unsigned int a : LARGE_WIDTH;
unsigned int b : LARGE_WIDTH;
unsigned int c : LARGE_WIDTH;
unsigned int d : LARGE_WIDTH;
} T2;
int failures1 = 0;
int failures2 = 0;
void test1(void)
{
T1 a = { 0, 0, 0, 0 };
printf("\nunsigned int : %d\n", SMALL_WIDTH);
if (!(~a.a < 0)) {
++failures1;
}
printf("~a.a < 0 : %d\n", ~a.a < 0);
if (!(0 > ~a.b)) {
++failures1;
}
printf("0 > ~a.b : %d\n",0 > ~a.b);
if (!(a.c > -1)) {
++failures1;
}
printf("a.c > -1 : %d\n", a.c > -1);
if (!(-1 < a.d)) {
++failures1;
}
printf("-1 < a.d : %d\n", -1 < a.d);
printf("Failures: %d\n", failures1);
}
void test2(void)
{
T1 b = { 0, 0, 0, 0 };
printf("\nunsigned int : %d\n", LARGE_WIDTH);
if (!(~b.a < 0)) {
++failures2;
}
printf("~b.a < 0 : %d\n", ~b.a < 0);
if (!(0 > ~b.b)) {
++failures2;
}
printf("0 > ~b.b : %d\n", 0 > ~b.b);
if (!(b.c > -1)) {
++failures2;
}
printf("b.c > -1 : %d\n", b.c > -1);
if (!(-1 < b.d)) {
++failures2;
}
printf("-1 < b.d : %d\n", -1 < b.d);
printf("Failures: %d\n", failures2);
}
int main(void)
{
test1();
test2();
return failures1 + failures2;
}

View File

@ -23,6 +23,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <limits.h>
static unsigned char failures = 0; static unsigned char failures = 0;
@ -35,7 +36,7 @@ enum e10u {
static struct enum_bitfield_uint { static struct enum_bitfield_uint {
enum e10u x : 1; enum e10u x : 1;
enum e10u y : 8; enum e10u y : 8;
enum e10u z : 16; enum e10u z : CHAR_BIT * sizeof (enum e10u);
} e10ubf = {0, E10U_200, E10U_1000}; } e10ubf = {0, E10U_200, E10U_1000};
static void test_enum_bitfield_uint(void) static void test_enum_bitfield_uint(void)
@ -68,11 +69,11 @@ static void test_enum_bitfield_uint(void)
failures++; failures++;
} }
/* Check signedness, should be unsigned. */ /* Check signedness, should be signed. */
{ {
long v = e10ubf.x - 2; long v = e10ubf.x - 2;
if (v < 0) { if (v >= 0) {
printf ("Got negative v = %ld, expected large positive.\n", v); printf ("Got non-negative v (= e10ubf.x - 2) = %ld, expected negative.\n", v);
failures++; failures++;
} }
} }
@ -85,6 +86,15 @@ static void test_enum_bitfield_uint(void)
printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z); printf ("Got e10ubf.z = %u, expected 1023.\n", e10ubf.z);
failures++; failures++;
} }
/* Check signedness, should be unsigned. */
{
long v = e10ubf.z - 1024;
if (v < 0) {
printf ("Got negative v (= e10ubf.z - 1024) = %ld, expected positive.\n", v);
failures++;
}
}
} }
/* Enum with underlying type signed int. */ /* Enum with underlying type signed int. */
@ -97,7 +107,7 @@ enum e11i {
static struct enum_bitfield_int { static struct enum_bitfield_int {
enum e11i x : 2; enum e11i x : 2;
enum e11i y : 8; enum e11i y : 8;
enum e11i z : 16; enum e11i z : CHAR_BIT * sizeof (enum e11i);
} e11ibf = {E11I_M1, E11I_100, E11I_1000}; } e11ibf = {E11I_M1, E11I_100, E11I_1000};
static void test_enum_bitfield_int(void) static void test_enum_bitfield_int(void)
@ -133,8 +143,8 @@ static void test_enum_bitfield_int(void)
/* Check signedness, should be signed. */ /* Check signedness, should be signed. */
{ {
long v = e11ibf.x - 2; long v = e11ibf.x - 2;
if (v > 0) { if (v >= 0) {
printf ("Got positive v = %ld, expected negative.\n", v); printf ("Got non-negative v (= e11ibf.x - 2) = %ld, expected negative.\n", v);
failures++; failures++;
} }
} }
@ -147,6 +157,15 @@ static void test_enum_bitfield_int(void)
printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z); printf ("Got e11ibf.z = %d, expected 1023.\n", e11ibf.z);
failures++; failures++;
} }
/* Check signedness, should be signed. */
{
long v = e11ibf.z - 1024;
if (v >= 0) {
printf ("Got non-negative v (= e11ibf.z - 1024) = %ld, expected negative.\n", v);
failures++;
}
}
} }
/* Enum with underlying type unsigned char. */ /* Enum with underlying type unsigned char. */
@ -157,7 +176,7 @@ enum e7uc {
static struct enum_bitfield_uchar { static struct enum_bitfield_uchar {
enum e7uc x : 1; enum e7uc x : 1;
enum e7uc y : 4; enum e7uc y : 4;
enum e7uc z : 8; enum e7uc z : CHAR_BIT;
} e7ucbf = {0, 10, E7UC_100}; } e7ucbf = {0, 10, E7UC_100};
static void test_enum_bitfield_uchar(void) static void test_enum_bitfield_uchar(void)
@ -212,7 +231,7 @@ enum e8sc {
static struct enum_bitfield_char { static struct enum_bitfield_char {
enum e8sc x : 1; enum e8sc x : 1;
enum e8sc y : 4; enum e8sc y : 4;
enum e8sc z : 8; enum e8sc z : CHAR_BIT;
} e8scbf = {0, 5, E8SC_100}; } e8scbf = {0, 5, E8SC_100};
static void test_enum_bitfield_char(void) static void test_enum_bitfield_char(void)

33
test/val/opsize.c Normal file
View File

@ -0,0 +1,33 @@
/* Test for result types of certain unary operations */
#include <stdio.h>
signed char x;
struct S {
unsigned char a : 3;
unsigned int b : 3;
} s;
int main(void)
{
_Static_assert(sizeof (++x) == sizeof (char), "++x result should not have promoted type");
_Static_assert(sizeof (--x) == sizeof (char), "--x result should not have promoted type");
_Static_assert(sizeof (x++) == sizeof (char), "x++ result should not have promoted type");
_Static_assert(sizeof (x--) == sizeof (char), "x-- result should not have promoted type");
_Static_assert(sizeof (x=0) == sizeof (char), "x=0 result should not have promoted type");
_Static_assert(sizeof (+x) == sizeof (int), "+x result should have promoted type");
_Static_assert(sizeof (-x) == sizeof (int), "-x result should have promoted type");
_Static_assert(sizeof (~x) == sizeof (int), "~x result should have promoted type");
_Static_assert(sizeof (+s.a) == sizeof (int), "+s.a result should have promoted type");
_Static_assert(sizeof (-s.a) == sizeof (int), "-s.a result should have promoted type");
_Static_assert(sizeof (~s.a) == sizeof (int), "~s.a result should have promoted type");
_Static_assert(sizeof (+s.b) == sizeof (int), "+s.b result should have promoted type");
_Static_assert(sizeof (-s.b) == sizeof (int), "-s.b result should have promoted type");
_Static_assert(sizeof (~s.b) == sizeof (int), "~s.b result should have promoted type");
return 0;
}