1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-08 15:29:37 +00:00

Merge branch 'master' into fptest

This commit is contained in:
mrdudz 2024-01-22 18:37:23 +01:00
commit 650868854d
36 changed files with 628 additions and 77 deletions

View File

@ -5,16 +5,17 @@ SYMBOLS {
__BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size
__EXEHDR__: type = import;
__BOOTLDR__: type = import;
__DEFDIR__: type = import;
__UPLOADER__: type = import;
__UPLOADERSIZE__: type = export, value = $61;
__HEADERSIZE__: type = export, value = 64;
}
MEMORY {
ZP: file = "", define = yes, start = $0000, size = $0100;
HEADER: file = %O, start = $0000, size = $0040;
HEADER: file = %O, start = $0000, size = __HEADERSIZE__;
BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__;
DIR: file = %O, start = $0000, size = 8;
MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__;
UPLDR: file = %O, define = yes, start = $BFDC, size = $005C;
DIR: file = %O, start = $0000, size = 16;
MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__;
UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
@ -30,8 +31,8 @@ SEGMENTS {
RODATA: load = MAIN, type = ro, define = yes;
DATA: load = MAIN, type = rw, define = yes;
BSS: load = MAIN, type = bss, define = yes;
UPCODE: load = UPLDR, type = ro, define = yes;
UPDATA: load = UPLDR, type = rw, define = yes;
UPCODE: load = UPLOAD, type = ro, define = yes;
UPDATA: load = UPLOAD, type = rw, define = yes;
}
FEATURES {
CONDES: type = constructor,

View File

@ -331,12 +331,28 @@ usage.
<item>_filetype
<item>_datetime
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt
<item>rebootafterexit
<item>ser_apple2_slot
<item>tgi_apple2_mix
</itemize>
<sect1>Apple IIgs specific functions in accelerator.h<p>
In addition to those, the <tt/accelerator.h/ header file contains three functions
to help determine whether the program is running on a IIgs, and change the IIgs
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
usage.
<itemize>
<item>detect_iigs
<item>get_iigs_speed
<item>set_iigs_speed
</itemize>
<sect1>Hardware access<p>
There's currently no support for direct hardware access. This does not mean

View File

@ -332,6 +332,8 @@ usage.
<item>_filetype
<item>_datetime
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt
<item>rebootafterexit
<item>ser_apple2_slot
<item>tgi_apple2_mix
@ -340,6 +342,20 @@ usage.
</itemize>
<sect1>Apple IIgs specific functions in accelerator.h<p>
In addition to those, the <tt/accelerator.h/ header file contains three functions
to help determine whether the program is running on a IIgs, and change the IIgs
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
usage.
<itemize>
<item>detect_iigs
<item>get_iigs_speed
<item>set_iigs_speed
</itemize>
<sect1>Hardware access<p>
There's currently no support for direct hardware access. This does not mean

View File

@ -71,18 +71,21 @@ function.
<item><ref id="detect_c64dtv" name="detect_c64dtv">
<item><ref id="detect_c65" name="detect_c65">
<item><ref id="detect_chameleon" name="detect_chameleon">
<item><ref id="detect_iigs" name="detect_iigs">
<item><ref id="detect_scpu" name="detect_scpu">
<item><ref id="detect_turbomaster" name="detect_turbomaster">
<item><ref id="get_c128_speed" name="get_c128_speed">
<item><ref id="get_c64dtv_speed" name="get_c64dtv_speed">
<item><ref id="get_c65_speed" name="get_c65_speed">
<item><ref id="get_chameleon_speed" name="get_chameleon_speed">
<item><ref id="get_iigs_speed" name="get_iigs_speed">
<item><ref id="get_scpu_speed" name="get_scpu_speed">
<item><ref id="get_turbomaster_speed" name="get_turbomaster_speed">
<item><ref id="set_c128_speed" name="set_c128_speed">
<item><ref id="set_c64dtv_speed" name="set_c64dtv_speed">
<item><ref id="set_c65_speed" name="set_c65_speed">
<item><ref id="set_chameleon_speed" name="set_chameleon_speed">
<item><ref id="set_iigs_speed" name="set_iigs_speed">
<item><ref id="set_scpu_speed" name="set_scpu_speed">
<item><ref id="set_turbomaster_speed" name="set_turbomaster_speed">
</itemize>
@ -104,6 +107,8 @@ function.
<itemize>
<item>_dos_type
<item><ref id="get_ostype" name="get_ostype">
<item><ref id="gmtime_dt" name="gmtime_dt">
<item><ref id="mktime_dt" name="mktime_dt">
<item>rebootafterexit
<item><ref id="videomode" name="videomode">
</itemize>
@ -3453,6 +3458,26 @@ used in presence of a prototype.
</quote>
<sect1>detect_iigs<label id="detect_iigs"><p>
<quote>
<descrip>
<tag/Function/Check whether we are running on an Apple IIgs..
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char detect_iigs (void);/
<tag/Description/The function returns a 1 if running on an Apple IIgs.
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="get_iigs_speed" name="get_iigs_speed">,
<ref id="set_iigs_speed" name="set_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>detect_scpu<label id="detect_scpu"><p>
<quote>
@ -4167,6 +4192,27 @@ header files define constants that can be used to check the return code.
</quote>
<sect1>get_iigs_speed<label id="get_iigs_speed"><p>
<quote>
<descrip>
<tag/Function/Get the current speed of the Apple IIgs.
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char get_iigs_speed (void);/
<tag/Description/The function returns the current speed of the Apple IIgs.
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
<item>See the accelerator.h header for the speed definitions.
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="detect_iigs" name="detect_iigs">,
<ref id="set_iigs_speed" name="set_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>get_scpu_speed<label id="get_scpu_speed"><p>
<quote>
@ -6985,6 +7031,30 @@ clean-up when exiting the program.
</quote>
<sect1>set_iigs_speed<label id="set_iigs_speed"><p>
<quote>
<descrip>
<tag/Function/Set the current speed of the Apple IIgs.
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char __fastcall__ set_iigs_speed (unsigned char speed);/
<tag/Description/The function sets the speed of the Apple IIgs CPU (and returns
the new speed).
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
<item>See the accelerator.h header for the speed definitions.
<item>Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are
considered SPEED_FAST).
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="detect_iigs" name="detect_iigs">,
<ref id="get_iigs_speed" name="get_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>set_scpu_speed<label id="set_scpu_speed"><p>
<quote>

View File

@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void);
* 0x01 : C64 Turbo Master cartridge present
*/
unsigned char __fastcall__ set_iigs_speed (unsigned char speed);
/* Set the speed of the Apple IIgs CPU.
*
* Possible values:
* SPEED_SLOW : 1 Mhz mode
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
* an accelerator)
*
* Any other value will be interpreted as SPEED_FAST.
*/
unsigned char get_iigs_speed (void);
/* Get the speed of the Apple IIgs CPU.
*
* Possible return values:
* SPEED_SLOW : 1 Mhz mode
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
* an accelerator)
*/
unsigned char detect_iigs (void);
/* Check whether we are running on an Apple IIgs.
*
* Possible return values:
* 0x00 : No
* 0x01 : Yes
*/
/* End of accelerator.h */
#endif

