Merge branch 'cc65:master' into kim1

This commit is contained in:
Rutger van Bergen 2022-09-19 20:03:23 +02:00 committed by GitHub
commit 2b07204952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
218 changed files with 92413 additions and 995 deletions

View File

@ -96,6 +96,23 @@ jobs:
name: cc65-snapshot-win64.zip
path: cc65-snapshot-win64.zip
- name: Get the online documents repo.
uses: actions/checkout@v2
with:
repository: cc65/doc
path: doc.git
- name: Update the online documents.
run: |
cd doc.git
rm *.*
cp ../html/*.* .
git config user.name "cc65-github"
git config user.email "cc65.nomail@github.com"
git config push.default simple
git add -A
git commit -m "Updated from cc65 commit ${GITHUB_SHA}."
#git push -v
# enter secrets under "repository secrets"
- name: Upload snapshot to sourceforge
uses: nogsantos/scp-deploy@master
@ -106,6 +123,5 @@ jobs:
port: ${{ secrets.SSH_PORT }}
user: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_KEY }}
# TODO: Update docs at https://github.com/cc65/doc
# TODO: Publish snapshot zip at https://github.com/cc65/cc65.github.io

View File

@ -24,6 +24,10 @@ This is an ongoing controversial topic - everyone knows that. However, the follo
The (bash) scripts used to check the above rules can be found in ```.github/check```. You can also run all checks using ```make check```.
### identifiers and symbol names
* any symbols that are exported from source files and/or appear in header files should not be in the "_symbol" form in C, or "__symbol" form in assembly. This way we evade the problem that "_symbol" may or may not be reserved by that standard.
### misc
* 80 characters is the desired maximum width of files. But, it isn't a "strong" rule; sometimes, you will want to type longer lines, in order to keep the parts of expressions or comments together on the same line.
@ -159,6 +163,13 @@ The only exception to the above are actions that are exclusive to the github act
* the printf family of function does not completely implement all printf modifiers and does not behave as expected in some cases - all this should be documented in detail
## Floating point support
The first step is implementing the datatype "float" as IEEE488 floats. Help welcomed!
* WIP compiler/library changes are here: https://github.com/cc65/cc65/pull/1777
* free software library with testbench is here: http://www.jhauser.us/arithmetic/
## Test suite
* specific tests to check the optimizer (rather than the codegenerator) are needed.

View File

@ -1,7 +1,7 @@
# About cc65
cc65 is a complete cross development package for 65(C)02 systems, including
a powerful macro assembler, a C compiler, linker, librarian and several
a powerful macro assembler, a C compiler, linker, archiver and several
other tools. cc65 has C and runtime library support for many of the old 6502 machines.
For details look at the [Website](https://cc65.github.io).

View File

@ -538,11 +538,11 @@ NMIVec := $0318
; YM2151 audio chip
.struct YM2151
.org $9F40
.union
STATUS .byte
ADDR .byte
.endunion
.union
DATA .byte
STATUS .byte
.endunion
.endstruct
; X16 Emulator device

View File

@ -1,3 +1,7 @@
FEATURES {
STARTADDRESS: default = $1000;
}
SYMBOLS {
__ORIXHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2K stack
@ -7,7 +11,7 @@ SYMBOLS {
MEMORY {
ZP: file = "", define = yes, start = $00B0, size = $003A;
ORIXHDR: file = %O, type = ro, start = $0000, size = $001F;
MAIN: file = %O, define = yes, start = $0800, size = __RAMEND__ - __MAIN_START__;
MAIN: file = %O, define = yes, start = %S, size = __RAMEND__ - __MAIN_START__;
BSS: file = "", start = __ONCE_RUN__, size = __RAMEND__ - __STACKSIZE__ - __ONCE_RUN__;
}
SEGMENTS {

View File

@ -6,7 +6,7 @@ SYMBOLS {
}
MEMORY {
ZP: file = "", start = $0002, size = $001A, define = yes;
LOADADDR: file = %O, start = $1001, size = $0002;
LOADADDR: file = %O, start = %S - 2, size = $0002;
MAIN: file = %O, start = %S, size = $0DF3 - %S;
}
SEGMENTS {

View File

@ -442,19 +442,7 @@ package delivers the same feature.</em>
You can switch back to the ATASCII mapping by including
"<tt/atari_atascii_charmap.h/".
A final note: Since cc65 has currently some difficulties with string merging
under different mappings, defining remapped strings works only flawlessly
with static array initialization:
<tscreen><verb>
#include &lt;atari_screen_charmap.h&gt;
char pcScreenMappingString[] = "Hello Atari!";
#include &lt;atari_atascii_charmap.h&gt;
char pcAtasciiMappingString[] = "Hello Atari!";
</verb></tscreen>
delivers correct results, while
Example:
<tscreen><verb>
#include &lt;atari_screen_charmap.h&gt;
@ -464,8 +452,6 @@ char* pcScreenMappingString = "Hello Atari!";
char* pcAtasciiMappingString = "Hello Atari!";
</verb></tscreen>
does not.
<sect1>Keyboard codes<p>
For direct keyboard scanning in conjunction with e.g. the OS location "CH" (764/$2FC),

View File

@ -1202,17 +1202,34 @@ The compiler defines several macros at startup:
This macro is defined if the target is the Commodore Plus/4 (-t plus4).
<tag><tt>__STDC_HOSTED__</tt></tag>
This macro is expands to the integer constant 1.
<tag><tt>__SIM6502__</tt></tag>
This macro is defined if the target is sim65 in 6502 mode (-t sim6502).
<tag><tt>__SIM65C02__</tt></tag>
This macro is defined if the target is sim65 in 65C02 mode (-t sim65c02).
<tag><tt>__STDC_HOSTED__</tt></tag>
This macro expands to the integer constant 1.
<tag><tt>__STDC_NO_ATOMICS__</tt></tag>
This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65).
<tag><tt>__STDC_NO_COMPLEX__</tt></tag>
This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65).
<tag><tt>__STDC_NO_THREADS__</tt></tag>
This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65).
<tag><tt>__STDC_NO_VLA__</tt></tag>
This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65).
<tag><tt>__SUPERVISION__</tt></tag>
This macro is defined if the target is the Supervision (-t supervision).

View File

@ -145,6 +145,7 @@ extern void c64_ram_emd[];
extern void c64_ramcart_emd[];
extern void c64_reu_emd[];
extern void c64_vdc_emd[];
extern void c64_rrr_emd[];
extern void dtv_himem_emd[];
extern void c64_hitjoy_joy[];
extern void c64_numpad_joy[];

View File

@ -285,11 +285,11 @@ struct __vera {
/* Audio chip */
struct __ym2151 {
unsigned char reg; /* Register number for data */
union {
unsigned char reg; /* Register number for data */
unsigned char data;
unsigned char status; /* Busy flag */
};
unsigned char data;
};
#define YM2151 (*(volatile struct __ym2151 *)0x9F40)

View File

@ -83,7 +83,6 @@
#define TGI_COLOR_YELLOW COLOR_YELLOW
#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN
#define TGI_COLOR_GREEN COLOR_GREEN
#define TGI_COLOR_DARKBROWN COLOR_DARKBROWN
#define TGI_COLOR_PURPLE COLOR_PURPLE
#define TGI_COLOR_BLUE COLOR_BLUE
#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE

View File

@ -148,7 +148,7 @@ unsigned char __fastcall__ ser_open (const struct ser_params* params);
/* "Open" the port by setting the port parameters and enable interrupts. */
unsigned char ser_close (void);
/* "Close" the port. Clear buffers and and disable interrupts. */
/* "Close" the port. Clear buffers and disable interrupts. */
unsigned char __fastcall__ ser_get (char* b);
/* Get a character from the serial port. If no characters are available, the
@ -171,6 +171,3 @@ unsigned char __fastcall__ ser_ioctl (unsigned char code, void* data);
/* End of serial.h */
#endif

View File

@ -54,7 +54,7 @@ typedef unsigned size_t;
/* NULL pointer */
#ifndef _HAVE_NULL
#define NULL 0
#define NULL ((void *) 0)
#define _HAVE_NULL
#endif

View File

@ -57,7 +57,9 @@
;----------------------------------------------------------------------------
; I/O definitions
ACIA = $C088
Offset = $8F ; Move 6502 false read out of I/O to page $BF
ACIA = $C088-Offset
ACIA_DATA = ACIA+0 ; Data register
ACIA_STATUS = ACIA+1 ; Status register
ACIA_CMD = ACIA+2 ; Command register
@ -197,6 +199,7 @@ SER_OPEN:
asl
asl
asl
adc Offset ; Assume carry to be clear
tax
; Check if the handshake setting is valid

363
libsrc/c64/emd/c64-rrr.s Normal file
View File

@ -0,0 +1,363 @@
;
; Extended Memory Driver for the Action Replay/Retro Replay RAM
;
; original Version 1.0 by Johannes Braun 2006-08-22 <hannenz@freenet.de>
; ------------------------------------------------------------------------
.include "zeropage.inc"
.include "em-kernel.inc"
.include "em-error.inc"
.macpack generic
.macpack module
; ------------------------------------------------------------------------
c64_ram = ptr1 ; use some more expressive identifiers...
rr_ram = ptr2
len = ptr3
aux = ptr4
temp = tmp1
Lo_Mem = $0100 ; location of Lo_Code (must be below $1000 or above $e000)
RRMODE_OFF = $02
RRMODE_CART_RAM = $23
; ------------------------------------------------------------------------
; Header. Includes jump table
module_header _c64_rrr_emd
; Driver signature
.byte $65, $6d, $64 ; "emd"
.byte EMD_API_VERSION ; EM API version number
; Library reference
.addr $0000
; Jump table
.addr INSTALL
.addr UNINSTALL
.addr PAGECOUNT
.addr MAP
.addr USE
.addr COMMIT
.addr COPYFROM
.addr COPYTO
; ------------------------------------------------------------------------
; Data.
.bss
window: .res 256 ; the memory window (256 bytes)
pagecount: .res 1 ; Number of available pages
.rodata
dummy:
.word window ; a "pseudo"-em_copy_struct, used by em_map/ em_commit
.byte 0 ; to pass over to COPYTO/COPYFROM
curpage:
.byte $ff ; just this byte is changed according to the desired page
.byte 0
.word 256
.code
;----------------------------------------------------------------------------------------
;unsigned char __fastcall__ em_install(void *driver);
;returns an error code
;----------------------------------------------------------------------------------------
INSTALL:
ldx #c2-c1
: lda c1,x
sta Lo_Mem,x
dex
bpl :-
;ldx #$ff
stx curpage ; invalidate current page
ldy #RRMODE_OFF
sei
jmp Lo_Mem+8 ; jump to the code below
; copied to Lo_Mem
c1:
;detectmodes:
.byte RRMODE_CART_RAM | $00
.byte RRMODE_CART_RAM | $08
.byte RRMODE_CART_RAM | $10
.byte RRMODE_CART_RAM | $18
.byte RRMODE_CART_RAM | $80
.byte RRMODE_CART_RAM | $88
.byte RRMODE_CART_RAM | $90
.byte RRMODE_CART_RAM | $98
; first save c64 memory
lda $8888
pha
; tag c64 memory
lda #$00
sta $8888
ldx #$07
:
; try accessing rr-ram
;lda detectmodes, x
lda Lo_Mem, x
sta $de00
; tag (hopefully) rr memory
txa
ora #$80
sta $8888
dex
bpl :-
;ldy #RRMODE_OFF
sty $de00
; now if C64 memory is $80, there is no AR/RR
; if C64 memory is $00, there is a AR/RR.
lda $8888
beq detectpages
lda #0
beq hasnopages
detectpages:
; we can now read the highest available bank nr from the highest bank :)
lda #RRMODE_CART_RAM | $98
sta $de00
ldx $8888
inx
txa
; 8k = 32 pages
asl
asl
asl
asl
asl
hasnopages:
;ldy #RRMODE_OFF
sty $de00 ; c64 ram again
sta pagecount
; restore c64 memory
pla
sta $8888
cli
ldx pagecount
beq no
; no error
lda #0
tax
rts
no:
lda #4 ; return #4: error code for "device not present"
rts
c2:
;----------------------------------------------------------------------------------------
;void em_uninstall(void);
;----------------------------------------------------------------------------------------
UNINSTALL:
return_null:
lda #$00
tax
rts
;----------------------------------------------------------------------------------------
;unsigned __fastcall__ em_pagecount(void);
;----------------------------------------------------------------------------------------
PAGECOUNT:
lda pagecount ; always return 32kb (128 pages)
ldx #$00
rts
;----------------------------------------------------------------------------------------
;void* __fastcall__ em_use(unsigned page);
;----------------------------------------------------------------------------------------
USE:
cmp #$80 ; valid page?
bcs return_null ; no, return NULL pointer
sta curpage ; set to current page
return_win:
lda #<window ; return pointer to window
ldx #>window
return: rts
;----------------------------------------------------------------------------------------
;void* __fastcall__ em_map(unsigned page);
;----------------------------------------------------------------------------------------
MAP:
cmp pagecount
bcs return_null
sta curpage
lda #<dummy ; load .A/.X with adress of data for COPYFROM-call (which expects the
ldx #>dummy ; adress in .A/.X)
jsr COPYFROM
bcs return_win ; function returns pointer to window (returns always with carry set!)
;----------------------------------------------------------------------------------------
;void __fastcall__ em_commit(void);
;----------------------------------------------------------------------------------------
COMMIT:
lda curpage
cmp pagecount
bcs return
lda #<dummy ; load .A/.X with adress of data for COPYTO-call (which expects the
ldx #>dummy ; adress in .A/.X)
;----------------------------------------------------------------------------------------
;void __fastcall__ em_copyto (struct em_copy *copy_data);
;----------------------------------------------------------------------------------------
COPYTO:
jsr get_struct_data ;read the parameters passed in the em_struct pointed to by .A/.X upon call
;copy the main copyto routine into Lo_Mem
ldy #Lo_Code1_End - Lo_Code1
: lda Lo_Code1-1,y
sta Lo_Mem-1,y
dey
bne :-
COMMON:
sei
jmp Lo_Mem
;this part will be executed in Lo_Mem (!) by COPYFROM
Lo_Code2:
; copy byte rr -> c64
stx $de00 ;map in rr-ram
lda (rr_ram),y ;get byte from rr-ram
sty $de00 ;RR-ROM will be mapped to $8000-$a000 but write access will go to c64-ram anyway!!
sta (c64_ram),y ;and write to c64-ram
nop ;pad to same size as Lo_Code1
nop
Lo_Code2_End:
;this part will be executed in Lo_Mem (!) by COPYTO
Lo_Code1:
; copy byte c64 -> rr
lda (c64_ram),y ;read 1 byte from c64-ram
stx $de00 ;map in rr-ram
sta (rr_ram),y ;write byte to rr-ram
lda #$02 ;map in c64-ram again
sta $de00
;12 bytes
;this part is common for both COPYFROM/COPYTO and executed in Lo_Mem, too
Lo_Code_Common:
inc c64_ram ;increase pointers
bne :+
inc c64_ram+1
: inc rr_ram
bne @skip
inc rr_ram+1
lda rr_ram+1
cmp #$a0 ;wrap around 16k boundary in rr-ram window ($8000-$a000)
bne @skip
lda #$80 ;reset pointer to $8000
sta rr_ram+1
txa ;adjust value in .X to map in next 16k-bank in rr-ram
adc #7 ;carry is set because of former CMP, so it adds 8
tax
;27 bytes
@skip: lda c64_ram
cmp len
lda c64_ram+1
sbc len+1
bcc Lo_Code1
lda #2 ;CHANGE to LDA #0 if driver is called from ROM
sta $de00
cli
rts ;17 bytes = 56 bytes Lo_Code ($38)
Lo_Code1_End:
;----------------------------------------------------------------------------------------
;void __fastcall__ em_copyfrom(struct em_copy *copy_data);
;copy from extended memory into linear memory
;----------------------------------------------------------------------------------------
COPYFROM:
jsr get_struct_data
ldy #Lo_Code2_End - Lo_Code2 ;copy routine into Lo_Mem
: lda Lo_Code2-1,y
sta Lo_Mem-1,y
dey
bne :-
ldy #Lo_Code1_End-Lo_Code_Common
: lda Lo_Code_Common-1,y
sta Lo_Mem+11,y
dey
bne :-
beq COMMON ;and execute...
;----------------------------------------------------------------------------------------
;read the struct data located at (.A/.X)
;and setup parameters for stash/ fetch operation
;----------------------------------------------------------------------------------------
get_struct_data:
;read and process the values from the em_copy struct passed to as parameters rameter to the
;functions em_copyto and em_copyfrom
sta aux ;store adress of struct (passed in .A/.X) into a zp pointer
stx aux+1
ldy #0 ;index 0
lda (aux),y ;read c64-adress lo
sta c64_ram
iny
lda (aux),y ;read c64-adress hi
sta c64_ram+1 ;(c64_ram) --> points to c64-adress space
iny
lda (aux),y ;read rr-adress lo
sta rr_ram
iny
lda (aux),y ;rr-adress hi
pha ;remember
and #$1f
ora #$80 ;adjust into 16k-window ($8000-$a000)
sta rr_ram+1
pla ;re-get hi byte of rr-adress
and #$60 ;isolate bits 5 and 6
lsr
lsr ;shift into bits 3 and 4
ora #$23 ;set bit 5 (select ram) and 1+2 (game/exrom setting for ULTIMAX-mode)
tax ;.X has now the value to write into $de00 to acess rr-ram at desired 16k-bank
iny
iny ;skip unused byte
lda (aux),y ;read length lo-byte
clc
adc c64_ram ;add to c64-addres
sta len
iny
lda (aux),y ;length hi-byte
adc c64_ram+1
sta len+1 ;tmp2: length, tmp3 contains end adress of transfer in c64-ram.
rts
;55 bytes

