commit
4c0ee3e0d8
69 changed files with 2026 additions and 1181 deletions
|
@ -63,7 +63,7 @@ PTR_READ_DEST := $2C ; Used for XFREAD and XWRITE only in TELEMON 3.
|
|||
|
||||
ADCLK := $40 ; Address for clock display
|
||||
TIMEUS := $42
|
||||
TIMEUD := $44
|
||||
TIMEUD := $44 ; Counter clock (1/10 of a second)
|
||||
|
||||
|
||||
HRSX := $46
|
||||
|
@ -277,6 +277,7 @@ XRECLK = $3C ; Reset clock
|
|||
XCLCL = $3D ; Close clock
|
||||
XWRCLK = $3E ; Displays clock in the adress in A & Y registers
|
||||
|
||||
; Sound primitives
|
||||
XSONPS = $40 ; Send data to PSG register (14 values)
|
||||
XOUPS = $42 ; Send Oups sound into PSG
|
||||
XPLAY = $43 ; Play a sound
|
||||
|
@ -284,10 +285,25 @@ XSOUND = $44
|
|||
XMUSIC = $45
|
||||
XZAP = $46 ; Send Zap sound to PSG
|
||||
XSHOOT = $47
|
||||
|
||||
; Path Management
|
||||
XGETCWD = $48 ; Get current CWD
|
||||
XPUTCWD = $49 ; Chdir
|
||||
|
||||
; File management
|
||||
XMKDIR = $4B ; Create a folder. Only available in TELEMON 3.x (bank 7 of Orix)
|
||||
|
||||
XHCHRS = $4C ; Hard copy hires
|
||||
|
||||
; File management
|
||||
XRM = $4D ; Remove a folder or a file. Only available in TELEMON 3.x (bank 7 of Orix)
|
||||
|
||||
XFWR = $4E ; Put a char on the first screen. Only available in TELEMON 3.x (bank 7 of Orix)
|
||||
XGOKBD = $52
|
||||
|
||||
; Keyboard primitives
|
||||
XALLKB = $50 ; Read Keyboard, and populate KBDCOL
|
||||
XKBDAS = $51 ; Ascii conversion
|
||||
XGOKBD = $52 ; Swap keyboard type (Qwerty, French ...)
|
||||
|
||||
; Buffer management
|
||||
XECRBU = $54 ; Write A or AY in the buffer
|
||||
|
@ -299,8 +315,27 @@ XDEFBU = $59 ; Reset all value of the buffer
|
|||
XBUSY = $5A ; Test if the buffer is empty
|
||||
|
||||
XMALLOC = $5B ; Only in TELEMON 3.x (bank 7 of Orix)
|
||||
|
||||
; RS232 primitives
|
||||
XSDUMP = $5C ; RS232 input dump
|
||||
XCONSO = $5D ; Swap screen into RS232 terminal
|
||||
XSLOAD = $5E ; Read a file from RS232
|
||||
XSSAVE = $5F ; Write a file to RS232
|
||||
|
||||
; Minitel primitives
|
||||
XMLOAD = $60 ; Read a file from Minitel
|
||||
XMSAVE = $61 ; Write a file to Minitel
|
||||
|
||||
XFREE = $62 ; Only in TELEMON 3.x (bank 7 of Orix)
|
||||
|
||||
; Next Minitel primitives
|
||||
XWCXFI = $63 ; Wait connection
|
||||
XLIGNE = $64 ;
|
||||
XDECON = $65 ; Minitel disconnection
|
||||
XMOUT = $66 ; Send a byte to minitel (from A)
|
||||
|
||||
XSOUT = $67 ; Send accumulator value (A) to RS232, available in TELEMON 2.4 & 3.x : if RS232 buffer is full, the Oric Telestrat freezes
|
||||
|
||||
XHRSSE = $8C ; Set hires position cursor
|
||||
XDRAWA = $8D ; Draw a line absolute
|
||||
XDRAWR = $8E ; Draw a line (relative)
|
||||
|
@ -337,7 +372,7 @@ FLGTEL := $20D
|
|||
KOROM := $20E ; Used to compute the size of all rom bank. The result is store here. The value is in KB
|
||||
KORAM := $20F ; Used to compute the size of all ram bank. The result is store here. The value is in KB
|
||||
; Time management
|
||||
TIMED := $210
|
||||
TIMED := $210 ; Clock (1/10 of seconds)
|
||||
TIMES := $211
|
||||
TIMEM := $212
|
||||
TIMEH := $213
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
;
|
||||
; Vic20 generic definitions. Stolen mostly from c64.inc - Steve Schmidtke
|
||||
; VIC-20 generic definitions. Stolen mostly from c64.inc -- Steve Schmidtke
|
||||
;
|
||||
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Zero page, Commodore stuff
|
||||
|
||||
|
@ -36,6 +35,8 @@ KBDREPEAT := $28a
|
|||
KBDREPEATRATE := $28b
|
||||
KBDREPEATDELAY := $28c
|
||||
|
||||
RSSTAT := $297 ; RS-232 device driver status
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Screen size
|
||||
|
||||
|
|
|
@ -1170,7 +1170,7 @@ When using cl65, you can leave it out with this command line:
|
|||
cl65 -Wl -D__SYSTEM_CHECK__=1 <arguments>
|
||||
</verb></tscreen>
|
||||
|
||||
The value you assign to <tt/__SYSTEM_CHECK_/ doesn't matter. If the
|
||||
The value you assign to <tt/__SYSTEM_CHECK__/ doesn't matter. If the
|
||||
<tt/__SYSTEM_CHECK__/ symbol is defined, the load chunk won't be included.
|
||||
|
||||
|
||||
|
|
|
@ -142,13 +142,20 @@ The functions listed below are special for the CX16. See the <url
|
|||
url="funcref.html" name="function reference"> for declarations and usage.
|
||||
|
||||
<itemize>
|
||||
<item>get_ostype
|
||||
<item>set_tv
|
||||
<item>videomode
|
||||
<item>vpeek
|
||||
<item>vpoke
|
||||
<item>get_ostype()
|
||||
<item>set_tv()
|
||||
<item>videomode()
|
||||
<item>vpeek()
|
||||
<item>vpoke()
|
||||
</itemize>
|
||||
|
||||
<tt/cpeekcolor()/ works differently on the Commander X16 than it does on other
|
||||
platforms. Each character has two colors: background and text (foreground).
|
||||
<tt/cpeekcolor()/ returns both colors. The high nybble describes the
|
||||
background color, the low nybble describes the text color. For example, if the
|
||||
function is used on the default screen, then it returns $61, which means
|
||||
white-on-blue.
|
||||
|
||||
|
||||
<sect1>CBM-specific functions<p>
|
||||
|
||||
|
@ -157,32 +164,32 @@ machines. See the <url url="funcref.html" name="function reference"> for
|
|||
declarations and usage.
|
||||
|
||||
<itemize>
|
||||
<item>cbm_close
|
||||
<item>cbm_closedir
|
||||
<item>cbm_k_basin
|
||||
<item>cbm_k_bsout
|
||||
<item>cbm_k_chkin
|
||||
<item>cbm_k_ckout
|
||||
<item>cbm_k_close
|
||||
<item>cbm_k_clrch
|
||||
<item>cbm_k_getin
|
||||
<item>cbm_k_load
|
||||
<item>cbm_k_open
|
||||
<item>cbm_k_readst
|
||||
<item>cbm_k_save
|
||||
<item>cbm_k_second
|
||||
<item>cbm_k_setlfs
|
||||
<item>cbm_k_setnam
|
||||
<item>cbm_k_tksa
|
||||
<item>cbm_load
|
||||
<item>cbm_open
|
||||
<item>cbm_opendir
|
||||
<item>cbm_read
|
||||
<item>cbm_readdir
|
||||
<item>cbm_save
|
||||
<item>cbm_write
|
||||
<item>get_tv
|
||||
<item>waitvsync
|
||||
<item>cbm_close()
|
||||
<item>cbm_closedir()
|
||||
<item>cbm_k_basin()
|
||||
<item>cbm_k_bsout()
|
||||
<item>cbm_k_chkin()
|
||||
<item>cbm_k_ckout()
|
||||
<item>cbm_k_close()
|
||||
<item>cbm_k_clrch()
|
||||
<item>cbm_k_getin()
|
||||
<item>cbm_k_load()
|
||||
<item>cbm_k_open()
|
||||
<item>cbm_k_readst()
|
||||
<item>cbm_k_save()
|
||||
<item>cbm_k_second()
|
||||
<item>cbm_k_setlfs()
|
||||
<item>cbm_k_setnam()
|
||||
<item>cbm_k_tksa()
|
||||
<item>cbm_load()
|
||||
<item>cbm_open()
|
||||
<item>cbm_opendir()
|
||||
<item>cbm_read()
|
||||
<item>cbm_readdir()
|
||||
<item>cbm_save()
|
||||
<item>cbm_write()
|
||||
<item>get_tv()
|
||||
<item>waitvsync()
|
||||
</itemize>
|
||||
|
||||
|
||||
|
|
|
@ -2813,6 +2813,8 @@ location of the cursor in the display screen RAM. That number can be passed to
|
|||
done to make it obvious that peeking doesn't move the cursor in any way. Your
|
||||
program must place the cursor where it wants to peek before it calls any of
|
||||
those functions.
|
||||
<item>On the cx16 (Commander X16) target, this function returns two values: the
|
||||
background color, in the high nybble, and the text color, in the low nybble.
|
||||
</itemize>
|
||||
<tag/Availability/cc65
|
||||
<tag/See also/
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
/* from ASCII to screen-code mapping, so you can write directly */
|
||||
/* to the screen memory. */
|
||||
/* */
|
||||
/* If this include is used, no additional macroes are needed. */
|
||||
/* If this include is used, no additional macros are needed. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@
|
|||
#define CH_LTEE '+'
|
||||
#define CH_RTEE '+'
|
||||
#define CH_CROSS '+'
|
||||
#define CH_HLINE '-'
|
||||
#define CH_VLINE '|'
|
||||
#define CH_CURS_UP 11
|
||||
#define CH_CURS_DOWN 10
|
||||
#define CH_CURS_LEFT 8
|
||||
|
@ -98,8 +100,8 @@
|
|||
/* Masks for joy_read */
|
||||
#define JOY_UP_MASK 0x10
|
||||
#define JOY_DOWN_MASK 0x08
|
||||
#define JOY_LEFT_MASK 0x01
|
||||
#define JOY_RIGHT_MASK 0x02
|
||||
#define JOY_LEFT_MASK 0x02
|
||||
#define JOY_RIGHT_MASK 0x01
|
||||
#define JOY_BTN_1_MASK 0x04
|
||||
|
||||
#define JOY_FIRE_MASK JOY_BTN_1_MASK
|
||||
|
@ -119,3 +121,9 @@ void shoot();
|
|||
void explode();
|
||||
|
||||
void kbdclick1();
|
||||
|
||||
/* The following #defines will cause the matching functions calls in conio.h
|
||||
** to be overlaid by macros with the same names, saving the function call
|
||||
** overhead.
|
||||
*/
|
||||
#define _bordercolor(color) COLOR_BLACK
|
||||
|
|
|
@ -114,7 +114,8 @@ int __fastcall__ uncompress (unsigned char* dest, unsigned* destLen,
|
|||
*/
|
||||
|
||||
|
||||
unsigned long __fastcall__ adler32 (unsigned long adler, const char* buf,
|
||||
unsigned long __fastcall__ adler32 (unsigned long adler,
|
||||
const unsigned char* buf,
|
||||
unsigned len);
|
||||
|
||||
/*
|
||||
|
@ -140,7 +141,8 @@ unsigned long __fastcall__ adler32 (unsigned long adler, const char* buf,
|
|||
*/
|
||||
|
||||
|
||||
unsigned long __fastcall__ crc32 (unsigned long crc, const char* buf,
|
||||
unsigned long __fastcall__ crc32 (unsigned long crc,
|
||||
const unsigned char* buf,
|
||||
unsigned len);
|
||||
/*
|
||||
Original zlib description:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
.export __STARTUP__ : absolute = 1 ; Mark as startup
|
||||
.export _exit, start, excexit, SP_save
|
||||
.export __LMARGN_save ; original LMARGN setting
|
||||
|
||||
.import initlib, donelib
|
||||
.import callmain, zerobss
|
||||
|
@ -82,14 +83,10 @@ start:
|
|||
|
||||
.endif
|
||||
|
||||
; Call the module constructors.
|
||||
|
||||
jsr initlib
|
||||
|
||||
; Set the left margin to 0.
|
||||
|
||||
lda LMARGN
|
||||
sta LMARGN_save
|
||||
sta __LMARGN_save
|
||||
ldy #0
|
||||
sty LMARGN
|
||||
|
||||
|
@ -104,6 +101,10 @@ start:
|
|||
dey ; Set Y to $FF
|
||||
sty CH ; remove keypress which might be in the input buffer
|
||||
|
||||
; Call the module constructors.
|
||||
|
||||
jsr initlib
|
||||
|
||||
; Push the command-line arguments; and, call main().
|
||||
|
||||
jsr callmain
|
||||
|
@ -119,7 +120,7 @@ excexit:jsr donelib ; Run module destructors; 'excexit' is called fr
|
|||
|
||||
; Restore the left margin.
|
||||
|
||||
lda LMARGN_save
|
||||
lda __LMARGN_save
|
||||
sta LMARGN
|
||||
|
||||
; Restore the kb mode.
|
||||
|
@ -196,7 +197,7 @@ excexit:jsr donelib ; Run module destructors; 'excexit' is called fr
|
|||
|
||||
SP_save: .res 1
|
||||
SHFLOK_save: .res 1
|
||||
LMARGN_save: .res 1
|
||||
__LMARGN_save: .res 1
|
||||
.ifndef __ATARIXL__
|
||||
APPMHI_save: .res 2
|
||||
.endif
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export SETTIM
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export SETTIM
|
||||
|
|
|
@ -38,7 +38,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export SETTIM
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 03.06.1999
|
||||
; 1999-06-03, Ullrich von Bassewitz
|
||||
; 2021-01-12, Greg King
|
||||
;
|
||||
; unsigned char cbm_k_readst (void);
|
||||
;
|
||||
|
@ -10,6 +11,5 @@
|
|||
|
||||
|
||||
_cbm_k_readst:
|
||||
jsr READST
|
||||
ldx #0 ; Clear high byte
|
||||
rts
|
||||
ldx #>$0000
|
||||
jmp READST
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export STOP
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export STOP
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 19.11.2002
|
||||
;
|
||||
; BSOUT replacement function for the PETs
|
||||
; BSOUT/CHROUT replacement function for the PETs
|
||||
;
|
||||
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
|
||||
.import checkst
|
||||
|
||||
|
||||
.proc BSOUT
|
||||
|
||||
jsr $FFD2 ; Call kernal function
|
||||
jsr $FFD2 ; Call Kernal function
|
||||
jmp checkst ; Check status, return carry on error
|
||||
|
||||
.endproc
|
||||
|
||||
CHROUT := BSOUT
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export STOP
|
||||
.export GETIN
|
||||
.export CLALL
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 22.11.2002
|
||||
;
|
||||
; BASIN replacement function
|
||||
; BASIN/CHRIN replacement function
|
||||
;
|
||||
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
|
||||
.include "plus4.inc"
|
||||
|
||||
|
@ -17,4 +18,4 @@
|
|||
rts ; Return to caller
|
||||
.endproc
|
||||
|
||||
|
||||
CHRIN := BASIN
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 22.11.2002
|
||||
;
|
||||
; BSOUT replacement function
|
||||
; BSOUT/CHROUT replacement function
|
||||
;
|
||||
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
|
||||
.include "plus4.inc"
|
||||
|
||||
|
@ -17,4 +18,4 @@
|
|||
rts ; Return to caller
|
||||
.endproc
|
||||
|
||||
|
||||
CHROUT := BSOUT
|
||||
|
|
14
libsrc/telestrat/bordercolor.s
Normal file
14
libsrc/telestrat/bordercolor.s
Normal file
|
@ -0,0 +1,14 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 06.08.1998
|
||||
;
|
||||
; unsigned char __fastcall__ bordercolor (unsigned char color);
|
||||
;
|
||||
|
||||
.export _bordercolor
|
||||
|
||||
.import return0
|
||||
|
||||
.include "telestrat.inc"
|
||||
|
||||
_bordercolor := return0
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
;
|
||||
|
||||
.export _cclearxy, _cclear
|
||||
.import update_adscr
|
||||
.import update_adscr, display_conio
|
||||
|
||||
.importzp tmp1
|
||||
.import popax
|
||||
|
@ -27,7 +27,7 @@ _cclear:
|
|||
@L1:
|
||||
stx tmp1 ; Save X
|
||||
lda #' ' ; Erase current char
|
||||
BRK_TELEMON XFWR
|
||||
jsr display_conio
|
||||
ldx tmp1
|
||||
dex
|
||||
bne @L1
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
;
|
||||
; jede jede@oric.org 2018-04-17
|
||||
;
|
||||
|
||||
; void chlinexy (unsigned char x, unsigned char y, unsigned char length);
|
||||
; void chline (unsigned char length);
|
||||
;
|
||||
|
||||
.export _chline
|
||||
.export _chlinexy, _chline
|
||||
|
||||
.import rvs, display_conio, update_adscr
|
||||
.import popax
|
||||
|
||||
.include "telestrat.inc"
|
||||
.include "zeropage.inc"
|
||||
|
||||
|
||||
.proc _chline
|
||||
sta tmp1
|
||||
@loop:
|
||||
lda #'-' ; horizontal line screen code
|
||||
BRK_TELEMON XWR0 ; macro send char to screen (channel 0 in telemon terms)
|
||||
dec tmp1
|
||||
bne @loop
|
||||
rts
|
||||
.endproc
|
||||
_chlinexy:
|
||||
pha ; Save the length
|
||||
jsr popax ; Get X and Y
|
||||
sta SCRY ; Store Y
|
||||
stx SCRX ; Store X
|
||||
jsr update_adscr
|
||||
pla ; Restore the length and run into _chline
|
||||
|
||||
_chline:
|
||||
tax ; Is the length zero?
|
||||
beq @L9 ; Jump if done
|
||||
@L1:
|
||||
lda #'-' ; Horizontal line screen code
|
||||
ora rvs
|
||||
|
||||
jsr display_conio
|
||||
|
||||
@L2: dex
|
||||
bne @L1
|
||||
@L9: rts
|
||||
|
||||
|
|
28
libsrc/telestrat/clock.s
Normal file
28
libsrc/telestrat/clock.s
Normal file
|
@ -0,0 +1,28 @@
|
|||
;
|
||||
; Jede, 2021-03-10
|
||||
;
|
||||
; clock_t clock (void);
|
||||
;
|
||||
|
||||
.export _clock
|
||||
.importzp sreg
|
||||
|
||||
.include "telestrat.inc"
|
||||
|
||||
.proc _clock
|
||||
|
||||
; Clear the timer high 16 bits
|
||||
|
||||
ldy #$00
|
||||
sty sreg
|
||||
sty sreg+1
|
||||
|
||||
; Read the timer
|
||||
|
||||
sei ; Disable interrupts
|
||||
lda TIMEUD ; TIMED contains 1/10 of a second from clock. Telestrat main cardridge simulate a clock from VIA6522 timer
|
||||
ldx TIMEUD+1
|
||||
cli ; Reenable interrupts
|
||||
|
||||
rts
|
||||
.endproc
|
|
@ -3,7 +3,7 @@
|
|||
;
|
||||
|
||||
.export _clrscr
|
||||
.import OLD_CHARCOLOR, OLD_BGCOLOR
|
||||
.import OLD_CHARCOLOR, OLD_BGCOLOR, BGCOLOR, CHARCOLOR
|
||||
|
||||
.include "telestrat.inc"
|
||||
|
||||
|
@ -23,22 +23,25 @@
|
|||
|
||||
|
||||
; reset prompt position
|
||||
lda #<(SCREEN+40)
|
||||
sta ADSCRL
|
||||
lda #>(SCREEN+40)
|
||||
sta ADSCRH
|
||||
lda #<SCREEN
|
||||
sta ADSCR
|
||||
lda #>SCREEN
|
||||
sta ADSCR+1
|
||||
|
||||
lda #$00
|
||||
sta SCRDY
|
||||
|
||||
; reset display position
|
||||
ldx #$01
|
||||
ldx #$00
|
||||
stx SCRY
|
||||
dex
|
||||
stx SCRX
|
||||
|
||||
; At this step X is equal to $00
|
||||
dex
|
||||
; At this step X is equal to $FF
|
||||
stx OLD_BGCOLOR
|
||||
stx OLD_BGCOLOR ; Black
|
||||
stx BGCOLOR
|
||||
|
||||
ldx #$07 ; White
|
||||
stx OLD_CHARCOLOR
|
||||
stx CHARCOLOR
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
|
|
@ -4,26 +4,50 @@
|
|||
; void cputc (char c);
|
||||
;
|
||||
|
||||
.export _cputc, cputdirect
|
||||
.export _cputc, _cputcxy, cputdirect, display_conio
|
||||
.export CHARCOLOR, OLD_CHARCOLOR, BGCOLOR, OLD_BGCOLOR
|
||||
|
||||
.import update_adscr
|
||||
.import popax
|
||||
|
||||
.include "telestrat.inc"
|
||||
|
||||
|
||||
_cputcxy:
|
||||
pha ; Save C
|
||||
jsr popax ; Get X and Y
|
||||
sta SCRY ; Store Y
|
||||
stx SCRX ; Store X
|
||||
jsr update_adscr
|
||||
pla
|
||||
|
||||
_cputc:
|
||||
cmp #$0D
|
||||
bne @not_CR
|
||||
ldy #$00
|
||||
sty SCRX
|
||||
rts
|
||||
@not_CR:
|
||||
cmp #$0A
|
||||
bne not_LF
|
||||
|
||||
inc SCRY
|
||||
jmp update_adscr
|
||||
|
||||
cputdirect:
|
||||
.proc _cputc
|
||||
not_LF:
|
||||
ldx CHARCOLOR
|
||||
cpx OLD_CHARCOLOR
|
||||
beq do_not_change_color_foreground
|
||||
|
||||
stx OLD_CHARCOLOR ; Store CHARCOLOR into OLD_CHARCOLOR
|
||||
|
||||
dec SCRX
|
||||
dec SCRX
|
||||
|
||||
pha
|
||||
txa ; Swap X to A because, X contains CHARCOLOR
|
||||
BRK_TELEMON XFWR ; Change color on the screen (foreground)
|
||||
inc SCRX
|
||||
|
||||
jsr display_conio
|
||||
|
||||
pla
|
||||
|
||||
do_not_change_color_foreground:
|
||||
|
@ -33,18 +57,37 @@ do_not_change_color_foreground:
|
|||
|
||||
stx OLD_BGCOLOR
|
||||
|
||||
dec SCRX ; Dec SCRX in order to place attribute before the right position
|
||||
|
||||
pha
|
||||
txa ; Swap X to A because, X contains BGCOLOR
|
||||
ORA #%00010000 ; Add 16 because background color is an attribute between 16 and 23. 17 is red background for example
|
||||
BRK_TELEMON XFWR ; Change color on the screen (background)
|
||||
ora #%00010000 ; Add 16 because background color is an attribute between 16 and 23. 17 is red background for example
|
||||
|
||||
jsr display_conio
|
||||
pla
|
||||
|
||||
do_not_change_color:
|
||||
BRK_TELEMON XFWR ; Macro send char to screen (channel 0)
|
||||
; it continues to display_conio
|
||||
|
||||
|
||||
|
||||
.proc display_conio
|
||||
; This routine is used to displays char on screen
|
||||
ldy SCRX
|
||||
sta (ADSCR),y
|
||||
iny
|
||||
cpy #SCREEN_XSIZE
|
||||
bne @no_inc
|
||||
ldy #$00
|
||||
sty SCRX
|
||||
|
||||
inc SCRY
|
||||
|
||||
jmp update_adscr
|
||||
|
||||
@no_inc:
|
||||
sty SCRX
|
||||
rts
|
||||
.endproc
|
||||
|
||||
.bss
|
||||
CHARCOLOR:
|
||||
.res 1
|
||||
|
@ -54,3 +97,4 @@ BGCOLOR:
|
|||
.res 1
|
||||
OLD_BGCOLOR:
|
||||
.res 1
|
||||
|
||||
|
|
40
libsrc/telestrat/cvline.s
Normal file
40
libsrc/telestrat/cvline.s
Normal file
|
@ -0,0 +1,40 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 2003-04-13
|
||||
;
|
||||
; void cvlinexy (unsigned char x, unsigned char y, unsigned char length);
|
||||
; void cvline (unsigned char length);
|
||||
;
|
||||
|
||||
.export _cvlinexy, _cvline
|
||||
|
||||
.import rvs, display_conio, update_adscr
|
||||
|
||||
.import popax
|
||||
|
||||
.include "telestrat.inc"
|
||||
|
||||
|
||||
_cvlinexy:
|
||||
pha ; Save the length
|
||||
jsr popax ; Get X and Y
|
||||
sta SCRY ; Store Y
|
||||
stx SCRX ; Store X
|
||||
jsr update_adscr
|
||||
pla ; Restore the length and run into _cvline
|
||||
|
||||
_cvline:
|
||||
tax ; Is the length zero?
|
||||
beq @L9 ; Jump if done
|
||||
@L1:
|
||||
lda #'|'
|
||||
ora rvs
|
||||
ldy SCRX
|
||||
sta (ADSCR),y
|
||||
; compute next line
|
||||
inc SCRY
|
||||
jsr update_adscr
|
||||
@L2: dex
|
||||
bne @L1
|
||||
@L9: rts
|
||||
|
||||
|
|
@ -8,9 +8,5 @@
|
|||
|
||||
.proc _gotox
|
||||
sta SCRX
|
||||
|
||||
lda #$FF
|
||||
sta OLD_CHARCOLOR
|
||||
sta OLD_BGCOLOR
|
||||
rts
|
||||
.endproc
|
||||
|
|
|
@ -29,30 +29,23 @@ gotoxy: jsr popa ; Get Y
|
|||
.endproc
|
||||
|
||||
.proc update_adscr
|
||||
; Force to set again color if cursor moves
|
||||
; $FF is used because we know that it's impossible to have this value with a color
|
||||
; It prevents a bug : If bgcolor or textcolor is set to black for example with no char displays,
|
||||
; next cputsxy will not set the attribute if y coordinate changes
|
||||
lda #$FF
|
||||
sta OLD_CHARCOLOR
|
||||
sta OLD_BGCOLOR
|
||||
|
||||
lda #<SCREEN
|
||||
sta ADSCRL
|
||||
sta ADSCR
|
||||
|
||||
lda #>SCREEN
|
||||
sta ADSCRH
|
||||
sta ADSCR+1
|
||||
|
||||
ldy SCRY
|
||||
beq out
|
||||
loop:
|
||||
lda ADSCRL
|
||||
lda ADSCR
|
||||
clc
|
||||
adc #SCREEN_XSIZE
|
||||
bcc skip
|
||||
inc ADSCRH
|
||||
inc ADSCR+1
|
||||
skip:
|
||||
sta ADSCRL
|
||||
sta ADSCR
|
||||
dey
|
||||
bne loop
|
||||
out:
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
|
||||
|
||||
initcwd:
|
||||
ldx #PWD_PTR
|
||||
BRK_TELEMON XVARS
|
||||
BRK_TELEMON(XGETCWD)
|
||||
|
||||
sta ptr1
|
||||
sty ptr1+1
|
||||
|
|
|
@ -12,21 +12,21 @@
|
|||
;int read (int fd, void* buf, unsigned count);
|
||||
|
||||
.proc _read
|
||||
sta ptr1 ; count
|
||||
stx ptr1+1 ; count
|
||||
jsr popax ; get buf
|
||||
|
||||
sta ptr1 ; Count
|
||||
stx ptr1+1 ; Count
|
||||
jsr popax ; Get buf
|
||||
|
||||
sta PTR_READ_DEST
|
||||
stx PTR_READ_DEST+1
|
||||
sta ptr2 ; in order to calculate nb of bytes read
|
||||
sta ptr2 ; In order to calculate nb of bytes read
|
||||
stx ptr2+1 ;
|
||||
|
||||
; jsr popax ; fp pointer don't care in this version
|
||||
|
||||
lda ptr1 ;
|
||||
ldy ptr1+1 ;
|
||||
BRK_TELEMON XFREAD ; calls telemon30 routine
|
||||
; compute nb of bytes read
|
||||
; Compute nb of bytes read
|
||||
lda PTR_READ_DEST+1
|
||||
sec
|
||||
sbc ptr2+1
|
||||
|
|
37
libsrc/telestrat/revers.s
Normal file
37
libsrc/telestrat/revers.s
Normal file
|
@ -0,0 +1,37 @@
|
|||
;
|
||||
; Ullrich von Bassewitz, 07.08.1998
|
||||
;
|
||||
; unsigned char revers (unsigned char onoff);
|
||||
;
|
||||
|
||||
.export _revers
|
||||
.export rvs
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
;
|
||||
|
||||
.code
|
||||
.proc _revers
|
||||
|
||||
ldx #$00 ; Assume revers off
|
||||
tay ; Test onoff
|
||||
beq L1 ; Jump if off
|
||||
ldx #$80 ; Load on value
|
||||
ldy #$00 ; Assume old value is zero
|
||||
L1: lda rvs ; Load old value
|
||||
stx rvs ; Set new value
|
||||
beq L2 ; Jump if old value zero
|
||||
iny ; Make old value = 1
|
||||
L2: ldx #$00 ; Load high byte of result
|
||||
tya ; Load low byte, set CC
|
||||
rts
|
||||
|
||||
.endproc
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
;
|
||||
|
||||
.bss
|
||||
rvs: .res 1
|
||||
|
||||
|
32
libsrc/telestrat/syschdir.s
Normal file
32
libsrc/telestrat/syschdir.s
Normal file
|
@ -0,0 +1,32 @@
|
|||
;
|
||||
; Jede (jede@oric.org), 2021-02-22
|
||||
;
|
||||
; unsigned char _syschdir (const char* name, ...);
|
||||
;
|
||||
|
||||
.export __syschdir
|
||||
.import addysp, popax
|
||||
.importzp tmp1
|
||||
.import initcwd
|
||||
|
||||
.include "telestrat.inc"
|
||||
.include "zeropage.inc"
|
||||
|
||||
|
||||
__syschdir:
|
||||
; Throw away all parameters except the name
|
||||
dey
|
||||
dey
|
||||
jsr addysp
|
||||
|
||||
; Get name
|
||||
jsr popax
|
||||
|
||||
stx tmp1
|
||||
ldy tmp1
|
||||
|
||||
; Call telemon primitive
|
||||
|
||||
BRK_TELEMON(XPUTCWD)
|
||||
|
||||
jmp initcwd ; Update cwd
|
|
@ -2,12 +2,13 @@
|
|||
;
|
||||
|
||||
.export _textcolor
|
||||
.import CHARCOLOR
|
||||
.import CHARCOLOR, OLD_CHARCOLOR
|
||||
.include "telestrat.inc"
|
||||
|
||||
.proc _textcolor
|
||||
ldx CHARCOLOR ; Get previous color
|
||||
sta CHARCOLOR
|
||||
stx OLD_CHARCOLOR
|
||||
txa ; Return previous color
|
||||
rts
|
||||
.endproc
|
||||
|
|
28
libsrc/vic20/c_readst.s
Normal file
28
libsrc/vic20/c_readst.s
Normal file
|
@ -0,0 +1,28 @@
|
|||
;
|
||||
; 1999-06-03, Ullrich von Bassewitz
|
||||
; 2021-01-12, Greg King
|
||||
;
|
||||
; unsigned char cbm_k_readst (void);
|
||||
;
|
||||
; This version works around a bug in VIC-20 Kernal's READST function.
|
||||
;
|
||||
|
||||
.include "vic20.inc"
|
||||
.include "../cbm/cbm.inc"
|
||||
|
||||
.export _cbm_k_readst
|
||||
|
||||
|
||||
_cbm_k_readst:
|
||||
ldx #>$0000
|
||||
lda DEVNUM
|
||||
cmp #CBMDEV_RS232
|
||||
beq @L1
|
||||
|
||||
jmp READST
|
||||
|
||||
; Work-around: Read the RS-232 status variable directly.
|
||||
|
||||
@L1: lda RSSTAT
|
||||
stx RSSTAT ; reset the status bits
|
||||
rts
|
|
@ -36,7 +36,9 @@
|
|||
.export CKOUT
|
||||
.export CLRCH
|
||||
.export BASIN
|
||||
.export CHRIN
|
||||
.export BSOUT
|
||||
.export CHROUT
|
||||
.export LOAD
|
||||
.export SAVE
|
||||
.export SETTIM
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
; 2001-11-18, Piotr Fusik
|
||||
; 2018-05-20, Christian Kruger
|
||||
;
|
||||
; unsigned long __fastcall__ adler32 (unsigned long adler, unsigned char* buf,
|
||||
; unsigned long __fastcall__ adler32 (unsigned long adler,
|
||||
; const unsigned char* buf,
|
||||
; unsigned len);
|
||||
;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
; 2001-11-14, Piotr Fusik
|
||||
; 2018-05-20, Christian Kruger
|
||||
;
|
||||
; unsigned long __fastcall__ crc32 (unsigned long crc, unsigned char* buf,
|
||||
; unsigned long __fastcall__ crc32 (unsigned long crc,
|
||||
; const unsigned char* buf,
|
||||
; unsigned len);
|
||||
;
|
||||
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#if defined(_WIN32)
|
||||
# include <process.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* common */
|
||||
#include "cmdline.h"
|
||||
|
@ -249,9 +255,8 @@ void LibOpen (const char* Name, int MustExist, int NeedTemp)
|
|||
if (NeedTemp) {
|
||||
|
||||
/* Create the temporary library name */
|
||||
NewLibName = xmalloc (strlen (Name) + strlen (".temp") + 1);
|
||||
strcpy (NewLibName, Name);
|
||||
strcat (NewLibName, ".temp");
|
||||
NewLibName = xmalloc (strlen (Name) + (sizeof (".temp-") - 1) + 2 * sizeof (unsigned int) + 1);
|
||||
sprintf (NewLibName, "%s.temp-%X", Name, (unsigned int)getpid ());
|
||||
|
||||
/* Create the temporary library */
|
||||
NewLib = fopen (NewLibName, "w+b");
|
||||
|
|
|
@ -78,7 +78,7 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr)
|
|||
hie1 (RExpr);
|
||||
|
||||
/* Check for equality of the structs/unions */
|
||||
if (TypeCmp (ltype, RExpr->Type) < TC_STRICT_COMPATIBLE) {
|
||||
if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) {
|
||||
TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1,
|
||||
"Incompatible types in assignment to '%s' from '%s'");
|
||||
}
|
||||
|
|
|
@ -2112,6 +2112,14 @@ void g_addaddr_local (unsigned flags attribute ((unused)), int offs)
|
|||
|
||||
/* Add the offset */
|
||||
offs -= StackPtr;
|
||||
if (IS_Get (&CodeSizeFactor) <= 100) {
|
||||
if (offs != 0) {
|
||||
/* We cannot address more then 256 bytes of locals anyway */
|
||||
g_inc (CF_INT | CF_CONST, offs);
|
||||
}
|
||||
/* Add the current stackpointer value */
|
||||
AddCodeLine ("jsr leaaxsp");
|
||||
} else {
|
||||
if (offs != 0) {
|
||||
/* We cannot address more then 256 bytes of locals anyway */
|
||||
L = GetLocalLabel();
|
||||
|
@ -2135,6 +2143,7 @@ void g_addaddr_local (unsigned flags attribute ((unused)), int offs)
|
|||
AddCodeLine ("adc sp+1");
|
||||
AddCodeLine ("tax");
|
||||
AddCodeLine ("tya");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -493,17 +493,14 @@ fncls_t GetFuncInfo (const char* Name, unsigned int* Use, unsigned int* Chg)
|
|||
** registers.
|
||||
*/
|
||||
*Use = REG_EAXY;
|
||||
} else if ((D->ParamCount > 0 ||
|
||||
(D->Flags & FD_EMPTY) != 0) &&
|
||||
(AutoCDecl ?
|
||||
IsQualFastcall (E->Type) :
|
||||
!IsQualCDecl (E->Type))) {
|
||||
} else if ((D->ParamCount > 0 || (D->Flags & FD_EMPTY) != 0) &&
|
||||
IsFastcallFunc (E->Type)) {
|
||||
/* Will use registers depending on the last param. If the last
|
||||
** param has incomplete type, or if the function has not been
|
||||
** prototyped yet, just assume __EAX__.
|
||||
*/
|
||||
if (D->LastParam != 0) {
|
||||
switch (SizeOf(D->LastParam->Type)) {
|
||||
switch (SizeOf (D->LastParam->Type)) {
|
||||
case 1u:
|
||||
*Use = REG_A;
|
||||
break;
|
||||
|
|
|
@ -2614,7 +2614,7 @@ static int LoadAAt (CodeSeg* S, int Idx, const LoadRegInfo* LRI, Collection* Ind
|
|||
if (Use == REG_X) {
|
||||
X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, E->LI);
|
||||
CS_InsertEntry (S, X, Idx++);
|
||||
} else if (Use == REG_A) {
|
||||
} else if (Use == REG_Y) {
|
||||
X = NewCodeEntry (OP65_TYA, AM65_IMP, 0, 0, E->LI);
|
||||
CS_InsertEntry (S, X, Idx++);
|
||||
} else if (Use == REG_A) {
|
||||
|
@ -3038,13 +3038,13 @@ int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E,
|
|||
/* Find the last index where the arg of E might be used or changed in the range (First, Last).
|
||||
** ReloadY indicates whether Y is supposed to be reloaded.
|
||||
** The code block in the range must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
{
|
||||
LoadRegInfo LRI;
|
||||
CodeEntry* X;
|
||||
unsigned CheckedFlags = LI_SRC_USE | LI_SRC_CHG;
|
||||
int Found = -1;
|
||||
int Found = First;
|
||||
|
||||
CHECK (Last <= (int)CollCount (&S->Entries));
|
||||
|
||||
|
@ -3066,8 +3066,8 @@ int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E,
|
|||
|
||||
/* TODO: We don't currently check for all cases */
|
||||
if ((LRI.Flags & (LI_DIRECT | LI_CHECK_ARG | LI_CHECK_Y | LI_RELOAD_Y)) == 0) {
|
||||
/* Just bail out as if the src would change right away */
|
||||
return 0;
|
||||
/* Just bail out as if the src would change everywhere */
|
||||
return First < Last ? Last - 1 : First;
|
||||
}
|
||||
|
||||
if ((LRI.Flags & LI_CHECK_Y) != 0) {
|
||||
|
@ -3145,11 +3145,11 @@ int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what
|
|||
/* Find the last possible spot where the queried ZPs, registers and/or processor
|
||||
** states might be changed in the range (First, Last). The code block in the
|
||||
** range must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
{
|
||||
CodeEntry* X;
|
||||
int Found = -1;
|
||||
int Found = First;
|
||||
|
||||
CHECK (Last <= (int)CollCount (&S->Entries));
|
||||
|
||||
|
@ -3169,11 +3169,11 @@ int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what)
|
|||
/* Find the last possible spot where the queried ZPs, registers and/or processor
|
||||
** states might be used in the range (First, Last). The code block in the range
|
||||
** must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
{
|
||||
CodeEntry* X;
|
||||
int Found = -1;
|
||||
int Found = First;
|
||||
|
||||
CHECK (Last <= (int)CollCount (&S->Entries));
|
||||
|
||||
|
|
|
@ -433,7 +433,7 @@ int FindArgLastUsageInOpenRange (CodeSeg* S, int First, int Last, CodeEntry* E,
|
|||
/* Find the last index where the arg of E might be used or changed in the range (First, Last).
|
||||
** ReloadY indicates whether Y is supposed to be reloaded.
|
||||
** The code block in the range must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
|
||||
int FindRegFirstChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
|
||||
|
@ -454,14 +454,14 @@ int FindRegLastChangeInOpenRange (CodeSeg* S, int First, int Last, unsigned what
|
|||
/* Find the last possible spot where the queried ZPs, registers and/or processor
|
||||
** states might be changed in the range (First, Last). The code block in the
|
||||
** range must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
|
||||
int FindRegLastUseInOpenRange (CodeSeg* S, int First, int Last, unsigned what);
|
||||
/* Find the last possible spot where the queried ZPs, registers and/or processor
|
||||
** states might be used in the range (First, Last). The code block in the range
|
||||
** must be basic without any jump backwards.
|
||||
** Return the index of the found entry, or -1 if not found.
|
||||
** Return the index of the found entry, or First if not found.
|
||||
*/
|
||||
|
||||
/* End of codeoptutil.h */
|
||||
|
|
|
@ -1085,11 +1085,26 @@ int HasUnknownSize (const Type* T)
|
|||
|
||||
int IsVariadicFunc (const Type* T)
|
||||
/* Return true if this is a function type or pointer to function type with
|
||||
** variable parameter list
|
||||
** variable parameter list.
|
||||
** Check fails if the type is not a function or a pointer to function.
|
||||
*/
|
||||
{
|
||||
FuncDesc* F = GetFuncDesc (T);
|
||||
return (F->Flags & FD_VARIADIC) != 0;
|
||||
return (GetFuncDesc (T)->Flags & FD_VARIADIC) != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int IsFastcallFunc (const Type* T)
|
||||
/* Return true if this is a function type or pointer to function type by
|
||||
** __fastcall__ calling convention.
|
||||
** Check fails if the type is not a function or a pointer to function.
|
||||
*/
|
||||
{
|
||||
if (UnqualifiedType (T->C) == T_PTR) {
|
||||
/* Pointer to function */
|
||||
++T;
|
||||
}
|
||||
return !IsVariadicFunc (T) && (AutoCDecl ? IsQualFastcall (T) : !IsQualCDecl (T));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ enum {
|
|||
T_QUAL_CONST = 0x001000,
|
||||
T_QUAL_VOLATILE = 0x002000,
|
||||
T_QUAL_RESTRICT = 0x004000,
|
||||
T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT,
|
||||
T_QUAL_NEAR = 0x008000,
|
||||
T_QUAL_FAR = 0x010000,
|
||||
T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR,
|
||||
|
@ -825,7 +826,14 @@ INLINE int IsQualCConv (const Type* T)
|
|||
|
||||
int IsVariadicFunc (const Type* T) attribute ((const));
|
||||
/* Return true if this is a function type or pointer to function type with
|
||||
** variable parameter list
|
||||
** variable parameter list.
|
||||
** Check fails if the type is not a function or a pointer to function.
|
||||
*/
|
||||
|
||||
int IsFastcallFunc (const Type* T) attribute ((const));
|
||||
/* Return true if this is a function type or pointer to function type by
|
||||
** __fastcall__ calling convention.
|
||||
** Check fails if the type is not a function or a pointer to function.
|
||||
*/
|
||||
|
||||
FuncDesc* GetFuncDesc (const Type* T) attribute ((const));
|
||||
|
|
|
@ -965,7 +965,7 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
|
|||
*/
|
||||
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
|
||||
SignednessSpecified);
|
||||
} else {
|
||||
} else if (Decl.Ident[0] != '\0') {
|
||||
Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
|
||||
if (IsAnonName (Decl.Ident)) {
|
||||
Entry->V.A.ANumber = UnionTagEntry->V.S.ACount++;
|
||||
|
@ -994,7 +994,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
|
|||
NextToken ();
|
||||
|
||||
/* Remember the symbol table and leave the struct level */
|
||||
FieldTab = GetSymTab ();
|
||||
FieldTab = GetFieldSymTab ();
|
||||
LeaveStructLevel ();
|
||||
|
||||
/* Return a fictitious symbol if errors occurred during parsing */
|
||||
|
@ -1167,7 +1167,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
|
|||
/* Add any full bytes to the struct size. */
|
||||
StructSize += BitOffs / CHAR_BITS;
|
||||
BitOffs %= CHAR_BITS;
|
||||
} else {
|
||||
} else if (Decl.Ident[0] != '\0') {
|
||||
Entry = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize);
|
||||
if (IsAnonName (Decl.Ident)) {
|
||||
Entry->V.A.ANumber = StructTagEntry->V.S.ACount++;
|
||||
|
@ -1208,7 +1208,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
|
|||
NextToken ();
|
||||
|
||||
/* Remember the symbol table and leave the struct level */
|
||||
FieldTab = GetSymTab ();
|
||||
FieldTab = GetFieldSymTab ();
|
||||
LeaveStructLevel ();
|
||||
|
||||
/* Return a fictitious symbol if errors occurred during parsing */
|
||||
|
@ -1821,7 +1821,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
|
|||
NextToken ();
|
||||
|
||||
/* Allow const, restrict, and volatile qualifiers */
|
||||
Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT);
|
||||
Qualifiers |= OptionalQualifiers (T_QUAL_CVR);
|
||||
|
||||
/* Parse the type that the pointer points to */
|
||||
Declarator (Spec, D, Mode);
|
||||
|
|
|
@ -68,6 +68,8 @@ IntStack WarningsAreErrors = INTSTACK(0); /* Treat warnings as errors */
|
|||
/* Warn about: */
|
||||
IntStack WarnConstComparison= INTSTACK(1); /* - constant comparison results */
|
||||
IntStack WarnNoEffect = INTSTACK(1); /* - statements without an effect */
|
||||
IntStack WarnPointerSign = INTSTACK(1); /* - pointer conversion to pointer differing in signedness */
|
||||
IntStack WarnPointerTypes = INTSTACK(1); /* - pointer conversion to incompatible pointer type */
|
||||
IntStack WarnRemapZero = INTSTACK(1); /* - remapping character code zero */
|
||||
IntStack WarnStructParam = INTSTACK(0); /* - structs passed by val */
|
||||
IntStack WarnUnknownPragma = INTSTACK(1); /* - unknown #pragmas */
|
||||
|
@ -87,6 +89,8 @@ static WarnMapEntry WarnMap[] = {
|
|||
{ &WarnConstComparison, "const-comparison" },
|
||||
{ &WarningsAreErrors, "error" },
|
||||
{ &WarnNoEffect, "no-effect" },
|
||||
{ &WarnPointerSign, "pointer-sign" },
|
||||
{ &WarnPointerTypes, "pointer-types" },
|
||||
{ &WarnRemapZero, "remap-zero" },
|
||||
{ &WarnStructParam, "struct-param" },
|
||||
{ &WarnUnknownPragma, "unknown-pragma" },
|
||||
|
|
|
@ -64,6 +64,8 @@ extern IntStack WarnEnable; /* Enable warnings */
|
|||
extern IntStack WarningsAreErrors; /* Treat warnings as errors */
|
||||
/* Warn about: */
|
||||
extern IntStack WarnConstComparison; /* - constant comparison results */
|
||||
extern IntStack WarnPointerSign; /* - pointer conversion to pointer differing in signedness */
|
||||
extern IntStack WarnPointerTypes; /* - pointer conversion to incompatible pointer type */
|
||||
extern IntStack WarnNoEffect; /* - statements without an effect */
|
||||
extern IntStack WarnRemapZero; /* - remapping character code zero */
|
||||
extern IntStack WarnStructParam; /* - structs passed by val */
|
||||
|
|
1170
src/cc65/expr.c
1170
src/cc65/expr.c
File diff suppressed because it is too large
Load diff
|
@ -74,13 +74,6 @@ void Store (ExprDesc* Expr, const Type* StoreType);
|
|||
** is NULL, use lval->Type instead.
|
||||
*/
|
||||
|
||||
int evalexpr (unsigned flags, void (*Func) (ExprDesc*), ExprDesc* Expr);
|
||||
/* Will evaluate an expression via the given function. If the result is a
|
||||
** constant, 0 is returned and the value is put in the Expr struct. If the
|
||||
** result is not constant, LoadExpr is called to bring the value into the
|
||||
** primary register and 1 is returned.
|
||||
*/
|
||||
|
||||
void Expression0 (ExprDesc* Expr);
|
||||
/* Evaluate an expression via hie0 and put the result into the primary register.
|
||||
** The expression is completely evaluated and all side effects complete.
|
||||
|
|
|
@ -397,6 +397,30 @@ int ED_IsConstBool (const ExprDesc* Expr)
|
|||
|
||||
|
||||
|
||||
int ED_IsConstTrue (const ExprDesc* Expr)
|
||||
/* Return true if the constant expression can be evaluated as boolean true at
|
||||
** compile time.
|
||||
*/
|
||||
{
|
||||
/* Non-zero arithmetics and objects addresses are boolean true */
|
||||
return (ED_IsConstAbsInt (Expr) && Expr->IVal != 0) ||
|
||||
(ED_IsAddrExpr (Expr));
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ED_IsConstFalse (const ExprDesc* Expr)
|
||||
/* Return true if the constant expression can be evaluated as boolean false at
|
||||
** compile time.
|
||||
*/
|
||||
{
|
||||
/* Zero arithmetics and null pointers are boolean false */
|
||||
return (ED_IsConstAbsInt (Expr) && Expr->IVal == 0) ||
|
||||
ED_IsNullPtr (Expr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ED_IsConst (const ExprDesc* Expr)
|
||||
/* Return true if the expression denotes a constant of some sort. This can be a
|
||||
** numeric constant, the address of a global variable (maybe with offset) or
|
||||
|
@ -408,6 +432,15 @@ int ED_IsConst (const ExprDesc* Expr)
|
|||
|
||||
|
||||
|
||||
int ED_IsQuasiConst (const ExprDesc* Expr)
|
||||
/* Return true if the expression denotes a quasi-constant of some sort. This
|
||||
** can be a numeric constant, a constant address or a stack variable address.
|
||||
*/
|
||||
{
|
||||
return (Expr->Flags & E_MASK_LOC) == E_LOC_NONE || ED_IsQuasiConstAddr (Expr);
|
||||
}
|
||||
|
||||
|
||||
int ED_IsConstAddr (const ExprDesc* Expr)
|
||||
/* Return true if the expression denotes a constant address of some sort. This
|
||||
** can be the address of a global variable (maybe with offset) or similar.
|
||||
|
@ -440,7 +473,7 @@ int ED_IsNullPtr (const ExprDesc* Expr)
|
|||
|
||||
|
||||
int ED_IsBool (const ExprDesc* Expr)
|
||||
/* Return true of the expression can be treated as a boolean, that is, it can
|
||||
/* Return true if the expression can be treated as a boolean, that is, it can
|
||||
** be an operand to a compare operation.
|
||||
*/
|
||||
{
|
||||
|
|
|
@ -643,12 +643,27 @@ int ED_IsConstAbsInt (const ExprDesc* Expr);
|
|||
int ED_IsConstBool (const ExprDesc* Expr);
|
||||
/* Return true if the expression can be constantly evaluated as a boolean. */
|
||||
|
||||
int ED_IsConstTrue (const ExprDesc* Expr);
|
||||
/* Return true if the constant expression can be evaluated as boolean true at
|
||||
** compile time.
|
||||
*/
|
||||
|
||||
int ED_IsConstFalse (const ExprDesc* Expr);
|
||||
/* Return true if the constant expression can be evaluated as boolean false at
|
||||
** compile time.
|
||||
*/
|
||||
|
||||
int ED_IsConst (const ExprDesc* Expr);
|
||||
/* Return true if the expression denotes a constant of some sort. This can be a
|
||||
** numeric constant, the address of a global variable (maybe with offset) or
|
||||
** similar.
|
||||
*/
|
||||
|
||||
int ED_IsQuasiConst (const ExprDesc* Expr);
|
||||
/* Return true if the expression denotes a quasi-constant of some sort. This
|
||||
** can be a numeric constant, a constant address or a stack variable address.
|
||||
*/
|
||||
|
||||
int ED_IsConstAddr (const ExprDesc* Expr);
|
||||
/* Return true if the expression denotes a constant address of some sort. This
|
||||
** can be the address of a global variable (maybe with offset) or similar.
|
||||
|
@ -663,7 +678,7 @@ int ED_IsNullPtr (const ExprDesc* Expr);
|
|||
/* Return true if the given expression is a NULL pointer constant */
|
||||
|
||||
int ED_IsBool (const ExprDesc* Expr);
|
||||
/* Return true of the expression can be treated as a boolean, that is, it can
|
||||
/* Return true if the expression can be treated as a boolean, that is, it can
|
||||
** be an operand to a compare operation with 0/NULL.
|
||||
*/
|
||||
|
||||
|
|
|
@ -558,10 +558,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||
PushLiteralPool (Func);
|
||||
|
||||
/* If this is a fastcall function, push the last parameter onto the stack */
|
||||
if ((D->Flags & FD_VARIADIC) == 0 && D->ParamCount > 0 &&
|
||||
(AutoCDecl ?
|
||||
IsQualFastcall (Func->Type) :
|
||||
!IsQualCDecl (Func->Type))) {
|
||||
if (D->ParamCount > 0 && IsFastcallFunc (Func->Type)) {
|
||||
unsigned Flags;
|
||||
|
||||
/* Generate the push */
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#include "stackptr.h"
|
||||
#include "symentry.h"
|
||||
#include "typecmp.h"
|
||||
#include "typeconv.h"
|
||||
#include "symtab.h"
|
||||
|
||||
|
||||
|
@ -92,6 +93,7 @@ static SymTable* SymTab0 = 0;
|
|||
static SymTable* SymTab = 0;
|
||||
static SymTable* TagTab0 = 0;
|
||||
static SymTable* TagTab = 0;
|
||||
static SymTable* FieldTab = 0;
|
||||
static SymTable* LabelTab = 0;
|
||||
static SymTable* SPAdjustTab = 0;
|
||||
static SymTable* FailSafeTab = 0; /* For errors */
|
||||
|
@ -390,9 +392,9 @@ void EnterStructLevel (void)
|
|||
** nested in struct scope are NOT local to the struct but visible in the
|
||||
** outside scope. So we will NOT create a new struct or enum table.
|
||||
*/
|
||||
S = NewSymTable (SYMTAB_SIZE_BLOCK);
|
||||
S->PrevTab = SymTab;
|
||||
SymTab = S;
|
||||
S = NewSymTable (SYMTAB_SIZE_STRUCT);
|
||||
S->PrevTab = FieldTab;
|
||||
FieldTab = S;
|
||||
}
|
||||
|
||||
|
||||
|
@ -401,7 +403,7 @@ void LeaveStructLevel (void)
|
|||
/* Leave a nested block for a struct definition */
|
||||
{
|
||||
/* Don't delete the table */
|
||||
SymTab = SymTab->PrevTab;
|
||||
FieldTab = FieldTab->PrevTab;
|
||||
}
|
||||
|
||||
|
||||
|
@ -548,6 +550,19 @@ SymEntry FindStructField (const Type* T, const char* Name)
|
|||
|
||||
|
||||
|
||||
static int IsDistinctRedef (const Type* lhst, const Type* rhst, typecmpcode_t Code, typecmpflag_t Flags)
|
||||
/* Return if type compatibility result is "worse" than Code or if any bit of
|
||||
** qualifier Flags is set.
|
||||
*/
|
||||
{
|
||||
typecmp_t Result = TypeCmp (lhst, rhst);
|
||||
if (Result.C < Code || (Result.F & TCF_MASK_QUAL & Flags) != 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags)
|
||||
/* Check and handle redefinition of existing symbols.
|
||||
** Complete array sizes and function descriptors as well.
|
||||
|
@ -564,7 +579,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
|
|||
|
||||
/* Existing typedefs cannot be redeclared as anything different */
|
||||
if (SCType == SC_TYPEDEF) {
|
||||
if (TypeCmp (E_Type, T) < TC_IDENTICAL) {
|
||||
if (IsDistinctRedef (E_Type, T, TC_IDENTICAL, TCF_MASK_QUAL)) {
|
||||
Error ("Conflicting types for typedef '%s'", Entry->Name);
|
||||
Entry = 0;
|
||||
}
|
||||
|
@ -590,7 +605,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
|
|||
Entry = 0;
|
||||
} else {
|
||||
/* New type must be compatible with the composite prototype */
|
||||
if (TypeCmp (Entry->Type, T) < TC_EQUAL) {
|
||||
if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
|
||||
Error ("Conflicting function types for '%s'", Entry->Name);
|
||||
Entry = 0;
|
||||
} else {
|
||||
|
@ -620,7 +635,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
|
|||
** is incomplete, complete it.
|
||||
*/
|
||||
if ((Size != UNSPECIFIED && ESize != UNSPECIFIED && Size != ESize) ||
|
||||
TypeCmp (T + 1, E_Type + 1) < TC_EQUAL) {
|
||||
IsDistinctRedef (E_Type + 1, T + 1, TC_IDENTICAL, TCF_MASK_QUAL)) {
|
||||
/* Conflicting element types */
|
||||
Error ("Conflicting array types for '%s[]'", Entry->Name);
|
||||
Entry = 0;
|
||||
|
@ -638,7 +653,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
|
|||
if (SCType != E_SCType) {
|
||||
Error ("Redefinition of '%s' as different kind of symbol", Entry->Name);
|
||||
Entry = 0;
|
||||
} else if (TypeCmp (E_Type, T) < TC_EQUAL) {
|
||||
} else if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
|
||||
Error ("Conflicting types for '%s'", Entry->Name);
|
||||
Entry = 0;
|
||||
} else if (E_SCType == SC_ENUMERATOR) {
|
||||
|
@ -850,7 +865,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs,
|
|||
/* Add a bit field to the local symbol table and return the symbol entry */
|
||||
{
|
||||
/* Do we have an entry with this name already? */
|
||||
SymEntry* Entry = FindSymInTable (SymTab, Name, HashStr (Name));
|
||||
SymEntry* Entry = FindSymInTable (FieldTab, Name, HashStr (Name));
|
||||
if (Entry) {
|
||||
|
||||
/* We have a symbol with this name already */
|
||||
|
@ -882,7 +897,7 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs,
|
|||
}
|
||||
|
||||
/* Add the entry to the symbol table */
|
||||
AddSymEntry (SymTab, Entry);
|
||||
AddSymEntry (FieldTab, Entry);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1070,9 +1085,9 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags)
|
|||
|
||||
|
||||
SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs)
|
||||
/* Add a local symbol and return the symbol entry */
|
||||
/* Add a local or struct/union field symbol and return the symbol entry */
|
||||
{
|
||||
SymTable* Tab = SymTab;
|
||||
SymTable* Tab = (Flags & SC_STRUCTFIELD) == 0 ? SymTab : FieldTab;
|
||||
ident Ident;
|
||||
|
||||
/* Do we have an entry with this name already? */
|
||||
|
@ -1267,6 +1282,16 @@ SymTable* GetGlobalSymTab (void)
|
|||
return SymTab0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SymTable* GetFieldSymTab (void)
|
||||
/* Return the current field symbol table */
|
||||
{
|
||||
return FieldTab;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SymTable* GetLabelSymTab (void)
|
||||
/* Return the global symbol table */
|
||||
{
|
||||
|
|
|
@ -166,7 +166,7 @@ SymEntry* AddLabelSym (const char* Name, unsigned Flags);
|
|||
/* Add a goto label to the symbol table */
|
||||
|
||||
SymEntry* AddLocalSym (const char* Name, const Type* T, unsigned Flags, int Offs);
|
||||
/* Add a local symbol and return the symbol entry */
|
||||
/* Add a local or struct/union field symbol and return the symbol entry */
|
||||
|
||||
SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags);
|
||||
/* Add an external or global symbol to the symbol table and return the entry */
|
||||
|
@ -185,6 +185,9 @@ SymTable* GetSymTab (void);
|
|||
SymTable* GetGlobalSymTab (void);
|
||||
/* Return the global symbol table */
|
||||
|
||||
SymTable* GetFieldSymTab (void);
|
||||
/* Return the current field symbol table */
|
||||
|
||||
SymTable* GetLabelSymTab (void);
|
||||
/* Return the label symbol table */
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ unsigned Test (unsigned Label, int Invert)
|
|||
DoDeferred (SQP_KEEP_NONE, &Expr);
|
||||
|
||||
/* Result is constant, so we know the outcome */
|
||||
Result = (Expr.IVal != 0);
|
||||
Result = (Expr.IVal != 0) ? TESTEXPR_TRUE : TESTEXPR_FALSE;
|
||||
|
||||
/* Constant rvalue */
|
||||
if (!Invert && Expr.IVal == 0) {
|
||||
|
@ -86,7 +86,12 @@ unsigned Test (unsigned Label, int Invert)
|
|||
DoDeferred (SQP_KEEP_NONE, &Expr);
|
||||
|
||||
/* Object addresses are non-NULL */
|
||||
Result = 1;
|
||||
Result = TESTEXPR_TRUE;
|
||||
|
||||
/* Condition is always true */
|
||||
if (Invert) {
|
||||
g_jump (Label);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@
|
|||
|
||||
|
||||
|
||||
#define TESTEXPR_UNKNOWN 0 /* Result of expression unknown */
|
||||
#define TESTEXPR_UNKNOWN -1 /* Result of expression unknown */
|
||||
#define TESTEXPR_TRUE 1 /* Expression yields true */
|
||||
#define TESTEXPR_FALSE 2 /* Expression yields false */
|
||||
#define TESTEXPR_FALSE 0 /* Expression yields false */
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <string.h>
|
||||
|
||||
/* cc65 */
|
||||
#include "funcdesc.h"
|
||||
#include "error.h"
|
||||
#include "global.h"
|
||||
#include "symtab.h"
|
||||
#include "typecmp.h"
|
||||
|
@ -49,51 +49,6 @@
|
|||
|
||||
|
||||
|
||||
static void SetResult (typecmp_t* Result, typecmp_t Val)
|
||||
/* Set a new result value if it is less than the existing one */
|
||||
{
|
||||
if (Val < *Result) {
|
||||
/* printf ("SetResult = %d\n", Val); */
|
||||
*Result = Val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int ParamsHaveDefaultPromotions (const FuncDesc* F)
|
||||
/* Check if any of the parameters of function F has a default promotion. In
|
||||
** this case, the function is not compatible with an empty parameter name list
|
||||
** declaration.
|
||||
*/
|
||||
{
|
||||
/* Get the symbol table */
|
||||
const SymTable* Tab = F->SymTab;
|
||||
|
||||
/* Get the first parameter in the list */
|
||||
const SymEntry* Sym = Tab->SymHead;
|
||||
|
||||
/* Walk over all parameters */
|
||||
while (Sym && (Sym->Flags & SC_PARAM)) {
|
||||
|
||||
/* If this is an integer type, check if the promoted type is equal
|
||||
** to the original type. If not, we have a default promotion.
|
||||
*/
|
||||
if (IsClassInt (Sym->Type)) {
|
||||
if (IntPromotion (Sym->Type) != Sym->Type) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the pointer to the next param */
|
||||
Sym = Sym->NextSym;
|
||||
}
|
||||
|
||||
/* No default promotions in the parameter list */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
||||
/* Compare two function symbol tables regarding function parameters. Return 1
|
||||
** if they are equal and 0 otherwise.
|
||||
|
@ -129,7 +84,7 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
|||
}
|
||||
|
||||
/* Compare this field */
|
||||
if (TypeCmp (Type1, Type2) < TC_EQUAL) {
|
||||
if (TypeCmp (Type1, Type2).C < TC_EQUAL) {
|
||||
/* Field types not equal */
|
||||
return 0;
|
||||
}
|
||||
|
@ -148,48 +103,177 @@ static int EqualFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
|||
|
||||
|
||||
|
||||
static void SetResult (typecmp_t* Result, typecmpcode_t Val)
|
||||
/* Set a new result value if it is less than the existing one */
|
||||
{
|
||||
if (Val < Result->C) {
|
||||
if (Result->Indirections > 0) {
|
||||
if (Val >= TC_STRICT_COMPATIBLE) {
|
||||
/* Arrays etc. */
|
||||
Result->C = Val;
|
||||
} else if (Result->Indirections == 1) {
|
||||
/* C Standard allows implicit conversion as long as one side is
|
||||
** a pointer to void type, but doesn't care which side is.
|
||||
*/
|
||||
if ((Result->F & TCF_MASK_VOID_PTR) != 0) {
|
||||
Result->C = TC_VOID_PTR;
|
||||
} else if (Val == TC_SIGN_DIFF) {
|
||||
/* Special treatment with pointee signedness difference */
|
||||
Result->C = TC_PTR_SIGN_DIFF;
|
||||
} else {
|
||||
/* Incompatible */
|
||||
Result->C = TC_PTR_INCOMPATIBLE;
|
||||
}
|
||||
} else {
|
||||
/* Pointer-to-pointer types must have compatible pointte types,
|
||||
** or they are just incompatible.
|
||||
*/
|
||||
Result->C = TC_PTR_INCOMPATIBLE;
|
||||
}
|
||||
} else {
|
||||
Result->C = Val;
|
||||
}
|
||||
/* printf ("SetResult = %d\n", Val); */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static typecmp_t* CmpQuals (const Type* lhst, const Type* rhst, typecmp_t* Result)
|
||||
/* Copare the types regarding thier qualifiers. Return the Result */
|
||||
{
|
||||
TypeCode LeftQual, RightQual;
|
||||
|
||||
/* Get the left and right qualifiers */
|
||||
LeftQual = GetQualifier (lhst);
|
||||
RightQual = GetQualifier (rhst);
|
||||
|
||||
/* If type is function without a calling convention set explicitly,
|
||||
** then assume the default one.
|
||||
*/
|
||||
if (IsTypeFunc (lhst)) {
|
||||
if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) {
|
||||
LeftQual |= (AutoCDecl || IsVariadicFunc (lhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
|
||||
}
|
||||
}
|
||||
if (IsTypeFunc (rhst)) {
|
||||
if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) {
|
||||
RightQual |= (AutoCDecl || IsVariadicFunc (rhst)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default address size qualifiers */
|
||||
if ((LeftQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) {
|
||||
LeftQual |= (IsTypeFunc (lhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ());
|
||||
}
|
||||
if ((RightQual & T_QUAL_ADDRSIZE) == T_QUAL_NONE) {
|
||||
RightQual |= (IsTypeFunc (rhst) ? CodeAddrSizeQualifier () : DataAddrSizeQualifier ());
|
||||
}
|
||||
|
||||
/* Just return if nothing to do */
|
||||
if (LeftQual == RightQual) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
/* On the first indirection level, different qualifiers mean that the types
|
||||
** are still compatible. On the second level, that is a (maybe minor) error.
|
||||
** We create a special return-code if a qualifier is dropped from a pointer.
|
||||
** But, different calling conventions are incompatible. Starting from the
|
||||
** next level, the types are incompatible if the qualifiers differ.
|
||||
*/
|
||||
/* (Debugging statement) */
|
||||
/* printf ("Ind = %d %06X != %06X\n", Result->Indirections, LeftQual, RightQual); */
|
||||
switch (Result->Indirections) {
|
||||
case 0:
|
||||
/* Compare C qualifiers */
|
||||
if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) {
|
||||
Result->F |= TCF_QUAL_IMPLICIT;
|
||||
} else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) {
|
||||
Result->F |= TCF_QUAL_DIFF;
|
||||
}
|
||||
|
||||
/* Compare address size qualifiers */
|
||||
if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) {
|
||||
Result->F |= TCF_ADDRSIZE_QUAL_DIFF;
|
||||
}
|
||||
|
||||
/* Compare function calling conventions */
|
||||
if ((LeftQual & T_QUAL_CCONV) != (RightQual & T_QUAL_CCONV)) {
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* A non-const value on the right is compatible to a
|
||||
** const one to the left, same for volatile.
|
||||
*/
|
||||
if ((LeftQual & T_QUAL_CVR) > (RightQual & T_QUAL_CVR)) {
|
||||
Result->F |= TCF_PTR_QUAL_IMPLICIT;
|
||||
} else if ((LeftQual & T_QUAL_CVR) != (RightQual & T_QUAL_CVR)) {
|
||||
Result->F |= TCF_PTR_QUAL_DIFF;
|
||||
}
|
||||
|
||||
/* Compare address size qualifiers */
|
||||
if ((LeftQual & T_QUAL_ADDRSIZE) != (RightQual & T_QUAL_ADDRSIZE)) {
|
||||
Result->F |= TCF_ADDRSIZE_QUAL_DIFF;
|
||||
}
|
||||
|
||||
/* Compare function calling conventions */
|
||||
if ((!IsTypeFunc (lhst) && !IsTypeFunc (rhst)) ||
|
||||
(LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) {
|
||||
break;
|
||||
}
|
||||
/* else fall through */
|
||||
|
||||
default:
|
||||
/* Pointer types mismatch */
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
|
||||
/* Recursively compare two types. */
|
||||
{
|
||||
unsigned Indirections;
|
||||
unsigned ElementCount;
|
||||
SymEntry* Sym1;
|
||||
SymEntry* Sym2;
|
||||
FuncDesc* F1;
|
||||
FuncDesc* F2;
|
||||
TypeCode LeftType, RightType;
|
||||
long LeftCount, RightCount;
|
||||
|
||||
|
||||
/* Initialize stuff */
|
||||
Indirections = 0;
|
||||
ElementCount = 0;
|
||||
|
||||
/* Compare two types. Determine, where they differ */
|
||||
while (lhs->C != T_END) {
|
||||
|
||||
TypeCode LeftType, RightType;
|
||||
TypeCode LeftSign, RightSign;
|
||||
TypeCode LeftQual, RightQual;
|
||||
long LeftCount, RightCount;
|
||||
|
||||
/* Check if the end of the type string is reached */
|
||||
if (rhs->C == T_END) {
|
||||
/* End of comparison reached */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Compare qualifiers */
|
||||
if (CmpQuals (lhs, rhs, Result)->C == TC_INCOMPATIBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the left and right types, signs and qualifiers */
|
||||
/* Get the left and right types */
|
||||
LeftType = (GetUnderlyingTypeCode (lhs) & T_MASK_TYPE);
|
||||
RightType = (GetUnderlyingTypeCode (rhs) & T_MASK_TYPE);
|
||||
LeftSign = GetSignedness (lhs);
|
||||
RightSign = GetSignedness (rhs);
|
||||
LeftQual = GetQualifier (lhs);
|
||||
RightQual = GetQualifier (rhs);
|
||||
|
||||
/* If the left type is a pointer and the right is an array, both
|
||||
** are compatible.
|
||||
/* If one side is a pointer and the other side is an array, both are
|
||||
** compatible.
|
||||
*/
|
||||
if (LeftType == T_TYPE_PTR && RightType == T_TYPE_ARRAY) {
|
||||
RightType = T_TYPE_PTR;
|
||||
SetResult (Result, TC_PTR_DECAY);
|
||||
}
|
||||
if (LeftType == T_TYPE_ARRAY && RightType == T_TYPE_PTR) {
|
||||
LeftType = T_TYPE_PTR;
|
||||
SetResult (Result, TC_STRICT_COMPATIBLE);
|
||||
}
|
||||
|
||||
|
@ -236,74 +320,30 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
|
|||
/* 'char' is neither 'signed char' nor 'unsigned char' */
|
||||
if ((IsISOChar (lhs) && !IsISOChar (rhs)) ||
|
||||
(!IsISOChar (lhs) && IsISOChar (rhs))) {
|
||||
SetResult (Result, TC_COMPATIBLE);
|
||||
}
|
||||
|
||||
/* On indirection level zero, a qualifier or sign difference is
|
||||
** accepted. The types are no longer equal, but compatible.
|
||||
*/
|
||||
if (LeftSign != RightSign) {
|
||||
if (ElementCount == 0) {
|
||||
SetResult (Result, TC_SIGN_DIFF);
|
||||
} else {
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (LeftType == T_TYPE_FUNC) {
|
||||
/* If a calling convention wasn't set explicitly,
|
||||
** then assume the default one.
|
||||
/* On indirection level zero, a sign difference is accepted.
|
||||
** The types are no longer equal, but compatible.
|
||||
*/
|
||||
if ((LeftQual & T_QUAL_CCONV) == T_QUAL_NONE) {
|
||||
LeftQual |= (AutoCDecl || IsVariadicFunc (lhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
|
||||
}
|
||||
if ((RightQual & T_QUAL_CCONV) == T_QUAL_NONE) {
|
||||
RightQual |= (AutoCDecl || IsVariadicFunc (rhs)) ? T_QUAL_CDECL : T_QUAL_FASTCALL;
|
||||
}
|
||||
}
|
||||
|
||||
if (LeftQual != RightQual) {
|
||||
/* On the first indirection level, different qualifiers mean
|
||||
** that the types still are compatible. On the second level,
|
||||
** that is a (maybe minor) error. We create a special return-code
|
||||
** if a qualifier is dropped from a pointer. But, different calling
|
||||
** conventions are incompatible. Starting from the next level,
|
||||
** the types are incompatible if the qualifiers differ.
|
||||
*/
|
||||
/* (Debugging statement) */
|
||||
/* printf ("Ind = %d %06X != %06X\n", Indirections, LeftQual, RightQual); */
|
||||
switch (Indirections) {
|
||||
case 0:
|
||||
SetResult (Result, TC_STRICT_COMPATIBLE);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* A non-const value on the right is compatible to a
|
||||
** const one to the left, same for volatile.
|
||||
*/
|
||||
if ((LeftQual & T_QUAL_CONST) < (RightQual & T_QUAL_CONST) ||
|
||||
(LeftQual & T_QUAL_VOLATILE) < (RightQual & T_QUAL_VOLATILE)) {
|
||||
SetResult (Result, TC_QUAL_DIFF);
|
||||
} else {
|
||||
SetResult (Result, TC_STRICT_COMPATIBLE);
|
||||
}
|
||||
|
||||
if (LeftType != T_TYPE_FUNC || (LeftQual & T_QUAL_CCONV) == (RightQual & T_QUAL_CCONV)) {
|
||||
break;
|
||||
}
|
||||
/* else fall through */
|
||||
|
||||
default:
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
return;
|
||||
}
|
||||
if (GetSignedness (lhs) != GetSignedness (rhs)) {
|
||||
SetResult (Result, TC_SIGN_DIFF);
|
||||
}
|
||||
|
||||
/* Check for special type elements */
|
||||
switch (LeftType) {
|
||||
case T_TYPE_PTR:
|
||||
++Indirections;
|
||||
++Result->Indirections;
|
||||
if (Result->Indirections == 1) {
|
||||
if ((GetUnderlyingTypeCode (lhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) {
|
||||
Result->F |= TCF_VOID_PTR_ON_LEFT;
|
||||
}
|
||||
if ((GetUnderlyingTypeCode (rhs + 1) & T_MASK_TYPE) == T_TYPE_VOID) {
|
||||
Result->F |= TCF_VOID_PTR_ON_RIGHT;
|
||||
}
|
||||
} else {
|
||||
Result->F &= ~TCF_MASK_VOID_PTR;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TYPE_FUNC:
|
||||
|
@ -311,29 +351,16 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
|
|||
F1 = GetFuncDesc (lhs);
|
||||
F2 = GetFuncDesc (rhs);
|
||||
|
||||
/* If one of both functions has an empty parameter list (which
|
||||
** does also mean, it is not a function definition, because the
|
||||
** flag is reset in this case), it is considered equal to any
|
||||
** other definition, provided that the other has no default
|
||||
** promotions in the parameter list. If none of both parameter
|
||||
** lists is empty, we have to check the parameter lists and
|
||||
** other attributes.
|
||||
/* If one of both function declarations has an empty parameter
|
||||
** list (which does also mean, it is not a function definition,
|
||||
** because the flag is reset in this case), it is ignored for
|
||||
** parameter comparison and considered equal to the other one,
|
||||
** provided both have the same return type and other attributes.
|
||||
** If neither of both parameter lists is empty, we have to check
|
||||
** the parameter lists.
|
||||
*/
|
||||
if (F1->Flags & FD_EMPTY) {
|
||||
if ((F2->Flags & FD_EMPTY) == 0) {
|
||||
if (ParamsHaveDefaultPromotions (F2)) {
|
||||
/* Flags differ */
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (F2->Flags & FD_EMPTY) {
|
||||
if (ParamsHaveDefaultPromotions (F1)) {
|
||||
/* Flags differ */
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ((F1->Flags & FD_EMPTY) == 0 &&
|
||||
(F2->Flags & FD_EMPTY) == 0) {
|
||||
|
||||
/* Check the remaining flags */
|
||||
if ((F1->Flags & ~FD_IGNORE) != (F2->Flags & ~FD_IGNORE)) {
|
||||
|
@ -364,8 +391,14 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
|
|||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We take into account which side is more specified */
|
||||
if (LeftCount == UNSPECIFIED) {
|
||||
SetResult (Result, TC_UNSPECIFY);
|
||||
} else {
|
||||
SetResult (Result, TC_EQUAL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TYPE_STRUCT:
|
||||
|
@ -397,11 +430,10 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
|
|||
/* Next type string element */
|
||||
++lhs;
|
||||
++rhs;
|
||||
++ElementCount;
|
||||
}
|
||||
|
||||
/* Check if end of rhs reached */
|
||||
if (rhs->C == T_END) {
|
||||
/* Check if lhs and rhs both reached ends */
|
||||
if (lhs->C == T_END && rhs->C == T_END) {
|
||||
SetResult (Result, TC_IDENTICAL);
|
||||
} else {
|
||||
SetResult (Result, TC_INCOMPATIBLE);
|
||||
|
@ -414,7 +446,7 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs)
|
|||
/* Compare two types and return the result */
|
||||
{
|
||||
/* Assume the types are identical */
|
||||
typecmp_t Result = TC_IDENTICAL;
|
||||
typecmp_t Result = TYPECMP_INITIALIZER;
|
||||
|
||||
#if 0
|
||||
printf ("Left : "); PrintRawType (stdout, lhs);
|
||||
|
@ -432,134 +464,18 @@ typecmp_t TypeCmp (const Type* lhs, const Type* rhs)
|
|||
|
||||
|
||||
|
||||
static Type* DoComposite (Type* lhs, const Type* rhs);
|
||||
|
||||
static void CompositeFuncParams (const FuncDesc* F1, const FuncDesc* F2)
|
||||
/* Composite two function symbol tables regarding function parameters */
|
||||
void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg)
|
||||
/* Print error or warning message about type compatibility with proper type names */
|
||||
{
|
||||
/* Get the symbol tables */
|
||||
const SymTable* Tab1 = F1->SymTab;
|
||||
const SymTable* Tab2 = F2->SymTab;
|
||||
|
||||
/* Composite the parameter lists */
|
||||
const SymEntry* Sym1 = Tab1->SymHead;
|
||||
const SymEntry* Sym2 = Tab2->SymHead;
|
||||
|
||||
/* Composite the fields */
|
||||
while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) {
|
||||
|
||||
/* Get the symbol types */
|
||||
Type* Type1 = Sym1->Type;
|
||||
Type* Type2 = Sym2->Type;
|
||||
|
||||
/* If either of both functions is old style, apply the default
|
||||
** promotions to the parameter type.
|
||||
*/
|
||||
if (F1->Flags & FD_OLDSTYLE) {
|
||||
if (IsClassInt (Type1)) {
|
||||
Type1 = IntPromotion (Type1);
|
||||
}
|
||||
}
|
||||
if (F2->Flags & FD_OLDSTYLE) {
|
||||
if (IsClassInt (Type2)) {
|
||||
Type2 = IntPromotion (Type2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Composite this field */
|
||||
DoComposite (Type1, Type2);
|
||||
|
||||
/* Get the pointers to the next fields */
|
||||
Sym1 = Sym1->NextSym;
|
||||
Sym2 = Sym2->NextSym;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Type* DoComposite (Type* lhs, const Type* rhs)
|
||||
/* Recursively composite two types into lhs */
|
||||
{
|
||||
FuncDesc* F1;
|
||||
FuncDesc* F2;
|
||||
long LeftCount, RightCount;
|
||||
|
||||
/* Composite two types */
|
||||
while (lhs->C != T_END) {
|
||||
|
||||
/* Check if the end of the type string is reached */
|
||||
if (rhs->C == T_END) {
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/* Check for sanity */
|
||||
CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs));
|
||||
|
||||
/* Check for special type elements */
|
||||
|
||||
if (IsTypeFunc (lhs)) {
|
||||
/* Composite the function descriptors */
|
||||
F1 = GetFuncDesc (lhs);
|
||||
F2 = GetFuncDesc (rhs);
|
||||
|
||||
/* If one of both functions has an empty parameter list (which
|
||||
** does also mean, it is not a function definition, because the
|
||||
** flag is reset in this case), it is replaced by the other
|
||||
** definition, provided that the other has no default
|
||||
** promotions in the parameter list. If none of both parameter
|
||||
** lists is empty, we have to composite the parameter lists and
|
||||
** other attributes.
|
||||
*/
|
||||
if ((F1->Flags & FD_EMPTY) == FD_EMPTY) {
|
||||
if ((F2->Flags & FD_EMPTY) == 0) {
|
||||
/* Copy the parameters and flags */
|
||||
TypeCopy (lhs, rhs);
|
||||
F1->Flags = F2->Flags;
|
||||
}
|
||||
} else if ((F2->Flags & FD_EMPTY) == 0) {
|
||||
/* Composite the parameter lists */
|
||||
CompositeFuncParams (F1, F2);
|
||||
}
|
||||
|
||||
} else if (IsTypeArray (lhs)) {
|
||||
/* Check member count */
|
||||
LeftCount = GetElementCount (lhs);
|
||||
RightCount = GetElementCount (rhs);
|
||||
|
||||
/* Set composite type if it is requested */
|
||||
if (LeftCount != UNSPECIFIED) {
|
||||
SetElementCount (lhs, LeftCount);
|
||||
} else if (RightCount != UNSPECIFIED) {
|
||||
SetElementCount (lhs, RightCount);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next type string element */
|
||||
++lhs;
|
||||
++rhs;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType)
|
||||
/* Refine the existing function descriptor with a new one */
|
||||
{
|
||||
FuncDesc* Old = GetFuncDesc (OldType);
|
||||
FuncDesc* New = GetFuncDesc (NewType);
|
||||
|
||||
CHECK (Old != 0 && New != 0);
|
||||
|
||||
if ((New->Flags & FD_EMPTY) == 0) {
|
||||
if ((Old->Flags & FD_EMPTY) == 0) {
|
||||
DoComposite (OldType, NewType);
|
||||
StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER;
|
||||
StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER;
|
||||
GetFullTypeNameBuf (&NewTypeName, NewType);
|
||||
GetFullTypeNameBuf (&OldTypeName, OldType);
|
||||
if (IsError) {
|
||||
Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName));
|
||||
} else {
|
||||
TypeCopy (OldType, NewType);
|
||||
Old->Flags &= ~FD_EMPTY;
|
||||
Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName));
|
||||
}
|
||||
}
|
||||
|
||||
return Old;
|
||||
SB_Done (&OldTypeName);
|
||||
SB_Done (&NewTypeName);
|
||||
}
|
||||
|
|
|
@ -48,18 +48,44 @@
|
|||
|
||||
|
||||
|
||||
/* Degree of type compatibility. Must be in ascending order */
|
||||
/* Degree of type compatibility affected. Must be in ascending order */
|
||||
typedef enum {
|
||||
TC_INCOMPATIBLE, /* Distinct types */
|
||||
TC_SIGN_DIFF, /* Signedness differs */
|
||||
TC_COMPATIBLE = TC_SIGN_DIFF, /* Compatible types */
|
||||
TC_QUAL_DIFF, /* Types differ in qualifier of pointer */
|
||||
TC_STRICT_COMPATIBLE, /* Strict compatibility */
|
||||
TC_EQUAL, /* Types are equivalent */
|
||||
TC_PTR_SIGN_DIFF, /* Pointee signedness differs */
|
||||
TC_PTR_INCOMPATIBLE, /* Distinct pointer types */
|
||||
TC_VOID_PTR, /* Non-void and void pointers */
|
||||
TC_STRICT_COMPATIBLE, /* Strict compatibility according to the C Standard */
|
||||
TC_PTR_DECAY, /* rhs is an array and lhs is a pointer */
|
||||
TC_EQUAL, /* Array types with unspecified lengths */
|
||||
TC_UNSPECIFY, /* lhs has unspecified length while rhs has specified length */
|
||||
TC_IDENTICAL /* Types are identical */
|
||||
} typecmpcode_t;
|
||||
|
||||
/* Degree of type compatibility affected by qualifiers as well as some extra info */
|
||||
typedef enum {
|
||||
TCF_NONE = 0x00, /* None of the below */
|
||||
TCF_VOID_PTR_ON_LEFT = 0x01, /* lhs is a void pointer */
|
||||
TCF_VOID_PTR_ON_RIGHT = 0x02, /* rhs is a void pointer */
|
||||
TCF_MASK_VOID_PTR = TCF_VOID_PTR_ON_LEFT | TCF_VOID_PTR_ON_RIGHT,
|
||||
TCF_QUAL_DIFF = 0x04, /* CVR qualifiers differ in a way that doesn't matter */
|
||||
TCF_QUAL_IMPLICIT = 0x08, /* CVR qualifiers of lhs are stricter than those of rhs */
|
||||
TCF_PTR_QUAL_DIFF = 0x10, /* CVR qualifiers of pointers differ */
|
||||
TCF_PTR_QUAL_IMPLICIT = 0x20, /* CVR qualifiers of pointers are stricter on lhs than those on rhs */
|
||||
TCF_MASK_C_QUAL_DIFF = 0x3C, /* All C Standard qualifiers */
|
||||
TCF_ADDRSIZE_QUAL_DIFF = 0x40, /* Address size qualifiers differ */
|
||||
TCF_CCONV_QUAL_DIFF = 0x80, /* Function calling conventions differ. Unused now */
|
||||
TCF_INCOMPATIBLE_QUAL = TCF_ADDRSIZE_QUAL_DIFF | TCF_CCONV_QUAL_DIFF,
|
||||
TCF_MASK_QUAL = TCF_MASK_C_QUAL_DIFF | TCF_INCOMPATIBLE_QUAL,
|
||||
} typecmpflag_t;
|
||||
|
||||
typedef struct {
|
||||
typecmpcode_t C;
|
||||
typecmpflag_t F;
|
||||
int Indirections;
|
||||
} typecmp_t;
|
||||
|
||||
|
||||
#define TYPECMP_INITIALIZER { TC_IDENTICAL, TCF_NONE, 0 }
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
|
@ -70,8 +96,9 @@ typedef enum {
|
|||
typecmp_t TypeCmp (const Type* lhs, const Type* rhs);
|
||||
/* Compare two types and return the result */
|
||||
|
||||
FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType);
|
||||
/* Refine the existing function descriptor with a new one */
|
||||
void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg);
|
||||
/* Print error or warning message about type compatibility with proper type names */
|
||||
|
||||
|
||||
|
||||
/* End of typecmp.h */
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include "error.h"
|
||||
#include "expr.h"
|
||||
#include "loadexpr.h"
|
||||
#include "scanner.h"
|
||||
#include "typecmp.h"
|
||||
#include "typeconv.h"
|
||||
|
||||
|
@ -55,24 +54,6 @@
|
|||
|
||||
|
||||
|
||||
void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg)
|
||||
/* Print error or warning message about type conversion with proper type names */
|
||||
{
|
||||
StrBuf NewTypeName = STATIC_STRBUF_INITIALIZER;
|
||||
StrBuf OldTypeName = STATIC_STRBUF_INITIALIZER;
|
||||
GetFullTypeNameBuf (&NewTypeName, NewType);
|
||||
GetFullTypeNameBuf (&OldTypeName, OldType);
|
||||
if (IsError) {
|
||||
Error (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName));
|
||||
} else {
|
||||
Warning (Msg, SB_GetConstBuf (&NewTypeName), SB_GetConstBuf (&OldTypeName));
|
||||
}
|
||||
SB_Done (&OldTypeName);
|
||||
SB_Done (&NewTypeName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoConversion (ExprDesc* Expr, const Type* NewType)
|
||||
/* Emit code to convert the given expression to a new type. */
|
||||
{
|
||||
|
@ -208,7 +189,7 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType)
|
|||
PrintRawType (stdout, NewType);
|
||||
#endif
|
||||
/* First, do some type checking */
|
||||
int HasWarning = 0;
|
||||
typecmp_t Result = TYPECMP_INITIALIZER;
|
||||
int HasError = 0;
|
||||
const char* Msg = 0;
|
||||
const Type* OldType = Expr->Type;
|
||||
|
@ -219,20 +200,13 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType)
|
|||
HasError = 1;
|
||||
}
|
||||
|
||||
/* If both types are strictly compatible, no conversion is needed */
|
||||
if (TypeCmp (NewType, OldType) >= TC_STRICT_COMPATIBLE) {
|
||||
/* We're already done */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If both types are the same, no conversion is needed */
|
||||
Result = TypeCmp (NewType, OldType);
|
||||
if (Result.C < TC_IDENTICAL && (IsTypeArray (OldType) || IsTypeFunc (OldType))) {
|
||||
/* If Expr is an array or a function, convert it to a pointer */
|
||||
Expr->Type = PtrConversion (Expr->Type);
|
||||
|
||||
/* If we have changed the type, check again for strictly compatibility */
|
||||
if (Expr->Type != OldType &&
|
||||
TypeCmp (NewType, Expr->Type) >= TC_STRICT_COMPATIBLE) {
|
||||
/* We're already done */
|
||||
return;
|
||||
/* Recompare */
|
||||
Result = TypeCmp (NewType, Expr->Type);
|
||||
}
|
||||
|
||||
/* Check for conversion problems */
|
||||
|
@ -253,37 +227,36 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType)
|
|||
/* Handle conversions to pointer type */
|
||||
if (IsClassPtr (Expr->Type)) {
|
||||
|
||||
/* Pointer to pointer assignment is valid, if:
|
||||
/* Implicit pointer-to-pointer conversion is valid, if:
|
||||
** - both point to the same types, or
|
||||
** - the rhs pointer is a void pointer, or
|
||||
** - the lhs pointer is a void pointer.
|
||||
** Note: We additionally allow converting function pointers to and from
|
||||
** void pointers, just with warnings.
|
||||
*/
|
||||
if (!IsTypeVoid (IndirectConst (NewType)) && !IsTypeVoid (Indirect (Expr->Type))) {
|
||||
/* Compare the types */
|
||||
switch (TypeCmp (NewType, Expr->Type)) {
|
||||
|
||||
case TC_INCOMPATIBLE:
|
||||
HasWarning = 1;
|
||||
Msg = "Incompatible pointer assignment to '%s' from '%s'";
|
||||
/* Use the pointer type in the diagnostic */
|
||||
OldType = Expr->Type;
|
||||
break;
|
||||
|
||||
case TC_QUAL_DIFF:
|
||||
HasWarning = 1;
|
||||
Msg = "Pointer assignment to '%s' from '%s' discards qualifiers";
|
||||
/* Use the pointer type in the diagnostic */
|
||||
OldType = Expr->Type;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Ok */
|
||||
break;
|
||||
if (Result.C == TC_PTR_SIGN_DIFF) {
|
||||
/* Specific warning for pointee signedness difference */
|
||||
if (IS_Get (&WarnPointerSign)) {
|
||||
TypeCompatibilityDiagnostic (NewType, Expr->Type,
|
||||
0, "Pointer conversion to '%s' from '%s' changes pointee signedness");
|
||||
}
|
||||
} else if ((Result.C <= TC_PTR_INCOMPATIBLE ||
|
||||
(Result.F & TCF_INCOMPATIBLE_QUAL) != 0)) {
|
||||
/* Incompatible pointee types or qualifiers */
|
||||
if (IS_Get (&WarnPointerTypes)) {
|
||||
TypeCompatibilityDiagnostic (NewType, Expr->Type,
|
||||
0, "Incompatible pointer conversion to '%s' from '%s'");
|
||||
}
|
||||
}
|
||||
|
||||
if ((Result.F & TCF_PTR_QUAL_DIFF) != 0) {
|
||||
/* Discarding qualifiers is a bad thing and we always warn */
|
||||
TypeCompatibilityDiagnostic (NewType, Expr->Type,
|
||||
0, "Pointer conversion to '%s' from '%s' discards qualifiers");
|
||||
}
|
||||
|
||||
} else if (IsClassInt (Expr->Type)) {
|
||||
/* Int to pointer assignment is valid only for constant zero */
|
||||
/* Int to pointer conversion is valid only for constant zero */
|
||||
if (!ED_IsConstAbsInt (Expr) || Expr->IVal != 0) {
|
||||
Warning ("Converting integer to pointer without a cast");
|
||||
}
|
||||
|
@ -291,11 +264,12 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType)
|
|||
HasError = 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if (Result.C < TC_IDENTICAL) {
|
||||
/* Invalid automatic conversion */
|
||||
HasError = 1;
|
||||
}
|
||||
|
||||
/* Set default diagnostic message */
|
||||
if (Msg == 0) {
|
||||
Msg = "Converting to '%s' from '%s'";
|
||||
}
|
||||
|
@ -303,10 +277,6 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType)
|
|||
if (HasError) {
|
||||
TypeCompatibilityDiagnostic (NewType, OldType, 1, Msg);
|
||||
} else {
|
||||
if (HasWarning) {
|
||||
TypeCompatibilityDiagnostic (NewType, OldType, 0, Msg);
|
||||
}
|
||||
|
||||
/* Both types must be complete */
|
||||
if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) {
|
||||
/* Do the actual conversion */
|
||||
|
@ -350,13 +320,13 @@ void TypeCast (ExprDesc* Expr)
|
|||
/* Convert functions and arrays to "pointer to" object */
|
||||
Expr->Type = PtrConversion (Expr->Type);
|
||||
|
||||
if (TypeCmp (NewType, Expr->Type) >= TC_QUAL_DIFF) {
|
||||
/* If the new type only differs in qualifiers, just use it to
|
||||
** replace the old one.
|
||||
if (TypeCmp (NewType, Expr->Type).C >= TC_PTR_INCOMPATIBLE) {
|
||||
/* If the new type has the same underlying presentation, just
|
||||
** use it to replace the old one.
|
||||
*/
|
||||
ReplaceType (Expr, NewType);
|
||||
} else if (IsCastType (Expr->Type)) {
|
||||
/* Convert the value. The rsult has always the new type */
|
||||
/* Convert the value. The result has always the new type */
|
||||
DoConversion (Expr, NewType);
|
||||
} else {
|
||||
TypeCompatibilityDiagnostic (NewType, Expr->Type, 1,
|
||||
|
@ -379,3 +349,158 @@ void TypeCast (ExprDesc* Expr)
|
|||
/* The result is always an rvalue */
|
||||
ED_MarkExprAsRVal (Expr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ComposeFuncParamList (const FuncDesc* F1, const FuncDesc* F2)
|
||||
/* Compose two function symbol tables regarding function parameters into F1 */
|
||||
{
|
||||
/* Get the symbol tables */
|
||||
const SymTable* Tab1 = F1->SymTab;
|
||||
const SymTable* Tab2 = F2->SymTab;
|
||||
|
||||
/* Compose the parameter lists */
|
||||
SymEntry* Sym1 = Tab1->SymHead;
|
||||
SymEntry* Sym2 = Tab2->SymHead;
|
||||
|
||||
/* Sanity check */
|
||||
CHECK ((F1->Flags & FD_EMPTY) == 0 && (F2->Flags & FD_EMPTY) == 0);
|
||||
|
||||
/* Compose the fields */
|
||||
while (Sym1 && (Sym1->Flags & SC_PARAM) && Sym2 && (Sym2->Flags & SC_PARAM)) {
|
||||
|
||||
/* Get the symbol types */
|
||||
Type* Type1 = Sym1->Type;
|
||||
Type* Type2 = Sym2->Type;
|
||||
|
||||
/* If either of both functions is old style, apply the default
|
||||
** promotions to the parameter type.
|
||||
*/
|
||||
if (F1->Flags & FD_OLDSTYLE) {
|
||||
if (IsClassInt (Type1)) {
|
||||
Type1 = IntPromotion (Type1);
|
||||
}
|
||||
}
|
||||
if (F2->Flags & FD_OLDSTYLE) {
|
||||
if (IsClassInt (Type2)) {
|
||||
Type2 = IntPromotion (Type2);
|
||||
}
|
||||
}
|
||||
|
||||
/* When we compose two function parameter lists with any FD_OLDSTYLE
|
||||
** flags set, we are either refining the declaration of the function
|
||||
** with its definition seen, or determining the result type of a
|
||||
** ternary operation. In either case, we can just replace the types
|
||||
** with the promoted ones since the original types of the parameters
|
||||
** only matters inside the function definition.
|
||||
*/
|
||||
if (Type1 != Sym1->Type) {
|
||||
Sym1->Type = TypeDup (Type1);
|
||||
}
|
||||
|
||||
/* Compose this field */
|
||||
TypeComposition (Sym1->Type, Type2);
|
||||
|
||||
/* Get the pointers to the next fields */
|
||||
Sym1 = Sym1->NextSym;
|
||||
Sym2 = Sym2->NextSym;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TypeComposition (Type* lhs, const Type* rhs)
|
||||
/* Recursively compose two types into lhs. The two types must have compatible
|
||||
** type or this fails with a critical check.
|
||||
*/
|
||||
{
|
||||
FuncDesc* F1;
|
||||
FuncDesc* F2;
|
||||
long LeftCount, RightCount;
|
||||
|
||||
/* Compose two types */
|
||||
while (lhs->C != T_END) {
|
||||
|
||||
/* Check if the end of the type string is reached */
|
||||
if (rhs->C == T_END) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for sanity */
|
||||
CHECK (GetUnderlyingTypeCode (lhs) == GetUnderlyingTypeCode (rhs));
|
||||
|
||||
/* Check for special type elements */
|
||||
if (IsTypeFunc (lhs)) {
|
||||
/* Compose the function descriptors */
|
||||
F1 = GetFuncDesc (lhs);
|
||||
F2 = GetFuncDesc (rhs);
|
||||
|
||||
/* If F1 has an empty parameter list (which does also mean, it is
|
||||
** not a function definition, because the flag is reset in this
|
||||
** case), its declaration is replaced by the other declaration. If
|
||||
** neither of the parameter lists is empty, we have to compose them
|
||||
** as well as other attributes.
|
||||
*/
|
||||
if ((F1->Flags & FD_EMPTY) == FD_EMPTY) {
|
||||
if ((F2->Flags & FD_EMPTY) == 0) {
|
||||
/* Copy the parameters and flags */
|
||||
TypeCopy (lhs, rhs);
|
||||
F1->Flags = F2->Flags;
|
||||
}
|
||||
} else if ((F2->Flags & FD_EMPTY) == 0) {
|
||||
/* Compose the parameter lists */
|
||||
ComposeFuncParamList (F1, F2);
|
||||
/* Prefer non-old-style */
|
||||
if ((F2->Flags & FD_OLDSTYLE) == 0) {
|
||||
F1->Flags &= ~FD_OLDSTYLE;
|
||||
}
|
||||
}
|
||||
} else if (IsTypeArray (lhs)) {
|
||||
/* Check member count */
|
||||
LeftCount = GetElementCount (lhs);
|
||||
RightCount = GetElementCount (rhs);
|
||||
|
||||
/* Set composite type if it is requested */
|
||||
if (LeftCount != UNSPECIFIED) {
|
||||
SetElementCount (lhs, LeftCount);
|
||||
} else if (RightCount != UNSPECIFIED) {
|
||||
SetElementCount (lhs, RightCount);
|
||||
}
|
||||
} else {
|
||||
/* Combine the qualifiers */
|
||||
if (IsClassPtr (lhs)) {
|
||||
++lhs;
|
||||
++rhs;
|
||||
lhs->C |= GetQualifier (rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Next type string element */
|
||||
++lhs;
|
||||
++rhs;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType)
|
||||
/* Refine the existing function descriptor with a new one */
|
||||
{
|
||||
FuncDesc* Old = GetFuncDesc (OldType);
|
||||
FuncDesc* New = GetFuncDesc (NewType);
|
||||
|
||||
CHECK (Old != 0 && New != 0);
|
||||
|
||||
if ((New->Flags & FD_EMPTY) == 0) {
|
||||
if ((Old->Flags & FD_EMPTY) == 0) {
|
||||
TypeComposition (OldType, NewType);
|
||||
} else {
|
||||
TypeCopy (OldType, NewType);
|
||||
Old->Flags &= ~FD_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return Old;
|
||||
}
|
||||
|
|
|
@ -49,9 +49,6 @@
|
|||
|
||||
|
||||
|
||||
void TypeCompatibilityDiagnostic (const Type* NewType, const Type* OldType, int IsError, const char* Msg);
|
||||
/* Print error or warning message about type conversion with proper type names */
|
||||
|
||||
void TypeConversion (ExprDesc* Expr, const Type* NewType);
|
||||
/* Do an automatic conversion of the given expression to the new type. Output
|
||||
** warnings or errors where this automatic conversion is suspicious or
|
||||
|
@ -61,6 +58,14 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType);
|
|||
void TypeCast (ExprDesc* Expr);
|
||||
/* Handle an explicit cast. */
|
||||
|
||||
void TypeComposition (Type* lhs, const Type* rhs);
|
||||
/* Recursively compose two types into lhs. The two types must have compatible
|
||||
** type or this fails with a critical check.
|
||||
*/
|
||||
|
||||
FuncDesc* RefineFuncDesc (Type* OldType, const Type* NewType);
|
||||
/* Refine the existing function descriptor with a new one */
|
||||
|
||||
|
||||
|
||||
/* End of typeconv.h */
|
||||
|
|
|
@ -64,6 +64,11 @@ $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR)
|
|||
$(if $(QUIET),echo misc/bug760.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
$(WORKDIR)/bug1437.$1.$2.prg: bug1437.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
$(if $(QUIET),echo misc/bug1437.$1.$2.prg)
|
||||
$(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||
|
||||
# should compile, but gives an error
|
||||
$(WORKDIR)/bug1209-ind-goto-rev.$1.$2.prg: bug1209-ind-goto-rev.c | $(WORKDIR)
|
||||
@echo "FIXME: " $$@ "currently does not compile."
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
/* bug #1263 - erroneous error for K & R function declaration */
|
||||
|
||||
enum E { I = 0 };
|
||||
extern int f(enum E);
|
||||
|
||||
extern int f();
|
||||
int f(e)
|
||||
enum E e;
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
extern int g(int);
|
||||
int g(e)
|
||||
enum E e;
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return f(I);
|
||||
return f(I) + g(I);
|
||||
}
|
33
test/val/bug1431.c
Normal file
33
test/val/bug1431.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
$ cl65 -Osir --codesize 180 -S -o main.s main.c
|
||||
main.c(9): Internal compiler error:
|
||||
Code generation messed up: StackPtr is -2, should be -4
|
||||
|
||||
Input: if (wcnt > btw) {
|
||||
|
||||
$ git bisect bad
|
||||
aa6fdf58b8a17b747090fb521f3d9106e0c56d1c is the first bad commit
|
||||
commit aa6fdf58b8a17b747090fb521f3d9106e0c56d1c
|
||||
Author: acqn <acqn163@outlook.com>
|
||||
Date: Mon Feb 8 09:03:19 2021 +0800
|
||||
|
||||
Addresses in constant subtraction expressions now work.
|
||||
Fixed codegen for cast type subtraction in constant expressions.
|
||||
*/
|
||||
|
||||
unsigned long fptr = 0x40001;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned int btw = 500;
|
||||
unsigned int wcnt;
|
||||
|
||||
wcnt = 512U - (fptr % 512U);
|
||||
|
||||
if (wcnt > btw) {
|
||||
wcnt = btw;
|
||||
}
|
||||
|
||||
return wcnt == 500 ? 0 : 1;
|
||||
}
|
||||
|
35
test/val/bug1437.c
Normal file
35
test/val/bug1437.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
/* bug #1437 enum declaration in a struct/union is invisible in the scope where the struct/union is declared */
|
||||
|
||||
struct nodelist1 {
|
||||
struct {
|
||||
enum { DEAD1, LIVE1, ONCE1, TWICE1 } live1;
|
||||
} s;
|
||||
} firstnode1 = {ONCE1};
|
||||
|
||||
enum nodestate2 { DEAD2, LIVE2, ONCE2, TWICE2 } live2;
|
||||
|
||||
union nodelist2 {
|
||||
enum nodestate2 live2;
|
||||
} firstnode2 = { {TWICE2} };
|
||||
|
||||
struct T {
|
||||
int I;
|
||||
int;
|
||||
enum E {
|
||||
I
|
||||
};
|
||||
};
|
||||
|
||||
int failures = 0;
|
||||
|
||||
int main (void)
|
||||
{
|
||||
if (firstnode1.s.live1 != ONCE1) {
|
||||
++failures;
|
||||
}
|
||||
if (firstnode2.live2 != TWICE2) {
|
||||
++failures;
|
||||
}
|
||||
return failures;
|
||||
}
|
35
test/val/bug1438.c
Normal file
35
test/val/bug1438.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
/* Issue #1438 fix #1439 - crash in cc65, related to delayed post-counting
|
||||
|
||||
this is an odd issue, the compile would crash *sometimes*, perhaps in one
|
||||
of ten compilation runs.
|
||||
*/
|
||||
|
||||
/* #define __fastcall__ */
|
||||
|
||||
unsigned short a[10] = {0,1,2,3,4,5,6,7,8,9};
|
||||
|
||||
unsigned short __fastcall__ func2(void)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
void func1(unsigned short *wp)
|
||||
{
|
||||
*wp++ = func2();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
func1(&a[3]);
|
||||
if (a[2] != 2) {
|
||||
return 1;
|
||||
}
|
||||
if (a[3] != 42) {
|
||||
return 1;
|
||||
}
|
||||
if (a[4] != 4) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
111
test/val/constexpr.c
Normal file
111
test/val/constexpr.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
|
||||
/*
|
||||
|
||||
This tests a couple of expressions which yield constant results. While we cant
|
||||
really check if the compiler figures out they are constant, we can still check
|
||||
if they are being compiled/evaluated correctly.
|
||||
|
||||
related:
|
||||
|
||||
pr #1424 - More compile-time constant expressions regarding object addresses
|
||||
issue #1196 - Constant expressions in general
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int fails = 0;
|
||||
|
||||
#define TESTEXPR(expr) \
|
||||
if (!(expr)) { \
|
||||
printf("fail line %d\n", __LINE__); \
|
||||
fails++; \
|
||||
}
|
||||
|
||||
#define TESTEXPRFALSE(expr) \
|
||||
if (expr) { \
|
||||
printf("fail line %d\n", __LINE__); \
|
||||
fails++; \
|
||||
}
|
||||
|
||||
int a;
|
||||
volatile int b;
|
||||
const int c = 1;
|
||||
#define d 1
|
||||
enum { e = 1 };
|
||||
int f() { return 1; }
|
||||
|
||||
/* we cant really test these at runtime (because the result is constant, but not
|
||||
* compile-time known), so compile only */
|
||||
void test0(void)
|
||||
{
|
||||
TESTEXPR(a); /* Pure: Yes; Static: No; Immutable: No; Compile-Time-Known: No */
|
||||
TESTEXPR(b); /* Pure: No?; Static: No; Immutable: No; Compile-Time-Known: No */
|
||||
TESTEXPR(&a > &b); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */
|
||||
}
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
TESTEXPR(1); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPR(c); /* Pure: Yes; Static: ???; Immutable: ???; Compile-Time-Known: ??? */
|
||||
TESTEXPR(d); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPR(e); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPR(c == c); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPRFALSE(c != c);
|
||||
TESTEXPR(f() == f()); /* Pure: Yes; Static: Yes?; Immutable: Yes; Compile-Time-Known: Yes? */
|
||||
TESTEXPRFALSE(f() != f());
|
||||
TESTEXPR(&a == &a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPRFALSE(&a != &a);
|
||||
TESTEXPR(&a != 0); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes* */
|
||||
TESTEXPRFALSE(&a == 0);
|
||||
/* in a real program we cant rely on these, but in this test we can */
|
||||
TESTEXPR(&a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */
|
||||
TESTEXPR((int)&a != 0); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: No */
|
||||
TESTEXPRFALSE((int)&a == 0);
|
||||
TESTEXPR(&a != &b); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: ??** */
|
||||
TESTEXPRFALSE(&a == &b);
|
||||
/* this may fail in a real world program, but here we can rely on it anyway */
|
||||
TESTEXPR(b == b); /* Pure: No?; Static: No; Immutable: No; Compile-Time-Known: No */
|
||||
TESTEXPRFALSE(b != b);
|
||||
/* NOT detected by the compiler as constant */
|
||||
TESTEXPR(a == a); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPRFALSE(a != a);
|
||||
TESTEXPR(f()); /* Pure: Yes; Static: Yes?; Immutable: Yes; Compile-Time-Known: Yes? */
|
||||
}
|
||||
|
||||
/* Taken from #1196 reply */
|
||||
struct S {
|
||||
int a;
|
||||
int b;
|
||||
} s[2];
|
||||
|
||||
void test2(void)
|
||||
{
|
||||
TESTEXPR((void*)&s == (void*)&s[0]);
|
||||
TESTEXPRFALSE((void*)&s != (void*)&s[0]);
|
||||
TESTEXPR(&s[0] < &s[1]);
|
||||
TESTEXPR(&s[0].b > &s[0].a);
|
||||
TESTEXPR(&s[0].b < &s[1].a);
|
||||
}
|
||||
|
||||
/* we abuse the close function here, close(-1) will return -1 */
|
||||
extern int close(int fd);
|
||||
|
||||
void test3(void)
|
||||
{
|
||||
TESTEXPR(close(-1)); /* Pure: No; Static: No; Immutable: No; Compile-Time-Known: No */
|
||||
TESTEXPR((close(-1), 1)) /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
/* Error: Scalar expression expected */
|
||||
// TESTEXPR((void)close(-1)); /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
TESTEXPR(sizeof(close(-1))); /* Pure: Yes; Static: Yes; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
/* NOT detected by the compiler as constant */
|
||||
TESTEXPRFALSE(close(-1) * 0); /* Pure: No; Static: No; Immutable: Yes; Compile-Time-Known: Yes */
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
return fails;
|
||||
}
|
41
test/val/pr1423.c
Normal file
41
test/val/pr1423.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* pr #1423 - Codegen fix for certain cases of object addresses as boolean */
|
||||
|
||||
unsigned char fails = 0;
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
int a;
|
||||
while (&a) {
|
||||
return;
|
||||
}
|
||||
fails++;
|
||||
return;
|
||||
}
|
||||
|
||||
void test2(void)
|
||||
{
|
||||
int a;
|
||||
do {
|
||||
return;
|
||||
} while (&a);
|
||||
fails++;
|
||||
return;
|
||||
}
|
||||
|
||||
void test3(void)
|
||||
{
|
||||
int a;
|
||||
for (;&a;) {
|
||||
return;
|
||||
}
|
||||
fails++;
|
||||
return;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
return fails;
|
||||
}
|
45
test/val/pr1425.c
Normal file
45
test/val/pr1425.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
/* pr #1425 - Ternary fixes */
|
||||
|
||||
unsigned char fails = 0;
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
int x = 0;
|
||||
x ? (void)x-- : (void)1;
|
||||
if (x != 0) {
|
||||
fails++;
|
||||
}
|
||||
}
|
||||
|
||||
int test2(void)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
x ? (void)x--, (void)y++ : (void)1;
|
||||
if (x != 0) {
|
||||
fails++;
|
||||
}
|
||||
if (y != 0) {
|
||||
fails++;
|
||||
}
|
||||
}
|
||||
|
||||
void test3(void)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
x ? ((void)x--, (void)y++) : (void)1;
|
||||
if (x != 0) {
|
||||
fails++;
|
||||
}
|
||||
if (y != 0) {
|
||||
fails++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
return fails;
|
||||
}
|
Loading…
Add table
Reference in a new issue