View File

@ -0,0 +1,17 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; void __fastcall__ detect_iigs(void)
;
.export _detect_iigs
.import ostype, return0, return1
.include "apple2.inc"
; Returns 1 if running on IIgs, 0 otherwise
_detect_iigs:
lda ostype
bpl :+
jmp return1
: jmp return0

View File

@ -0,0 +1,22 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; unsigned char __fastcall__ get_iigs_speed(void)
;
.export _get_iigs_speed
.import ostype, return0
.include "apple2.inc"
.include "accelerator.inc"
_get_iigs_speed:
lda ostype ; Return SLOW if not IIgs
bpl :+
lda CYAREG ; Check current setting
bpl :+
lda #SPEED_FAST
ldx #$00
rts
.assert SPEED_SLOW = 0, error
: jmp return0 ; SPEED_SLOW

View File

@ -5,7 +5,7 @@
;
.constructor initostype, 9
.export _get_ostype
.export _get_ostype, ostype
; Identify machine according to:
; Apple II Miscellaneous TechNote #7, Apple II Family Identification

View File

@ -0,0 +1,29 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; unsigned char __fastcall__ detect_iigs(unsigned char speed)
;
.export _set_iigs_speed
.import ostype, return0
.include "apple2.inc"
.include "accelerator.inc"
_set_iigs_speed:
tax ; Keep parameter
lda ostype ; Return if not IIgs
bmi :+
jmp return0
: lda CYAREG
cpx #SPEED_SLOW
beq :+
ora #%10000000
bne set_speed
: and #%01111111
set_speed:
sta CYAREG
txa
ldx #$00
rts