View File

@ -42,15 +42,18 @@
/* Code */
/*****************************************************************************/
/*
CAUTION: we need to reserve enough space to be able to hold the maximum
length string:
1234567890123456789012345678901234567
"Wednesday September ..1 00:00:00 1970"
*/
char* __fastcall__ asctime (const struct tm* timep)
{
static char buf[26];
static char buf[38];
/* Format into given buffer and return the result */
return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0;
}

View File

@ -11,6 +11,7 @@
;
.export _isascii
.import return0
_isascii:
asl a ; high-bit to carry
@ -19,6 +20,4 @@ _isascii:
adc #$FF ; calculate return value based on carry
rts
@L1: lda #$00 ; return false
tax
rts
@L1: jmp return0 ; return false

View File

@ -5,17 +5,20 @@
; /* Wait for the start of the next video field. */
;
; VERA's vertical sync causes IRQs which increment the jiffy timer.
;
; Updated by ZeroByteOrg to use Kernal API RDTIM to retreive the TIMER variable
;
.export _waitvsync
.importzp tmp1
.import RDTIM
.include "cx16.inc"
_waitvsync:
ldx RAM_BANK ; (TIMER is in RAM bank 0)
stz RAM_BANK
lda TIMER + 2
: cmp TIMER + 2
beq :- ; Wait for next jiffy
stx RAM_BANK
rts
.proc _waitvsync: near
jsr RDTIM
sta tmp1
keep_waiting:
jsr RDTIM
cmp tmp1
beq keep_waiting
rts
.endproc

View File

@ -1063,7 +1063,7 @@ static char StackHandler (void)
break;
case 'a':
#ifdef CH_CURS_UP:
#ifdef CH_CURS_UP
case CH_CURS_UP:
#endif
--StackAddr;

View File

@ -29,6 +29,6 @@ _tgi_unload:
jmp _mod_free ; Free the driver
no_driver:
lda #<TGI_ERR_NO_DRIVER
lda #TGI_ERR_NO_DRIVER
sta _tgi_error
rts

View File

