Merge branch 'cc65:master' into master

This commit is contained in:
rumbledethumps
2025-05-23 18:53:28 -07:00
committed by GitHub
30 changed files with 676 additions and 61 deletions

View File

@@ -354,6 +354,7 @@ usage.
<item>allow_lowercase
<item>beep
<item>dir_entry_count
<item>get_tv
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt

View File

@@ -333,6 +333,7 @@ usage.
<item>_datetime
<item>beep
<item>dir_entry_count
<item>get_tv
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt

View File

@@ -833,11 +833,9 @@ names like "Loop". Here is an example:
<sect1>Unnamed labels<p>
If you really want to write messy code, there are also unnamed labels. To define
an unnamed label, use either <tt>@:</tt> (<tt>.LOCALCHAR</tt> is respected if it
is set) or sole <tt>:</tt>.
an unnamed label, use sole <tt>:</tt>.
To reference an unnamed label, use <tt>@</tt> (<tt>.LOCALCHAR</tt> is respected
if it is set) or <tt>:</tt> with several <tt>-</tt> or <tt>+</tt> characters.
To reference an unnamed label, use <tt>:</tt> with several <tt>-</tt> or <tt>+</tt> characters.
The <tt>-</tt> characters will create a back reference (n'th label backwards),
the <tt>+</tt> will create a forward reference (n'th label in forward direction).
As an alternative, angle brackets <tt>&lt;</tt> and <tt>&gt;</tt> may be used
@@ -847,12 +845,12 @@ Example:
<tscreen><verb>
cpy #0
beq @++
@:
beq :++
:
sta $2007
dey
bne @-
@:
bne :-
:
rts
</verb></tscreen>

View File

@@ -1053,6 +1053,16 @@ This cc65 version has some extensions to the ISO C standard.
unsigned char foo = 0b101; // sets it to 5
</verb></tscreen>
<item> The character escape '\e', a GCC C extension, is accepted.
In ASCII this is the escape character 0x1B, which may be
remapped in other character sets via a #pragma charmap.
It can be disabled with the <tt><ref id="option--standard"
name="--standard"></tt> option.
<tscreen><verb>
unsigned char foo = '\e'; // sets it to 0x1B or equivalent
</verb></tscreen>
</itemize>
<p>

View File

@@ -98,6 +98,7 @@ function.
<item>allow_lowercase
<item><ref id="beep" name="beep">
<item><ref id="dir_entry_count" name="dir_entry_count">
<item><ref id="get_tv" name="get_tv">
<item><ref id="get_ostype" name="get_ostype">
<item><ref id="gmtime_dt" name="gmtime_dt">
<item><ref id="mktime_dt" name="mktime_dt">
@@ -111,6 +112,7 @@ function.
<item>_dos_type
<item><ref id="beep" name="beep">
<item><ref id="dir_entry_count" name="dir_entry_count">
<item><ref id="get_tv" name="get_tv">
<item><ref id="get_ostype" name="get_ostype">
<item><ref id="gmtime_dt" name="gmtime_dt">
<item><ref id="mktime_dt" name="mktime_dt">
@@ -140,7 +142,7 @@ function.
<!-- <item><ref id="_setcolor_low" name="_setcolor_low"> -->
<item><ref id="_sound" name="_sound">
<item><ref id="get_ostype" name="get_ostype">
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
</itemize>
(incomplete)
@@ -227,7 +229,7 @@ function.
<!-- <item><ref id="cbm_readdir" name="cbm_readdir"> -->
<!-- <item><ref id="cbm_save" name="cbm_save"> -->
<!-- <item><ref id="cbm_write" name="cbm_write"> -->
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
<item><ref id="kbrepeat" name="kbrepeat">
<item><ref id="waitvsync" name="waitvsync">
</itemize>
@@ -348,7 +350,7 @@ function.
<itemize>
<!-- <item><ref id="get_numbanks" name="get_numbanks"> -->
<item><ref id="get_ostype" name="get_ostype">
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
<!-- <item><ref id="set_tv" name="set_tv"> -->
<!-- <item><ref id="vera_layer_enable" name="vera_layer_enable"> -->
<!-- <item><ref id="vera_sprites_enable" name="vera_sprites_enable"> -->
@@ -443,7 +445,7 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>.
<sect1><tt/gamate.h/<label id="gamate.h"><p>
<itemize>
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
<item><ref id="waitvsync" name="waitvsync">
</itemize>
@@ -550,7 +552,7 @@ see also <tt>testcode/lib/em-test.c</tt> and <tt>samples/multidemo.c</tt>.
<sect1><tt/nes.h/<label id="nes.h"><p>
<itemize>
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
<item><ref id="waitvsync" name="waitvsync">
</itemize>
@@ -568,7 +570,7 @@ It does not declare any functions.
<sect1><tt/pce.h/<label id="pce.h"><p>
<itemize>
<!-- <item><ref id="get_tv" name="get_tv"> -->
<item><ref id="get_tv" name="get_tv">
<item><ref id="waitvsync" name="waitvsync">
</itemize>
@@ -4108,6 +4110,31 @@ be used in presence of a prototype.
</quote>
<sect1>get_tv<label id="get_tv"><p>
<quote>
<descrip>
<tag/Function/The function returns the system's vertical blank frequency.
<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">,
<ref id="atari.h" name="atari.h">, <ref id="cbm.h" name="cbm.h">,
<ref id="cx16.h" name="cx16.h">, <ref id="gamate.h" name="gamate.h">,
<ref id="nes.h" name="nes.h">, <ref id="pce.h" name="pce.h">/
<tag/Declaration/<tt/unsigned char get_tv (void);/
<tag/Description/<tt/get_tv/ is machine dependent and does not exist for
all supported targets. If it exists, it returns a number that identifies the
frequency at which the screen vertical blank happens (either 50 or 60Hz),
if possible.
<tag/Notes/<itemize>
<item>The function does not exist on all platforms.
<item>Return TV_NTSC for 60Hz systems, TV_PAL for 50Hz systems, or
TV_OTHER if the scan frequency can not be determined.
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/Example/None.
</descrip>
</quote>
<sect1>get_ostype<label id="get_ostype"><p>
<quote>

View File

@@ -122,6 +122,11 @@
#define APPLE_IIGS1 0x81 /* Apple IIgs (ROM 1) */
#define APPLE_IIGS3 0x83 /* Apple IIgs (ROM 3) */
/* Return codes for get_tv() */
#define TV_NTSC 0
#define TV_PAL 1
#define TV_OTHER 2
extern unsigned char _dos_type;
/* Valid _dos_type values:
**
@@ -200,6 +205,9 @@ extern void a2_lo_tgi[];
void beep (void);
/* Beep beep. */
unsigned char get_tv (void);
/* Get the machine vblank frequency. Returns one of the TV_xxx codes. */
unsigned char get_ostype (void);
/* Get the machine type. Returns one of the APPLE_xxx codes. */

View File

@@ -3,7 +3,7 @@
/* cx16.h */
/* */
/* CX16 system-specific definitions */
/* For prerelease 39 */
/* For prerelease 43 */
/* */
/* */
/* This software is provided "as-is", without any expressed or implied */
@@ -176,6 +176,11 @@ enum {
#define VIDEOMODE_40x15 0x04
#define VIDEOMODE_20x30 0x05
#define VIDEOMODE_20x15 0x06
#define VIDEOMODE_22x23 0x07
#define VIDEOMODE_64x50 0x08
#define VIDEOMODE_64x25 0x09
#define VIDEOMODE_32x50 0x0A
#define VIDEOMODE_32x25 0x0B
#define VIDEOMODE_80COL VIDEOMODE_80x60
#define VIDEOMODE_40COL VIDEOMODE_40x30
#define VIDEOMODE_320x240 0x80

189
libsrc/apple2/get_tv.s Normal file
View File

@@ -0,0 +1,189 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2025
;
; unsigned char __fastcall__ get_tv(void)
;
.export _get_tv
.import _set_iigs_speed, _get_iigs_speed
.import ostype
.constructor calibrate_tv, 2
.include "accelerator.inc"
.include "apple2.inc"
.include "get_tv.inc"
.segment "ONCE"
; Cycle wasters
waste_72:
jsr waste_36
waste_36:
jsr waste_12
waste_24:
jsr waste_12
waste_12:
rts
.proc calibrate_tv
lda ostype
bmi iigs
cmp #$20
bcc iip
cmp #$40
bcc iie
iic: jmp calibrate_iic
iigs: jmp calibrate_iigs
iie: jmp calibrate_iie
iip: rts ; Keep TV::OTHER.
.endproc
; Magic numbers
WASTE_LOOP_CYCLES = 92 ; The wait loop total cycles
NTSC_LOOP_COUNT = 17030/WASTE_LOOP_CYCLES ; How many loops expected on NTSC
PAL_LOOP_COUNT = 20280/WASTE_LOOP_CYCLES ; How many loops expected on PAL
STOP_PTRIG = 16500/WASTE_LOOP_CYCLES ; Stop PTRIG at 16.5ms
; Carry set at enter: wait for VBL +
; Carry clear at enter: wait for VBL -
; Increments X every 92 cycles.
.proc count_until_vbl_bit
lda #$10 ; BPL
bcc :+
lda #$30 ; BMI
: sta sign
; Wait for VBLsign change with 92 cycles loops.
; Hit PTRIG repeatedly so that accelerators will slow down.
; But stop hitting PTRIG after 16.5ms cycles, so that on the //c,
; the VBLINT will not be reset right before we get it. 16.5ms
; is a good value because it's far enough from 17ms for NTSC
; models, and close enough to 20.2ms for PAL models that accelerators
; will stay slow until there. (5ms usually).
: cpx #STOP_PTRIG ; 2 - see if we spent 16.5ms already
bcs notrig ; 4 / 5 - if so, stop hitting PTRIG
sta PTRIG ; 8 - otherwise hit it
bcc count ; 11
notrig:
nop ; 7 - keep cycle count constant when not
nop ; 9 - hitting PTRIG
nop ; 11
count:
inx ; 13
jsr waste_72 ; 85
bit RDVBLBAR ; 89 - Wait for VBL change
sign:
bpl :- ; 92 - patched with bpl/bmi
rts
.endproc
.proc calibrate_iic
php
sei
sta IOUDISOFF
lda RDVBLMSK
pha ; Back up for cleanup
bit ENVBL
bit PTRIG ; Reset VBL interrupt flag
: bit RDVBLBAR ; Wait for one VBL
bpl :-
bit PTRIG ; Reset VBL interrupt flag again
ldx #$00
clc
jsr count_until_vbl_bit
pla ; Cleanup
asl
bcs :+ ; VBL interrupts were already enabled
bit DISVBL
: sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on.
plp
jmp calibrate_done
.endproc
.proc calibrate_iie
: bit RDVBLBAR ; Wait for bit 7 to be off (VBL start)
bmi :-
: bit RDVBLBAR ; Wait for bit 7 to be on (VBL end)
bpl :-
; Wait and count during a full cycle
ldx #$00
sec
jsr count_until_vbl_bit
clc
jsr count_until_vbl_bit
jmp calibrate_done
.endproc
.proc calibrate_iigs
; Backup speed and slow down
jsr _get_iigs_speed
pha
lda #SPEED_SLOW
jsr _set_iigs_speed
; The same as IIe, but reverted, because... something?
: bit RDVBLBAR ; Wait for bit 7 to be on (VBL start)
bpl :-
: bit RDVBLBAR ; Wait for bit 7 to be off (VBL end)
bmi :-
; Wait and count during a full cycle
ldx #$00
clc
jsr count_until_vbl_bit
sec
jsr count_until_vbl_bit
jsr calibrate_done
; Restore user speed
pla
jmp _set_iigs_speed
.endproc
.proc calibrate_done
; Consider X +/- 3 to be valid,
; anything else is unknown.
lda #TV::NTSC
cpx #NTSC_LOOP_COUNT-3
bcc unexpected
cpx #NTSC_LOOP_COUNT+3
bcc matched
lda #TV::PAL
cpx #PAL_LOOP_COUNT-3
bcc unexpected
cpx #PAL_LOOP_COUNT+3
bcs unexpected
matched:
sta tv
unexpected:
rts
.endproc
.code
; The only thing remaining from that code after init
.proc _get_tv
lda tv
ldx #>$0000
rts
.endproc
.segment "INIT"
tv: .byte TV::OTHER

View File

@@ -147,6 +147,7 @@ icbll_copy:
sta dataptr+1
lda ICBLL,x
sta copylen
beq copied ; length = 0 if EOF
pha ; remember for return value
ldy #0
ldx index
@@ -159,7 +160,7 @@ copy: lda linebuf,x
bne copy
pla ; length
pha ; save length to return at okdone
copied: pha ; save length to return at okdone
clc
adc index

View File

@@ -6,6 +6,60 @@
; Almost 7 times faster, uses no RAM (vs 14 bytes BSS), and takes 1/4 the space
; vs the official C source.
;
;
; C implementation was:
; void decompress_lz4 (unsigned char *in, unsigned char *out, const int outlen) {
; unsigned char token, tmp;
; unsigned int offset;
; unsigned char *end = out+outlen;
; unsigned char *copysrc;
;
; while (out < end) {
; token = *in++;
; offset = token >> 4;
;
; token &= 0x0f;
; token += 4; // Minmatch
;
; if (offset == 15) {
; moreliterals:
; tmp = *in++;
; offset += tmp;
; if (tmp == 255)
; goto moreliterals;
; }
;
; if (offset) {
; memcpy(out, in, offset);
; out += offset;
; in += offset;
; }
;
; if (out >= end) {
; return;
; }
;
; offset = (*in);
; in++;
; offset += (*in)<<8;
; in++;
;
; copysrc = out - offset;
; offset = token;
;
; if (token == 19) {
; morematches:
; tmp = *in++;
; offset += tmp;
; if (tmp == 255)
; goto morematches;
; }
;
; memcpy(out, copysrc, offset);
; out += offset;
; }
; }
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4

View File

@@ -9,6 +9,13 @@
; #define VIDEOMODE_40x15 0x04
; #define VIDEOMODE_20x30 0x05
; #define VIDEOMODE_20x15 0x06
; #define VIDEOMODE_22x23 0x07
; #define VIDEOMODE_64x50 0x08
; #define VIDEOMODE_64x25 0x09
; #define VIDEOMODE_32x50 0x0A
; #define VIDEOMODE_32x25 0x0B
; #define VIDEOMODE_80COL VIDEOMODE_80x60
; #define VIDEOMODE_40COL VIDEOMODE_40x30
; #define VIDEOMODE_320x240 0x80
; #define VIDEOMODE_SWAP (-1)
;

View File

@@ -92,7 +92,7 @@ static Collection LPStack = STATIC_COLLECTION_INITIALIZER;
static Literal* NewLiteral (const void* Buf, unsigned Len)
static Literal* NewLiteral (const StrBuf* S)
/* Create a new literal and return it */
{
/* Allocate memory */
@@ -103,7 +103,7 @@ static Literal* NewLiteral (const void* Buf, unsigned Len)
L->RefCount = 0;
L->Output = 0;
SB_Init (&L->Data);
SB_AppendBuf (&L->Data, Buf, Len);
SB_Append (&L->Data, S);
/* Return the new literal */
return L;
@@ -162,7 +162,7 @@ void ReleaseLiteral (Literal* L)
void TranslateLiteral (Literal* L)
/* Translate a literal into the target charset */
{
TgtTranslateBuf (SB_GetBuf (&L->Data), SB_GetLen (&L->Data));
TgtTranslateStrBuf (&L->Data);
}
@@ -468,18 +468,18 @@ void OutputGlobalLiteralPool (void)
Literal* AddLiteral (const char* S)
/* Add a literal string to the literal pool. Return the literal. */
{
return AddLiteralBuf (S, strlen (S) + 1);
StrBuf SB;
SB_InitFromString(&SB, S);
return AddLiteralStr(&SB);
}
Literal* AddLiteralBuf (const void* Buf, unsigned Len)
/* Add a buffer containing a literal string to the literal pool. Return the
** literal.
*/
Literal* AddLiteralStr (const StrBuf* S)
/* Add a literal string to the literal pool. Return the literal. */
{
/* Create a new literal */
Literal* L = NewLiteral (Buf, Len);
Literal* L = NewLiteral (S);
/* Add the literal to the correct pool */
if (IS_Get (&WritableStrings)) {
@@ -491,11 +491,3 @@ Literal* AddLiteralBuf (const void* Buf, unsigned Len)
/* Return the new literal */
return L;
}
Literal* AddLiteralStr (const StrBuf* S)
/* Add a literal string to the literal pool. Return the literal. */
{
return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
}

View File

@@ -125,11 +125,6 @@ void OutputGlobalLiteralPool (void);
Literal* AddLiteral (const char* S);
/* Add a literal string to the literal pool. Return the literal. */
Literal* AddLiteralBuf (const void* Buf, unsigned Len);
/* Add a buffer containing a literal string to the literal pool. Return the
** literal.
*/
Literal* AddLiteralStr (const StrBuf* S);
/* Add a literal string to the literal pool. Return the literal. */

View File

@@ -2647,8 +2647,9 @@ static void DoDefine (void)
** "There shall be white-space between the identifier and the
** replacement list in the definition of an object-like macro."
** Note: C89 doesn't have this constraint.
** Note: if there is no replacement list, a space is not required.
*/
if (Std == STD_C99 && !IsSpace (CurC)) {
if (Std == STD_C99 && !IsSpace (CurC) && CurC != 0) {
PPWarning ("ISO C99 requires whitespace after the macro name");
}

View File

@@ -163,6 +163,12 @@ static const struct Keyword {
typedef uint32_t scan_t;
/* ParseChar return values */
typedef struct {
int Val;
int Cooked;
} parsedchar_t;
/*****************************************************************************/
/* code */
/*****************************************************************************/
@@ -326,13 +332,16 @@ static void SetTok (int tok)
static int ParseChar (void)
static parsedchar_t ParseChar (void)
/* Parse a character token. Converts escape chars into character codes. */
{
parsedchar_t Result;
int C;
int HadError;
int Count;
Result.Cooked = 1;
/* Check for escape chars */
if (CurC == '\\') {
NextChar ();
@@ -346,6 +355,14 @@ static int ParseChar (void)
case 'b':
C = '\b';
break;
case 'e':
if (IS_Get(&Standard) != STD_CC65) {
goto IllegalEscape;
}
/* we'd like to use \e here, but */
/* not all build systems support it */
C = '\x1B';
break;
case 'f':
C = '\f';
break;
@@ -373,6 +390,7 @@ static int ParseChar (void)
case 'x':
case 'X':
/* Hex character constant */
Result.Cooked = 0;
if (!IsXDigit (NextC)) {
Error ("\\x used with no following hex digits");
C = ' ';
@@ -401,6 +419,7 @@ static int ParseChar (void)
case '6':
case '7':
/* Octal constant */
Result.Cooked = 0;
Count = 1;
C = HexVal (CurC);
while (IsODigit (NextC) && Count++ < 3) {
@@ -411,6 +430,7 @@ static int ParseChar (void)
Error ("Octal character constant out of range");
break;
default:
IllegalEscape:
C = CurC;
Error ("Illegal escaped character: 0x%02X", CurC);
break;
@@ -423,7 +443,12 @@ static int ParseChar (void)
NextChar ();
/* Do correct sign extension */
return SignExtendChar (C);
Result.Val = SignExtendChar(C);
if (Result.Cooked) {
Result.Cooked = Result.Val;
}
return Result;
}
@@ -431,7 +456,7 @@ static int ParseChar (void)
static void CharConst (void)
/* Parse a character constant token */
{
int C;
parsedchar_t C;
if (CurC == 'L') {
/* Wide character constant */
@@ -457,7 +482,8 @@ static void CharConst (void)
}
/* Translate into target charset */
NextTok.IVal = SignExtendChar (C);
NextTok.IVal = SignExtendChar (C.Val);
NextTok.Cooked = C.Cooked;
/* Character constants have type int */
NextTok.Type = type_int;
@@ -468,6 +494,9 @@ static void CharConst (void)
static void StringConst (void)
/* Parse a quoted string token */
{
/* result from ParseChar */
parsedchar_t ParsedChar;
/* String buffer */
StrBuf S = AUTO_STRBUF_INITIALIZER;
@@ -494,7 +523,8 @@ static void StringConst (void)
Error ("Unexpected newline");
break;
}
SB_AppendChar (&S, ParseChar ());
ParsedChar = ParseChar ();
SB_AppendCharCooked(&S, ParsedChar.Val, ParsedChar.Cooked);
}
/* Skip closing quote char if there was one */
@@ -689,6 +719,7 @@ static void NumericConst (void)
/* Set the value and the token */
NextTok.IVal = IVal;
NextTok.Cooked = 0;
NextTok.Tok = TOK_ICONST;
} else {
@@ -805,7 +836,12 @@ static void GetNextInputToken (void)
if (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST) {
TranslateLiteral (NextTok.SVal);
} else if (NextTok.Tok == TOK_CCONST || NextTok.Tok == TOK_WCCONST) {
NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal));
if (NextTok.Cooked) {
NextTok.IVal = SignExtendChar (TgtTranslateChar (NextTok.IVal));
}
else {
NextTok.IVal = SignExtendChar (NextTok.IVal);
}
}
}

View File

@@ -213,6 +213,7 @@ typedef struct Token Token;
struct Token {
token_t Tok; /* The token itself */
long IVal; /* The integer attribute */
int Cooked; /* The "cooked" flag for char constants */
Double FVal; /* The float attribute */
struct Literal* SVal; /* String literal is any */
ident Ident; /* Identifier if IDENT */

View File

@@ -82,6 +82,7 @@ StrBuf* SB_InitFromString (StrBuf* B, const char* S)
B->Len = strlen (S);
B->Index = 0;
B->Buf = (char*) S;
B->Cooked = (char*) S;
return B;
}
@@ -92,6 +93,7 @@ void SB_Done (StrBuf* B)
{
if (B->Allocated) {
xfree (B->Buf);
xfree (B->Cooked);
}
}
@@ -146,10 +148,12 @@ void SB_Realloc (StrBuf* B, unsigned NewSize)
*/
if (B->Allocated) {
/* Just reallocate the block */
B->Buf = xrealloc (B->Buf, NewAllocated);
B->Buf = xrealloc (B->Buf, NewAllocated);
B->Cooked = xrealloc (B->Cooked, NewAllocated);
} else {
/* Allocate a new block and copy */
B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
B->Cooked = memcpy (xmalloc (NewAllocated), B->Cooked, B->Len);
}
/* Remember the new block size */
@@ -178,10 +182,12 @@ static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
/* Free the old buffer if there is one */
if (B->Allocated) {
xfree (B->Buf);
xfree (B->Cooked);
}
/* Allocate a fresh block */
B->Buf = xmalloc (NewAllocated);
B->Buf = xmalloc (NewAllocated);
B->Cooked = xmalloc (NewAllocated);
/* Remember the new block size */
B->Allocated = NewAllocated;
@@ -222,6 +228,7 @@ void SB_Terminate (StrBuf* B)
SB_Realloc (B, NewLen);
}
B->Buf[B->Len] = '\0';
B->Cooked[B->Len] = '\0';
}
@@ -234,6 +241,22 @@ void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
SB_CheapRealloc (Target, Size);
}
memcpy (Target->Buf, Buf, Size);
memcpy (Target->Cooked, Buf, Size); /* nothing raw */
}
Target->Len = Size;
}
void SB_CopyBufCooked (StrBuf* Target, const char* Buf, const char* Cooked, unsigned Size)
/* Copy Buf and Cooked to Target, discarding the old contents of Target */
{
if (Size) {
if (Target->Allocated < Size) {
SB_CheapRealloc (Target, Size);
}
memcpy (Target->Buf, Buf, Size);
memcpy (Target->Cooked, Cooked, Size);
}
Target->Len = Size;
}
@@ -254,7 +277,7 @@ void SB_CopyStr (StrBuf* Target, const char* S)
void SB_Copy (StrBuf* Target, const StrBuf* Source)
/* Copy Source to Target, discarding the old contents of Target */
{
SB_CopyBuf (Target, Source->Buf, Source->Len);
SB_CopyBufCooked (Target, Source->Buf, Source->Cooked, Source->Len);
Target->Index = Source->Index;
}
#endif
@@ -269,6 +292,21 @@ void SB_AppendChar (StrBuf* B, int C)
SB_Realloc (B, NewLen);
}
B->Buf[B->Len] = (char) C;
B->Cooked[B->Len] = (char) C;
B->Len = NewLen;
}
void SB_AppendCharCooked (StrBuf* B, int C, int Cooked)
/* Append a character to a string buffer */
{
unsigned NewLen = B->Len + 1;
if (NewLen > B->Allocated) {
SB_Realloc (B, NewLen);
}
B->Buf[B->Len] = (char) C;
B->Cooked[B->Len] = (char) (Cooked ? C : 0);
B->Len = NewLen;
}
@@ -282,6 +320,7 @@ void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size)
SB_Realloc (B, NewLen);
}
memcpy (B->Buf + B->Len, S, Size);
memcpy (B->Cooked + B->Len, S, Size);
B->Len = NewLen;
}
@@ -301,7 +340,13 @@ void SB_AppendStr (StrBuf* B, const char* S)
void SB_Append (StrBuf* Target, const StrBuf* Source)
/* Append the contents of Source to Target */
{
SB_AppendBuf (Target, Source->Buf, Source->Len);
unsigned NewLen = Target->Len + Source->Len;
if (NewLen > Target->Allocated) {
SB_Realloc (Target, NewLen);
}
memcpy (Target->Buf + Target->Len, Source->Buf, Source->Len);
memcpy (Target->Cooked + Target->Len, Source->Cooked, Source->Len);
Target->Len = NewLen;
}
#endif