54
libsrc/apple2/sleep.s Normal file
View File

@ -0,0 +1,54 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; void __fastcall__ sleep(unsigned s)
;
;
.export _sleep
.import _get_iigs_speed
.import _set_iigs_speed
.import WAIT
.importzp tmp1
.include "accelerator.inc"
; This functions uses the Apple2 WAIT ROM routine to waste a certain
; amount of cycles and returns approximately after the numbers of
; seconds passed in AX.
;
; It takes 1023730 cycles when called with AX=1 (1,0007s),
; 10236364 cycles when called with AX=10 (10,006 seconds),
; 306064298 cycles with AX=300 (299.2 seconds).
;
; Caveat: IRQs firing during calls to sleep will make the sleep longer
; by the amount of cycles it takes to handle the IRQ.
;
_sleep:
stx tmp1 ; High byte of s in X
tay ; Low byte in A
ora tmp1
bne :+
rts
: jsr _get_iigs_speed ; Save current CPU speed
pha
lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT
jsr _set_iigs_speed
sleep_1s:
ldx #$0A ; Loop 10 times
sleep_100ms:
lda #$C7 ; Sleep about 99ms
jsr WAIT
lda #$0D ; About 1ms
jsr WAIT
dex
bne sleep_100ms
dey
bne sleep_1s
dec tmp1
bmi done
dey ; Down to #$FF
bne sleep_1s
done:
pla ; Restore CPU speed
jmp _set_iigs_speed

20
libsrc/apple2/wait.s Normal file
View File

@ -0,0 +1,20 @@
;
; Colin Leroy-Mira, 2024
;
; WAIT routine
;
.export WAIT
.include "apple2.inc"
.segment "LOWCODE"
WAIT:
; Switch in ROM and call WAIT
bit $C082
jsr $FCA8 ; Vector to WAIT routine
; Switch in LC bank 2 for R/O and return
bit $C080
rts

View File

@ -5,21 +5,11 @@
;
.ifdef __APPLE2ENH__
.constructor initvsync
.export _waitvsync
.import _get_ostype
.import ostype
.include "apple2.inc"
.segment "ONCE"
initvsync:
jsr _get_ostype
sta ostype
rts
.code
_waitvsync:
bit ostype
bmi iigs ; $8x
@ -53,8 +43,4 @@ iic: sei
cli
rts
.segment "INIT"
ostype: .res 1
.endif ; __APPLE2ENH__

View File

@ -18,7 +18,7 @@
.import ldeaxysp, decsp2, pushax, incsp8
.import tosandeax,decax1,tosdiveax,axlong,ldaxysp
.import lynxskip0, lynxblock,tosasreax
.import __BLOCKSIZE__
.import __BANK0BLOCKSIZE__
.importzp _FileCurrBlock
.segment "CODE"
@ -32,15 +32,15 @@
jsr ldeaxysp
jsr pusheax
ldx #$00
lda #<(__BLOCKSIZE__/1024 + 9)
lda #<(__BANK0BLOCKSIZE__/1024 + 9)
jsr tosasreax
sta _FileCurrBlock
jsr lynxblock
ldy #$05
jsr ldeaxysp
jsr pusheax
lda #<(__BLOCKSIZE__-1)
ldx #>(__BLOCKSIZE__-1)
lda #<(__BANK0BLOCKSIZE__-1)
ldx #>(__BANK0BLOCKSIZE__-1)
jsr axlong
jsr tosandeax
eor #$FF