@ -1298,7 +1298,7 @@ static void EmitCode (EffAddr* A)
break;
case 2:
if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y))) {
if (CPU == CPU_65816 && (A->AddrModeBit & (AM65_ABS | AM65_ABS_X | AM65_ABS_Y | AM65_ABS_X_IND))) {
/* This is a 16 bit mode that uses an address. If in 65816,
** mode, force this address into 16 bit range to allow
** addressing inside a 64K segment.

View File

@ -489,23 +489,6 @@ void MacDef (unsigned Style)
** the .LOCAL command is detected and removed, at this time.
*/
while (1) {
/* Check for include */
if (CurTok.Tok == TOK_INCLUDE && Style == MAC_STYLE_CLASSIC) {
/* Include another file */
NextTok ();
/* Name must follow */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
} else {
SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
/* Error opening the file, skip remainder of line */
SkipUntilSep ();
}
}
NextTok ();
}
/* Check for end of macro */
if (Style == MAC_STYLE_CLASSIC) {
/* In classic macros, only .endmacro is allowed */

View File

@ -103,6 +103,7 @@
<ClInclude Include="cc65\macrotab.h" />
<ClInclude Include="cc65\opcodes.h" />
<ClInclude Include="cc65\output.h" />
<ClInclude Include="cc65\ppexpr.h" />
<ClInclude Include="cc65\pragma.h" />
<ClInclude Include="cc65\preproc.h" />
<ClInclude Include="cc65\reginfo.h" />
@ -182,6 +183,7 @@
<ClCompile Include="cc65\main.c" />
<ClCompile Include="cc65\opcodes.c" />
<ClCompile Include="cc65\output.c" />
<ClCompile Include="cc65\ppexpr.c" />
<ClCompile Include="cc65\pragma.c" />
<ClCompile Include="cc65\preproc.c" />
<ClCompile Include="cc65\reginfo.c" />

View File

@ -49,21 +49,6 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
token_t Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@ -605,6 +590,12 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
/* Normal straight 'op=' */
OpAssignArithmetic (Gen, Expr, Op);
}
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@ -725,4 +716,10 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
/* Expression is an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}

View File

@ -1797,7 +1797,6 @@ void CE_GenRegInfo (CodeEntry* E, RegContents* InputRegs)
}
} else if (strcmp (E->Arg, "tosaslax") == 0) {
if (RegValIsKnown (In->RegA) && (In->RegA & 0x0F) >= 8) {
printf ("Hey!\n");
Out->RegA = 0;
}
} else if (strcmp (E->Arg, "tosorax") == 0) {

View File

@ -461,7 +461,8 @@ void g_importstartup (void)
void g_importmainargs (void)
/* Forced import of a special symbol that handles arguments to main */
/* Forced import of a special symbol that handles arguments to main. This will
happen only when the compiler sees a main function that takes arguments. */
{
AddTextLine ("\t.forceimport\tinitmainargs");
}
@ -4920,5 +4921,25 @@ void g_switch (Collection* Nodes, unsigned DefaultLabel, unsigned Depth)
void g_asmcode (struct StrBuf* B)
/* Output one line of assembler code. */
{
AddCodeLine ("%.*s", (int) SB_GetLen (B), SB_GetConstBuf (B));
int len = (int) SB_GetLen(B);
const char *buf = SB_GetConstBuf(B);
/* remove whitespace at end of line */
/* NOTE: This masks problems in ParseInsn(), which in some cases seems to
rely on no whitespace being present at the end of a line in generated
code (see issue #1252). However, it generally seems to be a good
idea to remove trailing whitespace from (inline) assembly, so we
do it anyway. */
while (len) {
switch (buf[len - 1]) {
case '\n':
case ' ':
case '\t':
--len;
continue;
}
break;
}
AddCodeLine ("%.*s", len, buf);
}

View File

@ -82,8 +82,11 @@ static void Parse (void)
SymEntry* Entry;
FuncDesc* FuncDef = 0;
/* Go... */
NextToken ();
/* Initialization for deferred operations */
InitDeferredOps ();
/* Fill up the next token with a bogus semicolon and start the tokenizer */
NextTok.Tok = TOK_SEMI;
NextToken ();
/* Parse until end of input */
@ -207,7 +210,7 @@ static void Parse (void)
/* Allow initialization */
if (CurTok.Tok == TOK_ASSIGN) {
/* This is a definition */
/* This is a definition with storage */
if (SymIsDef (Entry)) {
Error ("Global variable '%s' has already been defined",
Entry->Name);
@ -218,14 +221,16 @@ static void Parse (void)
** void types in ISO modes.
*/
if (Size == 0) {
if (!IsTypeVoid (Decl.Type)) {
if (!IsEmptiableObjectType (Decl.Type)) {
if (!IsTypeArray (Decl.Type)) {
/* Size is unknown and not an array */
Error ("Variable '%s' has unknown size", Decl.Ident);
Error ("Cannot initialize variable '%s' of unknown size", Decl.Ident);
}
} else if (IS_Get (&Standard) != STD_CC65) {
/* We cannot declare variables of type void */
Error ("Illegal type for variable '%s'", Decl.Ident);
Error ("Illegal type '%s' for variable '%s'",
GetFullTypeName (Decl.Type),
Decl.Ident);
}
}
@ -249,16 +254,26 @@ static void Parse (void)
ParseInit (Entry->Type);
} else {
/* This is a declaration */
if (IsTypeVoid (Decl.Type)) {
/* We cannot declare variables of type void */
Error ("Illegal type for variable '%s'", Decl.Ident);
Entry->Flags &= ~(SC_STORAGE | SC_DEF);
} else if (Size == 0 && SymIsDef (Entry)) {
} else if (Size == 0 && SymIsDef (Entry) && !IsEmptiableObjectType (Decl.Type)) {
/* Size is unknown. Is it an array? */
if (!IsTypeArray (Decl.Type)) {
Error ("Variable '%s' has unknown size", Decl.Ident);
}
} else {
/* Check for enum forward declaration.
** Warn about it when extensions are not allowed.
*/
if (Size == 0 && IsTypeEnum (Decl.Type)) {
if (IS_Get (&Standard) != STD_CC65) {
Warning ("ISO C forbids forward references to 'enum' types");
}
}
/* A global (including static) uninitialized variable is
** only a tentative definition. For example, this is valid:
** int i;
@ -285,17 +300,9 @@ static void Parse (void)
}
/* Make the symbol zeropage according to the segment address size */
if ((Entry->Flags & SC_EXTERN) != 0) {
if ((Entry->Flags & SC_STATIC) != 0) {
if (GetSegAddrSize (GetSegName (CS->CurDSeg)) == ADDR_SIZE_ZP) {
Entry->Flags |= SC_ZEROPAGE;
/* Check for enum forward declaration.
** Warn about it when extensions are not allowed.
*/
if (Size == 0 && IsTypeEnum (Decl.Type)) {
if (IS_Get (&Standard) != STD_CC65) {
Warning ("ISO C forbids forward references to 'enum' types");
}
}
}
}
@ -334,6 +341,9 @@ static void Parse (void)
}
}
/* Done with deferred operations */
DoneDeferredOps ();
}
@ -386,6 +396,11 @@ void Compile (const char* FileName)
DefineNumericMacro ("__EAGERLY_INLINE_FUNCS__", 1);
}
/* Placeholders for __FILE__, __LINE__ and __COUNTER__ macros */
DefineTextMacro ("__FILE__", "");
DefineTextMacro ("__LINE__", "");
DefineTextMacro ("__COUNTER__", "");
/* __TIME__ and __DATE__ macros */
Time = time (0);
TM = localtime (&Time);
@ -399,7 +414,13 @@ void Compile (const char* FileName)
/* DefineNumericMacro ("__STDC__", 1); <- not now */
DefineNumericMacro ("__STDC_HOSTED__", 1);
InitDeferredOps ();
/* Stuff unsupported */
if (IS_Get (&Standard) > STD_C99) {
DefineNumericMacro ("__STDC_NO_ATOMICS__", 1);
DefineNumericMacro ("__STDC_NO_COMPLEX__", 1);
DefineNumericMacro ("__STDC_NO_THREADS__", 1);
DefineNumericMacro ("__STDC_NO_VLA__", 1);
}
/* Create the base lexical level */
EnterGlobalLevel ();
@ -419,6 +440,9 @@ void Compile (const char* FileName)
/* Generate the code generator preamble */
g_preamble ();
/* Init preprocessor */
InitPreprocess ();
/* Open the input file */
OpenMainFile (FileName);
@ -429,10 +453,8 @@ void Compile (const char* FileName)
OpenOutputFile ();
/* Preprocess each line and write it to the output file */
while (NextLine ()) {
Preprocess ();
WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
while (PreprocessNextLine ())
{ /* Nothing */ }
/* Close the output file */
CloseOutputFile ();
@ -490,9 +512,11 @@ void Compile (const char* FileName)
}
}
}
}
DoneDeferredOps ();
/* Done with preprocessor */
DonePreprocess ();
if (Debug) {
PrintMacroStats (stdout);

View File

@ -805,7 +805,11 @@ unsigned CheckedSizeOf (const Type* T)
{
unsigned Size = SizeOf (T);
if (Size == 0) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T));
if (HasUnknownSize (T + 1)) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T));
} else {
Error ("Size of type '%s' is 0", GetFullTypeName (T));
}
Size = SIZEOF_CHAR; /* Don't return zero */
}
return Size;
@ -821,7 +825,11 @@ unsigned CheckedPSizeOf (const Type* T)
{
unsigned Size = PSizeOf (T);
if (Size == 0) {
Error ("Size of type '%s' is unknown", GetFullTypeName (T + 1));
if (HasUnknownSize (T + 1)) {
Error ("Pointer to type '%s' of unknown size", GetFullTypeName (T + 1));
} else {
Error ("Pointer to type '%s' of 0 size", GetFullTypeName (T + 1));
}
Size = SIZEOF_CHAR; /* Don't return zero */
}
return Size;
@ -999,6 +1007,25 @@ const Type* PtrConversion (const Type* T)
const Type* StdConversion (const Type* T)
/* If the type is a function, convert it to pointer to function. If the
** expression is an array, convert it to pointer to first element. If the
** type is an integer, do integeral promotion. Otherwise return T.
*/
{
if (IsTypeFunc (T)) {
return AddressOf (T);
} else if (IsTypeArray (T)) {
return AddressOf (GetElementType (T));
} else if (IsClassInt (T)) {
return IntPromotion (T);
} else {
return T;
}
}
const Type* IntPromotion (const Type* T)
/* Apply the integer promotions to T and return the result. The returned type
** string may be T if there is no need to change it.

View File

@ -368,6 +368,12 @@ const Type* PtrConversion (const Type* T);
** return T.
*/
const Type* StdConversion (const Type* T);
/* If the type is a function, convert it to pointer to function. If the
** expression is an array, convert it to pointer to first element. If the
** type is an integer, do integeral promotion. Otherwise return T.
*/
const Type* IntPromotion (const Type* T);
/* Apply the integer promotions to T and return the result. The returned type
** string may be T if there is no need to change it.

View File

@ -48,13 +48,6 @@
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
token_t Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/* Descriptors for the operations */
static GenDesc GenPASGN = { TOK_PLUS_ASSIGN, GEN_NOPUSH, g_add };
static GenDesc GenSASGN = { TOK_MINUS_ASSIGN, GEN_NOPUSH, g_sub };
@ -243,7 +236,7 @@ static const GenDesc* FindGen (token_t Tok, const GenDesc* Table)
/* Find a token in a generator table */
{
while (Table->Tok != TOK_INVALID) {
if (Table->Tok == Tok) {
if ((token_t)Table->Tok == Tok) {
return Table;
}
++Table;
@ -311,13 +304,9 @@ void PushAddr (const ExprDesc* Expr)
static void WarnConstCompareResult (const ExprDesc* Expr)
/* If the result of a comparison is constant, this is suspicious when not in
** preprocessor mode.
*/
/* If the result of a comparison is constant, this is suspicious */
{
if (!Preprocessing &&
!ED_NeedsConst (Expr) &&
IS_Get (&WarnConstComparison) != 0) {
if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) {
Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false");
}
}
@ -649,6 +638,9 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr)
/* Sufficient to pop the processor flags */
AddCodeLine ("plp");
}
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@ -772,9 +764,10 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED)
} else {
/* No prototype available. Convert array to "pointer to first
** element", and function to "pointer to function".
** element", function to "pointer to function" and do integral
** promotion if necessary.
*/
Expr.Type = PtrConversion (Expr.Type);
TypeConversion (&Expr, StdConversion (Expr.Type));
}
@ -822,6 +815,9 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED)
}
}
/* Propagate viral flags */
ED_PropagateFrom (ED, &Expr);
/* Check for end of argument list */
if (CurTok.Tok != TOK_COMMA) {
break;
@ -1064,6 +1060,9 @@ static void FunctionCall (ExprDesc* Expr)
}
Expr->Type = ReturnType;
/* We assume all function calls had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@ -1072,58 +1071,19 @@ static void Primary (ExprDesc* E)
/* This is the lowest level of the expression parser. */
{
SymEntry* Sym;
/* Character and integer constants. */
if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) {
E->IVal = CurTok.IVal;
E->Flags |= E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Floating point constant */
if (CurTok.Tok == TOK_FCONST) {
E->V.FVal = CurTok.FVal;
E->Flags |= E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
if (CurTok.Tok == TOK_LPAREN) {
NextToken ();
hie0 (E);
ConsumeRParen ();
return;
}
/* If we run into an identifier in preprocessing mode, we assume that this
** is an undefined macro and replace it by a constant value of zero.
*/
if (Preprocessing && CurTok.Tok == TOK_IDENT) {
NextToken ();
ED_MakeConstAbsInt (E, 0);
return;
}
/* All others may only be used if the expression evaluation is not called
** recursively by the preprocessor.
*/
if (Preprocessing) {
/* Illegal expression in PP mode */
Error ("Preprocessor expression expected");
ED_MakeConstAbsInt (E, 1);
return;
}
unsigned Flags = E->Flags & E_MASK_KEEP_MAKE;
switch (CurTok.Tok) {
case TOK_LPAREN:
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
hie0 (E);
ConsumeRParen ();
break;
case TOK_BOOL_AND:
/* A computed goto label address */
if (IS_Get (&Standard) >= STD_CC65) {
@ -1133,7 +1093,7 @@ static void Primary (ExprDesc* E)
/* output its label */
E->Flags = E_RTYPE_RVAL | E_LOC_CODE | E_ADDRESS_OF;
E->Name = Entry->V.L.Label;
E->Type = NewPointerTo (type_void);
E->Type = type_void_p;
NextToken ();
} else {
Error ("Computed gotos are a C extension, not supported with this --standard");
@ -1157,9 +1117,9 @@ static void Primary (ExprDesc* E)
/* Cannot use type symbols */
Error ("Variable identifier expected");
/* Assume an int type to make E valid */
E->Flags |= E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
return;
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
break;
}
/* Mark the symbol as referenced */
@ -1172,11 +1132,7 @@ static void Primary (ExprDesc* E)
if ((Sym->Flags & SC_CONST) == SC_CONST) {
/* Enum or some other numeric constant */
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->IVal = Sym->V.ConstVal;
} else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
E->IVal = Sym->V.ConstVal;
} else if ((Sym->Flags & SC_AUTO) == SC_AUTO) {
/* Local variable. If this is a parameter for a variadic
** function, we have to add some address calculations, and the
@ -1191,6 +1147,10 @@ static void Primary (ExprDesc* E)
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->IVal = Sym->V.Offs;
}
} else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
} else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) {
/* Register variable, zero page based */
E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL;
@ -1199,10 +1159,10 @@ static void Primary (ExprDesc* E)
/* Static variable */
if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_DECL)) {
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
E->Name = (uintptr_t) Sym->Name;
} else {
E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
E->Name = Sym->V.L.Label;
E->Name = Sym->V.L.Label;
}
} else {
/* Local static variable */
@ -1248,7 +1208,7 @@ static void Primary (ExprDesc* E)
/* Undeclared Variable */
Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0);
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
E->Type = type_int;
Error ("Undefined symbol: '%s'", Ident);
}
@ -1260,6 +1220,8 @@ static void Primary (ExprDesc* E)
/* String literal */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
E->V.LVal = UseLiteral (CurTok.SVal);
/* Translate into target charset */
TranslateLiteral (E->V.LVal);
} else {
E->V.LVal = CurTok.SVal;
}
@ -1270,10 +1232,28 @@ static void Primary (ExprDesc* E)
NextToken ();
break;
case TOK_ICONST:
case TOK_CCONST:
case TOK_WCCONST:
/* Character and integer constants */
E->IVal = CurTok.IVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_FCONST:
/* Floating point constant */
E->V.FVal = CurTok.FVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_ASM:
/* ASM statement */
AsmStatement ();
E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED;
E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED | E_SIDE_EFFECTS;
E->Type = type_void;
break;
@ -1658,6 +1638,9 @@ static void PreInc (ExprDesc* Expr)
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@ -1685,6 +1668,9 @@ static void PreDec (ExprDesc* Expr)
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
@ -1721,6 +1707,9 @@ static void PostInc (ExprDesc* Expr)
LoadExpr (CF_NONE, Expr);
AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
@ -1728,6 +1717,9 @@ static void PostInc (ExprDesc* Expr)
/* Do the increment */
DoInc (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the increment until after the value of this expression is used */
@ -1771,6 +1763,9 @@ static void PostDec (ExprDesc* Expr)
LoadExpr (CF_NONE, Expr);
AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
@ -1778,6 +1773,9 @@ static void PostDec (ExprDesc* Expr)
/* Do the decrement */
DoDec (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the decrement until after the value of this expression is used */
@ -1952,7 +1950,7 @@ void hie10 (ExprDesc* Expr)
/* The & operator yields an rvalue address */
ED_AddrExpr (Expr);
}
Expr->Type = NewPointerTo (Expr->Type);
Expr->Type = AddressOf (Expr->Type);
break;
case TOK_SIZEOF:
@ -2238,6 +2236,9 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */
/* We have an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
}
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
@ -2271,9 +2272,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
Tok = CurTok.Tok;
NextToken ();
/* If lhs is a function, convert it to pointer to function */
/* If lhs is a function, convert it to the address of the function */
if (IsTypeFunc (Expr->Type)) {
Expr->Type = NewPointerTo (Expr->Type);
Expr->Type = AddressOf (Expr->Type);
}
/* Get the lhs on stack */
@ -2293,9 +2294,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
/* Get the right hand side */
MarkedExprWithCheck (hienext, &Expr2);
/* If rhs is a function, convert it to pointer to function */
/* If rhs is a function, convert it to the address of the function */
if (IsTypeFunc (Expr2.Type)) {
Expr2.Type = NewPointerTo (Expr2.Type);
Expr2.Type = AddressOf (Expr2.Type);
}
/* Check for a numeric constant expression */
@ -2340,7 +2341,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
} else if (IsClassPtr (Expr->Type)) {
if (IsClassPtr (Expr2.Type)) {
/* Pointers are allowed in comparison */
if (TypeCmp (Expr->Type, Expr2.Type).C < TC_STRICT_COMPATIBLE) {
if (TypeCmp (Expr->Type, Expr2.Type).C < TC_VOID_PTR) {
/* Warn about distinct pointer types */
TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0,
"Distinct pointer types comparing '%s' with '%s'");
@ -2659,6 +2660,9 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */
/* Result type is always boolean */
Done: Expr->Type = type_bool;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
@ -3069,12 +3073,15 @@ static void parseadd (ExprDesc* Expr, int DoArrayRef)
Error ("Invalid operands for binary operator '+'");
} else {
/* Array and function types must be converted to pointer types */
Expr->Type = PtrConversion (Expr->Type);
Expr->Type = StdConversion (Expr->Type);
}
}
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@ -3348,10 +3355,13 @@ static void parsesub (ExprDesc* Expr)
}
/* Result type is either a pointer or an integer */
Expr->Type = PtrConversion (Expr->Type);
Expr->Type = StdConversion (Expr->Type);
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
@ -3441,48 +3451,6 @@ static void hie2 (ExprDesc* Expr)
static void hieAndPP (ExprDesc* Expr)
/* Process "exp && exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
*Expr = NoCodeConstAbsIntExpr (hie2);
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get rhs */
ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2);
/* Combine the two */
Expr->IVal = (Expr->IVal && Expr2.IVal);
}
}
static void hieOrPP (ExprDesc *Expr)
/* Process "exp || exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
*Expr = NoCodeConstAbsIntExpr (hieAndPP);
while (CurTok.Tok == TOK_BOOL_OR) {
/* Skip the && */
NextToken ();
/* Get rhs */
ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP);
/* Combine the two */
Expr->IVal = (Expr->IVal || Expr2.IVal);
}
}
static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
/* Process "exp && exp". This should only be called within hieOr.
** Return true if logic AND does occur.
@ -3603,6 +3571,12 @@ static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
}
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Last expression */
@ -3768,6 +3742,11 @@ static void hieOr (ExprDesc *Expr)
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Convert to bool */
@ -3821,11 +3800,7 @@ static void hieQuest (ExprDesc* Expr)
Type* ResultType; /* Type of result */
/* Call the lower level eval routine */
if (Preprocessing) {
ExprWithCheck (hieOrPP, Expr);
} else {
ExprWithCheck (hieOr, Expr);
}
ExprWithCheck (hieOr, Expr);
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
@ -4059,6 +4034,14 @@ static void hieQuest (ExprDesc* Expr)
/* Setup the target expression */
Expr->Type = ResultType;
/* Propagate viral flags */
if ((Expr2.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
if ((Expr3.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr3);
}
}
}
@ -4136,26 +4119,39 @@ void hie0 (ExprDesc *Expr)
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_NONE, Expr);
/* If the expression didn't generate code or isn't cast to type void,
** emit a warning.
/* If the expression has no observable effect and isn't cast to type
** void, emit a warning and remove useless code if any.
*/
GetCodePos (&End);
if (!ED_MayHaveNoEffect (Expr) &&
CodeRangeIsEmpty (&Start, &End) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Expression result unused");
if (CodeRangeIsEmpty (&Start, &End) ||
(Expr->Flags & E_SIDE_EFFECTS) == 0) {
if (!ED_MayHaveNoEffect (Expr) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Left-hand operand of comma expression has no effect");
}
/* Remove code with no effect */
RemoveCode (&Start);
}
PrevErrorCount = ErrorCount;
/* Remember the current code position */
GetCodePos (&Start);
/* Keep viral flags propagated from subexpressions */
Flags |= Expr->Flags & E_MASK_VIRAL;
/* Reset the expression */
ED_Init (Expr);
Expr->Flags = Flags;
Expr->Flags = Flags & ~E_MASK_VIRAL;
NextToken ();
hie1 (Expr);
/* Propagate viral flags */
Expr->Flags |= Flags & E_MASK_VIRAL;
}
}
@ -4217,7 +4213,8 @@ ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*))
if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) {
Error ("Constant expression expected");
/* To avoid any compiler errors, make the expression a valid const */
ED_MakeConstAbsInt (&Expr, 1);
Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_RESULT;
Expr.Flags |= E_LOC_NONE;
}
/* Return by value */

View File