View File

@@ -53,10 +53,17 @@
/*****************************************************************************/
/* We want to track whether a character is "raw" or not. */
/* "raw" characters should NOT be translated when translating a string. */
/* We do this by keeping a second array parallel to "Buf" called "Cooked". */
/* Think of "cooked" as the inverse of "raw". */
/* If Cooked[n] is 0, then the character is raw and should not be translated. */
/* This was done to keep LIT_STR_BUFFER sane. */
typedef struct StrBuf StrBuf;
struct StrBuf {
char* Buf; /* Pointer to buffer */
char* Cooked; /* Pointer to cooked buffer */
unsigned Len; /* Length of the string */
unsigned Index; /* Used for reading (Get and friends) */
unsigned Allocated; /* Size of allocated memory */
@@ -66,13 +73,13 @@ struct StrBuf {
extern const StrBuf EmptyStrBuf;
/* Initializer for static string bufs */
#define STATIC_STRBUF_INITIALIZER { 0, 0, 0, 0 }
#define STATIC_STRBUF_INITIALIZER { 0, 0, 0, 0, 0 }
/* Initializer for auto string bufs */
#define AUTO_STRBUF_INITIALIZER { 0, 0, 0, 0 }
#define AUTO_STRBUF_INITIALIZER { 0, 0, 0, 0, 0 }
/* Initialize with a string literal (beware: evaluates str twice!) */
#define LIT_STRBUF_INITIALIZER(str) { (char*)str, sizeof(str)-1, 0, 0 }
#define LIT_STRBUF_INITIALIZER(str) { (char*)str, (char *)str, sizeof(str)-1, 0, 0 }
@@ -164,6 +171,16 @@ INLINE char* SB_GetBuf (StrBuf* B)
# define SB_GetBuf(B) (B)->Buf
#endif
#if defined(HAVE_INLINE)
INLINE char* SB_GetCooked (StrBuf* B)
/* Return a cooked pointer */
{
return B->Cooked;
}
#else
# define SB_GetCooked(B) (B)->Cooked
#endif
#if defined(HAVE_INLINE)
INLINE char SB_At (const StrBuf* B, unsigned Index)
/* Get a character from the buffer */
@@ -310,6 +327,9 @@ void SB_Terminate (StrBuf* B);
void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size);
/* Copy Buf to Target, discarding the old contents of Target */
void SB_CopyBufCooked (StrBuf* Target, const char* Buf, const char *Cooked, unsigned Size);
/* Copy Buf and Cooked to Target, discarding the old contents of Target */
#if defined(HAVE_INLINE)
INLINE void SB_CopyStr (StrBuf* Target, const char* S)
/* Copy S to Target, discarding the old contents of Target */
@@ -325,7 +345,7 @@ void SB_CopyStr (StrBuf* Target, const char* S);
INLINE void SB_Copy (StrBuf* Target, const StrBuf* Source)
/* Copy Source to Target, discarding the old contents of Target */
{
SB_CopyBuf (Target, Source->Buf, Source->Len);
SB_CopyBufCooked (Target, Source->Buf, Source->Cooked, Source->Len);
Target->Index = Source->Index;
}
#else
@@ -336,6 +356,9 @@ void SB_Copy (StrBuf* Target, const StrBuf* Source);
void SB_AppendChar (StrBuf* B, int C);
/* Append a character to a string buffer */
void SB_AppendCharCooked (StrBuf* B, int C, int Cooked);
/* Append a character to a string buffer, raw if Cooked == 0 */
void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size);
/* Append a character buffer to the end of the string buffer */
@@ -354,7 +377,13 @@ void SB_AppendStr (StrBuf* B, const char* S);
INLINE void SB_Append (StrBuf* Target, const StrBuf* Source)
/* Append the contents of Source to Target */
{
SB_AppendBuf (Target, Source->Buf, Source->Len);
unsigned NewLen = Target->Len + Source->Len;
if (NewLen > Target->Allocated) {
SB_Realloc (Target, NewLen);
}
memcpy (Target->Buf + Target->Len, Source->Buf, Source->Len);
memcpy (Target->Cooked + Target->Len, Source->Cooked, Source->Len);
Target->Len = NewLen;
}
#else
void SB_Append (StrBuf* Target, const StrBuf* Source);