View File

@ -17,7 +17,7 @@
.include "extzp.inc"
.export lynxskip0, lynxread0
.export lynxblock
.import __BLOCKSIZE__
.import __BANK0BLOCKSIZE__
.code
@ -88,7 +88,7 @@ lynxblock:
lda __iodat
sta IODAT
stz _FileBlockByte
lda #<($100-(>__BLOCKSIZE__))
lda #<($100-(>__BANK0BLOCKSIZE__))
sta _FileBlockByte+1
ply
plx

View File

@ -33,7 +33,7 @@ loop1:
cont1:
jsr read_byte
sta (load_ptr2),y
sta PALETTE ; feedback ;-)
sta PALETTE + 1 ; feedback ;-)
iny
bne loop1
inc load_ptr2+1
@ -69,6 +69,8 @@ again:
; last action : clear interrupt
;
exit:
lda #$10
sta INTRST
clc
rts

View File

@ -8,12 +8,18 @@
.import incsp2
.importzp sp, ptr1
.macpack cpu
.proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved)
ldy #1
lda (sp),y ; get hi byte
sta ptr1+1 ; into ptr hi
dey ; no optimization for 65C02 here to have Y=0 at exit!
dey ; dey even for for 65C02 here to have Y=0 at exit!
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (sp) ; get lo byte
.else
lda (sp),y ; get lo byte
.endif
sta ptr1 ; to ptr lo
jmp incsp2
.endproc

View File

@ -7,6 +7,8 @@
.export push0, pusha0, pushax
.importzp sp
.macpack cpu
push0: lda #0
pusha0: ldx #0
@ -29,7 +31,11 @@ pusha0: ldx #0
sta (sp),y ; (27)
pla ; (31)
dey ; (33)
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (sp) ; (37)
.else
sta (sp),y ; (38)
rts ; (44)
.endif
rts ; (44/43)
.endproc

View File