@ -26,13 +26,20 @@
#define SQP_KEEP_NONE 0x00
#define SQP_KEEP_TEST 0x01U
#define SQP_KEEP_EAX 0x02U
#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */
#define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */
/* Generator attributes */
#define GEN_NOPUSH 0x01 /* Don't push lhs */
#define GEN_COMM 0x02 /* Operator is commutative */
#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */
/* Map a generator function and its attributes to a token */
typedef struct GenDesc {
long Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/*****************************************************************************/

View File

@ -125,6 +125,7 @@ enum {
E_LOADED = 0x1000, /* Expression is loaded in primary */
E_CC_SET = 0x2000, /* Condition codes are set */
E_HAVE_MARKS = 0x4000, /* Code marks are valid */
E_SIDE_EFFECTS = 0x8000, /* Expression has had side effects */
/* Optimization hints */
E_MASK_NEED = 0x030000,
@ -181,6 +182,9 @@ enum {
/* Expression result must be known to the compiler and generate no code to load */
E_EVAL_C_CONST = E_EVAL_COMPILER_KNOWN | E_EVAL_NO_CODE,
/* Flags to combine from subexpressions */
E_MASK_VIRAL = E_SIDE_EFFECTS,
/* Flags to keep in subexpressions of most operations other than ternary */
E_MASK_KEEP_SUBEXPR = E_MASK_EVAL,
@ -467,6 +471,16 @@ INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr)
# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED)
#endif
#if defined(HAVE_INLINE)
INLINE void ED_PropagateFrom (ExprDesc* Expr, const ExprDesc* SubExpr)
/* Propagate viral flags from subexpression */
{
Expr->Flags |= SubExpr->Flags & E_MASK_VIRAL;
}
#else
# define ED_PropagateFrom(Expr, SubExpr) (void)((Expr)->Flags |= (SubExpr)->Flags & E_MASK_VIRAL)
#endif
#if defined(HAVE_INLINE)
INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr)
/* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */

View File

@ -54,6 +54,7 @@
#include "input.h"
#include "lineinfo.h"
#include "output.h"
#include "preproc.h"
@ -91,6 +92,9 @@ struct AFile {
FILE* F; /* Input file stream */
IFile* Input; /* Points to corresponding IFile */
int SearchPath; /* True if we've added a path for this file */
char* PName; /* Presumed name of the file */
PPIfStack IfStack; /* PP #if stack */
int MissingNL; /* Last input line was missing a newline */
};
/* List of all input files */
@ -102,6 +106,9 @@ static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
/* Input stack used when preprocessing. */
static Collection InputStack = STATIC_COLLECTION_INITIALIZER;
/* Counter for the __COUNTER__ macro */
static unsigned MainFileCounter;
/*****************************************************************************/
@ -156,6 +163,9 @@ static AFile* NewAFile (IFile* IF, FILE* F)
AF->Line = 0;
AF->F = F;
AF->Input = IF;
AF->PName = 0;
AF->IfStack.Index = -1;
AF->MissingNL = 0;
/* Increment the usage counter of the corresponding IFile. If this
** is the first use, set the file data and output debug info if
@ -204,6 +214,9 @@ static AFile* NewAFile (IFile* IF, FILE* F)
static void FreeAFile (AFile* AF)
/* Free an AFile structure */
{
if (AF->PName != 0) {
xfree (AF->PName);
}
xfree (AF);
}
@ -257,6 +270,12 @@ void OpenMainFile (const char* Name)
/* Allocate a new AFile structure for the file */
MainFile = NewAFile (IF, F);
/* Use this file with PP */
SetPPIfStack (&MainFile->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
/* Allocate the input line buffer */
Line = NewStrBuf ();
@ -264,6 +283,9 @@ void OpenMainFile (const char* Name)
** the main file before the first line is read.
*/
UpdateLineInfo (MainFile->Input, MainFile->Line, Line);
/* Initialize the __COUNTER__ counter */
MainFileCounter = 0;
}
@ -274,6 +296,7 @@ void OpenIncludeFile (const char* Name, InputType IT)
char* N;
FILE* F;
IFile* IF;
AFile* AF;
/* Check for the maximum include nesting */
if (CollCount (&AFiles) > MAX_INC_NESTING) {
@ -311,12 +334,18 @@ void OpenIncludeFile (const char* Name, InputType IT)
Print (stdout, 1, "Opened include file '%s'\n", IF->Name);
/* Allocate a new AFile structure */
(void) NewAFile (IF, F);
AF = NewAFile (IF, F);
/* Use this file with PP */
SetPPIfStack (&AF->IfStack);
/* Begin PP for this file */
PreprocessBegin ();
}
static void CloseIncludeFile (void)
void CloseIncludeFile (void)
/* Close an include file and switch to the higher level file. Set Input to
** NULL if this was the main file.
*/
@ -329,14 +358,18 @@ static void CloseIncludeFile (void)
/* Must have an input file when called */
PRECONDITION (AFileCount > 0);
/* End preprocessor in this file */
PreprocessEnd ();
/* Get the current active input file */
Input = (AFile*) CollLast (&AFiles);
Input = CollLast (&AFiles);
/* Close the current input file (we're just reading so no error check) */
fclose (Input->F);
/* Delete the last active file from the active file collection */
CollDelete (&AFiles, AFileCount-1);
--AFileCount;
CollDelete (&AFiles, AFileCount);
/* If we had added an extra search path for this AFile, remove it */
if (Input->SearchPath) {
@ -345,6 +378,12 @@ static void CloseIncludeFile (void)
/* Delete the active file structure */
FreeAFile (Input);
/* Use previous file with PP if it is not the main file */
if (AFileCount > 0) {
Input = CollLast (&AFiles);
SetPPIfStack (&Input->IfStack);
}
}
@ -436,47 +475,49 @@ StrBuf* InitLine (StrBuf* Buf)
int NextLine (void)
/* Get a line from the current input. Returns 0 on end of file. */
/* Get a line from the current input. Returns 0 on end of file with no new
** input bytes.
*/
{
int C;
AFile* Input;
/* Clear the current line */
ClearLine ();
SB_Clear (Line);
/* If there is no file open, bail out, otherwise get the current input file */
if (CollCount (&AFiles) == 0) {
/* Must have an input file when called */
if (CollCount(&AFiles) == 0) {
return 0;
}
/* Get the current input file */
Input = CollLast (&AFiles);
/* Read characters until we have one complete line */
while (1) {
/* Read the next character */
int C = fgetc (Input->F);
C = fgetc (Input->F);
/* Check for EOF */
if (C == EOF) {
/* Accept files without a newline at the end */
if (SB_NotEmpty (Line)) {
if (!Input->MissingNL || SB_NotEmpty (Line)) {
/* Accept files without a newline at the end */
++Input->Line;
break;
}
/* Leave the current file */
CloseIncludeFile ();
/* Assume no new line */
Input->MissingNL = 1;
/* If there is no file open, bail out, otherwise get the
** previous input file and start over.
*/
if (CollCount (&AFiles) == 0) {
return 0;
}
Input = CollLast (&AFiles);
continue;
break;
}
/* Assume no new line */
Input->MissingNL = 1;
/* Check for end of line */
if (C == '\n') {
@ -497,6 +538,7 @@ int NextLine (void)
if (SB_LookAtLast (Line) == '\\') {
Line->Buf[Line->Len-1] = '\n';
} else {
Input->MissingNL = 0;
break;
}
@ -517,6 +559,38 @@ int NextLine (void)
/* Create line information for this line */
UpdateLineInfo (Input->Input, Input->Line, Line);
/* Done */
return C != EOF || SB_NotEmpty (Line);
}
int PreprocessNextLine (void)
/* Get a line from opened input files and do preprocess. Returns 0 on end of
** main file.
*/
{
while (NextLine() == 0) {
/* If there is no input file open, bail out. Otherwise get the previous
** input file and start over.
*/
if (CollCount (&AFiles) == 0) {
return 0;
}
/* Leave the current file */
CloseIncludeFile ();
}
/* Do preprocess anyways */
Preprocess ();
/* Write it to the output file if in preprocess-only mode */
if (PreprocessOnly) {
WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
/* Done */
return 1;
}
@ -536,17 +610,11 @@ const char* GetCurrentFile (void)
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
return AF->Input->Name;
const AFile* AF = CollLast (&AFiles);
return AF->PName == 0 ? AF->Input->Name : AF->PName;
} else {
/* No open file. Use the main file if we have one. */
unsigned IFileCount = CollCount (&IFiles);
if (IFileCount > 0) {
const IFile* IF = (const IFile*) CollAt (&IFiles, 0);
return IF->Name;
} else {
return "(outside file scope)";
}
/* No open file */
return "(outside file scope)";
}
}
@ -557,7 +625,7 @@ unsigned GetCurrentLine (void)
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1);
const AFile* AF = CollLast (&AFiles);
return AF->Line;
} else {
/* No open file */
@ -567,6 +635,43 @@ unsigned GetCurrentLine (void)
void SetCurrentLine (unsigned LineNum)
/* Set the line number in the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
AFile* AF = CollLast (&AFiles);
AF->Line = LineNum;
}
}
void SetCurrentFilename (const char* Name)
/* Set the presumed name of the current input file */
{
unsigned AFileCount = CollCount (&AFiles);
if (AFileCount > 0) {
size_t Len = strlen (Name);
AFile* AF = CollLast (&AFiles);
if (AF->PName != 0) {
xfree (AF->PName);
}
AF->PName = xmalloc (Len + 1);
memcpy (AF->PName, Name, Len + 1);
}
}
unsigned GetCurrentCounter (void)
/* Return the counter number in the current input file */
{
return MainFileCounter++;
}
static void WriteEscaped (FILE* F, const char* Name)
/* Write a file name to a dependency file escaping spaces */
{

View File

@ -46,7 +46,7 @@
/*****************************************************************************/
/* data */
/* Data */
/*****************************************************************************/
@ -84,6 +84,11 @@ void OpenMainFile (const char* Name);
void OpenIncludeFile (const char* Name, InputType IT);
/* Open an include file and insert it into the tables. */
void CloseIncludeFile (void);
/* Close an include file and switch to the higher level file. Set Input to
** NULL if this was the main file.
*/
void NextChar (void);
/* Read the next character from the input stream and make CurC and NextC
** valid. If end of line is reached, both are set to NUL, no more lines
@ -99,7 +104,14 @@ StrBuf* InitLine (StrBuf* Buf);
*/
int NextLine (void);
/* Get a line from the current input. Returns 0 on end of file. */
/* Get a line from the current input. Returns 0 on end of file with no new
** input bytes.
*/
int PreprocessNextLine (void);
/* Get a line from opened input files and do preprocess. Returns 0 on end of
** main file.
*/
const char* GetInputFile (const struct IFile* IF);
/* Return a filename from an IFile struct */
@ -110,6 +122,15 @@ const char* GetCurrentFile (void);
unsigned GetCurrentLine (void);
/* Return the line number in the current input file */
void SetCurrentLine (unsigned LineNum);
/* Set the line number in the current input file */
void SetCurrentFilename (const char* Name);
/* Set the presumed name of the current input file */
unsigned GetCurrentCounter (void);
/* Return the counter number in the current input file */
void CreateDependencies (void);
/* Create dependency files requested by the user */

View File

@ -126,9 +126,6 @@ static void FreeLiteral (Literal* L)
static void OutputLiteral (Literal* L)
/* Output one literal to the currently active data segment */
{
/* Translate the literal into the target charset */
TranslateLiteral (L);
/* Define the label for the literal */
g_defliterallabel (L->Label);
@ -387,9 +384,6 @@ static void OutputReadOnlyLiterals (Collection* Literals)
continue;
}
/* Translate the literal into the target charset */
TranslateLiteral (L);
/* Check if this literal is part of another one. Since the literals
** are sorted by size (larger ones first), it can only be part of a
** literal with a smaller index.

View File

@ -277,4 +277,8 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr)
}
}
if (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)) {
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
}

View File

@ -174,8 +174,8 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg)
Sym->Flags |= SC_REF;
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate a variable of unknown size */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {
@ -370,8 +370,8 @@ static void ParseAutoDecl (Declaration* Decl)
}
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate an incomplete variable */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {
@ -428,8 +428,8 @@ static void ParseStaticDecl (Declaration* Decl)
}
/* Cannot allocate a variable of zero size */
if (Size == 0) {
/* Cannot allocate an incomplete variable */
if (HasUnknownSize (Sym->Type)) {
if (IsTypeArray (Decl->Type)) {
Error ("Array '%s' has unknown size", Decl->Ident);
} else {

View File

@ -42,6 +42,7 @@
/* cc65 */
#include "error.h"
#include "preproc.h"
#include "macrotab.h"
@ -56,6 +57,9 @@
#define MACRO_TAB_SIZE 211
static Macro* MacroTab[MACRO_TAB_SIZE];
/* The undefined macros list head */
static Macro* UndefinedMacrosListHead;
/*****************************************************************************/
@ -108,6 +112,29 @@ void FreeMacro (Macro* M)
Macro* CloneMacro (const Macro* M)
/* Clone a macro definition. The function is not insert the macro into the
** macro table, thus the cloned instance cannot be freed with UndefineMacro.
** Use FreeMacro for that.
*/
{
Macro* New = NewMacro (M->Name);
unsigned I;
for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
/* Copy the argument */
const char* Arg = CollAtUnchecked (&M->FormalArgs, I);
CollAppend (&New->FormalArgs, xstrdup (Arg));
}
New->ArgCount = M->ArgCount;
New->Variadic = M->Variadic;
SB_Copy (&New->Replacement, &M->Replacement);
return New;
}
void DefineNumericMacro (const char* Name, long Val)
/* Define a macro for a numeric constant */
{
@ -150,10 +177,11 @@ void InsertMacro (Macro* M)
int UndefineMacro (const char* Name)
/* Search for the macro with the given name and remove it from the macro
** table if it exists. Return 1 if a macro was found and deleted, return
** 0 otherwise.
Macro* UndefineMacro (const char* Name)
/* Search for the macro with the given name, if it exists, remove it from
** the defined macro table and insert it to a list for pending deletion.
** Return the macro if it was found and removed, return 0 otherwise.
** To safely free the removed macro, use FreeUndefinedMacros().
*/
{
/* Get the hash value of the macro name */
@ -173,11 +201,12 @@ int UndefineMacro (const char* Name)
L->Next = M->Next;
}
/* Delete the macro */
FreeMacro (M);
/* Add this macro to pending deletion list */
M->Next = UndefinedMacrosListHead;
UndefinedMacrosListHead = M;
/* Done */
return 1;
return M;
}
/* Next macro */
@ -191,6 +220,23 @@ int UndefineMacro (const char* Name)
void FreeUndefinedMacros (void)
/* Free all undefined macros */
{
Macro* Next;
while (UndefinedMacrosListHead != 0) {
Next = UndefinedMacrosListHead->Next;
/* Delete the macro */
FreeMacro (UndefinedMacrosListHead);
UndefinedMacrosListHead = Next;
}
}
Macro* FindMacro (const char* Name)
/* Find a macro with the given name. Return the macro definition or NULL */
{
@ -201,6 +247,10 @@ Macro* FindMacro (const char* Name)
Macro* M = MacroTab[Hash];
while (M) {
if (strcmp (M->Name, Name) == 0) {
/* Check for some special macro names */
if (Name[0] == '_') {
HandleSpecialMacro (M, Name);
}
/* Found it */
return M;
}
@ -245,7 +295,7 @@ void AddMacroArg (Macro* M, const char* Arg)
for (I = 0; I < CollCount (&M->FormalArgs); ++I) {
if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) {
/* Found */
Error ("Duplicate macro parameter: '%s'", Arg);
PPError ("Duplicate macro parameter: '%s'", Arg);
break;
}
}

View File

@ -82,6 +82,12 @@ void FreeMacro (Macro* M);
** table, use UndefineMacro for that.
*/
Macro* CloneMacro (const Macro* M);
/* Clone a macro definition. The function is not insert the macro into the
** macro table, thus the cloned instance cannot be freed with UndefineMacro.
** Use FreeMacro for that.
*/
void DefineNumericMacro (const char* Name, long Val);
/* Define a macro for a numeric constant */
@ -91,12 +97,16 @@ void DefineTextMacro (const char* Name, const char* Val);
void InsertMacro (Macro* M);
/* Insert the given macro into the macro table. */
int UndefineMacro (const char* Name);
/* Search for the macro with the given name and remove it from the macro
** table if it exists. Return 1 if a macro was found and deleted, return
** 0 otherwise.
Macro* UndefineMacro (const char* Name);
/* Search for the macro with the given name, if it exists, remove it from
** the defined macro table and insert it to a list for pending deletion.
** Return the macro if it was found and removed, return 0 otherwise.
** To safely free the removed macro, use FreeUndefinedMacros().
*/
void FreeUndefinedMacros (void);
/* Free all undefined macros */
Macro* FindMacro (const char* Name);
/* Find a macro with the given name. Return the macro definition or NULL */

878
src/cc65/ppexpr.c Normal file
View File

@ -0,0 +1,878 @@
/*****************************************************************************/
/* */
/* ppexpr.h */
/* */
/* Expressions for C preprocessor */
/* */
/* */
/* */
/* (C) 2022 The cc65 Authors */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
/* cc65 */
#include "error.h"
#include "scanner.h"
#include "ppexpr.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* PP expression parser status */
static int PPEvaluationEnabled = 0;
static int PPEvaluationFailed = 0;
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static void PPhie0 (PPExpr* Expr);
static void PPhie1 (PPExpr* Expr);
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static token_t PPFindTok (token_t Tok, const token_t* Table)
/* Find a token in a generator table */
{
while (*Table != TOK_INVALID) {
if (*Table == Tok) {
return Tok;
}
++Table;
}
return TOK_INVALID;
}
static void PPExprInit (PPExpr* Expr)
/* Initialize the expression */
{
Expr->IVal = 0;
Expr->Flags = PPEXPR_NONE;
}
static void PPErrorSkipLine (void)
/* Set the expression parser error flag, skip the remain tokens till the end
** of the line, clear the current and the next tokens.
*/
{
PPEvaluationFailed = 1;
SkipTokens (0, 0);
CurTok.Tok = TOK_CEOF;
NextTok.Tok = TOK_CEOF;
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static void PPhiePrimary (PPExpr* Expr)
/* This is the lowest level of the PP expression parser */
{
switch (CurTok.Tok) {
case TOK_ICONST:
case TOK_CCONST:
case TOK_WCCONST:
/* Character and integer constants */
Expr->IVal = CurTok.IVal;
/* According to the C standard, all signed types act as intmax_t
** and all unsigned types act as uintmax_t.
*/
if (IsSignUnsigned (CurTok.Type)) {
Expr->Flags |= PPEXPR_UNSIGNED;
}
NextToken ();
break;
case TOK_FCONST:
/* Floating point constant */
PPError ("Floating constant in preprocessor expression");
Expr->IVal = 0;
NextToken ();
break;
case TOK_LPAREN:
/* Parse parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
PPhie0 (Expr);
ConsumeRParen ();
break;
case TOK_IDENT:
/* Assume that this identifier is an undefined macro and replace
** it by a constant value of zero.
*/
NextToken ();
Expr->Flags |= PPEXPR_UNDEFINED;
Expr->IVal = 0;
break;
case TOK_CEOF:
/* Error recovery */
break;
default:
/* Illegal expression in PP mode */
PPError ("Preprocessor expression expected");
PPErrorSkipLine ();
break;
}
}
static void PPhie11 (PPExpr* Expr)
/* Handle compound types (structs and arrays) etc which are invalid in PP */
{
/* Evaluate the lhs */
PPhiePrimary (Expr);
/* Check for a rhs */
while (CurTok.Tok == TOK_INC || CurTok.Tok == TOK_DEC ||
CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN ||
CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) {
switch (CurTok.Tok) {
case TOK_LBRACK:
PPError ("Token \".\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_LPAREN:
/* Function call syntax is not recognized in preprocessor
** expressions.
*/
PPError ("Missing binary operator before token \"(\"");
PPErrorSkipLine ();
break;
case TOK_DOT:
PPError ("Token \".\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PTR_REF:
PPError ("Token \"->\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_INC:
PPError ("Token \"++\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DEC:
PPError ("Token \"--\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
default:
Internal ("Invalid token in PPhie11: %d", CurTok.Tok);
}
}
/* Check for excessive expressions */
if (!TokIsPunc (&CurTok)) {
PPError ("Missing binary operator");
PPErrorSkipLine ();
}
}
void PPhie10 (PPExpr* Expr)
/* Handle prefixing unary operators */
{
switch (CurTok.Tok) {
case TOK_INC:
PPError ("Token \"++\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DEC:
PPError ("Token \"--\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PLUS:
NextToken ();
PPhie10 (Expr);
Expr->IVal = +Expr->IVal;
break;
case TOK_MINUS:
NextToken ();
PPhie10 (Expr);
Expr->IVal = -Expr->IVal;
break;
case TOK_COMP:
NextToken ();
PPhie10 (Expr);
Expr->IVal = ~Expr->IVal;
break;
case TOK_BOOL_NOT:
NextToken ();
PPhie10 (Expr);
Expr->IVal = !Expr->IVal;
break;
case TOK_CEOF:
/* Error recovery */
break;
case TOK_STAR:
case TOK_AND:
case TOK_SIZEOF:
default:
/* Type cast, sizeof, *, &, are not recognized in preprocessor
** expressions. So everything is treated as as expression here.
*/
PPhie11 (Expr);
break;
}
}
static void PPhie_internal (const token_t* Ops, /* List of generators */
PPExpr* Expr,
void (*hienext) (PPExpr*))
/* Helper function */
{
token_t Tok;
hienext (Expr);
while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) {
PPExpr Rhs;
PPExprInit (&Rhs);
/* Remember the operator token, then skip it */
NextToken ();
/* Get the right hand side */
hienext (&Rhs);
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* If either side is unsigned, the result is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
/* Handle the op differently for signed and unsigned integers */
if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_PLUS:
Expr->IVal = (Val1 + Val2);
break;
case TOK_MINUS:
Expr->IVal = (Val1 - Val2);
break;
case TOK_MUL:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
PPError ("Division by zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
PPError ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("PPhie_internal: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_PLUS:
Expr->IVal = (Val1 + Val2);
break;
case TOK_MINUS:
Expr->IVal = (Val1 - Val2);
break;
case TOK_MUL:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
PPError ("Division by zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
PPError ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("PPhie_internal: got token 0x%X\n", Tok);
}
}
}
}
}
static void PPhie_compare (const token_t* Ops, /* List of generators */
PPExpr* Expr,
void (*hienext) (PPExpr*))
/* Helper function for the compare operators */
{
token_t Tok;
hienext (Expr);
while ((Tok = PPFindTok (CurTok.Tok, Ops)) != 0) {
PPExpr Rhs;
PPExprInit (&Rhs);
/* Skip the operator token */
NextToken ();
/* Get the right hand side */
hienext (&Rhs);
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* If either side is unsigned, the comparison is unsigned */
Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED;
/* Determine if this is a signed or unsigned compare */
if ((Expr->Flags & PPEXPR_UNSIGNED) == 0) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("PPhie_compare: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Rhs.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("PPhie_compare: got token 0x%X\n", Tok);
}
}
}
}
/* The result is signed */
Expr->Flags &= ~PPEXPR_UNSIGNED;
}
static void PPhie9 (PPExpr* Expr)
/* Handle "*", "/" and "%" operators */
{
static const token_t PPhie9_ops[] = {
TOK_STAR,
TOK_DIV,
TOK_MOD,
TOK_INVALID
};
PPhie_internal (PPhie9_ops, Expr, PPhie10);
}
static void PPhie8 (PPExpr* Expr)
/* Handle "+" and "-" binary operators */
{
static const token_t PPhie8_ops[] = {
TOK_PLUS,
TOK_MINUS,
TOK_INVALID
};
PPhie_internal (PPhie8_ops, Expr, PPhie9);
}
static void PPhie7 (PPExpr* Expr)
/* Handle the "<<" and ">>" shift operators */
{
/* Evaluate the lhs */
PPhie8 (Expr);
while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) {
token_t Op; /* The operator token */
PPExpr Rhs;
PPExprInit (&Rhs);
/* Remember the operator, then skip its token */
Op = CurTok.Tok;
NextToken ();
/* Get the right hand side */
PPhie8 (&Rhs);
/* Evaluate */
if (PPEvaluationEnabled && !PPEvaluationFailed) {
/* To shift by a negative value is equivalent to shift to the
** opposite direction.
*/
if ((Rhs.Flags & PPEXPR_UNSIGNED) != 0 && Rhs.IVal > (long)LONG_BITS) {
Rhs.IVal = (long)LONG_BITS;
}
if (Op == TOK_SHR) {
Rhs.IVal = -Rhs.IVal;
}
/* Evaluate the result */
if ((Expr->Flags & PPEXPR_UNSIGNED) != 0) {
if (Rhs.IVal >= (long)LONG_BITS) {
/* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
} else if (Rhs.IVal < -(long)LONG_BITS) {
Expr->IVal = 0;
} else if (Rhs.IVal < 0) {
Expr->IVal = (unsigned long)Expr->IVal >> -Rhs.IVal;
}
} else {
if (Rhs.IVal >= (long)(LONG_BITS - 1)) {
/* For now we use (unsigned) long types for integer constants */
PPWarning ("Integer overflow in preprocessor expression");
Expr->IVal = 0;
} else if (Rhs.IVal > 0) {
Expr->IVal <<= Rhs.IVal;
} else if (Rhs.IVal < -(long)LONG_BITS) {
Expr->IVal = -1;
} else if (Rhs.IVal < 0) {
Expr->IVal >>= Expr->IVal >> -Rhs.IVal;
}
}
}
}
}
static void PPhie6 (PPExpr* Expr)
/* Handle greater-than type relational operators */
{
static const token_t PPhie6_ops [] = {
TOK_LT,
TOK_LE,
TOK_GE,
TOK_GT,
TOK_INVALID
};
PPhie_compare (PPhie6_ops, Expr, PPhie7);
}
static void PPhie5 (PPExpr* Expr)
/* Handle "==" and "!=" relational operators */
{
static const token_t PPhie5_ops[] = {
TOK_EQ,
TOK_NE,
TOK_INVALID
};
PPhie_compare (PPhie5_ops, Expr, PPhie6);
}
static void PPhie4 (PPExpr* Expr)
/* Handle the bitwise AND "&" operator */
{
static const token_t PPhie4_ops[] = {
TOK_AND,
TOK_INVALID
};
PPhie_internal (PPhie4_ops, Expr, PPhie5);
}
static void PPhie3 (PPExpr* Expr)
/* Handle the bitwise exclusive OR "^" operator */
{
static const token_t PPhie3_ops[] = {
TOK_XOR,
TOK_INVALID
};
PPhie_internal (PPhie3_ops, Expr, PPhie4);
}
static void PPhie2 (PPExpr* Expr)
/* Handle the bitwise OR "|" operator */
{
static const token_t PPhie2_ops[] = {
TOK_OR,
TOK_INVALID
};
PPhie_internal (PPhie2_ops, Expr, PPhie3);
}
static void PPhieAnd (PPExpr* Expr)
/* Handle the logical AND "expr1 && expr2" operator */
{
/* Get one operand */
PPhie2 (Expr);
if (CurTok.Tok == TOK_BOOL_AND) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr One;
/* Do logical and */
Expr->IVal = (Expr->IVal != 0);
if (Expr->IVal == 0) {
PPEvaluationEnabled = 0;
}
/* While there are more expressions */
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get one operand */
PPExprInit (&One);
PPhie2 (&One);
/* Evaluate */
if (PPEvaluationEnabled) {
if (One.IVal == 0) {
/* Skip evaluating remaining */
PPEvaluationEnabled = 0;
/* The value of the result will be false */
Expr->IVal = 0;
}
}
}
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhieOr (PPExpr* Expr)
/* Handle the logical OR "||" operator */
{
/* Call the next level parser */
PPhieAnd (Expr);
if (CurTok.Tok == TOK_BOOL_OR) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr One;
/* Do logical or */
Expr->IVal = (Expr->IVal != 0);
if (Expr->IVal != 0) {
PPEvaluationEnabled = 0;
}
/* While there are more expressions */
while (CurTok.Tok == TOK_BOOL_OR) {
/* Skip the || */
NextToken ();
/* Get rhs subexpression */
PPExprInit (&One);
PPhieAnd (&One);
/* Evaluate */
if (PPEvaluationEnabled) {
if (One.IVal != 0) {
/* Skip evaluating remaining */
PPEvaluationEnabled = 0;
/* The value of the result will be true */
Expr->IVal = 1;
}
}
}
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhieQuest (PPExpr* Expr)
/* Handle the ternary "expr1 ? expr2 : expr3 " operator */
{
/* Call the lower level eval routine */
PPhieOr (Expr);
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
int PPEvaluationEnabledPrev = PPEvaluationEnabled;
PPExpr Expr2; /* Expression 2 */
PPExpr Expr3; /* Expression 3 */
/* Skip the question mark */
NextToken ();
/* Disable evaluation for Expr2 if the condition is false */
if (Expr->IVal == 0) {
PPEvaluationEnabled = 0;
}
/* Parse second expression */
PPExprInit (&Expr2);
PPhie0 (&Expr2);
/* Skip the colon */
ConsumeColon ();
/* Disable evaluation for Expr3 if the condition is true */
if (Expr->IVal != 0) {
PPEvaluationEnabled = 0;
}
/* Parse third expression */
PPExprInit (&Expr3);
PPhie1 (&Expr3);
/* Set the result */
Expr->IVal = Expr->IVal ? Expr2.IVal != 0 : Expr3.IVal != 0;
/* Restore evaluation as before */
PPEvaluationEnabled = PPEvaluationEnabledPrev;
}
}
static void PPhie1 (PPExpr* Expr)
/* Handle first level of expression hierarchy */
{
PPhieQuest (Expr);
if (!PPEvaluationEnabled) {
/* Skip evaluation */
return;
}
switch (CurTok.Tok) {
case TOK_ASSIGN:
PPError ("Token \"=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_PLUS_ASSIGN:
PPError ("Token \"+=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MINUS_ASSIGN:
PPError ("Token \"-=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MUL_ASSIGN:
PPError ("Token \"*=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_DIV_ASSIGN:
PPError ("Token \"/=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_MOD_ASSIGN:
PPError ("Token \"%%=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_SHL_ASSIGN:
PPError ("Token \"<<=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_SHR_ASSIGN:
PPError ("Token \">>=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_AND_ASSIGN:
PPError ("Token \"&=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_OR_ASSIGN:
PPError ("Token \"|=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
case TOK_XOR_ASSIGN:
PPError ("Token \"^=\" is not valid in preprocessor expressions");
PPErrorSkipLine ();
break;
default:
break;
}
}
static void PPhie0 (PPExpr* Expr)
/* Handle the comma "," operator */
{
PPhie1 (Expr);
while (CurTok.Tok == TOK_COMMA) {
/* Skip the comma */
NextToken ();
/* Reset the expression */
PPExprInit (Expr);
/* Use the next operand as the value instead */
PPhie1 (Expr);
}
}
void ParsePPExprInLine (PPExpr* Expr)
/* Parse a line for PP expression */
{
/* Initialize the parser status */
PPEvaluationFailed = 0;
PPEvaluationEnabled = 1;
PPParserRunning = 1;
/* Parse */
PPExprInit (Expr);
PPhie0 (Expr);
/* If the evaluation fails, the result is always zero */
if (PPEvaluationFailed) {
Expr->IVal = 0;
PPEvaluationFailed = 0;
}
/* Restore parser status */
PPParserRunning = 0;
}

76
src/cc65/ppexpr.h Normal file
View File

@ -0,0 +1,76 @@
/*****************************************************************************/
/* */
/* ppexpr.h */
/* */
/* Expressions for C preprocessor */
/* */
/* */
/* */
/* (C) 2022 The cc65 Authors */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#ifndef PPEXPR_H
#define PPEXPR_H
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* PPExpr data struct */
typedef struct PPExpr PPExpr;
struct PPExpr
{
long IVal;
unsigned Flags;
};
/* PPExpr initializers */
#define AUTO_PPEXPR_INITIALIZER { 0, 0 }
#define STATIC_PPEXPR_INITIALIZER { 0, 0 }
/* PPExpr flags */
#define PPEXPR_NONE 0U
#define PPEXPR_UNSIGNED 1U
#define PPEXPR_UNDEFINED 2U
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void ParsePPExprInLine (PPExpr* Expr);
/* Parse a line for PP expression */
/* End of ppexpr.h */
#endif

File diff suppressed because it is too large Load Diff

View File

@ -39,18 +39,35 @@
/*****************************************************************************/
/* data */
/* Forwards */
/*****************************************************************************/
/* Set when the preprocessor calls NoCodeConstExpr() recursively */
extern unsigned char Preprocessing;
typedef struct Macro Macro;
/*****************************************************************************/
/* code */
/* Data */
/*****************************************************************************/
/* Maximum #if depth per file */
#define MAX_PP_IFS 256
/* Data struct used for per-file-directive handling */
typedef struct PPIfStack PPIfStack;
struct PPIfStack {
unsigned char Stack[MAX_PP_IFS];
int Index;
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@ -58,6 +75,24 @@ extern unsigned char Preprocessing;
void Preprocess (void);
/* Preprocess a line */
void SetPPIfStack (PPIfStack* Stack);
/* Specify which PP #if stack to use */
void PreprocessBegin (void);
/* Initialize preprocessor with current file */
void PreprocessEnd (void);
/* Preprocessor done with current file */
void InitPreprocess (void);
/* Init preprocessor */
void DonePreprocess (void);
/* Done with preprocessor */
void HandleSpecialMacro (Macro* M, const char* Name);
/* Handle special "magic" macros that may change */
/* End of preproc.h */

View File

@ -69,6 +69,7 @@
Token CurTok; /* The current token */
Token NextTok; /* The next token */
int PPParserRunning; /* Is tokenizer used by the preprocessor */
@ -188,10 +189,12 @@ static int SkipWhite (void)
{
while (1) {
while (CurC == '\0') {
if (NextLine () == 0) {
/* If reading next line fails or is disabled with directives, bail
** out.
*/
if (PPParserRunning || PreprocessNextLine () == 0) {
return 0;
}
Preprocess ();
}
if (IsSpace (CurC)) {
NextChar ();
@ -245,6 +248,45 @@ int IsSym (char* S)
int IsPPNumber (int Cur, int Next)
/* Return 1 if the two successive characters indicate a pp-number, otherwise
** return 0.
*/
{
return Cur != '.' ? IsDigit (Cur) : IsDigit (Next);
}
void CopyPPNumber (StrBuf* Target)
/* Copy a pp-number from the input to Target */
{
int Std;
if (!IsPPNumber (CurC, NextC)) {
return;
}
/* P-exp is only valid in C99 and later */
Std = IS_Get (&Standard);
while (IsIdent (CurC) || IsDigit (CurC) || CurC == '.') {
SB_AppendChar (Target, CurC);
if (NextC == '+' || NextC == '-') {
if (CurC == 'e' || CurC == 'E' ||
(Std >= STD_C99 && (CurC == 'p' || CurC == 'P'))) {
SB_AppendChar (Target, NextC);
NextChar ();
} else {
NextChar ();
break;
}
}
NextChar ();
}
}
static void UnknownChar (char C)
/* Error message for unknown character */
{
@ -370,6 +412,15 @@ static void CharConst (void)
{
int C;
if (CurC == 'L') {
/* Wide character constant */
NextTok.Tok = TOK_WCCONST;
NextChar ();
} else {
/* Narrow character constant */
NextTok.Tok = TOK_CCONST;
}
/* Skip the quote */
NextChar ();
@ -384,9 +435,6 @@ static void CharConst (void)
NextChar ();
}
/* Setup values and attributes */
NextTok.Tok = TOK_CCONST;
/* Translate into target charset */
NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
@ -457,76 +505,77 @@ static void StringConst (void)
static void NumericConst (void)
/* Parse a numeric constant */
{
unsigned Base; /* Temporary number base */
unsigned Prefix; /* Base according to prefix */
StrBuf S = STATIC_STRBUF_INITIALIZER;
unsigned Base; /* Temporary number base according to prefix */
unsigned Index;
StrBuf Src = AUTO_STRBUF_INITIALIZER;
int IsFloat;
char C;
unsigned DigitVal;
unsigned long IVal; /* Value */
/* Get the pp-number first, then parse on it */
CopyPPNumber (&Src);
SB_Terminate (&Src);
SB_Reset (&Src);
/* Check for a leading hex, octal or binary prefix and determine the
** possible integer types.
*/
if (CurC == '0') {
if (SB_Peek (&Src) == '0') {
/* Gobble 0 and examine next char */
NextChar ();
if (toupper (CurC) == 'X') {
Base = Prefix = 16;
NextChar (); /* gobble "x" */
} else if (toupper (CurC) == 'B' && IS_Get (&Standard) >= STD_CC65) {
Base = Prefix = 2;
NextChar (); /* gobble 'b' */
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) == 'X' &&
IsXDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) {
Base = 16;
SB_Skip (&Src); /* gobble "x" */
} else if (toupper (SB_Peek (&Src)) == 'B' &&
IS_Get (&Standard) >= STD_CC65 &&
IsDigit (SB_LookAt (&Src, SB_GetIndex (&Src) + 1))) {
Base = 2;
SB_Skip (&Src); /* gobble 'b' */
} else {
Base = 10; /* Assume 10 for now - see below */
Prefix = 8; /* Actual prefix says octal */
}
} else {
Base = Prefix = 10;
Base = 10;
}
/* Because floating point numbers don't have octal prefixes (a number
** with a leading zero is decimal), we first have to read the number
** before converting it, so we can determine if it's a float or an
** integer.
/* Because floating point numbers don't have octal prefixes (a number with
** a leading zero is decimal), we first have to read the number before
** converting it, so we can determine if it's a float or an integer.
*/
while (IsXDigit (CurC) && HexVal (CurC) < Base) {
SB_AppendChar (&S, CurC);
NextChar ();
Index = SB_GetIndex (&Src);
while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) {
SB_Skip (&Src);
}
SB_Terminate (&S);
/* The following character tells us if we have an integer or floating
** point constant. Note: Hexadecimal floating point constants aren't
** supported in C89.
*/
IsFloat = (CurC == '.' ||
(Base == 10 && toupper (CurC) == 'E') ||
(Base == 16 && toupper (CurC) == 'P' && IS_Get (&Standard) >= STD_C99));
IsFloat = (C == '.' ||
(Base == 10 && toupper (C) == 'E') ||
(Base == 16 && toupper (C) == 'P' && IS_Get (&Standard) >= STD_C99));
/* If we don't have a floating point type, an octal prefix results in an
** octal base.
*/
if (!IsFloat && Prefix == 8) {
/* An octal prefix for an integer type results in an octal base */
if (!IsFloat && Base == 10 && SB_LookAt (&Src, 0) == '0') {
Base = 8;
}
/* Since we do now know the correct base, convert the remembered input
** into a number.
*/
SB_Reset (&S);
/* Since we now know the correct base, convert the input into a number */
SB_SetIndex (&Src, Index);
IVal = 0;
while ((C = SB_Get (&S)) != '\0') {
while ((C = SB_Peek (&Src)) != '\0' && (Base <= 10 ? IsDigit (C) : IsXDigit (C))) {
DigitVal = HexVal (C);
if (DigitVal >= Base) {
Error ("Numeric constant contains digits beyond the radix");
Error ("Invalid digit \"%c\" beyond radix %u constant", C, Base);
SB_Clear (&Src);
break;
}
IVal = (IVal * Base) + DigitVal;
SB_Skip (&Src);
}
/* We don't need the string buffer any longer */
SB_Done (&S);
/* Distinguish between integer and floating point constants */
if (!IsFloat) {
@ -537,27 +586,32 @@ static void NumericConst (void)
** possible to convert the data to unsigned long even if the IT_ULONG
** flag were not set, but we are not doing that.
*/
if (toupper (CurC) == 'U') {
if (toupper (SB_Peek (&Src)) == 'U') {
/* Unsigned type */
NextChar ();
if (toupper (CurC) != 'L') {
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) != 'L') {
Types = IT_UINT | IT_ULONG;
} else {
NextChar ();
SB_Skip (&Src);
Types = IT_ULONG;
}
} else if (toupper (CurC) == 'L') {
} else if (toupper (SB_Peek (&Src)) == 'L') {
/* Long type */
NextChar ();
if (toupper (CurC) != 'U') {
SB_Skip (&Src);
if (toupper (SB_Peek (&Src)) != 'U') {
Types = IT_LONG | IT_ULONG;
WarnTypes = IT_ULONG;
} else {
NextChar ();
SB_Skip (&Src);
Types = IT_ULONG;
}
} else {
if (Prefix == 10) {
if (SB_Peek (&Src) != '\0') {
Error ("Invalid suffix \"%s\" on integer constant",
SB_GetConstBuf (&Src) + SB_GetIndex (&Src));
}
if (Base == 10) {
/* Decimal constants are of any type but uint */
Types = IT_INT | IT_LONG | IT_ULONG;
WarnTypes = IT_LONG | IT_ULONG;
@ -621,16 +675,16 @@ static void NumericConst (void)
Double FVal = FP_D_FromInt (IVal); /* Convert to double */
/* Check for a fractional part and read it */
if (CurC == '.') {
if (SB_Peek (&Src) == '.') {
Double Scale;
/* Skip the dot */
NextChar ();
SB_Skip (&Src);
/* Read fractional digits */
Scale = FP_D_Make (1.0);
while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) {
while (IsXDigit (SB_Peek (&Src)) && (DigitVal = HexVal (SB_Peek (&Src))) < Base) {
/* Get the value of this digit */
Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale);
/* Add it to the float value */
@ -638,25 +692,25 @@ static void NumericConst (void)
/* Scale base */
Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal));
/* Skip the digit */
NextChar ();
SB_Skip (&Src);
}
}
/* Check for an exponent and read it */
if ((Base == 16 && toupper (CurC) == 'F') ||
(Base == 10 && toupper (CurC) == 'E')) {
if ((Base == 16 && toupper (SB_Peek (&Src)) == 'P') ||
(Base == 10 && toupper (SB_Peek (&Src)) == 'E')) {
unsigned Digits;
unsigned Exp;
/* Skip the exponent notifier */
NextChar ();
SB_Skip (&Src);
/* Read an optional sign */
if (CurC == '-') {
NextChar ();
} else if (CurC == '+') {
NextChar ();
if (SB_Peek (&Src) == '-') {
SB_Skip (&Src);
} else if (SB_Peek (&Src) == '+') {
SB_Skip (&Src);
}
/* Read exponent digits. Since we support only 32 bit floats
@ -667,11 +721,11 @@ static void NumericConst (void)
*/
Digits = 0;
Exp = 0;
while (IsDigit (CurC)) {
while (IsDigit (SB_Peek (&Src))) {
if (++Digits <= 3) {
Exp = Exp * 10 + HexVal (CurC);
Exp = Exp * 10 + HexVal (SB_Peek (&Src));
}
NextChar ();
SB_Skip (&Src);
}
/* Check for errors: We must have exponent digits, and not more
@ -690,10 +744,14 @@ static void NumericConst (void)
}
/* Check for a suffix and determine the type of the constant */
if (toupper (CurC) == 'F') {
NextChar ();
if (toupper (SB_Peek (&Src)) == 'F') {
SB_Skip (&Src);
NextTok.Type = type_float;
} else {
if (SB_Peek (&Src) != '\0') {
Error ("Invalid suffix \"%s\" on floating constant",
SB_GetConstBuf (&Src) + SB_GetIndex (&Src));
}
NextTok.Type = type_double;
}
@ -702,6 +760,9 @@ static void NumericConst (void)
NextTok.Tok = TOK_FCONST;
}
/* We don't need the string buffer any longer */
SB_Done (&Src);
}
@ -743,39 +804,38 @@ void NextToken (void)
}
/* Determine the next token from the lookahead */
if (IsDigit (CurC) || (CurC == '.' && IsDigit (NextC))) {
if (IsPPNumber (CurC, NextC)) {
/* A number */
NumericConst ();
return;
}
/* Check for wide character literals */
if (CurC == 'L' && NextC == '\"') {
StringConst ();
return;
/* Check for wide character constants and literals */
if (CurC == 'L') {
if (NextC == '\"') {
StringConst ();
return;
} else if (NextC == '\'') {
CharConst ();
return;
}
}
/* Check for keywords and identifiers */
if (IsSym (token)) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
/* Reserved word found */
return;
if (!PPParserRunning) {
/* Check for a keyword */
if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
/* Reserved word found */
return;
}
}
/* No reserved word, check for special symbols */
if (token[0] == '_' && token[1] == '_') {
/* Special symbols */
if (strcmp (token+2, "FILE__") == 0) {
NextTok.SVal = AddLiteral (GetCurrentFile());
NextTok.Tok = TOK_SCONST;
return;
} else if (strcmp (token+2, "LINE__") == 0) {
NextTok.Tok = TOK_ICONST;
NextTok.IVal = GetCurrentLine();
NextTok.Type = type_int;
return;
} else if (strcmp (token+2, "func__") == 0) {
if (strcmp (token+2, "func__") == 0) {
/* __func__ is only defined in functions */
if (CurrentFunc) {
NextTok.SVal = AddLiteral (F_GetFuncName (CurrentFunc));
@ -1011,6 +1071,15 @@ void NextToken (void)
SetTok (TOK_COMP);
break;
case '#':
NextChar ();
if (CurC == '#') {
SetTok (TOK_DOUBLE_HASH);
} else {
NextTok.Tok = TOK_HASH;
}
break;
default:
UnknownChar (CurC);

View File

@ -79,6 +79,10 @@ typedef enum token_t {
TOK_FASTCALL,
TOK_CDECL,
/* Address sizes */
TOK_FAR,
TOK_NEAR,
/* Tokens denoting types */
TOK_FIRST_TYPE,
TOK_ENUM = TOK_FIRST_TYPE,
@ -95,94 +99,101 @@ typedef enum token_t {
TOK_VOID,
TOK_LAST_TYPE = TOK_VOID,
/* Control statements */
/* Selection statements */
TOK_IF,
TOK_ELSE,
TOK_SWITCH,
/* Iteration statements */
TOK_WHILE,
TOK_DO,
TOK_FOR,
TOK_GOTO,
TOK_IF,
TOK_RETURN,
TOK_SWITCH,
TOK_WHILE,
TOK_ASM,
/* Jump statements */
TOK_GOTO,
TOK_CONTINUE,
TOK_BREAK,
TOK_RETURN,
/* Labels */
TOK_CASE,
TOK_DEFAULT,
TOK_BREAK,
TOK_CONTINUE,
TOK_ELSE,
TOK_ELLIPSIS,
/* Misc. */
TOK_ATTRIBUTE,
TOK_PRAGMA,
TOK_STATIC_ASSERT,
TOK_ASM,
TOK_SIZEOF,
TOK_IDENT,
TOK_SEMI,
/* Primary operators */
TOK_LBRACK,
/* Punctuators */
TOK_FIRST_PUNC,
TOK_LBRACK = TOK_FIRST_PUNC,
TOK_RBRACK,
TOK_LPAREN,
TOK_RPAREN,
TOK_LCURLY,
TOK_RCURLY,
TOK_DOT,
TOK_PTR_REF,
TOK_LCURLY,
TOK_RBRACK,
TOK_COMP,
TOK_INC,
TOK_PLUS_ASSIGN,
TOK_PLUS,
TOK_COMMA,
TOK_DEC,
TOK_MINUS_ASSIGN,
TOK_RCURLY,
TOK_MINUS,
TOK_MUL_ASSIGN,
TOK_ADDR,
TOK_AND = TOK_ADDR, /* Alias */
TOK_STAR,
TOK_MUL = TOK_STAR, /* Alias */
TOK_DIV_ASSIGN,
TOK_DIV,
TOK_BOOL_AND,
TOK_AND_ASSIGN,
TOK_AND,
TOK_NE,
TOK_PLUS,
TOK_MINUS,
TOK_COMP,
TOK_BOOL_NOT,
TOK_BOOL_OR,
TOK_OR_ASSIGN,
TOK_OR,
TOK_EQ,
TOK_ASSIGN,
/* Inequalities */
TOK_LE,
TOK_LT,
TOK_GE,
TOK_GT,
TOK_SHL_ASSIGN,
TOK_SHL,
TOK_SHR_ASSIGN,
TOK_SHR,
TOK_XOR_ASSIGN,
TOK_XOR,
TOK_MOD_ASSIGN,
TOK_DIV,
TOK_MOD,
TOK_SHL,
TOK_SHR,
TOK_LT,
TOK_GT,
TOK_LE,
TOK_GE,
TOK_EQ,
TOK_NE,
TOK_XOR,
TOK_OR,
TOK_BOOL_AND,
TOK_BOOL_OR,
TOK_QUEST,
TOK_COLON,
TOK_RPAREN,
TOK_SCONST,
TOK_SEMI,
TOK_ELLIPSIS,
TOK_ASSIGN,
TOK_MUL_ASSIGN,
TOK_DIV_ASSIGN,
TOK_MOD_ASSIGN,
TOK_PLUS_ASSIGN,
TOK_MINUS_ASSIGN,
TOK_SHL_ASSIGN,
TOK_SHR_ASSIGN,
TOK_AND_ASSIGN,
TOK_XOR_ASSIGN,
TOK_OR_ASSIGN,
TOK_COMMA,
TOK_HASH,
TOK_HASH_HASH,
TOK_DOUBLE_HASH = TOK_HASH_HASH, /* Alias */
TOK_LAST_PUNC = TOK_DOUBLE_HASH,
/* Primary expressions */
TOK_ICONST,
TOK_CCONST,
TOK_WCCONST,
TOK_FCONST,
TOK_SCONST,
TOK_WCSCONST,
TOK_ATTRIBUTE,
TOK_STATIC_ASSERT,
TOK_FAR,
TOK_NEAR,
TOK_IDENT,
TOK_A,
TOK_X,
TOK_Y,
TOK_AX,
TOK_EAX,
TOK_PRAGMA
TOK_EAX
} token_t;
@ -210,6 +221,7 @@ struct Token {
extern Token CurTok; /* The current token */
extern Token NextTok; /* The next token */
extern int PPParserRunning; /* Is tokenizer used by the preprocessor */
@ -219,6 +231,17 @@ extern Token NextTok; /* The next token */
#if defined(HAVE_INLINE)
INLINE int TokIsPunc (const Token* T)
/* Return true if the token is a punctuator */
{
return (T->Tok >= TOK_FIRST_PUNC && T->Tok <= TOK_LAST_PUNC);
}
#else
# define TokIsPunc(T) \
((T)->Tok >= TOK_FIRST_PUNC && (T)->Tok <= TOK_LAST_PUNC)
#endif
#if defined(HAVE_INLINE)
INLINE int TokIsStorageClass (const Token* T)
/* Return true if the token is a storage class specifier */
@ -262,6 +285,14 @@ void SymName (char* S);
int IsSym (char* S);
/* If a symbol follows, read it and return 1, otherwise return 0 */
int IsPPNumber (int Cur, int Next);
/* Return 1 if the two successive characters indicate a pp-number, otherwise
** return 0.
*/
void CopyPPNumber (StrBuf* Target);
/* Copy a pp-number from the input to Target */
void NextToken (void);
/* Get next token from input stream */

View File

@ -237,5 +237,8 @@ MakeRVal:
/* Set the type of the result */
Expr->Type = ResultType;
/* Propagate from subexpressions */
Expr->Flags |= Expr2.Flags & E_MASK_VIRAL;
}
}

View File

@ -185,6 +185,9 @@ static void ParseArg (ArgDesc* Arg, const Type* Type, ExprDesc* Expr)
/* Use the type of the argument for the push */
Arg->Flags |= TypeOf (Arg->Expr.Type);
/* Propagate from subexpressions */
Expr->Flags |= Arg->Expr.Flags & E_MASK_VIRAL;
}
@ -1365,6 +1368,9 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
ExitPoint:
/* We expect the closing brace */
ConsumeRParen ();
/* Propagate from subexpressions */
Expr->Flags |= Arg.Flags & E_MASK_VIRAL;
}
@ -1405,4 +1411,7 @@ void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
/* Call the handler function */
D->Handler (F, lval);
/* We assume all function calls had side effects */
lval->Flags |= E_SIDE_EFFECTS;
}

View File

@ -609,17 +609,23 @@ static void Statement (int* PendingToken)
Expr.Flags |= E_NEED_NONE;
Expression0 (&Expr);
/* If the statement didn't generate code, and is not of type
** void, emit a warning.
/* If the statement has no observable effect and isn't cast to type
** void, emit a warning and remove useless code if any.
*/
GetCodePos (&End);
if (!ED_YetToLoad (&Expr) &&
!ED_MayHaveNoEffect (&Expr) &&
CodeRangeIsEmpty (&Start, &End) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Expression result unused");
if (CodeRangeIsEmpty (&Start, &End) ||
(Expr.Flags & E_SIDE_EFFECTS) == 0) {
if (!ED_MayHaveNoEffect (&Expr) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Statement has no effect");
}
/* Remove code with no effect */
RemoveCode (&Start);
}
CheckSemi (PendingToken);
}

View File

@ -206,7 +206,7 @@ void SwitchStatement (void)
void CaseLabel (void)
/* Handle a case sabel */
/* Handle a case label */
{
ExprDesc CaseExpr; /* Case label expression */
long Val; /* Case label value */

View File

@ -728,6 +728,7 @@ static int HandleSymRedefinition (SymEntry* Entry, const Type* T, unsigned Flags
/* New type must be compatible with the composite prototype */
if (IsDistinctRedef (E_Type, T, TC_EQUAL, TCF_MASK_QUAL)) {
Error ("Conflicting function types for '%s'", Entry->Name);
TypeCompatibilityDiagnostic (T, E_Type, 0, "'%s' vs '%s'");
Entry = 0;
} else {
/* Refine the existing composite prototype with this new

View File

@ -143,10 +143,10 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType)
/* Do the integer constant <-> absolute address conversion if necessary */
if (IsClassPtr (NewType)) {
Expr->Flags &= ~E_LOC_NONE;
Expr->Flags &= ~E_MASK_LOC;
Expr->Flags |= E_LOC_ABS | E_ADDRESS_OF;
} else if (IsClassInt (NewType)) {
Expr->Flags &= ~(E_LOC_ABS | E_ADDRESS_OF);
Expr->Flags &= ~(E_MASK_LOC | E_ADDRESS_OF);
Expr->Flags |= E_LOC_NONE;
}

View File

@ -776,14 +776,20 @@ static void PrintUnresolved (ExpCheckFunc F, void* Data)
Import* Imp = E->ImpList;
const char* name = GetString (E->Name);
while (Imp) {
unsigned J;
for (J = 0; J < CollCount (&Imp->RefLines); ++J) {
const LineInfo* LI = CollConstAt (&Imp->RefLines, J);
fprintf (stderr,
"%s:%u: Error: Unresolved external '%s'\n",
GetSourceName (LI),
GetSourceLine (LI),
name);
unsigned J, count = CollCount (&Imp->RefLines);
/* The count is 0 when the import was not added by an input file,
but by the compiler itself. */
if (count == 0) {
fprintf (stderr, "Error: Unresolved external '%s'\n", name);
} else {
for (J = 0; J < count; ++J) {
const LineInfo* LI = CollConstAt (&Imp->RefLines, J);
fprintf (stderr,
"%s:%u: Error: Unresolved external '%s'\n",
GetSourceName (LI),
GetSourceLine (LI),
name);
}
}
Imp = Imp->Next;
}

View File

@ -809,6 +809,11 @@ testcode.d64: testcode
$(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe))
# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe))
testcode.d81: testcode
@$(C1541) -format testcode,AA d81 $@ >$(NULLDEV)
$(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe))
$(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe))
# --------------------------------------------------------------------------
# Rule to make an Apple II disk with all testcode. Needs the AppleCommander
# program, available at https://applecommander.github.io/, and a template disk

View File

@ -2,6 +2,10 @@
# var. to build for another target system.
SYS ?= c64
# This one comes with the VICE emulator.
# See http://vice-emu.sourceforge.net/
C1541 ?= c1541
# Just the usual way to find out if we're
# using cmd.exe to execute make rules.
ifneq ($(shell echo),)
@ -32,11 +36,20 @@ endif
EXELIST_c64 = \
petscii.prg \
cbmdir-test.prg
cbmdir-test.prg \
cbmread.prg
EXTRALIST_c64 = \
read-0 \
read-1 \
read-8
EXELIST_vic20 = \
cbmdir-test.prg
DISK_c64 = testcode.d64
DISK_vic20 = testcode.d64
ifneq ($(EXELIST_$(SYS)),)
testcode: $(EXELIST_$(SYS))
else
@ -66,5 +79,38 @@ else
$(CL) -t $(SYS) -Oris -o $@ $<
endif
cbmread.prg: cbmread.c
ifeq ($(SYS),vic20)
$(CL) -t $(SYS) -C vic20-32k.cfg -Oris -o $@ $<
else
$(CL) -t $(SYS) -Oris -o $@ $<
endif
# --------------------------------------------------------------------------
# Rule to make a CBM disk with all testcode. Needs the c1541 program that comes
# with the VICE emulator.
define D64_WRITE_PRG_recipe
$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" "$(subst ?,$(SPACE),$(subst .prg,,$(notdir $(file))))",p >$(NULLDEV)
endef # D64_WRITE_PRG_recipe
define D64_WRITE_SEQ_recipe
$(C1541) -attach $@ -write "$(subst ?,$(SPACE),$(file))" $(notdir $(file)),s >$(NULLDEV)
endef # D64_WRITE_SEQ_recipe
disk: $(DISK_$(SYS))
testcode.d64: testcode
@$(C1541) -format "testcode,00" d64 $@ >$(NULLDEV)
$(foreach file,$(EXELIST_$(SYS)),$(D64_WRITE_PRG_recipe))
$(foreach file,$(EXTRALIST_$(SYS)),$(D64_WRITE_SEQ_recipe))
# $(foreach file,$(EMD) $(MOU) $(TGI),$(D64_WRITE_SEQ_recipe))
clean:
@$(DEL) *.lbl petscii.prg cbmdir-test.prg 2>$(NULLDEV)
@$(DEL) $(DISK_c64)
@$(DEL) $(DISK_vic20)

82
targettest/cbm/cbmread.c Normal file
View File

@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cbm.h>
int res = 0;
int tests = 0;
#define LFN 8
#define MAXREADSIZE 256
static char data[MAXREADSIZE];
void test1 (char * what, char * filename, int len1, int len2, int rlen1, int rlen2)
{
int rlen;
unsigned char err;
printf ("1:%s (%d bytes)..." , what, rlen1);
err = cbm_open (LFN, 8, 8, filename);
if (err != 0) {
printf ("\nError: could not open file.\n");
goto test1exit;
}
rlen = cbm_read (LFN, data, rlen1);
if (rlen == -1) {
printf ("\ncbm_read error (%d)\n", _oserror);
res++;
goto test1exit;
}
if (rlen != len1) {
printf ("\nError: 1st read returned %d instead of %d\n", rlen, len1);
res++;
goto test1exit;
}
printf (" OK\n");
printf ("2:%s (%d bytes)..." , what, rlen2);
rlen = cbm_read (LFN, data, rlen2);
if (rlen == -1) {
printf ("\ncbm_read error (%d)\n", _oserror);
res++;
goto test1exit;
}
if (rlen != len2) {
printf ("\nError: 2nd read returned %d instead of %d\n", rlen, len2);
res++;
goto test1exit;
}
printf (" OK\n");
test1exit:
cbm_close (LFN);
tests++;
}
int main(void)
{
test1 ("Read empty file", "read-0", 0, 0, 0, 0);
test1 ("Read empty file", "read-0", 0, 0, 1, 1);
test1 ("Read empty file", "read-0", 0, 0, 8, 8);
test1 ("Read 1 byte file", "read-1", 0, 0, 0, 0);
test1 ("Read 1 byte file", "read-1", 1, 0, 1, 1);
test1 ("Read 1 byte file", "read-1", 1, 0, 8, 8);
test1 ("Read 8 byte file", "read-8", 0, 0, 0, 0);
test1 ("Read 8 byte file", "read-8", 1, 1, 1, 1);
test1 ("Read 8 byte file", "read-8", 4, 4, 4, 4);
test1 ("Read 8 byte file", "read-8", 8, 0, 8, 8);
test1 ("Read 8 byte file", "read-8", 8, 0,10, 1);
printf ("%d/%d Tests failed.\n", res, tests);
return res;
}

0
targettest/cbm/read-0 Normal file
View File

1
targettest/cbm/read-1 Normal file
View File

@ -0,0 +1 @@
B

1
targettest/cbm/read-8 Normal file
View File

@ -0,0 +1 @@
GЊЛЬн

View File

@ -96,6 +96,7 @@ static emd_test_t drivers[] = {
{ '8', "C64DTV himem", "dtv-himem.emd" },
{ '9', "65816 extra banks", "c64-65816.emd" },
{ 'k', "Kerberos", "c64-kerberos.emd" },
{ 'r', "Retro Replay RAM", "c64-rrr.emd" },
#endif
#if defined(__C128__)

View File

@ -0,0 +1,16 @@
; 2022-01-17 Spiro Trikaliotis
.macro TESTER
lda #2
.endmacro
test:
ldx #0
TESTER
ldx #15
TESTER
inx
TESTER
TESTER
dex
rts

View File

@ -0,0 +1,16 @@
; 2022-01-17 Spiro Trikaliotis
.macro TESTER val
lda #val
.endmacro
test:
ldx #0
TESTER 1
ldx #15
TESTER 2
inx
TESTER 3
TESTER 4
dex
rts

View File

@ -0,0 +1,14 @@
; 2022-06-15 Spiro Trikaliotis
; upper case pseudo-op
.ASCIIZ ""
.ASCIIZ "Hello World"
.ASCIIZ "Hello ","World"
.ASCIIZ "Hello ","World"," ","I"," ","am"," ","testing"
; lower case pseudo-op
.asciiz ""
.asciiz "Hello World"
.asciiz "Hello ","World"
.asciiz "Hello ","World"," ","I"," ","am"," ","testing"

View File

@ -0,0 +1,81 @@
; 2022-06-15 Spiro Trikaliotis
.ASSERT * = $0000, warning, "Code not at $0000"
.assert * = $0000, warning, "Code not at $0000"
.ASSERT * = $0001, warning, "Code not at $0001"
.assert * = $0001, warning, "Code not at $0001"
.ASSERT * = $1000, warning, "Code not at $1000"
.assert * = $1000, warning, "Code not at $1000"
.ASSERT * = $1001, warning, "Code not at $1001"
.assert * = $1001, warning, "Code not at $1001"
.ASSERT * = $8000, warning, "Code not at $8000"
.assert * = $8000, warning, "Code not at $8000"
.ASSERT * = $8001, warning, "Code not at $8001"
.assert * = $8001, warning, "Code not at $8001"
nop
.ASSERT * = $0000, warning, "Code not at $0000"
.assert * = $0000, warning, "Code not at $0000"
.ASSERT * = $0001, warning, "Code not at $0001"
.assert * = $0001, warning, "Code not at $0001"
.ASSERT * = $1000, warning, "Code not at $1000"
.assert * = $1000, warning, "Code not at $1000"
.ASSERT * = $1001, warning, "Code not at $1001"
.assert * = $1001, warning, "Code not at $1001"
.ASSERT * = $8000, warning, "Code not at $8000"
.assert * = $8000, warning, "Code not at $8000"
.ASSERT * = $8001, warning, "Code not at $8001"
.assert * = $8001, warning, "Code not at $8001"
.org $8000
.ASSERT * = $0000, warning, "Code not at $0000"
.assert * = $0000, warning, "Code not at $0000"
.ASSERT * = $0001, warning, "Code not at $0001"
.assert * = $0001, warning, "Code not at $0001"
.ASSERT * = $1000, warning, "Code not at $1000"
.assert * = $1000, warning, "Code not at $1000"
.ASSERT * = $1001, warning, "Code not at $1001"
.assert * = $1001, warning, "Code not at $1001"
.ASSERT * = $8000, warning, "Code not at $8000"
.assert * = $8000, warning, "Code not at $8000"
.ASSERT * = $8001, warning, "Code not at $8001"
.assert * = $8001, warning, "Code not at $8001"
nop
.ASSERT * = $0000, warning, "Code not at $0000"
.assert * = $0000, warning, "Code not at $0000"
.ASSERT * = $0001, warning, "Code not at $0001"
.assert * = $0001, warning, "Code not at $0001"
.ASSERT * = $1000, warning, "Code not at $1000"
.assert * = $1000, warning, "Code not at $1000"
.ASSERT * = $1001, warning, "Code not at $1001"
.assert * = $1001, warning, "Code not at $1001"
.ASSERT * = $8000, warning, "Code not at $8000"
.assert * = $8000, warning, "Code not at $8000"
.ASSERT * = $8001, warning, "Code not at $8001"
.assert * = $8001, warning, "Code not at $8001"

View File

@ -0,0 +1,28 @@
; 2022-06-15 Spiro Trikaliotis
.ASSERT * = $0000, error, "Code not at $0000"
.assert * = $0000, error, "Code not at $0000"
.ASSERT * = $0001, error, "Code not at $0001"
.assert * = $0001, error, "Code not at $0001"
.ASSERT * = $1000, error, "Code not at $1000"
.assert * = $1000, error, "Code not at $1000"
.ASSERT * = $1001, error, "Code not at $1001"
.assert * = $1001, error, "Code not at $1001"
.ASSERT * = $8000, error, "Code not at $8000"
.assert * = $8000, error, "Code not at $8000"
.ASSERT * = $8001, error, "Code not at $8001"
.assert * = $8001, error, "Code not at $8001"
.org $8000
.ASSERT * = $8000, error, "Code not at $8000"
.assert * = $8000, error, "Code not at $8000"
.ASSERT * = $8001, error, "Code not at $8001"
.assert * = $8001, error, "Code not at $8001"

View File

@ -0,0 +1,8 @@
; 2022-06-18 Spiro Trikaliotis
.ASSERT * = $0000, error, "Code not at $0000"
.ASSERT * = $0001, error, "Code not at $0001"
.ASSERT * = $1000, error, "Code not at $1000"
.ASSERT * = $1001, error, "Code not at $1001"
.ASSERT * = $8000, error, "Code not at $8000"
.ASSERT * = $8001, error, "Code not at $8001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $0000, error, "Code not at $0000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $0001, error, "Code not at $0001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $1000, error, "Code not at $1000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $1001, error, "Code not at $1001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $8000, error, "Code not at $8000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $8001, error, "Code not at $8001"

View File

@ -0,0 +1,81 @@
; 2022-06-15 Spiro Trikaliotis
.ASSERT * = $0000, ldwarning, "Code not at $0000"
.assert * = $0000, ldwarning, "Code not at $0000"
.ASSERT * = $0001, ldwarning, "Code not at $0001"
.assert * = $0001, ldwarning, "Code not at $0001"
.ASSERT * = $1000, ldwarning, "Code not at $1000"
.assert * = $1000, ldwarning, "Code not at $1000"
.ASSERT * = $1001, ldwarning, "Code not at $1001"
.assert * = $1001, ldwarning, "Code not at $1001"
.ASSERT * = $8000, ldwarning, "Code not at $8000"
.assert * = $8000, ldwarning, "Code not at $8000"
.ASSERT * = $8001, ldwarning, "Code not at $8001"
.assert * = $8001, ldwarning, "Code not at $8001"
nop
.ASSERT * = $0000, ldwarning, "Code not at $0000"
.assert * = $0000, ldwarning, "Code not at $0000"
.ASSERT * = $0001, ldwarning, "Code not at $0001"
.assert * = $0001, ldwarning, "Code not at $0001"
.ASSERT * = $1000, ldwarning, "Code not at $1000"
.assert * = $1000, ldwarning, "Code not at $1000"
.ASSERT * = $1001, ldwarning, "Code not at $1001"
.assert * = $1001, ldwarning, "Code not at $1001"
.ASSERT * = $8000, ldwarning, "Code not at $8000"
.assert * = $8000, ldwarning, "Code not at $8000"
.ASSERT * = $8001, ldwarning, "Code not at $8001"
.assert * = $8001, ldwarning, "Code not at $8001"
.org $8000
.ASSERT * = $0000, ldwarning, "Code not at $0000"
.assert * = $0000, ldwarning, "Code not at $0000"
.ASSERT * = $0001, ldwarning, "Code not at $0001"
.assert * = $0001, ldwarning, "Code not at $0001"
.ASSERT * = $1000, ldwarning, "Code not at $1000"
.assert * = $1000, ldwarning, "Code not at $1000"
.ASSERT * = $1001, ldwarning, "Code not at $1001"
.assert * = $1001, ldwarning, "Code not at $1001"
.ASSERT * = $8000, ldwarning, "Code not at $8000"
.assert * = $8000, ldwarning, "Code not at $8000"
.ASSERT * = $8001, ldwarning, "Code not at $8001"
.assert * = $8001, ldwarning, "Code not at $8001"
nop
.ASSERT * = $0000, ldwarning, "Code not at $0000"
.assert * = $0000, ldwarning, "Code not at $0000"
.ASSERT * = $0001, ldwarning, "Code not at $0001"
.assert * = $0001, ldwarning, "Code not at $0001"
.ASSERT * = $1000, ldwarning, "Code not at $1000"
.assert * = $1000, ldwarning, "Code not at $1000"
.ASSERT * = $1001, ldwarning, "Code not at $1001"
.assert * = $1001, ldwarning, "Code not at $1001"
.ASSERT * = $8000, ldwarning, "Code not at $8000"
.assert * = $8000, ldwarning, "Code not at $8000"
.ASSERT * = $8001, ldwarning, "Code not at $8001"
.assert * = $8001, ldwarning, "Code not at $8001"

View File

@ -0,0 +1,28 @@
; 2022-06-15 Spiro Trikaliotis
.ASSERT * = $0000, error, "Code not at $0000"
.assert * = $0000, error, "Code not at $0000"
.ASSERT * = $0001, error, "Code not at $0001"
.assert * = $0001, error, "Code not at $0001"
.ASSERT * = $1000, error, "Code not at $1000"
.assert * = $1000, error, "Code not at $1000"
.ASSERT * = $1001, error, "Code not at $1001"
.assert * = $1001, error, "Code not at $1001"
.ASSERT * = $8000, error, "Code not at $8000"
.assert * = $8000, error, "Code not at $8000"
.ASSERT * = $8001, error, "Code not at $8001"
.assert * = $8001, error, "Code not at $8001"
.org $8000
.ASSERT * = $8000, error, "Code not at $8000"
.assert * = $8000, error, "Code not at $8000"
.ASSERT * = $8001, error, "Code not at $8001"
.assert * = $8001, error, "Code not at $8001"

View File

@ -0,0 +1,8 @@
; 2022-06-18 Spiro Trikaliotis
.ASSERT * = $0000, lderror, "Code not at $0000"
.ASSERT * = $0001, lderror, "Code not at $0001"
.ASSERT * = $1000, lderror, "Code not at $1000"
.ASSERT * = $1001, lderror, "Code not at $1001"
.ASSERT * = $8000, lderror, "Code not at $8000"
.ASSERT * = $8001, lderror, "Code not at $8001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $0000, lderror, "Code not at $0000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $0001, lderror, "Code not at $0001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $1000, lderror, "Code not at $1000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $1001, lderror, "Code not at $1001"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $8000, lderror, "Code not at $8000"

View File

@ -0,0 +1,3 @@
; 2022-06-20 Spiro Trikaliotis
.assert * = $8001, lderror, "Code not at $8001"

View File

@ -0,0 +1,20 @@
; 2022-06-20 Spiro Trikaliotis
.byte 0
.align 4
.byte 4
.word 12
.word 18
.align 1
.byte 1
.align 8
.byte 8
.align 8
.byte 8
.align 128
.byte 128

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.case -
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.case off
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.CASE OFF
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.case +
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.case on
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.CASE ON
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

View File

@ -0,0 +1,10 @@
; 2022-06-20 Spiro Trikaliotis
.case
CamelCase:
lda #0
Test:
beq CamelCase
bne camelcase

3526
test/asm/listing/100-byte.s Normal file

File diff suppressed because it is too large Load Diff

3526
test/asm/listing/101-byt.s Normal file

File diff suppressed because it is too large Load Diff

6076
test/asm/listing/102-word.s Normal file

File diff suppressed because it is too large Load Diff

6076
test/asm/listing/103-dbyt.s Normal file

File diff suppressed because it is too large Load Diff

4874
test/asm/listing/104-dword.s Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ else
endif
ifdef QUIET
.SILENT:
# .SILENT:
endif
CA65 := $(if $(wildcard ../../../bin/ca65*),../../../bin/ca65,ca65)
@ -49,33 +49,82 @@ $(WORKDIR)/$1.bin: $1.s $(ISEQUAL)
$(if $(QUIET),echo asm/$1.bin)
# compile without generating listing
$(CA65) -t none -o $$(@:.bin=.o) $$<
$(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib
ifneq ($(wildcard $1.bin-ref),)
$(ISEQUAL) $1.bin-ref $$@
ifeq ($(wildcard control/$1.err),)
$(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1
ifeq ($(wildcard control/$1.no-ld65),)
$(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1
endif
else
$(CA65) -t none -o $$(@:.bin=.o) $$< > $$(@:.bin=.err) 2>&1 || true
ifeq ($(wildcard control/$1.no-ld65),)
$(LD65) -t none -o $$@ $$(@:.bin=.o) none.lib > $$(@:.bin=.ld65-err) 2>&1 || true
endif
endif
$(CA65) -t none -l $$(@:.bin=.list.orig) -o $$(@:.bin=.list-o) $$<
$(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib
ifneq ($(wildcard ref/$1.err-ref),)
$(ISEQUAL) ref/$1.err-ref $$(@:.bin=.err)
else
$(ISEQUAL) --empty $$(@:.bin=.err)
endif
ifneq ($(wildcard ref/$1.bin-ref),)
$(ISEQUAL) --binary ref/$1.bin-ref $$@
endif
ifneq ($(wildcard ref/$1.ld65err-ref),)
@echo cat $$(@:.bin=.ld65-err)
cat $$(@:.bin=.ld65-err)
@echo
@echo
-diff -u ref/$1.ld65err-ref $$(@:.bin=.ld65-err)
@echo
@echo
$(ISEQUAL) --wildcards ref/$1.ld65err-ref $$(@:.bin=.ld65-err)
else
ifneq ($(wildcard $(WORKDIR)/$1.ld65-err),)
$(ISEQUAL) --empty $$(@:.bin=.ld65-err)
endif
endif
# compile with listing file
ifeq ($(wildcard control/$1.err),)
$(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1
ifeq ($(wildcard control/$1.no-ld65),)
$(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1
endif
else
$(CA65) -t none -l $$(@:.bin=.list-lst) -o $$(@:.bin=.list-o) $$< > $$(@:.bin=.list-err) 2>&1 || true
ifeq ($(wildcard control/$1.no-ld65),)
$(LD65) -t none -o $$(@:.bin=.list-bin) $$(@:.bin=.list-o) none.lib > $$(@:.bin=.list-ld65-err) 2>&1 || true
endif
endif
ifneq ($(wildcard ref/$1.err-ref),)
$(ISEQUAL) ref/$1.err-ref $$(@:.bin=.list-err)
else
$(ISEQUAL) --empty $$(@:.bin=.list-err)
endif
ifneq ($(wildcard ref/$1.ld65err-ref),)
$(ISEQUAL) --wildcards ref/$1.ld65err-ref $$(@:.bin=.list-ld65-err)
else
ifneq ($(wildcard $(WORKDIR)/$1.list-ld65-err),)
$(ISEQUAL) --empty $$(@:.bin=.list-ld65-err)
endif
endif
# check if the result bin is the same as without listing file
ifeq ($(wildcard control/$1.err),)
$(ISEQUAL) $$@ $$(@:.bin=.list-bin)
endif
ifneq ($(wildcard $1.list-ref),)
ifneq ($(wildcard ref/$1.list-ref),)
# we have a reference file, compare that, too
# remove first line which contains a version number
tail -n +2 $$(@:.bin=.lst.orig) > $$(@:.bin=.lst)
$(ISEQUAL) $1.list-ref $$(@:.bin=.lst)
$(ISEQUAL) --skip=1 ref/$1.list-ref $$(@:.bin=.list-lst)
endif
# $(CA65) -t none -f -l $$(@:.bin=.flist.orig) -o $$(@:.bin=.flist-o) $$<
# $(LD65) -t none -o $$(@:.bin=.flist-bin) $$(@:.bin=.flist-o) none.lib
# # check if the result bin is the same as without listing file
# $(ISEQUAL) $$@ $$(@:.bin=.flist-bin)
endef # LISTING_template

Some files were not shown because too many files have changed in this diff Show More