View File

@@ -121,7 +121,19 @@ void TgtTranslateStrBuf (StrBuf* Buf)
** system character set.
*/
{
TgtTranslateBuf (SB_GetBuf (Buf), SB_GetLen (Buf));
unsigned char* B = (unsigned char*)SB_GetBuf(Buf);
unsigned char* Cooked = (unsigned char*)SB_GetCooked(Buf);
unsigned Len = SB_GetLen(Buf);
/* Translate */
while (Len--) {
if (*Cooked) {
*B = Tab[*B];
}
/* else { *B = *B; } */
++B;
++Cooked;
}
}
@@ -129,7 +141,7 @@ void TgtTranslateStrBuf (StrBuf* Buf)
void TgtTranslateSet (unsigned Index, unsigned char C)
/* Set the translation code for the given character */
{
CHECK (Index < sizeof (Tab));
CHECK (Index < (sizeof (Tab) / sizeof(Tab[0])));
Tab[Index] = C;
}

View File

@@ -149,7 +149,7 @@ unsigned GetGranularity (attr_t Style)
case atSkip:
default:
Internal ("GetGraularity called for style = %d", Style);
Internal ("GetGranularity called for style = %d", Style);
return 0;
}
}

View File

@@ -12,7 +12,7 @@ endif
WORKDIR = ../testwrk
.PHONY: test continue mostlyclean clean
.PHONY: test continue mostlyclean clean success_message
test:
@$(MAKE) mostlyclean
@@ -25,8 +25,15 @@ continue:
@$(MAKE) -C ref all
@$(MAKE) -C err all
@$(MAKE) -C standard all
@$(MAKE) -C standard_err all
@$(MAKE) -C misc all
@$(MAKE) -C todo all
@$(MAKE) success_message
success_message:
$(info ###################################)
$(info ### validation suite successful ###)
$(info ###################################)
mostlyclean:
@$(MAKE) -C asm clean
@@ -35,6 +42,7 @@ mostlyclean:
@$(MAKE) -C ref clean
@$(MAKE) -C err clean
@$(MAKE) -C standard clean
@$(MAKE) -C standard_err clean
@$(MAKE) -C misc clean
@$(MAKE) -C todo clean

View File

@@ -110,6 +110,12 @@ $(WORKDIR)/bug2515.$1.$2.prg: bug2515.c | $(WORKDIR)
$(NOT) $(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< 2>$(WORKDIR)/bug2515.$1.$2.out
$(ISEQUAL) $(WORKDIR)/bug2515.$1.$2.out bug2515.ref
# should not issue any warnings in C99 mode
$(WORKDIR)/bug2637.$1.$2.prg: bug2637.c | $(WORKDIR)
$(if $(QUIET),echo misc/bug2637.$1.$2.prg)
$(CC65) --standard c99 -t sim$2 -$1 -o $$(@:.prg=.s) $$< 2>$(WORKDIR)/bug2637.$1.$2.out
$(ISEQUAL) $(WORKDIR)/bug2637.$1.$2.out bug2637.ref
# this one requires -Werror
$(WORKDIR)/bug1768.$1.$2.prg: bug1768.c | $(WORKDIR)
$(if $(QUIET),echo misc/bug1768.$1.$2.prg)

15
test/misc/bug2637.c Normal file
View File

@@ -0,0 +1,15 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
// compile with --standard c99
int main()
{
return 0;
}

0
test/misc/bug2637.ref Normal file
View File

View File

@@ -12,6 +12,8 @@ compiler is working as expected (when the tests behave as described):
/val - The bulk of tests are contained here, individual tests should exit with
an exit code of EXIT_SUCCESS when they pass, or EXIT_FAILURE on error.
/err - contains tests that MUST NOT compile
/standard - like the tests in /val, the tests must exit with EXIT_SUCCESS on
success. Unlike the tests in /val these are not compiled for every
combination of optimizer options, but instead always with -Osir and then
@@ -19,6 +21,10 @@ compiler is working as expected (when the tests behave as described):
to check for regressions in standard conformance of the compiler and the
library.
/standard_err - like the tests in /err, these tests MUST NOT compile, and like
the tests in /standard, these are compiled -Osir and then for each
supported C-Standard.
/ref - These tests produce output that must be compared with reference output.
Normally the reference output is produced by compiling the program on the
host (using gcc mostly) and then running them on the host. Tests should
@@ -43,8 +49,6 @@ compiler is working as expected (when the tests behave as described):
only ever use this as a last resort when something can not be tested by
other means.
/err - contains tests that MUST NOT compile
/todo and /misc generally contain the tests that fail because of known bugs:

View File

@@ -0,0 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
/* this should succeed on all three standards
* yet use only \e on CC65
*/
int main(void) {
#if __CC65_STD__ == __CC65_STD_CC65__
printf("\e");
#endif
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,55 @@
# Makefile for the tests that MUST NOT compile
ifneq ($(shell echo),)
CMD_EXE = 1
endif
ifdef CMD_EXE
S = $(subst /,\,/)
NOT = - # Hack
NULLDEV = nul:
MKDIR = mkdir $(subst /,\,$1)
RMDIR = -rmdir /s /q $(subst /,\,$1)
else
S = /
NOT = !
NULLDEV = /dev/null
MKDIR = mkdir -p $1
RMDIR = $(RM) -r $1
endif
ifdef QUIET
.SILENT:
NULLERR = 2>$(NULLDEV)
endif
CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65)
WORKDIR = ../../testwrk/standard_err
OPTIONS = c89 c99 cc65
.PHONY: all clean
SOURCES := $(wildcard *.c)
TESTS = $(foreach option,$(OPTIONS),$(SOURCES:%.c=$(WORKDIR)/%.$(option).6502.prg))
all: $(TESTS)
$(WORKDIR):
$(call MKDIR,$(WORKDIR))
define PRG_template
$(WORKDIR)/%.$1.$2.prg: %.c | $(WORKDIR)
$(if $(QUIET),echo standard_err/$$*.$1.$2.prg)
$(NOT) $(CC65) -t sim$2 $$(CC65FLAGS) -Osir --add-source --standard $1 -o $$(@:.prg=.s) $$< $(NULLERR)
endef # PRG_template
$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),6502)))
#$(foreach option,$(OPTIONS),$(eval $(call PRG_template,$(option),65c02)))
clean:
@$(call RMDIR,$(WORKDIR))