@ -121,7 +121,7 @@ static void Parse (void)
}
/* Read the declaration specifier */
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE);
/* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO ||
@ -163,19 +163,19 @@ static void Parse (void)
break;
}
/* Check if we must reserve storage for the variable. We do this,
**
** - if it is not a typedef or function,
** - if we don't had a storage class given ("int i")
** - if the storage class is explicitly specified as static,
** - or if there is an initialization.
**
** This means that "extern int i;" will not get storage allocated
** in this translation unit.
*/
/* The symbol is now visible in the file scope */
if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC &&
(Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
/* The variable is visible in the file scope */
/* Check if we must reserve storage for the variable. We do this,
**
** - if it is not a typedef or function,
** - if we don't had a storage class given ("int i")
** - if the storage class is explicitly specified as static,
** - or if there is an initialization.
**
** This means that "extern int i;" will not get storage allocated
** in this translation unit.
*/
if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE ||
(Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC ||
((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN &&
@ -189,7 +189,6 @@ static void Parse (void)
** or semicolon, it must be followed by a function body.
*/
if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) {
/* The function is now visible in the file scope */
if (CurTok.Tok == TOK_LCURLY) {
/* A definition */
Decl.StorageClass |= SC_DEF;
@ -560,6 +559,10 @@ void Compile (const char* FileName)
if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
Warning ("Static function '%s' used but never defined",
Entry->Name);
} else if ((Entry->Flags & SC_INLINE) != 0) {
Warning ("Inline function '%s' %s but never defined",
Entry->Name,
SymIsRef (Entry) ? "used" : "declared");
}
}
}

View File

@ -87,6 +87,7 @@ unsigned OptLongAssign (CodeSeg* S)
L[0] = CS_GetEntry (S, I);
if (CS_GetEntries (S, L+1, I+1, 12)) {
CodeEntry* N;
if (/* Check the opcode sequence */
L[0]->OPC == OP65_LDA &&
L[1]->OPC == OP65_STA &&
@ -119,11 +120,13 @@ unsigned OptLongAssign (CodeSeg* S)
!RegXUsed (S, I+12) &&
!CS_RangeHasLabel(S, I, 12)) {
L[1]->AM = L[11]->AM;
CE_SetArg(L[1], L[11]->Arg);
N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI);
CS_DelEntry (S, I+1);
CS_InsertEntry (S, N, I+1);
L[3]->AM = L[9]->AM;
CE_SetArg(L[3], L[9]->Arg);
N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
CS_DelEntry (S, I+3);
CS_InsertEntry (S, N, I+3);
CS_DelEntries (S, I+8, 4);
@ -179,6 +182,7 @@ unsigned OptLongCopy (CodeSeg* S)
L[0] = CS_GetEntry (S, I);
if (CS_GetEntries (S, L+1, I+1, 12)) {
CodeEntry *N;
if (L[0]->OPC == OP65_LDA &&
!strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) &&
!strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") &&
@ -210,11 +214,13 @@ unsigned OptLongCopy (CodeSeg* S)
!RegXUsed (S, I+11) &&
!CS_RangeHasLabel(S, I, 12)) {
L[1]->AM = L[11]->AM;
CE_SetArg(L[1], L[11]->Arg);
N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI);
CS_DelEntry (S, I+1);
CS_InsertEntry (S, N, I+1);
L[3]->AM = L[9]->AM;
CE_SetArg(L[3], L[9]->Arg);
N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
CS_DelEntry (S, I+3);
CS_InsertEntry (S, N, I+3);
CS_DelEntries (S, I+8, 4);

View File

@ -124,9 +124,37 @@ static unsigned ParseOneStorageClass (void)
static unsigned ParseOneFuncSpec (void)
/* Parse and return a function specifier */
{
unsigned FuncSpec = 0;
/* Check the function specifier given */
switch (CurTok.Tok) {
case TOK_INLINE:
FuncSpec = SC_INLINE;
NextToken ();
break;
case TOK_NORETURN:
FuncSpec = SC_NORETURN;
NextToken ();
break;
default:
break;
}
return FuncSpec;
}
static int ParseStorageClass (DeclSpec* Spec)
/* Parse storage class specifiers. Return true if a specifier is read even if
** it was duplicated or disallowed. */
** it was duplicated or disallowed.
*/
{
/* Check the storage class given */
unsigned StorageClass = ParseOneStorageClass ();
@ -151,6 +179,31 @@ static int ParseStorageClass (DeclSpec* Spec)
static int ParseFuncSpecClass (DeclSpec* Spec)
/* Parse function specifiers. Return true if a specifier is read even if it
** was duplicated or disallowed.
*/
{
/* Check the function specifiers given */
unsigned FuncSpec = ParseOneFuncSpec ();
if (FuncSpec == 0) {
return 0;
}
while (FuncSpec != 0) {
if ((Spec->StorageClass & FuncSpec) != 0) {
Warning ("Duplicate function specifier");
}
Spec->StorageClass |= FuncSpec;
FuncSpec = ParseOneFuncSpec ();
}
return 1;
}
static void DuplicateQualifier (const char* Name)
/* Print an error message */
{
@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
*/
{
TypeCode Q = T_QUAL_NONE;
int Continue;
int HasStorageClass;
int HasFuncSpec;
do {
/* There may be type qualifiers *before* any storage class specifiers */
@ -311,11 +365,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
*Qualifiers |= Q;
/* Parse storage class specifiers anyway then check */
Continue = ParseStorageClass (Spec);
if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) {
HasStorageClass = ParseStorageClass (Spec);
if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) {
Error ("Unexpected storage class specified");
}
} while (Continue || Q != T_QUAL_NONE);
/* Parse function specifiers anyway then check */
HasFuncSpec = ParseFuncSpecClass (Spec);
if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) {
Error ("Unexpected function specifiers");
}
} while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec);
}
@ -2375,6 +2435,14 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
/* Parse attributes for this declarator */
ParseAttribute (D);
/* 'inline' is only allowed on functions */
if (Mode != DM_ACCEPT_PARAM_IDENT &&
(D->StorageClass & SC_TYPEMASK) != SC_FUNC &&
(D->StorageClass & SC_INLINE) == SC_INLINE) {
Error ("'inline' on non-function declaration");
D->StorageClass &= ~SC_INLINE;
}
/* Check a few pre-C99 things */
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
/* Check and warn about an implicit int return in the function */
@ -2478,7 +2546,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage)
Spec->Flags &= ~DS_DEF_STORAGE;
/* Parse the type specifiers */
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC);
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC);
/* If no explicit storage class is given, use the default */
if ((Spec->StorageClass & SC_STORAGEMASK) == 0) {
@ -2495,7 +2563,9 @@ void CheckEmptyDecl (const DeclSpec* Spec)
** warning if not.
*/
{
if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) {
Error ("'inline' on empty declaration");
} else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
/* No declaration at all */
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
Warning ("Declaration does not declare anything");

View File

@ -1421,7 +1421,7 @@ static void Primary (ExprDesc* E)
} else {
/* Let's see if this is a C99-style declaration */
DeclSpec Spec;
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO);
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
Error ("Mixed declarations and code are not supported in cc65");

