diff --git a/doc/apple2.sgml b/doc/apple2.sgml index 2cc62d7ae..9cff996b7 100644 --- a/doc/apple2.sgml +++ b/doc/apple2.sgml @@ -354,6 +354,7 @@ usage. allow_lowercase beep dir_entry_count +get_tv get_ostype gmtime_dt mktime_dt diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml index 987b63fd0..094ddd93e 100644 --- a/doc/apple2enh.sgml +++ b/doc/apple2enh.sgml @@ -333,6 +333,7 @@ usage. _datetime beep dir_entry_count +get_tv get_ostype gmtime_dt mktime_dt diff --git a/doc/ca65.sgml b/doc/ca65.sgml index fd15d02a5..80224a84e 100644 --- a/doc/ca65.sgml +++ b/doc/ca65.sgml @@ -833,11 +833,9 @@ names like "Loop". Here is an example: Unnamed labels

If you really want to write messy code, there are also unnamed labels. To define -an unnamed label, use either @: (.LOCALCHAR is respected if it -is set) or sole :. +an unnamed label, use sole :. -To reference an unnamed label, use @ (.LOCALCHAR is respected -if it is set) or : with several - or + characters. +To reference an unnamed label, use : with several - or + characters. The - characters will create a back reference (n'th label backwards), the + will create a forward reference (n'th label in forward direction). As an alternative, angle brackets < and > may be used @@ -847,12 +845,12 @@ Example: cpy #0 - beq @++ - @: + beq :++ + : sta $2007 dey - bne @- - @: + bne :- + : rts diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 781e460a8..6793603d5 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -1053,6 +1053,16 @@ This cc65 version has some extensions to the ISO C standard. unsigned char foo = 0b101; // sets it to 5 + 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 option. + + + unsigned char foo = '\e'; // sets it to 0x1B or equivalent + +

diff --git a/doc/funcref.sgml b/doc/funcref.sgml index 5c603b130..eec04b929 100644 --- a/doc/funcref.sgml +++ b/doc/funcref.sgml @@ -98,6 +98,7 @@ function. allow_lowercase + @@ -111,6 +112,7 @@ function. _dos_type + @@ -140,7 +142,7 @@ function. - + (incomplete) @@ -227,7 +229,7 @@ function. - + @@ -348,7 +350,7 @@ function. - + @@ -443,7 +445,7 @@ see also testcode/lib/em-test.c and samples/multidemo.c.

- + @@ -550,7 +552,7 @@ see also testcode/lib/em-test.c and samples/multidemo.c.

- + @@ -568,7 +570,7 @@ It does not declare any functions.

- + @@ -4108,6 +4110,31 @@ be used in presence of a prototype. +get_tv

+ + + +, +, , +, , +, / + +The function does not exist on all platforms. +Return TV_NTSC for 60Hz systems, TV_PAL for 50Hz systems, or +TV_OTHER if the scan frequency can not be determined. + + + + + get_ostype

diff --git a/include/apple2.h b/include/apple2.h index 15055f412..9f7526f59 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -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. */ diff --git a/include/cx16.h b/include/cx16.h index 5bbd21247..6a3705631 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -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 diff --git a/libsrc/apple2/get_tv.s b/libsrc/apple2/get_tv.s new file mode 100644 index 000000000..830cc4ac1 --- /dev/null +++ b/libsrc/apple2/get_tv.s @@ -0,0 +1,189 @@ +; +; Colin Leroy-Mira , 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 diff --git a/libsrc/atari/read.s b/libsrc/atari/read.s index 228ca9ee2..b50bd2856 100644 --- a/libsrc/atari/read.s +++ b/libsrc/atari/read.s @@ -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 diff --git a/libsrc/common/lz4.s b/libsrc/common/lz4.s index 57773c86a..c53841897 100644 --- a/libsrc/common/lz4.s +++ b/libsrc/common/lz4.s @@ -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 diff --git a/libsrc/cx16/videomode.s b/libsrc/cx16/videomode.s index 998316858..46f461fca 100644 --- a/libsrc/cx16/videomode.s +++ b/libsrc/cx16/videomode.s @@ -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) ; diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index 5433f6d95..6d8827c60 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -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)); -} diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h index 5f444bfb8..959924e94 100644 --- a/src/cc65/litpool.h +++ b/src/cc65/litpool.h @@ -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. */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index d033520b8..5cdec3142 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -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"); } diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 879925c7c..f0ff664fd 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -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); + } } } diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 6fc3e5370..a8b8a8ab2 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -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 */ diff --git a/src/common/strbuf.c b/src/common/strbuf.c index 79419f1c4..c5b27ee63 100644 --- a/src/common/strbuf.c +++ b/src/common/strbuf.c @@ -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 diff --git a/src/common/strbuf.h b/src/common/strbuf.h index e0602a6c8..372b1be0b 100644 --- a/src/common/strbuf.h +++ b/src/common/strbuf.h @@ -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); diff --git a/src/common/tgttrans.c b/src/common/tgttrans.c index 3310eab81..3ac41f09e 100644 --- a/src/common/tgttrans.c +++ b/src/common/tgttrans.c @@ -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; } diff --git a/src/da65/attrtab.c b/src/da65/attrtab.c index b70e017a1..e35530afd 100644 --- a/src/da65/attrtab.c +++ b/src/da65/attrtab.c @@ -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; } } diff --git a/test/Makefile b/test/Makefile index fbdf8c5d1..495082fa4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/misc/Makefile b/test/misc/Makefile index ebae0964e..f5225b14b 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -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) diff --git a/test/misc/bug2637.c b/test/misc/bug2637.c new file mode 100644 index 000000000..f6b716465 --- /dev/null +++ b/test/misc/bug2637.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// compile with --standard c99 +int main() +{ + return 0; +} diff --git a/test/misc/bug2637.ref b/test/misc/bug2637.ref new file mode 100644 index 000000000..e69de29bb diff --git a/test/readme.txt b/test/readme.txt index d3f17148e..0aa8799b9 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -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: diff --git a/test/standard/issue2607_cc65.c b/test/standard/issue2607_cc65.c new file mode 100644 index 000000000..82ff9d1aa --- /dev/null +++ b/test/standard/issue2607_cc65.c @@ -0,0 +1,14 @@ +#include +#include + +/* 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; +} diff --git a/test/standard_err/Makefile b/test/standard_err/Makefile new file mode 100644 index 000000000..700a52eea --- /dev/null +++ b/test/standard_err/Makefile @@ -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)) diff --git a/test/standard_err/issue2607_not_cc65.c b/test/standard_err/issue2607_not_cc65.c new file mode 100644 index 000000000..5e56df557 --- /dev/null +++ b/test/standard_err/issue2607_not_cc65.c @@ -0,0 +1,14 @@ +#include +#include + +/* 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; +} diff --git a/test/val/bug2609.c b/test/val/bug2609.c new file mode 100644 index 000000000..418673e98 --- /dev/null +++ b/test/val/bug2609.c @@ -0,0 +1,72 @@ +/* Bug #2609 - charmap translation violates C specification 6.4.4.4 Character constant */ + +#include +#include +#include +#include + +#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; +} diff --git a/test/val/bug2610.c b/test/val/bug2610.c new file mode 100644 index 000000000..75169b81d --- /dev/null +++ b/test/val/bug2610.c @@ -0,0 +1,15 @@ +#include +#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; + } +}