Merge pull request #3 from cc65/master

Update
This commit is contained in:
polluks2 2021-04-04 02:34:41 +02:00 committed by GitHub
commit 4c0ee3e0d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 2026 additions and 1181 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,7 +46,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export SETTIM

View file

@ -35,7 +35,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export SETTIM

View file

@ -38,7 +38,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export SETTIM

View file

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

View file

@ -28,7 +28,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export STOP

View file

@ -28,7 +28,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export STOP

View file

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

View file

@ -8,6 +8,7 @@
.export CLRCH
.export BASIN
.export CHRIN
.export STOP
.export GETIN
.export CLALL

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,9 +8,5 @@
.proc _gotox
sta SCRX
lda #$FF
sta OLD_CHARCOLOR
sta OLD_BGCOLOR
rts
.endproc

View file

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

View file

@ -10,8 +10,7 @@
initcwd:
ldx #PWD_PTR
BRK_TELEMON XVARS
BRK_TELEMON(XGETCWD)
sta ptr1
sty ptr1+1

View file

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

View 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

View file

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

View file

@ -36,7 +36,9 @@
.export CKOUT
.export CLRCH
.export BASIN
.export CHRIN
.export BSOUT
.export CHROUT
.export LOAD
.export SAVE
.export SETTIM

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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" },

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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