mirror of
https://github.com/cc65/cc65.git
synced 2024-12-22 12:30:41 +00:00
Merge branch 'cc65:master' into kim1
This commit is contained in:
commit
2b07204952
20
.github/workflows/snapshot-on-push-master.yml
vendored
20
.github/workflows/snapshot-on-push-master.yml
vendored
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 <atari_screen_charmap.h>
|
||||
char pcScreenMappingString[] = "Hello Atari!";
|
||||
|
||||
#include <atari_atascii_charmap.h>
|
||||
char pcAtasciiMappingString[] = "Hello Atari!";
|
||||
</verb></tscreen>
|
||||
|
||||
delivers correct results, while
|
||||
Example:
|
||||
|
||||
<tscreen><verb>
|
||||
#include <atari_screen_charmap.h>
|
||||
@ -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),
|
||||
|
@ -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).
|
||||
|
@ -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[];
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ typedef unsigned size_t;
|
||||
|
||||
/* NULL pointer */
|
||||
#ifndef _HAVE_NULL
|
||||
#define NULL 0
|
||||
#define NULL ((void *) 0)
|
||||
#define _HAVE_NULL
|
||||
#endif
|
||||
|
||||
|
@ -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
363
libsrc/c64/emd/c64-rrr.s
Normal 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
279
src/cc65/expr.c
279
src/cc65/expr.c
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -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 */
|
||||
|
169
src/cc65/input.c
169
src/cc65/input.c
@ -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 */
|
||||
{
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
878
src/cc65/ppexpr.c
Normal 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
76
src/cc65/ppexpr.h
Normal 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
|
1362
src/cc65/preproc.c
1362
src/cc65/preproc.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -237,5 +237,8 @@ MakeRVal:
|
||||
|
||||
/* Set the type of the result */
|
||||
Expr->Type = ResultType;
|
||||
|
||||
/* Propagate from subexpressions */
|
||||
Expr->Flags |= Expr2.Flags & E_MASK_VIRAL;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
82
targettest/cbm/cbmread.c
Normal 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
0
targettest/cbm/read-0
Normal file
1
targettest/cbm/read-1
Normal file
1
targettest/cbm/read-1
Normal file
@ -0,0 +1 @@
|
||||
B
|
1
targettest/cbm/read-8
Normal file
1
targettest/cbm/read-8
Normal file
@ -0,0 +1 @@
|
||||
GЊЛЬн
|
@ -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__)
|
||||
|
16
test/asm/listing/001-macro-simple-listing.s
Normal file
16
test/asm/listing/001-macro-simple-listing.s
Normal 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
|
16
test/asm/listing/002-macro-param-listing.s
Normal file
16
test/asm/listing/002-macro-param-listing.s
Normal 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
|
14
test/asm/listing/020-asciiz.s
Normal file
14
test/asm/listing/020-asciiz.s
Normal 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"
|
81
test/asm/listing/030-assert-success.s
Normal file
81
test/asm/listing/030-assert-success.s
Normal 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"
|
28
test/asm/listing/031-assert-error.s
Normal file
28
test/asm/listing/031-assert-error.s
Normal 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"
|
8
test/asm/listing/032-assert-error2.s
Normal file
8
test/asm/listing/032-assert-error2.s
Normal 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"
|
3
test/asm/listing/032-assert-error3.s
Normal file
3
test/asm/listing/032-assert-error3.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $0000, error, "Code not at $0000"
|
3
test/asm/listing/032-assert-error4.s
Normal file
3
test/asm/listing/032-assert-error4.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $0001, error, "Code not at $0001"
|
3
test/asm/listing/032-assert-error5.s
Normal file
3
test/asm/listing/032-assert-error5.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $1000, error, "Code not at $1000"
|
3
test/asm/listing/032-assert-error6.s
Normal file
3
test/asm/listing/032-assert-error6.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $1001, error, "Code not at $1001"
|
3
test/asm/listing/032-assert-error7.s
Normal file
3
test/asm/listing/032-assert-error7.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $8000, error, "Code not at $8000"
|
3
test/asm/listing/032-assert-error8.s
Normal file
3
test/asm/listing/032-assert-error8.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $8001, error, "Code not at $8001"
|
81
test/asm/listing/033-assert-ldwarning-success.s
Normal file
81
test/asm/listing/033-assert-ldwarning-success.s
Normal 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"
|
28
test/asm/listing/034-assert-lderror1.s
Normal file
28
test/asm/listing/034-assert-lderror1.s
Normal 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"
|
8
test/asm/listing/034-assert-lderror2.s
Normal file
8
test/asm/listing/034-assert-lderror2.s
Normal 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"
|
3
test/asm/listing/034-assert-lderror3.s
Normal file
3
test/asm/listing/034-assert-lderror3.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $0000, lderror, "Code not at $0000"
|
3
test/asm/listing/034-assert-lderror4.s
Normal file
3
test/asm/listing/034-assert-lderror4.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $0001, lderror, "Code not at $0001"
|
3
test/asm/listing/034-assert-lderror5.s
Normal file
3
test/asm/listing/034-assert-lderror5.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $1000, lderror, "Code not at $1000"
|
3
test/asm/listing/034-assert-lderror6.s
Normal file
3
test/asm/listing/034-assert-lderror6.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $1001, lderror, "Code not at $1001"
|
3
test/asm/listing/034-assert-lderror7.s
Normal file
3
test/asm/listing/034-assert-lderror7.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $8000, lderror, "Code not at $8000"
|
3
test/asm/listing/034-assert-lderror8.s
Normal file
3
test/asm/listing/034-assert-lderror8.s
Normal file
@ -0,0 +1,3 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.assert * = $8001, lderror, "Code not at $8001"
|
20
test/asm/listing/040-align.s
Normal file
20
test/asm/listing/040-align.s
Normal 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
|
10
test/asm/listing/050-case-off-2.s
Normal file
10
test/asm/listing/050-case-off-2.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.case -
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-off-3.s
Normal file
10
test/asm/listing/050-case-off-3.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.case off
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-off-4.s
Normal file
10
test/asm/listing/050-case-off-4.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.CASE OFF
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-on-1.s
Normal file
10
test/asm/listing/050-case-on-1.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-on-2.s
Normal file
10
test/asm/listing/050-case-on-2.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.case +
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-on-3.s
Normal file
10
test/asm/listing/050-case-on-3.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.case on
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-on-4.s
Normal file
10
test/asm/listing/050-case-on-4.s
Normal file
@ -0,0 +1,10 @@
|
||||
; 2022-06-20 Spiro Trikaliotis
|
||||
|
||||
.CASE ON
|
||||
|
||||
CamelCase:
|
||||
lda #0
|
||||
|
||||
Test:
|
||||
beq CamelCase
|
||||
bne camelcase
|
10
test/asm/listing/050-case-on-5.s
Normal file
10
test/asm/listing/050-case-on-5.s
Normal 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
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
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
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
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
4874
test/asm/listing/104-dword.s
Normal file
File diff suppressed because it is too large
Load Diff
6717
test/asm/listing/105-faraddr.s
Normal file
6717
test/asm/listing/105-faraddr.s
Normal file
File diff suppressed because it is too large
Load Diff
6076
test/asm/listing/106-hibytes.s
Normal file
6076
test/asm/listing/106-hibytes.s
Normal file
File diff suppressed because it is too large
Load Diff
6076
test/asm/listing/107-lobytes.s
Normal file
6076
test/asm/listing/107-lobytes.s
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
|
||||
|
0
test/asm/listing/control/031-assert-error.err
Normal file
0
test/asm/listing/control/031-assert-error.err
Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user