View File

@ -518,6 +518,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
Error ("'main' cannot be declared as __fastcall__");
}
/* main() cannot be an inline function */
if ((Func->Flags & SC_INLINE) == SC_INLINE) {
Error ("'main' cannot be declared inline");
Func->Flags &= ~SC_INLINE;
}
/* Check return type */
if (GetUnqualRawTypeCode (ReturnType) == T_INT) {
/* Determine if this is a main function in a C99 environment that
@ -685,9 +691,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Leave the lexical level */
LeaveFunctionLevel ();
/* Eat the closing brace */
ConsumeRCurly ();
/* Restore the old literal pool, remembering the one for the function */
Func->V.F.LitPool = PopLiteralPool ();
@ -699,6 +702,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
/* Switch back to the old segments */
PopSegContext ();
/* Eat the closing brace after we've done everything with the function
** definition. This way we won't have troubles with pragmas right after
** the closing brace.
*/
ConsumeRCurly();
/* Reset the current function pointer */
FreeFunction (CurrentFunc);
CurrentFunc = 0;

View File

@ -584,7 +584,7 @@ void DeclareLocals (void)
}
/* Read the declaration specifier */
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO);
/* Check variable declarations. We need distinguish between a default
** int type and the end of variable declarations. So we will do the

View File

@ -76,6 +76,7 @@ typedef enum token_t {
/* Function specifiers */
TOK_INLINE,
TOK_NORETURN,
TOK_FASTCALL,
TOK_CDECL,

View File

@ -151,6 +151,10 @@ struct CodeEntry;
#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */
#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */
/* Function specifiers */
#define SC_INLINE 0x10000000U /* Inline function */
#define SC_NORETURN 0x20000000U /* Noreturn function */
/* Label definition or reference */

View File