View File

@@ -0,0 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
/* this should fail on all three standards
*/
int main(void) {
#if __CC65_STD__ != __CC65_STD_CC65__
printf("\e");
#else
#error "this needs to error on CC65 to make it through validation"
#endif
return EXIT_SUCCESS;
}

72
test/val/bug2609.c Normal file
View File

@@ -0,0 +1,72 @@
/* Bug #2609 - charmap translation violates C specification 6.4.4.4 Character constant */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#pragma charmap (0x07, 0x62) /* map \a to b */
static_assert('\a' == 0x62);
static_assert('\07' == 0x07);
static_assert('\x07' == 0x07);
#pragma charmap (0x07, 0x63) /* map \a to c */
static_assert('\a' == 0x63);
static_assert('\07' == 0x07);
static_assert('\x07' == 0x07);
#pragma charmap (0x07, 0x07) /* map \a back to x07 */
static_assert('\a' == 0x07);
static_assert('\07' == 0x07);
static_assert('\x07' == 0x07);
#pragma charmap (0x07, 0x61) /* map \a to a */
char *s = "\07\a\x07";
char t[] = { 7, 0x61, 7, 0 };
static_assert('\a' == 0x61);
static_assert('\07' == 0x07);
static_assert('\x07' == 0x07);
char c_back_a = '\a';
char c_hex_07 = '\x07';
char c_oct_07 = '\07';
int i_back_a = '\a';
int i_hex_07 = '\x07';
int i_oct_07 = '\07';
#define TEST(a,b) \
if (a != b) { printf("\n\n !FAIL! %s = %04x not %04x\n\n", #a, a, b); return EXIT_FAILURE; }
int main (void) {
int i;
TEST(c_back_a, 0x61)
TEST(c_hex_07, 0x07)
TEST(c_oct_07, 07)
TEST(i_back_a, 0x61)
TEST(i_hex_07, 0x07)
TEST(i_oct_07, 07)
assert('\a' == 0x61);
assert('\07' == 0x07);
assert('\x07' == 0x07);
if (strcmp(s,t) || s[0] == s[1]) {
printf("\n\n !FAIL! strcmp\n");
for (i = 0; i < 4; i++) {
printf("%02x ", s[i]);
}
printf("\n");
for (i = 0; i < 4; i++) {
printf("%02x ", t[i]);
}
printf("\n");
printf("\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

15
test/val/bug2610.c Normal file
View File

@@ -0,0 +1,15 @@
#include <stdio.h>
#if '\x0A' != 0x0A
#error "Suspicious character set translation"
#endif
int main()
{
char c = '\x0A';
if (c == 0x0A) {
printf("Ok\n");
return 0;
} else {
printf("Failed\n");
return 1;
}
}