@ -557,8 +557,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H
static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
/* Find the symbol with the given name in the table tree that starts with T */
static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name)
/* Find the visible symbol with the given name in the table tree that starts
** with Tab.
*/
{
/* Get the hash over the name */
unsigned Hash = HashStr (Name);
@ -574,7 +576,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
}
/* Bail out if we found it */
if (E != 0) {
if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) {
return E;
}
@ -589,9 +591,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
SymEntry* FindSym (const char* Name)
/* Find the symbol with the given name */
/* Find with the given name the symbol visible in the current scope */
{
return FindSymInTree (SymTab, Name);
return FindVisibleSymInTree (SymTab, Name);
}
@ -613,9 +615,9 @@ SymEntry* FindLocalSym (const char* Name)
SymEntry* FindTagSym (const char* Name)
/* Find the symbol with the given name in the tag table */
/* Find with the given name the tag symbol visible in the current scope */
{
return FindSymInTree (TagTab, Name);
return FindVisibleSymInTree (TagTab, Name);
}
@ -1356,6 +1358,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
Name);
Entry = 0;
} else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) {
/* If we are adding the symbol in the file scope, it is now
** visible there.
*/
if (SymTab == SymTab0) {
Entry->Flags &= ~SC_LOCALSCOPE;
}
/* The C standard specifies that the result is undefined if the
** same thing has both internal and external linkage. Most
** compilers choose to either give an error at compile time, or
@ -1415,6 +1424,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
}
if (Entry == 0) {
/* Hide the symbol in the file scope if we are declaring it in a
** local scope.
*/
if (Tab == SymTab0 && SymTab != SymTab0) {
Flags |= SC_LOCALSCOPE;
}
/* Create a new entry */
Entry = NewSymEntry (Name, Flags);
@ -1439,6 +1455,16 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
Entry->V.F.WrappedCall = WrappedCall;
Entry->V.F.WrappedCallData = WrappedCallData;
}
/* A files cope function declaration with the 'extern' storage
** class or without the 'inline' specifier ensures that the
** function definition (if any) is a non-inline definition.
*/
if (SymTab == SymTab0 &&
((Flags & SC_STORAGEMASK) == SC_EXTERN ||
(Flags & SC_INLINE) == 0)) {
Entry->Flags |= SC_NOINLINEDEF;
}
}
/* Add an alias of the global symbol to the local symbol table */
@ -1575,7 +1601,7 @@ void EmitExternals (void)
if (SymIsRef (Entry) && !SymIsDef (Entry)) {
/* An import */
g_defimport (Entry->Name, Flags & SC_ZEROPAGE);
} else if (SymIsDef (Entry)) {
} else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) {
/* An export */
g_defexport (Entry->Name, Flags & SC_ZEROPAGE);
}

View File

@ -142,7 +142,7 @@ void LeaveStructLevel (void);
SymEntry* FindSym (const char* Name);
/* Find the symbol with the given name */
/* Find with the given name the symbol visible in the current scope */
SymEntry* FindGlobalSym (const char* Name);
/* Find the symbol with the given name in the global symbol table only */
@ -151,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name);
/* Find the symbol with the given name in the current symbol table only */
SymEntry* FindTagSym (const char* Name);
/* Find the symbol with the given name in the tag table */
/* Find with the given name the tag symbol visible in the current scope */
SymEntry FindStructField (const Type* TypeArray, const char* Name);
/* Find a struct/union field in the fields list.

View File

@ -0,0 +1,15 @@
/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */
void f1(void)
{
extern int a;
}
/* 'a' is still invisible in the file scope */
int main(void)
{
return a * 0; /* Usage of 'a' should be an error */
}
int a = 42;

View File

@ -133,6 +133,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR)
$(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out
$(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref
# this one requires failure with --std=c89, it fails with --std=cc65 due to
# stricter checks
$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR)
$(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg)
$(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR)
# should not compile until 3-byte struct by value tests are re-enabled
$(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR)
$(if $(QUIET),echo misc/struct-by-value.$1.$2.prg)

View File

@ -0,0 +1,21 @@
/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */
/* This one should fail even in C89 */
void f1(void)
{
extern unsigned int f();
}
/* 'f' is still invisible in the file scope */
int main(void)
{
f(); /* Should be a conflict since the implicit function type is incompatible */
return 0;
}
unsigned int f()
{
return 42;
}

View File

@ -63,6 +63,7 @@ CUSTOMSOURCES = \
# exact error output is required
ERRORSOURCES = \
custom-reference-error.c \
inline-error.c \
bug1889-missing-identifier.c \
bug2312-preprocessor-error.c

View File

@ -1,4 +1,4 @@
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature
bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected
bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration
bug1889-missing-identifier.c:6: Error: Expression expected

36
test/ref/inline-error.c Normal file
View File

@ -0,0 +1,36 @@
/* C99 inline in declarations */
inline typedef int; /* Error */
static inline int; /* Error */
inline static int a1; /* Error */
int inline (*fp1)(void); /* Error */
typedef inline int f1_t(void); /* Error */
inline int f1a(void); /* OK here warning later */
inline extern int f1b(void); /* OK here warning later */
extern inline int f1b(void); /* Same as above */
inline static int f1c(void); /* OK here warning later */
static inline int f1c(void); /* Same as above */
void foo(inline int x); /* Error */
int a = sizeof (inline int); /* TODO: better error message */
int b = sizeof (inline int (int)); /* TODO: better error message */
inline int main(void) /* Error */
{
inline typedef int; /* Error */
static inline int; /* Error */
extern inline int a2; /* Error */
int inline (*fp2)(void); /* Error */
typedef inline int f2_t(void); /* Error */
inline int f2a(void); /* OK here warning later */
inline extern int f2b(void); /* OK here warning later */
extern inline int f2b(void); /* Same as above */
f1a(); /* Still imported */
f1b(); /* Still imported */
f1c(); /* Not imported */
f2a(); /* Still imported */
f2b(); /* Still imported */
}
/* Warning: non-external inline functions declared but undefined in TU */

View File

@ -0,0 +1,20 @@
inline-error.c:3: Error: 'inline' on empty declaration
inline-error.c:4: Error: 'inline' on empty declaration
inline-error.c:5: Error: 'inline' on non-function declaration
inline-error.c:6: Error: 'inline' on non-function declaration
inline-error.c:7: Error: 'inline' on non-function declaration
inline-error.c:14: Error: Unexpected function specifiers
inline-error.c:15: Error: Mixed declarations and code are not supported in cc65
inline-error.c:16: Error: Mixed declarations and code are not supported in cc65
inline-error.c:19: Error: 'main' cannot be declared inline
inline-error.c:20: Error: 'inline' on empty declaration
inline-error.c:21: Error: 'inline' on empty declaration
inline-error.c:22: Error: 'inline' on non-function declaration
inline-error.c:23: Error: 'inline' on non-function declaration
inline-error.c:24: Error: 'inline' on non-function declaration
inline-error.c:34: Warning: Variable 'fp2' is defined but never used
inline-error.c:37: Warning: Inline function 'f1a' used but never defined
inline-error.c:37: Warning: Inline function 'f1b' used but never defined
inline-error.c:37: Warning: Static function 'f1c' used but never defined
inline-error.c:37: Warning: Inline function 'f2a' used but never defined
inline-error.c:37: Warning: Inline function 'f2b' used but never defined

38
test/val/bug2357.c Normal file
View File

@ -0,0 +1,38 @@
/* bug #2357 - Compiler produces invalid code after d8a3938
*/
unsigned long test;
unsigned long longarray[7];
void jsr_threebytes(void) {
}
/* having replaced two sty $zp with two sta $abs, but forgetting
* to update the instruction size, coptlong.c could cause a build
* error "Error: Range error (131 not in [-128..127])" if the
* computed codesize was under 126, but the real codesize was above
* 127.
* This tests verifies that the bug is fixed.
*/
unsigned char __fastcall__ foo (unsigned char res)
{
if (res == 0) {
longarray[1]=test; /* 24 bytes - but the compiler thought 22 */
longarray[2]=test; /* 48 bytes - but 44 */
longarray[3]=test; /* 72 bytes - 66 */
longarray[4]=test; /* 96 bytes - 88 */
longarray[6]=test; /* 120 bytes - 110 */
jsr_threebytes(); /* 123 - 113 */
jsr_threebytes(); /* 126 - 116 */
jsr_threebytes(); /* 129 - 119 */
}
return 0;
}
int main (void)
{
foo(42);
return 0;
}

20
test/val/inline-func.c Normal file
View File

@ -0,0 +1,20 @@
/* C99 inline */
#include <stdlib.h>
inline static int f(int x, ...)
{
return x * 2;
}
extern inline int g(int x);
int main(void)
{
return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE;
}
int g(int x)
{
return x * 3;
}