Merge branch 'cc65:master' into master
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
|
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
|
||||||
|
|
||||||
|
Also, before you put a lot of work into implementing something you want to contribute, please get in touch with one of the developers and ask if what you are going to do is actually wanted and has a chance of being merged. Perhaps someone else is already working on it, or perhaps what you have in mind is not how we'd expect it to be - talking to us before you start might save you a lot of work in those cases.
|
||||||
|
|
||||||
(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.)
|
(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ For details look at the [Website](https://cc65.github.io).
|
|||||||
|
|
||||||
Project founders:
|
Project founders:
|
||||||
|
|
||||||
* John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted
|
* John R. Dunning: [original implementation](https://public.websites.umich.edu/~archive/atari/8bit/Languages/Cc65/) of the C compiler and runtime library, Atari hosted
|
||||||
* Ullrich von Bassewitz:
|
* Ullrich von Bassewitz:
|
||||||
* move the code to modern systems
|
* move the code to modern systems
|
||||||
* rewrite most parts of the compiler
|
* rewrite most parts of the compiler
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ SYMBOLS {
|
|||||||
__BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size
|
__BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size
|
||||||
__EXEHDR__: type = import;
|
__EXEHDR__: type = import;
|
||||||
__BOOTLDR__: type = import;
|
__BOOTLDR__: type = import;
|
||||||
__DEFDIR__: type = import;
|
|
||||||
__UPLOADER__: type = import;
|
__UPLOADER__: type = import;
|
||||||
|
__UPLOADERSIZE__: type = export, value = $61;
|
||||||
|
__HEADERSIZE__: type = export, value = 64;
|
||||||
}
|
}
|
||||||
MEMORY {
|
MEMORY {
|
||||||
ZP: file = "", define = yes, start = $0000, size = $0100;
|
ZP: file = "", define = yes, start = $0000, size = $0100;
|
||||||
HEADER: file = %O, start = $0000, size = $0040;
|
HEADER: file = %O, start = $0000, size = __HEADERSIZE__;
|
||||||
BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__;
|
BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__;
|
||||||
DIR: file = %O, start = $0000, size = 8;
|
DIR: file = %O, start = $0000, size = 16;
|
||||||
MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__;
|
MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__;
|
||||||
UPLDR: file = %O, define = yes, start = $BFDC, size = $005C;
|
UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061;
|
||||||
}
|
}
|
||||||
SEGMENTS {
|
SEGMENTS {
|
||||||
ZEROPAGE: load = ZP, type = zp;
|
ZEROPAGE: load = ZP, type = zp;
|
||||||
@@ -30,8 +31,8 @@ SEGMENTS {
|
|||||||
RODATA: load = MAIN, type = ro, define = yes;
|
RODATA: load = MAIN, type = ro, define = yes;
|
||||||
DATA: load = MAIN, type = rw, define = yes;
|
DATA: load = MAIN, type = rw, define = yes;
|
||||||
BSS: load = MAIN, type = bss, define = yes;
|
BSS: load = MAIN, type = bss, define = yes;
|
||||||
UPCODE: load = UPLDR, type = ro, define = yes;
|
UPCODE: load = UPLOAD, type = ro, define = yes;
|
||||||
UPDATA: load = UPLDR, type = rw, define = yes;
|
UPDATA: load = UPLOAD, type = rw, define = yes;
|
||||||
}
|
}
|
||||||
FEATURES {
|
FEATURES {
|
||||||
CONDES: type = constructor,
|
CONDES: type = constructor,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Special locations:
|
|||||||
</descrip><p>
|
</descrip><p>
|
||||||
|
|
||||||
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
|
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
|
||||||
However while running module constructors/destructors the Language Card is disabled.
|
However while running module constructors the Language Card is disabled.
|
||||||
|
|
||||||
Enabling the Language Card allows to use it as additional memory for cc65
|
Enabling the Language Card allows to use it as additional memory for cc65
|
||||||
generated code. However code is never automatically placed there. Rather code
|
generated code. However code is never automatically placed there. Rather code
|
||||||
@@ -330,13 +330,30 @@ usage.
|
|||||||
<item>_dos_type
|
<item>_dos_type
|
||||||
<item>_filetype
|
<item>_filetype
|
||||||
<item>_datetime
|
<item>_datetime
|
||||||
|
<item>beep
|
||||||
<item>get_ostype
|
<item>get_ostype
|
||||||
|
<item>gmtime_dt
|
||||||
|
<item>mktime_dt
|
||||||
<item>rebootafterexit
|
<item>rebootafterexit
|
||||||
<item>ser_apple2_slot
|
<item>ser_apple2_slot
|
||||||
<item>tgi_apple2_mix
|
<item>tgi_apple2_mix
|
||||||
</itemize>
|
</itemize>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>Apple IIgs specific functions in accelerator.h<p>
|
||||||
|
|
||||||
|
In addition to those, the <tt/accelerator.h/ header file contains three functions
|
||||||
|
to help determine whether the program is running on a IIgs, and change the IIgs
|
||||||
|
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
|
||||||
|
usage.
|
||||||
|
|
||||||
|
<itemize>
|
||||||
|
<item>detect_iigs
|
||||||
|
<item>get_iigs_speed
|
||||||
|
<item>set_iigs_speed
|
||||||
|
</itemize>
|
||||||
|
|
||||||
|
|
||||||
<sect1>Hardware access<p>
|
<sect1>Hardware access<p>
|
||||||
|
|
||||||
There's currently no support for direct hardware access. This does not mean
|
There's currently no support for direct hardware access. This does not mean
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ Special locations:
|
|||||||
</descrip><p>
|
</descrip><p>
|
||||||
|
|
||||||
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
|
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
|
||||||
However while running module constructors/destructors the Language Card is disabled.
|
However while running module constructors the Language Card is disabled.
|
||||||
|
|
||||||
Enabling the Language Card allows to use it as additional memory for cc65
|
Enabling the Language Card allows to use it as additional memory for cc65
|
||||||
generated code. However code is never automatically placed there. Rather code
|
generated code. However code is never automatically placed there. Rather code
|
||||||
@@ -331,7 +331,10 @@ usage.
|
|||||||
<item>_dos_type
|
<item>_dos_type
|
||||||
<item>_filetype
|
<item>_filetype
|
||||||
<item>_datetime
|
<item>_datetime
|
||||||
|
<item>beep
|
||||||
<item>get_ostype
|
<item>get_ostype
|
||||||
|
<item>gmtime_dt
|
||||||
|
<item>mktime_dt
|
||||||
<item>rebootafterexit
|
<item>rebootafterexit
|
||||||
<item>ser_apple2_slot
|
<item>ser_apple2_slot
|
||||||
<item>tgi_apple2_mix
|
<item>tgi_apple2_mix
|
||||||
@@ -340,6 +343,20 @@ usage.
|
|||||||
</itemize>
|
</itemize>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>Apple IIgs specific functions in accelerator.h<p>
|
||||||
|
|
||||||
|
In addition to those, the <tt/accelerator.h/ header file contains three functions
|
||||||
|
to help determine whether the program is running on a IIgs, and change the IIgs
|
||||||
|
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
|
||||||
|
usage.
|
||||||
|
|
||||||
|
<itemize>
|
||||||
|
<item>detect_iigs
|
||||||
|
<item>get_iigs_speed
|
||||||
|
<item>set_iigs_speed
|
||||||
|
</itemize>
|
||||||
|
|
||||||
|
|
||||||
<sect1>Hardware access<p>
|
<sect1>Hardware access<p>
|
||||||
|
|
||||||
There's currently no support for direct hardware access. This does not mean
|
There's currently no support for direct hardware access. This does not mean
|
||||||
|
|||||||
@@ -71,18 +71,21 @@ function.
|
|||||||
<item><ref id="detect_c64dtv" name="detect_c64dtv">
|
<item><ref id="detect_c64dtv" name="detect_c64dtv">
|
||||||
<item><ref id="detect_c65" name="detect_c65">
|
<item><ref id="detect_c65" name="detect_c65">
|
||||||
<item><ref id="detect_chameleon" name="detect_chameleon">
|
<item><ref id="detect_chameleon" name="detect_chameleon">
|
||||||
|
<item><ref id="detect_iigs" name="detect_iigs">
|
||||||
<item><ref id="detect_scpu" name="detect_scpu">
|
<item><ref id="detect_scpu" name="detect_scpu">
|
||||||
<item><ref id="detect_turbomaster" name="detect_turbomaster">
|
<item><ref id="detect_turbomaster" name="detect_turbomaster">
|
||||||
<item><ref id="get_c128_speed" name="get_c128_speed">
|
<item><ref id="get_c128_speed" name="get_c128_speed">
|
||||||
<item><ref id="get_c64dtv_speed" name="get_c64dtv_speed">
|
<item><ref id="get_c64dtv_speed" name="get_c64dtv_speed">
|
||||||
<item><ref id="get_c65_speed" name="get_c65_speed">
|
<item><ref id="get_c65_speed" name="get_c65_speed">
|
||||||
<item><ref id="get_chameleon_speed" name="get_chameleon_speed">
|
<item><ref id="get_chameleon_speed" name="get_chameleon_speed">
|
||||||
|
<item><ref id="get_iigs_speed" name="get_iigs_speed">
|
||||||
<item><ref id="get_scpu_speed" name="get_scpu_speed">
|
<item><ref id="get_scpu_speed" name="get_scpu_speed">
|
||||||
<item><ref id="get_turbomaster_speed" name="get_turbomaster_speed">
|
<item><ref id="get_turbomaster_speed" name="get_turbomaster_speed">
|
||||||
<item><ref id="set_c128_speed" name="set_c128_speed">
|
<item><ref id="set_c128_speed" name="set_c128_speed">
|
||||||
<item><ref id="set_c64dtv_speed" name="set_c64dtv_speed">
|
<item><ref id="set_c64dtv_speed" name="set_c64dtv_speed">
|
||||||
<item><ref id="set_c65_speed" name="set_c65_speed">
|
<item><ref id="set_c65_speed" name="set_c65_speed">
|
||||||
<item><ref id="set_chameleon_speed" name="set_chameleon_speed">
|
<item><ref id="set_chameleon_speed" name="set_chameleon_speed">
|
||||||
|
<item><ref id="set_iigs_speed" name="set_iigs_speed">
|
||||||
<item><ref id="set_scpu_speed" name="set_scpu_speed">
|
<item><ref id="set_scpu_speed" name="set_scpu_speed">
|
||||||
<item><ref id="set_turbomaster_speed" name="set_turbomaster_speed">
|
<item><ref id="set_turbomaster_speed" name="set_turbomaster_speed">
|
||||||
</itemize>
|
</itemize>
|
||||||
@@ -92,6 +95,7 @@ function.
|
|||||||
|
|
||||||
<itemize>
|
<itemize>
|
||||||
<item>_dos_type
|
<item>_dos_type
|
||||||
|
<item><ref id="beep" name="beep">
|
||||||
<item><ref id="get_ostype" name="get_ostype">
|
<item><ref id="get_ostype" name="get_ostype">
|
||||||
<item><ref id="gmtime_dt" name="gmtime_dt">
|
<item><ref id="gmtime_dt" name="gmtime_dt">
|
||||||
<item><ref id="mktime_dt" name="mktime_dt">
|
<item><ref id="mktime_dt" name="mktime_dt">
|
||||||
@@ -103,7 +107,10 @@ function.
|
|||||||
|
|
||||||
<itemize>
|
<itemize>
|
||||||
<item>_dos_type
|
<item>_dos_type
|
||||||
|
<item><ref id="beep" name="beep">
|
||||||
<item><ref id="get_ostype" name="get_ostype">
|
<item><ref id="get_ostype" name="get_ostype">
|
||||||
|
<item><ref id="gmtime_dt" name="gmtime_dt">
|
||||||
|
<item><ref id="mktime_dt" name="mktime_dt">
|
||||||
<item>rebootafterexit
|
<item>rebootafterexit
|
||||||
<item><ref id="videomode" name="videomode">
|
<item><ref id="videomode" name="videomode">
|
||||||
</itemize>
|
</itemize>
|
||||||
@@ -1766,10 +1773,11 @@ used in presence of a prototype.
|
|||||||
<descrip>
|
<descrip>
|
||||||
<tag/Function/Beep sound.
|
<tag/Function/Beep sound.
|
||||||
<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/
|
<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/
|
||||||
|
<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/
|
||||||
<tag/Declaration/<tt/void beep(void);/
|
<tag/Declaration/<tt/void beep(void);/
|
||||||
<tag/Description/<tt/beep/ makes a brief tone.
|
<tag/Description/<tt/beep/ makes a brief tone.
|
||||||
<tag/Notes/<itemize>
|
<tag/Notes/<itemize>
|
||||||
<item>The function is specific to the Sym-1.
|
<item>The function is specific to the Sym-1 and Apple2 platforms.
|
||||||
</itemize>
|
</itemize>
|
||||||
<tag/Availability/cc65
|
<tag/Availability/cc65
|
||||||
<tag/See also/
|
<tag/See also/
|
||||||
@@ -3453,6 +3461,26 @@ used in presence of a prototype.
|
|||||||
</quote>
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>detect_iigs<label id="detect_iigs"><p>
|
||||||
|
|
||||||
|
<quote>
|
||||||
|
<descrip>
|
||||||
|
<tag/Function/Check whether we are running on an Apple IIgs..
|
||||||
|
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
|
||||||
|
<tag/Declaration/<tt/unsigned char detect_iigs (void);/
|
||||||
|
<tag/Description/The function returns a 1 if running on an Apple IIgs.
|
||||||
|
<tag/Notes/<itemize>
|
||||||
|
<item>The function is specific to the Apple2 and Apple2enh platforms.
|
||||||
|
</itemize>
|
||||||
|
<tag/Availability/cc65 (not all platforms)
|
||||||
|
<tag/See also/
|
||||||
|
<ref id="get_iigs_speed" name="get_iigs_speed">,
|
||||||
|
<ref id="set_iigs_speed" name="set_iigs_speed">,
|
||||||
|
<tag/Example/None.
|
||||||
|
</descrip>
|
||||||
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
<sect1>detect_scpu<label id="detect_scpu"><p>
|
<sect1>detect_scpu<label id="detect_scpu"><p>
|
||||||
|
|
||||||
<quote>
|
<quote>
|
||||||
@@ -4167,6 +4195,27 @@ header files define constants that can be used to check the return code.
|
|||||||
</quote>
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>get_iigs_speed<label id="get_iigs_speed"><p>
|
||||||
|
|
||||||
|
<quote>
|
||||||
|
<descrip>
|
||||||
|
<tag/Function/Get the current speed of the Apple IIgs.
|
||||||
|
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
|
||||||
|
<tag/Declaration/<tt/unsigned char get_iigs_speed (void);/
|
||||||
|
<tag/Description/The function returns the current speed of the Apple IIgs.
|
||||||
|
<tag/Notes/<itemize>
|
||||||
|
<item>The function is specific to the Apple2 and Apple2enh platforms.
|
||||||
|
<item>See the accelerator.h header for the speed definitions.
|
||||||
|
</itemize>
|
||||||
|
<tag/Availability/cc65 (not all platforms)
|
||||||
|
<tag/See also/
|
||||||
|
<ref id="detect_iigs" name="detect_iigs">,
|
||||||
|
<ref id="set_iigs_speed" name="set_iigs_speed">,
|
||||||
|
<tag/Example/None.
|
||||||
|
</descrip>
|
||||||
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
<sect1>get_scpu_speed<label id="get_scpu_speed"><p>
|
<sect1>get_scpu_speed<label id="get_scpu_speed"><p>
|
||||||
|
|
||||||
<quote>
|
<quote>
|
||||||
@@ -6985,6 +7034,30 @@ clean-up when exiting the program.
|
|||||||
</quote>
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>set_iigs_speed<label id="set_iigs_speed"><p>
|
||||||
|
|
||||||
|
<quote>
|
||||||
|
<descrip>
|
||||||
|
<tag/Function/Set the current speed of the Apple IIgs.
|
||||||
|
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
|
||||||
|
<tag/Declaration/<tt/unsigned char __fastcall__ set_iigs_speed (unsigned char speed);/
|
||||||
|
<tag/Description/The function sets the speed of the Apple IIgs CPU (and returns
|
||||||
|
the new speed).
|
||||||
|
<tag/Notes/<itemize>
|
||||||
|
<item>The function is specific to the Apple2 and Apple2enh platforms.
|
||||||
|
<item>See the accelerator.h header for the speed definitions.
|
||||||
|
<item>Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are
|
||||||
|
considered SPEED_FAST).
|
||||||
|
</itemize>
|
||||||
|
<tag/Availability/cc65 (not all platforms)
|
||||||
|
<tag/See also/
|
||||||
|
<ref id="detect_iigs" name="detect_iigs">,
|
||||||
|
<ref id="get_iigs_speed" name="get_iigs_speed">,
|
||||||
|
<tag/Example/None.
|
||||||
|
</descrip>
|
||||||
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
<sect1>set_scpu_speed<label id="set_scpu_speed"><p>
|
<sect1>set_scpu_speed<label id="set_scpu_speed"><p>
|
||||||
|
|
||||||
<quote>
|
<quote>
|
||||||
|
|||||||
@@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void);
|
|||||||
* 0x01 : C64 Turbo Master cartridge present
|
* 0x01 : C64 Turbo Master cartridge present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
unsigned char __fastcall__ set_iigs_speed (unsigned char speed);
|
||||||
|
|
||||||
|
/* Set the speed of the Apple IIgs CPU.
|
||||||
|
*
|
||||||
|
* Possible values:
|
||||||
|
* SPEED_SLOW : 1 Mhz mode
|
||||||
|
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
|
||||||
|
* an accelerator)
|
||||||
|
*
|
||||||
|
* Any other value will be interpreted as SPEED_FAST.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned char get_iigs_speed (void);
|
||||||
|
|
||||||
|
/* Get the speed of the Apple IIgs CPU.
|
||||||
|
*
|
||||||
|
* Possible return values:
|
||||||
|
* SPEED_SLOW : 1 Mhz mode
|
||||||
|
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
|
||||||
|
* an accelerator)
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned char detect_iigs (void);
|
||||||
|
|
||||||
|
/* Check whether we are running on an Apple IIgs.
|
||||||
|
*
|
||||||
|
* Possible return values:
|
||||||
|
* 0x00 : No
|
||||||
|
* 0x01 : Yes
|
||||||
|
*/
|
||||||
|
|
||||||
/* End of accelerator.h */
|
/* End of accelerator.h */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,9 @@ extern void a2_lo_tgi[];
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void beep (void);
|
||||||
|
/* Beep beep. */
|
||||||
|
|
||||||
unsigned char get_ostype (void);
|
unsigned char get_ostype (void);
|
||||||
/* Get the machine type. Returns one of the APPLE_xxx codes. */
|
/* Get the machine type. Returns one of the APPLE_xxx codes. */
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ extern FILE* stderr;
|
|||||||
# define FILENAME_MAX (80+1)
|
# define FILENAME_MAX (80+1)
|
||||||
#elif defined(__TELESTRAT__)
|
#elif defined(__TELESTRAT__)
|
||||||
# define FILENAME_MAX (50+1)
|
# define FILENAME_MAX (50+1)
|
||||||
|
#elif defined(__SIM6502__)
|
||||||
|
# define FILENAME_MAX (1024+1)
|
||||||
|
#elif defined(__SIM65C02__)
|
||||||
|
# define FILENAME_MAX (1024+1)
|
||||||
#else
|
#else
|
||||||
# define FILENAME_MAX (16+1)
|
# define FILENAME_MAX (16+1)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
20
libsrc/apple2/beep.s
Normal file
20
libsrc/apple2/beep.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; void beep(void)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _beep
|
||||||
|
.import BELL
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
|
||||||
|
.segment "LOWCODE"
|
||||||
|
|
||||||
|
_beep:
|
||||||
|
lda CH ; Bell scrambles CH in 80col mode on IIgs, storing
|
||||||
|
pha ; it in OURCH and resetting CH to 0. Save it.
|
||||||
|
jsr BELL
|
||||||
|
pla
|
||||||
|
sta CH ; Restore CH
|
||||||
|
rts
|
||||||
20
libsrc/apple2/bell.s
Normal file
20
libsrc/apple2/bell.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; BELL routine
|
||||||
|
;
|
||||||
|
|
||||||
|
.export BELL
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
|
||||||
|
.segment "LOWCODE"
|
||||||
|
|
||||||
|
BELL:
|
||||||
|
; Switch in ROM and call BELL
|
||||||
|
bit $C082
|
||||||
|
jsr $FF3A ; BELL
|
||||||
|
|
||||||
|
; Switch in LC bank 2 for R/O and return
|
||||||
|
bit $C080
|
||||||
|
rts
|
||||||
@@ -40,12 +40,15 @@ _exit: ldx #<exit
|
|||||||
lda #>exit
|
lda #>exit
|
||||||
jsr reset ; Setup RESET vector
|
jsr reset ; Setup RESET vector
|
||||||
|
|
||||||
; Switch in ROM, in case it wasn't already switched in by a RESET.
|
; Switch in LC bank 2 for R/O in case it was switched out by a RESET.
|
||||||
bit $C082
|
bit $C080
|
||||||
|
|
||||||
; Call the module destructors.
|
; Call the module destructors.
|
||||||
jsr donelib
|
jsr donelib
|
||||||
|
|
||||||
|
; Switch in ROM.
|
||||||
|
bit $C082
|
||||||
|
|
||||||
; Restore the original RESET vector.
|
; Restore the original RESET vector.
|
||||||
exit: ldx #$02
|
exit: ldx #$02
|
||||||
: lda rvsave,x
|
: lda rvsave,x
|
||||||
|
|||||||
17
libsrc/apple2/detect_iigs.s
Normal file
17
libsrc/apple2/detect_iigs.s
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira <colin@colino.net>, 2024
|
||||||
|
;
|
||||||
|
; void __fastcall__ detect_iigs(void)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _detect_iigs
|
||||||
|
.import ostype, return0, return1
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
|
||||||
|
; Returns 1 if running on IIgs, 0 otherwise
|
||||||
|
_detect_iigs:
|
||||||
|
lda ostype
|
||||||
|
bpl :+
|
||||||
|
jmp return1
|
||||||
|
: jmp return0
|
||||||
22
libsrc/apple2/get_iigs_speed.s
Normal file
22
libsrc/apple2/get_iigs_speed.s
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira <colin@colino.net>, 2024
|
||||||
|
;
|
||||||
|
; unsigned char __fastcall__ get_iigs_speed(void)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _get_iigs_speed
|
||||||
|
.import ostype, return0
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
.include "accelerator.inc"
|
||||||
|
|
||||||
|
_get_iigs_speed:
|
||||||
|
lda ostype ; Return SLOW if not IIgs
|
||||||
|
bpl :+
|
||||||
|
lda CYAREG ; Check current setting
|
||||||
|
bpl :+
|
||||||
|
lda #SPEED_FAST
|
||||||
|
ldx #$00
|
||||||
|
rts
|
||||||
|
.assert SPEED_SLOW = 0, error
|
||||||
|
: jmp return0 ; SPEED_SLOW
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
;
|
;
|
||||||
|
|
||||||
.constructor initostype, 9
|
.constructor initostype, 9
|
||||||
.export _get_ostype
|
.export _get_ostype, ostype
|
||||||
|
|
||||||
; Identify machine according to:
|
; Identify machine according to:
|
||||||
; Apple II Miscellaneous TechNote #7, Apple II Family Identification
|
; Apple II Miscellaneous TechNote #7, Apple II Family Identification
|
||||||
|
|||||||
29
libsrc/apple2/set_iigs_speed.s
Normal file
29
libsrc/apple2/set_iigs_speed.s
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira <colin@colino.net>, 2024
|
||||||
|
;
|
||||||
|
; unsigned char __fastcall__ detect_iigs(unsigned char speed)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _set_iigs_speed
|
||||||
|
.import ostype, return0
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
.include "accelerator.inc"
|
||||||
|
|
||||||
|
_set_iigs_speed:
|
||||||
|
tax ; Keep parameter
|
||||||
|
lda ostype ; Return if not IIgs
|
||||||
|
bmi :+
|
||||||
|
jmp return0
|
||||||
|
|
||||||
|
: lda CYAREG
|
||||||
|
cpx #SPEED_SLOW
|
||||||
|
beq :+
|
||||||
|
ora #%10000000
|
||||||
|
bne set_speed
|
||||||
|
: and #%01111111
|
||||||
|
set_speed:
|
||||||
|
sta CYAREG
|
||||||
|
txa
|
||||||
|
ldx #$00
|
||||||
|
rts
|
||||||
54
libsrc/apple2/sleep.s
Normal file
54
libsrc/apple2/sleep.s
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira <colin@colino.net>, 2024
|
||||||
|
;
|
||||||
|
; void __fastcall__ sleep(unsigned s)
|
||||||
|
;
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _sleep
|
||||||
|
.import _get_iigs_speed
|
||||||
|
.import _set_iigs_speed
|
||||||
|
.import WAIT
|
||||||
|
.importzp tmp1
|
||||||
|
|
||||||
|
.include "accelerator.inc"
|
||||||
|
|
||||||
|
; This functions uses the Apple2 WAIT ROM routine to waste a certain
|
||||||
|
; amount of cycles and returns approximately after the numbers of
|
||||||
|
; seconds passed in AX.
|
||||||
|
;
|
||||||
|
; It takes 1023730 cycles when called with AX=1 (1,0007s),
|
||||||
|
; 10236364 cycles when called with AX=10 (10,006 seconds),
|
||||||
|
; 306064298 cycles with AX=300 (299.2 seconds).
|
||||||
|
;
|
||||||
|
; Caveat: IRQs firing during calls to sleep will make the sleep longer
|
||||||
|
; by the amount of cycles it takes to handle the IRQ.
|
||||||
|
;
|
||||||
|
_sleep:
|
||||||
|
stx tmp1 ; High byte of s in X
|
||||||
|
tay ; Low byte in A
|
||||||
|
ora tmp1
|
||||||
|
bne :+
|
||||||
|
rts
|
||||||
|
: jsr _get_iigs_speed ; Save current CPU speed
|
||||||
|
pha
|
||||||
|
lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT
|
||||||
|
jsr _set_iigs_speed
|
||||||
|
sleep_1s:
|
||||||
|
ldx #$0A ; Loop 10 times
|
||||||
|
sleep_100ms:
|
||||||
|
lda #$C7 ; Sleep about 99ms
|
||||||
|
jsr WAIT
|
||||||
|
lda #$0D ; About 1ms
|
||||||
|
jsr WAIT
|
||||||
|
dex
|
||||||
|
bne sleep_100ms
|
||||||
|
dey
|
||||||
|
bne sleep_1s
|
||||||
|
dec tmp1
|
||||||
|
bmi done
|
||||||
|
dey ; Down to #$FF
|
||||||
|
bne sleep_1s
|
||||||
|
done:
|
||||||
|
pla ; Restore CPU speed
|
||||||
|
jmp _set_iigs_speed
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.export _statvfs
|
.export _statvfs
|
||||||
.import _dio_query_sectsize
|
.import _dio_query_sectsize
|
||||||
.import mli_file_info, pushax, popax, popptr1
|
.import mli_file_info, pushax, popax, popptr1, pushptr1
|
||||||
.include "zeropage.inc"
|
.include "zeropage.inc"
|
||||||
.include "apple2.inc"
|
.include "apple2.inc"
|
||||||
.include "errno.inc"
|
.include "errno.inc"
|
||||||
@@ -45,9 +45,7 @@ _statvfs:
|
|||||||
sty vol_sep ; Register '/' index
|
sty vol_sep ; Register '/' index
|
||||||
lda #$00
|
lda #$00
|
||||||
sta (ptr1),y ; Cut pathname at first slash
|
sta (ptr1),y ; Cut pathname at first slash
|
||||||
: lda ptr1
|
: jsr pushptr1
|
||||||
ldx ptr1+1
|
|
||||||
jsr pushax
|
|
||||||
|
|
||||||
jsr mli_file_info
|
jsr mli_file_info
|
||||||
|
|
||||||
|
|||||||
20
libsrc/apple2/wait.s
Normal file
20
libsrc/apple2/wait.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; WAIT routine
|
||||||
|
;
|
||||||
|
|
||||||
|
.export WAIT
|
||||||
|
|
||||||
|
.include "apple2.inc"
|
||||||
|
|
||||||
|
.segment "LOWCODE"
|
||||||
|
|
||||||
|
WAIT:
|
||||||
|
; Switch in ROM and call WAIT
|
||||||
|
bit $C082
|
||||||
|
jsr $FCA8 ; Vector to WAIT routine
|
||||||
|
|
||||||
|
; Switch in LC bank 2 for R/O and return
|
||||||
|
bit $C080
|
||||||
|
rts
|
||||||
@@ -5,21 +5,11 @@
|
|||||||
;
|
;
|
||||||
.ifdef __APPLE2ENH__
|
.ifdef __APPLE2ENH__
|
||||||
|
|
||||||
.constructor initvsync
|
|
||||||
.export _waitvsync
|
.export _waitvsync
|
||||||
.import _get_ostype
|
.import ostype
|
||||||
|
|
||||||
.include "apple2.inc"
|
.include "apple2.inc"
|
||||||
|
|
||||||
.segment "ONCE"
|
|
||||||
|
|
||||||
initvsync:
|
|
||||||
jsr _get_ostype
|
|
||||||
sta ostype
|
|
||||||
rts
|
|
||||||
|
|
||||||
.code
|
|
||||||
|
|
||||||
_waitvsync:
|
_waitvsync:
|
||||||
bit ostype
|
bit ostype
|
||||||
bmi iigs ; $8x
|
bmi iigs ; $8x
|
||||||
@@ -53,8 +43,4 @@ iic: sei
|
|||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
|
|
||||||
.segment "INIT"
|
|
||||||
|
|
||||||
ostype: .res 1
|
|
||||||
|
|
||||||
.endif ; __APPLE2ENH__
|
.endif ; __APPLE2ENH__
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
.import findfreeiocb
|
.import findfreeiocb
|
||||||
.import incsp4
|
.import incsp4
|
||||||
.import ldaxysp,addysp
|
.import ldaxysp,addysp
|
||||||
.import ___oserror
|
.import ___oserror, returnFFFF
|
||||||
.ifdef UCASE_FILENAME
|
.ifdef UCASE_FILENAME
|
||||||
.import ucase_fn
|
.import ucase_fn
|
||||||
.endif
|
.endif
|
||||||
@@ -39,9 +39,7 @@ parmok: jsr findfreeiocb
|
|||||||
lda #<EMFILE ; "too many open files"
|
lda #<EMFILE ; "too many open files"
|
||||||
seterr: jsr ___directerrno
|
seterr: jsr ___directerrno
|
||||||
jsr incsp4 ; clean up stack
|
jsr incsp4 ; clean up stack
|
||||||
lda #$FF
|
jmp returnFFFF
|
||||||
tax
|
|
||||||
rts ; return -1
|
|
||||||
|
|
||||||
; process the mode argument
|
; process the mode argument
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
.export _cbm_read
|
.export _cbm_read
|
||||||
.importzp ptr1, ptr2, ptr3, tmp1
|
.importzp ptr1, ptr2, ptr3, tmp1
|
||||||
.import popax, popa
|
.import popax, popa, returnFFFF
|
||||||
.import ___oserror
|
.import ___oserror
|
||||||
|
|
||||||
|
|
||||||
@@ -107,7 +107,4 @@ _cbm_read:
|
|||||||
; CHKIN failed
|
; CHKIN failed
|
||||||
|
|
||||||
@E1: sta ___oserror
|
@E1: sta ___oserror
|
||||||
lda #$FF
|
jmp returnFFFF
|
||||||
tax
|
|
||||||
rts ; return -1
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
.export _cbm_write
|
.export _cbm_write
|
||||||
.importzp ptr1, ptr2, ptr3
|
.importzp ptr1, ptr2, ptr3
|
||||||
.import popax, popa
|
.import popax, popa, returnFFFF
|
||||||
.import ___oserror
|
.import ___oserror
|
||||||
|
|
||||||
|
|
||||||
@@ -88,7 +88,4 @@ _cbm_write:
|
|||||||
; Error entry, error code is in A
|
; Error entry, error code is in A
|
||||||
|
|
||||||
@E2: sta ___oserror
|
@E2: sta ___oserror
|
||||||
lda #$FF
|
jmp returnFFFF
|
||||||
tax
|
|
||||||
rts ; return -1
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
** _is_leap_year.h
|
|
||||||
**
|
|
||||||
** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net>
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef __IS_LEAP_YEAR_H
|
|
||||||
#define __IS_LEAP_YEAR_H
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned char __fastcall__ IsLeapYear (unsigned char Year);
|
|
||||||
/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206,
|
|
||||||
* without 1900 added */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* End of _is_leap_year.h */
|
|
||||||
#endif
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
;
|
|
||||||
; Colin Leroy-Mira, 2024
|
|
||||||
;
|
|
||||||
; unsigned char __fastcall__ IsLeapYear (unsigned char Year)
|
|
||||||
; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206,
|
|
||||||
; without 1900 added.
|
|
||||||
;
|
|
||||||
|
|
||||||
.export _IsLeapYear
|
|
||||||
|
|
||||||
_IsLeapYear:
|
|
||||||
ldx #$00 ; Prepare X for rts
|
|
||||||
cmp #$00 ; Y 0 (1900) is not a leap year
|
|
||||||
beq NotLeap
|
|
||||||
cmp #$C8 ; Y 200 (2100) is not a leap year
|
|
||||||
beq NotLeap
|
|
||||||
and #$03 ; Year % 4 == 0 means leap year
|
|
||||||
bne NotLeap
|
|
||||||
lda #$01 ; Return 1
|
|
||||||
rts
|
|
||||||
NotLeap:
|
|
||||||
lda #$00 ; Return 0
|
|
||||||
rts
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* gmtime.c */
|
|
||||||
/* */
|
|
||||||
/* Convert calendar time into broken down time in UTC */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* (C) 2002 Ullrich von Bassewitz */
|
|
||||||
/* Wacholderweg 14 */
|
|
||||||
/* D-70597 Stuttgart */
|
|
||||||
/* EMail: uz@musoftware.de */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* 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. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
struct tm* __fastcall__ _time_t_to_tm (const time_t t)
|
|
||||||
{
|
|
||||||
static struct tm timebuf;
|
|
||||||
/* Since our ints are just 16 bits, split the given time into seconds,
|
|
||||||
** hours and days. Each of the values will fit in a 16 bit variable.
|
|
||||||
** The mktime routine will then do the rest.
|
|
||||||
*/
|
|
||||||
timebuf.tm_sec = t % 3600;
|
|
||||||
timebuf.tm_min = 0;
|
|
||||||
timebuf.tm_hour = (t / 3600) % 24;
|
|
||||||
timebuf.tm_mday = (t / (3600UL * 24UL)) + 1;
|
|
||||||
timebuf.tm_mon = 0;
|
|
||||||
timebuf.tm_year = 70; /* Base value is 1/1/1970 */
|
|
||||||
|
|
||||||
/* Call mktime to do the final conversion */
|
|
||||||
mktime (&timebuf);
|
|
||||||
|
|
||||||
/* Return the result */
|
|
||||||
return &timebuf;
|
|
||||||
}
|
|
||||||
129
libsrc/common/_time_t_to_tm.s
Normal file
129
libsrc/common/_time_t_to_tm.s
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; struct tm* __fastcall__ _time_t_to_tm (const time_t t)
|
||||||
|
;
|
||||||
|
; Helper to gmtime and localtime. Breaks down a number of
|
||||||
|
; seconds since Jan 1, 1970 into days, hours and seconds,
|
||||||
|
; so that each of them fits in 16 bits; passes the
|
||||||
|
; result to _mktime which fixes all values in the struct,
|
||||||
|
; and returns a pointer to the struct to callers.
|
||||||
|
;
|
||||||
|
|
||||||
|
.export __time_t_to_tm
|
||||||
|
.import udiv32, _mktime
|
||||||
|
.importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4
|
||||||
|
|
||||||
|
.include "time.inc"
|
||||||
|
|
||||||
|
.macpack cpu
|
||||||
|
|
||||||
|
__time_t_to_tm:
|
||||||
|
; Divide number of seconds since epoch, in ptr1:sreg,
|
||||||
|
; by 86400 to get the number of days since epoch, and
|
||||||
|
; the number of seconds today in the remainder.
|
||||||
|
|
||||||
|
; Load t as dividend (sreg is already set by the caller)
|
||||||
|
sta ptr1
|
||||||
|
stx ptr1+1
|
||||||
|
|
||||||
|
; Load 86400 as divisor
|
||||||
|
lda #$80
|
||||||
|
sta ptr3
|
||||||
|
lda #$51
|
||||||
|
sta ptr3+1
|
||||||
|
lda #$01
|
||||||
|
sta ptr4
|
||||||
|
lda #$00
|
||||||
|
sta ptr4+1
|
||||||
|
|
||||||
|
; Clear TM buf while we have zero in A
|
||||||
|
ldx #.sizeof(tm)-1
|
||||||
|
: sta TM,x
|
||||||
|
dex
|
||||||
|
bpl :-
|
||||||
|
|
||||||
|
; Divide t/86400
|
||||||
|
jsr udiv32
|
||||||
|
|
||||||
|
; Store the quotient (the number of full days), and increment
|
||||||
|
; by one as epoch starts at day 1.
|
||||||
|
clc
|
||||||
|
lda ptr1
|
||||||
|
adc #1
|
||||||
|
sta TM + tm::tm_mday
|
||||||
|
lda ptr1+1
|
||||||
|
adc #0
|
||||||
|
sta TM + tm::tm_mday+1
|
||||||
|
|
||||||
|
; Now divide the number of remaining seconds by 3600,
|
||||||
|
; to get the number of hours, and the seconds in the
|
||||||
|
; current hour, in neat 16-bit integers.
|
||||||
|
|
||||||
|
; Load the previous division's remainder (in ptr2:tmp3:tmp4)
|
||||||
|
; as dividend
|
||||||
|
lda ptr2
|
||||||
|
sta ptr1
|
||||||
|
lda ptr2+1
|
||||||
|
sta ptr1+1
|
||||||
|
lda tmp3
|
||||||
|
sta sreg
|
||||||
|
; We ignore the high byte stored in tmp4 because it will be
|
||||||
|
; zero. We'll zero sreg+1 right below, when we'll have
|
||||||
|
; a convenient zero already in A.
|
||||||
|
|
||||||
|
; Load divisor
|
||||||
|
lda #<3600
|
||||||
|
sta ptr3
|
||||||
|
lda #>3600
|
||||||
|
sta ptr3+1
|
||||||
|
|
||||||
|
; Zero the two high bytes of the divisor and the high byte
|
||||||
|
; of the dividend.
|
||||||
|
.if .cpu .bitand CPU_ISET_65SC02
|
||||||
|
stz ptr4
|
||||||
|
stz ptr4+1
|
||||||
|
stz sreg+1
|
||||||
|
.else
|
||||||
|
lda #$00
|
||||||
|
sta ptr4
|
||||||
|
sta ptr4+1
|
||||||
|
sta sreg+1
|
||||||
|
.endif
|
||||||
|
|
||||||
|
; Do the division
|
||||||
|
jsr udiv32
|
||||||
|
|
||||||
|
; Store year
|
||||||
|
lda #70
|
||||||
|
sta TM + tm::tm_year
|
||||||
|
|
||||||
|
; Store hours (the quotient of the last division)
|
||||||
|
lda ptr1
|
||||||
|
sta TM + tm::tm_hour
|
||||||
|
lda ptr1+1
|
||||||
|
sta TM + tm::tm_hour+1
|
||||||
|
|
||||||
|
; Store seconds (the remainder of the last division)
|
||||||
|
lda ptr2
|
||||||
|
sta TM + tm::tm_sec
|
||||||
|
lda ptr2+1
|
||||||
|
sta TM + tm::tm_sec+1
|
||||||
|
|
||||||
|
; The rest of the struct tm fields are zero. mktime
|
||||||
|
; will take care of shifting extra seconds to minutes,
|
||||||
|
; and extra days to months and years.
|
||||||
|
|
||||||
|
; Call mktime
|
||||||
|
lda #<TM
|
||||||
|
ldx #>TM
|
||||||
|
jsr _mktime
|
||||||
|
|
||||||
|
; And return our pointer
|
||||||
|
lda #<TM
|
||||||
|
ldx #>TM
|
||||||
|
rts
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
TM: .tag tm
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* asctime.c */
|
|
||||||
/* */
|
|
||||||
/* Convert a broken down time into a string */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* (C) 2002 Ullrich von Bassewitz */
|
|
||||||
/* Wacholderweg 14 */
|
|
||||||
/* D-70597 Stuttgart */
|
|
||||||
/* EMail: uz@musoftware.de */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* 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. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* 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[38];
|
|
||||||
|
|
||||||
/* Format into given buffer and return the result */
|
|
||||||
return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0;
|
|
||||||
}
|
|
||||||
81
libsrc/common/asctime.s
Normal file
81
libsrc/common/asctime.s
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; char* __fastcall__ asctime (const struct tm* timep)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _asctime
|
||||||
|
.import _strftime, pushax
|
||||||
|
.importzp ptr1
|
||||||
|
.include "time.inc"
|
||||||
|
|
||||||
|
.macpack cpu
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Special values
|
||||||
|
|
||||||
|
; We need to be able to store up to 38 bytes:
|
||||||
|
; 1234567890123456789012345678901234567
|
||||||
|
; "Wednesday September ..1 00:00:00 1970"
|
||||||
|
MAX_BUF_LEN = 38
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Code
|
||||||
|
|
||||||
|
_asctime:
|
||||||
|
; Backup timep
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
pha
|
||||||
|
phx
|
||||||
|
.else
|
||||||
|
sta ptr1
|
||||||
|
stx ptr1+1
|
||||||
|
.endif
|
||||||
|
|
||||||
|
; Push buf
|
||||||
|
lda #<buf
|
||||||
|
ldx #>buf
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Push sizeof(buf)
|
||||||
|
lda #<MAX_BUF_LEN
|
||||||
|
ldx #>MAX_BUF_LEN
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Push format string
|
||||||
|
lda #<fmt
|
||||||
|
ldx #>fmt
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Restore timep
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
.else
|
||||||
|
lda ptr1
|
||||||
|
ldx ptr1+1
|
||||||
|
.endif
|
||||||
|
|
||||||
|
; Call formatter
|
||||||
|
jsr _strftime
|
||||||
|
|
||||||
|
; Check return status
|
||||||
|
bne :+
|
||||||
|
cpx #$00
|
||||||
|
bne :+
|
||||||
|
rts
|
||||||
|
|
||||||
|
: lda #<buf
|
||||||
|
ldx #>buf
|
||||||
|
rts
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
fmt: .byte '%'
|
||||||
|
.byte 'c'
|
||||||
|
.byte $0A
|
||||||
|
.byte $00
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
buf: .res MAX_BUF_LEN
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
; 2002-10-22, Greg King
|
; 2002-10-22, Greg King
|
||||||
;
|
;
|
||||||
; This signed-division function returns both the quotient and the remainder,
|
; This signed-division function returns both the quotient and the remainder,
|
||||||
; in this structure:
|
; in this structure: (quotient in sreg, remainder in AX)
|
||||||
;
|
;
|
||||||
; typedef struct {
|
; typedef struct {
|
||||||
; int rem, quot;
|
; int rem, quot;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
.export _fclose
|
.export _fclose
|
||||||
|
|
||||||
.import _close
|
.import _close, ___directerrno
|
||||||
.importzp ptr1
|
.importzp ptr1
|
||||||
|
|
||||||
.include "errno.inc"
|
.include "errno.inc"
|
||||||
@@ -31,10 +31,7 @@
|
|||||||
; File is not open
|
; File is not open
|
||||||
|
|
||||||
lda #EINVAL
|
lda #EINVAL
|
||||||
jsr ___seterrno
|
jmp ___directerrno
|
||||||
lda #$FF ; Return -1
|
|
||||||
tax
|
|
||||||
rts
|
|
||||||
|
|
||||||
; File is open. Reset the flags and close the file.
|
; File is open. Reset the flags and close the file.
|
||||||
|
|
||||||
@@ -47,4 +44,3 @@
|
|||||||
jmp _close ; Will set errno and return an error flag
|
jmp _close ; Will set errno and return an error flag
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
** fgetc.c
|
|
||||||
**
|
|
||||||
** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org)
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "_file.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int __fastcall__ fgetc (register FILE* f)
|
|
||||||
{
|
|
||||||
unsigned char c;
|
|
||||||
|
|
||||||
/* Check if the file is open or if there is an error condition */
|
|
||||||
if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) {
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a pushed back character, return it */
|
|
||||||
if (f->f_flags & _FPUSHBACK) {
|
|
||||||
f->f_flags &= ~_FPUSHBACK;
|
|
||||||
return f->f_pushback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read one byte */
|
|
||||||
switch (read (f->f_fd, &c, 1)) {
|
|
||||||
|
|
||||||
case -1:
|
|
||||||
/* Error */
|
|
||||||
f->f_flags |= _FERROR;
|
|
||||||
return EOF;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
/* EOF */
|
|
||||||
f->f_flags |= _FEOF;
|
|
||||||
return EOF;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Char read */
|
|
||||||
return c;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
92
libsrc/common/fgetc.s
Normal file
92
libsrc/common/fgetc.s
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; int __fastcall__ fgetc (register FILE* f)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _fgetc
|
||||||
|
.import _read, pusha0, pushax, popptr1, incsp2, returnFFFF
|
||||||
|
.importzp ptr1
|
||||||
|
|
||||||
|
.include "stdio.inc"
|
||||||
|
.include "_file.inc"
|
||||||
|
|
||||||
|
_fgetc:
|
||||||
|
sta ptr1
|
||||||
|
stx ptr1+1
|
||||||
|
jsr pushax ; Backup our ptr
|
||||||
|
|
||||||
|
ldy #_FILE::f_flags
|
||||||
|
lda (ptr1),y
|
||||||
|
tax
|
||||||
|
and #_FOPEN ; Check for file open
|
||||||
|
beq ret_eof
|
||||||
|
txa
|
||||||
|
and #(_FERROR|_FEOF); Check for error/eof
|
||||||
|
bne ret_eof
|
||||||
|
|
||||||
|
txa
|
||||||
|
and #_FPUSHBACK ; Check for pushed back char
|
||||||
|
beq do_read
|
||||||
|
|
||||||
|
txa
|
||||||
|
and #<(~_FPUSHBACK) ; Reset flag
|
||||||
|
sta (ptr1),y
|
||||||
|
|
||||||
|
.assert _FILE::f_pushback = _FILE::f_flags+1, error
|
||||||
|
iny
|
||||||
|
jsr incsp2 ; Drop our ptr copy
|
||||||
|
lda (ptr1),y ; Return pushed back char
|
||||||
|
ldx #$00
|
||||||
|
rts
|
||||||
|
|
||||||
|
do_read:
|
||||||
|
; Push _read parameters
|
||||||
|
ldy #_FILE::f_fd
|
||||||
|
lda (ptr1),y
|
||||||
|
jsr pusha0
|
||||||
|
|
||||||
|
lda #<c
|
||||||
|
ldx #>c
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
lda #$01
|
||||||
|
ldx #$00
|
||||||
|
|
||||||
|
; Read
|
||||||
|
jsr _read
|
||||||
|
|
||||||
|
; Check for errors
|
||||||
|
cmp #$00
|
||||||
|
beq set_feof
|
||||||
|
|
||||||
|
cmp #<(-1)
|
||||||
|
beq set_ferror
|
||||||
|
|
||||||
|
jsr incsp2
|
||||||
|
; Return char
|
||||||
|
ldx #$00
|
||||||
|
lda c
|
||||||
|
rts
|
||||||
|
|
||||||
|
ret_eof:
|
||||||
|
jsr incsp2
|
||||||
|
jmp returnFFFF
|
||||||
|
|
||||||
|
set_ferror:
|
||||||
|
lda #_FERROR
|
||||||
|
bne set_err
|
||||||
|
set_feof:
|
||||||
|
lda #_FEOF
|
||||||
|
set_err:
|
||||||
|
pha
|
||||||
|
jsr popptr1
|
||||||
|
pla
|
||||||
|
ldy #_FILE::f_flags
|
||||||
|
ora (ptr1),y
|
||||||
|
sta (ptr1),y
|
||||||
|
jmp returnFFFF
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
c: .res 1
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
** Ullrich von Bassewitz, 11.08.1998
|
|
||||||
**
|
|
||||||
** char* fgets (char* s, int size, FILE* f);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "_file.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
char* __fastcall__ fgets (char* s, unsigned size, register FILE* f)
|
|
||||||
{
|
|
||||||
register char* p = s;
|
|
||||||
unsigned i;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
/* Invalid size */
|
|
||||||
return (char*) _seterrno (EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read input */
|
|
||||||
i = 0;
|
|
||||||
while (--size) {
|
|
||||||
|
|
||||||
/* Get next character */
|
|
||||||
if ((c = fgetc (f)) == EOF) {
|
|
||||||
/* Error or EOF */
|
|
||||||
if ((f->f_flags & _FERROR) != 0 || i == 0) {
|
|
||||||
/* ERROR or EOF on first char */
|
|
||||||
*p = '\0';
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* EOF with data already read */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* One char more */
|
|
||||||
*p = c;
|
|
||||||
++p;
|
|
||||||
++i;
|
|
||||||
|
|
||||||
/* Stop at end of line */
|
|
||||||
if ((char)c == '\n') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Terminate the string */
|
|
||||||
*p = '\0';
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
119
libsrc/common/fgets.s
Normal file
119
libsrc/common/fgets.s
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; char* __fastcall__ fgets (char* s, unsigned size, register FILE* f)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _fgets
|
||||||
|
.import _fgetc, popptr1, pushptr1, popax, pushax, return0, ___errno
|
||||||
|
.importzp ptr1, ptr4
|
||||||
|
|
||||||
|
.include "errno.inc"
|
||||||
|
.include "stdio.inc"
|
||||||
|
.include "_file.inc"
|
||||||
|
|
||||||
|
.macpack cpu
|
||||||
|
|
||||||
|
terminate_ptr:
|
||||||
|
lda #$00
|
||||||
|
tax
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
sta (ptr4)
|
||||||
|
.else
|
||||||
|
tay
|
||||||
|
sta (ptr4),y
|
||||||
|
.endif
|
||||||
|
rts
|
||||||
|
|
||||||
|
_fgets:
|
||||||
|
sta ptr1
|
||||||
|
stx ptr1+1
|
||||||
|
|
||||||
|
jsr popax
|
||||||
|
sta size
|
||||||
|
stx size+1
|
||||||
|
|
||||||
|
jsr popax
|
||||||
|
sta ptr4
|
||||||
|
stx ptr4+1
|
||||||
|
sta buf
|
||||||
|
stx buf+1
|
||||||
|
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
stz didread
|
||||||
|
.else
|
||||||
|
lda #$00 ; We have read nothing yet
|
||||||
|
sta didread
|
||||||
|
.endif
|
||||||
|
|
||||||
|
; Check size
|
||||||
|
lda size
|
||||||
|
ora size+1
|
||||||
|
bne read_loop
|
||||||
|
lda #EINVAL
|
||||||
|
sta ___errno
|
||||||
|
jmp return0
|
||||||
|
|
||||||
|
read_loop:
|
||||||
|
lda size ; Dec size
|
||||||
|
bne :+
|
||||||
|
dec size+1
|
||||||
|
: dec size
|
||||||
|
|
||||||
|
bne :+ ; Check bound
|
||||||
|
ldx size+1
|
||||||
|
beq done
|
||||||
|
|
||||||
|
: jsr pushptr1 ; Push ptr1 for backup and load it to AX for fgetc
|
||||||
|
jsr _fgetc ; Read a char
|
||||||
|
|
||||||
|
pha
|
||||||
|
jsr popptr1 ; Get ptr1 back
|
||||||
|
pla
|
||||||
|
|
||||||
|
cpx #<EOF
|
||||||
|
beq got_eof
|
||||||
|
|
||||||
|
ldy #$01
|
||||||
|
sty didread ; We read at least one char
|
||||||
|
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
sta (ptr4)
|
||||||
|
.else
|
||||||
|
dey
|
||||||
|
sta (ptr4),y
|
||||||
|
.endif
|
||||||
|
|
||||||
|
inc ptr4
|
||||||
|
bne :+
|
||||||
|
inc ptr4+1
|
||||||
|
|
||||||
|
: cmp #$0A ; Stop at \n
|
||||||
|
beq done
|
||||||
|
|
||||||
|
clc
|
||||||
|
bcc read_loop
|
||||||
|
|
||||||
|
got_eof:
|
||||||
|
lda didread
|
||||||
|
beq stopped_at_first_char
|
||||||
|
ldy #_FILE::f_flags
|
||||||
|
lda (ptr1),y
|
||||||
|
and #_FERROR
|
||||||
|
bne stopped_at_first_char
|
||||||
|
|
||||||
|
done:
|
||||||
|
jsr terminate_ptr
|
||||||
|
ldx #>buf
|
||||||
|
lda #<buf
|
||||||
|
rts
|
||||||
|
|
||||||
|
stopped_at_first_char:
|
||||||
|
jmp terminate_ptr
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
c: .res 1
|
||||||
|
buf: .res 2
|
||||||
|
size: .res 2
|
||||||
|
didread:.res 1
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
;
|
;
|
||||||
|
|
||||||
.export _clearerr, _feof, _ferror, _fileno, _fflush
|
.export _clearerr, _feof, _ferror, _fileno, _fflush
|
||||||
.import return0
|
.import return0, ___directerrno
|
||||||
.importzp ptr1
|
.importzp ptr1
|
||||||
|
|
||||||
.include "_file.inc"
|
.include "_file.inc"
|
||||||
@@ -78,10 +78,7 @@ err: rts
|
|||||||
; If the file is not valid, fileno must set errno and return -1
|
; If the file is not valid, fileno must set errno and return -1
|
||||||
|
|
||||||
error: lda #<EBADF
|
error: lda #<EBADF
|
||||||
jsr ___seterrno
|
jmp ___directerrno
|
||||||
lda #$FF
|
|
||||||
tax
|
|
||||||
rts
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
;
|
;
|
||||||
@@ -89,5 +86,3 @@ error: lda #<EBADF
|
|||||||
;
|
;
|
||||||
|
|
||||||
_fflush = return0
|
_fflush = return0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
** gets.c
|
|
||||||
**
|
|
||||||
** Ullrich von Bassewitz, 11.08.1998
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "_file.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
char* __fastcall__ gets (char* s)
|
|
||||||
{
|
|
||||||
register char* p = s;
|
|
||||||
int c;
|
|
||||||
unsigned i = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
/* Get next character */
|
|
||||||
if ((c = fgetc (stdin)) == EOF) {
|
|
||||||
/* Error or EOF */
|
|
||||||
*p = '\0';
|
|
||||||
if (stdin->f_flags & _FERROR) {
|
|
||||||
/* ERROR */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
/* EOF */
|
|
||||||
if (i) {
|
|
||||||
return s;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* One char more. Newline ends the input */
|
|
||||||
if ((char) c == '\n') {
|
|
||||||
*p = '\0';
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
*p = c;
|
|
||||||
++p;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
47
libsrc/common/gets.s
Normal file
47
libsrc/common/gets.s
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; char* __fastcall__ gets (char* s)
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _gets
|
||||||
|
.import _fgets, _stdin, popax, pushax
|
||||||
|
.importzp ptr4
|
||||||
|
|
||||||
|
_gets:
|
||||||
|
; Push buffer
|
||||||
|
sta ptr4
|
||||||
|
stx ptr4+1
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Push size (there's no limit!)
|
||||||
|
lda #$FF
|
||||||
|
tax
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
lda _stdin
|
||||||
|
ldx _stdin+1
|
||||||
|
|
||||||
|
jsr _fgets
|
||||||
|
|
||||||
|
; Check return value
|
||||||
|
bne :+
|
||||||
|
cpx #$00
|
||||||
|
bne :+
|
||||||
|
rts
|
||||||
|
|
||||||
|
: ; At least one byte written.
|
||||||
|
jsr pushax ; Store returned pointer
|
||||||
|
|
||||||
|
; Remove \n if there is one.
|
||||||
|
lda ptr4 ; _fgets returns with ptr4 at
|
||||||
|
bne :+ ; end of buffer
|
||||||
|
dec ptr4+1
|
||||||
|
: dec ptr4
|
||||||
|
lda (ptr4),y ; _fgets returns with Y=0
|
||||||
|
cmp #$0A
|
||||||
|
bne :+
|
||||||
|
tya
|
||||||
|
sta (ptr4),y ; Set terminator over \n
|
||||||
|
|
||||||
|
: jmp popax
|
||||||
@@ -131,6 +131,7 @@ _malloc:
|
|||||||
sta ptr1
|
sta ptr1
|
||||||
bcc @L1
|
bcc @L1
|
||||||
inc ptr1+1
|
inc ptr1+1
|
||||||
|
beq OutOfHeapSpace ; if high byte's 0, we overflowed!
|
||||||
@L1: ldx ptr1+1
|
@L1: ldx ptr1+1
|
||||||
bne @L2
|
bne @L2
|
||||||
cmp #HEAP_MIN_BLOCKSIZE+1
|
cmp #HEAP_MIN_BLOCKSIZE+1
|
||||||
@@ -336,4 +337,3 @@ RetUserPtr:
|
|||||||
bcc @L9
|
bcc @L9
|
||||||
inx
|
inx
|
||||||
@L9: rts
|
@L9: rts
|
||||||
|
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* mktime.c */
|
|
||||||
/* */
|
|
||||||
/* Make calendar time from broken down time and cleanup */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* (C) 2002 Ullrich von Bassewitz */
|
|
||||||
/* Wacholderweg 14 */
|
|
||||||
/* D-70597 Stuttgart */
|
|
||||||
/* EMail: uz@musoftware.de */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* 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. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "_is_leap_year.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Data */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define JANUARY 0
|
|
||||||
#define FEBRUARY 1
|
|
||||||
#define DECEMBER 11
|
|
||||||
#define JAN_1_1970 4 /* 1/1/1970 is a thursday */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const unsigned char MonthLength [] = {
|
|
||||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
||||||
};
|
|
||||||
static const unsigned MonthDays [] = {
|
|
||||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
time_t __fastcall__ mktime (register struct tm* TM)
|
|
||||||
/* Make a time in seconds since 1/1/1970 from the broken down time in TM.
|
|
||||||
** A call to mktime does also correct the time in TM to contain correct
|
|
||||||
** values.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
register div_t D;
|
|
||||||
static int Max;
|
|
||||||
static unsigned DayCount;
|
|
||||||
|
|
||||||
/* Check if TM is valid */
|
|
||||||
if (TM == 0) {
|
|
||||||
/* Invalid data */
|
|
||||||
return (time_t) -1L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adjust seconds. */
|
|
||||||
D = div (TM->tm_sec, 60);
|
|
||||||
TM->tm_sec = D.rem;
|
|
||||||
|
|
||||||
/* Adjust minutes */
|
|
||||||
TM->tm_min += D.quot;
|
|
||||||
D = div (TM->tm_min, 60);
|
|
||||||
TM->tm_min = D.rem;
|
|
||||||
|
|
||||||
/* Adjust hours */
|
|
||||||
TM->tm_hour += D.quot;
|
|
||||||
D = div (TM->tm_hour, 24);
|
|
||||||
TM->tm_hour = D.rem;
|
|
||||||
|
|
||||||
/* Adjust days */
|
|
||||||
TM->tm_mday += D.quot;
|
|
||||||
|
|
||||||
/* Adjust year */
|
|
||||||
while (1) {
|
|
||||||
Max = 365UL + IsLeapYear (TM->tm_year);
|
|
||||||
if ((unsigned int)TM->tm_mday > Max) {
|
|
||||||
++TM->tm_year;
|
|
||||||
TM->tm_mday -= Max;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adjust month and year. This is an iterative process, since changing
|
|
||||||
** the month will change the allowed days for this month.
|
|
||||||
*/
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
/* Make sure, month is in the range 0..11 */
|
|
||||||
D = div (TM->tm_mon, 12);
|
|
||||||
TM->tm_mon = D.rem;
|
|
||||||
TM->tm_year += D.quot;
|
|
||||||
|
|
||||||
/* Now check if mday is in the correct range, if not, correct month
|
|
||||||
** and eventually year and repeat the process.
|
|
||||||
*/
|
|
||||||
if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) {
|
|
||||||
Max = 29;
|
|
||||||
} else {
|
|
||||||
Max = MonthLength[TM->tm_mon];
|
|
||||||
}
|
|
||||||
if ((unsigned int)TM->tm_mday > Max) {
|
|
||||||
/* Must correct month and eventually, year */
|
|
||||||
if (TM->tm_mon == DECEMBER) {
|
|
||||||
TM->tm_mon = JANUARY;
|
|
||||||
++TM->tm_year;
|
|
||||||
} else {
|
|
||||||
++TM->tm_mon;
|
|
||||||
}
|
|
||||||
TM->tm_mday -= Max;
|
|
||||||
} else {
|
|
||||||
/* Done */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, all time/date fields are now correct. Calculate the days in this
|
|
||||||
** year.
|
|
||||||
*/
|
|
||||||
TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1;
|
|
||||||
if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) {
|
|
||||||
++TM->tm_yday;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to
|
|
||||||
** somewhere in 2106) all years dividable by 4 are leap years(1),
|
|
||||||
** so dividing by 4 gives the days that must be added because of leap years.
|
|
||||||
** (and the last leap year before 1970 was 1968)
|
|
||||||
** (1): Exception on 2100, which is not leap, and handled just after.
|
|
||||||
*/
|
|
||||||
DayCount = ((unsigned) (TM->tm_year-70)) * 365U +
|
|
||||||
(((unsigned) (TM->tm_year-(68+1))) / 4) +
|
|
||||||
TM->tm_yday;
|
|
||||||
|
|
||||||
/* Handle the 2100 exception */
|
|
||||||
if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) {
|
|
||||||
DayCount--;
|
|
||||||
} else if (TM->tm_year > 200) {
|
|
||||||
DayCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the weekday */
|
|
||||||
TM->tm_wday = (JAN_1_1970 + DayCount) % 7;
|
|
||||||
|
|
||||||
/* No (US) daylight saving (for now) */
|
|
||||||
TM->tm_isdst = 0;
|
|
||||||
|
|
||||||
/* Return seconds since 1970 */
|
|
||||||
return DayCount * 86400UL +
|
|
||||||
((unsigned) TM->tm_hour) * 3600UL +
|
|
||||||
((unsigned) TM->tm_min) * 60U +
|
|
||||||
((unsigned) TM->tm_sec) -
|
|
||||||
_tz.timezone;
|
|
||||||
}
|
|
||||||
476
libsrc/common/mktime.s
Normal file
476
libsrc/common/mktime.s
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; time_t __fastcall__ mktime (register struct tm* TM)
|
||||||
|
;
|
||||||
|
; Converts a struct tm to a time_t timestamp, making sure
|
||||||
|
; day, month, year, hour, minute and seconds are in the
|
||||||
|
; correct range.
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _mktime
|
||||||
|
.import __tz
|
||||||
|
.import pushax, pusha0, pusheax
|
||||||
|
.import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax
|
||||||
|
.importzp ptr2, tmp3, sreg
|
||||||
|
|
||||||
|
.include "time.inc"
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Special values
|
||||||
|
|
||||||
|
FEBRUARY = 1
|
||||||
|
MARCH = 2
|
||||||
|
JAN_1_1970 = 4
|
||||||
|
N_SEC = 60
|
||||||
|
N_MIN = 60
|
||||||
|
N_HOUR = 24
|
||||||
|
N_MON = 12
|
||||||
|
N_DAY_YEAR = 365
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Helpers
|
||||||
|
|
||||||
|
; Helper to shift overflows from one field to the next
|
||||||
|
; Current field in Y, divisor in A
|
||||||
|
; Keeps remainder in current field, and adds the quotient
|
||||||
|
; to the next one
|
||||||
|
adjust_field:
|
||||||
|
pha ; Push divisor
|
||||||
|
iny ; Point to high byte of current field
|
||||||
|
lda (ptr2),y
|
||||||
|
tax
|
||||||
|
dey
|
||||||
|
sty tmp3 ; Store current field (_div will mess with
|
||||||
|
lda (ptr2),y ; tmp1 and tmp2)
|
||||||
|
jsr pushax
|
||||||
|
pla ; Load divisor
|
||||||
|
ldx #$00
|
||||||
|
|
||||||
|
jsr _div
|
||||||
|
|
||||||
|
ldy tmp3 ; Store remainder in current field
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
txa
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
lda sreg ; Add quotient to next field
|
||||||
|
iny
|
||||||
|
clc
|
||||||
|
adc (ptr2),y
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
lda sreg+1
|
||||||
|
adc (ptr2),y
|
||||||
|
sta (ptr2),y
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Returns 1 in A if the given year is a leap year. Expects a year
|
||||||
|
; from 0 to 206, without 1900 added.
|
||||||
|
is_leap_year:
|
||||||
|
cmp #$00 ; Y 0 (1900) is not a leap year
|
||||||
|
beq not_leap
|
||||||
|
cmp #$C8 ; Y 200 (2100) is not a leap year
|
||||||
|
beq not_leap
|
||||||
|
and #$03 ; Year % 4 == 0 means leap year
|
||||||
|
bne not_leap
|
||||||
|
lda #$01 ; Return 1
|
||||||
|
rts
|
||||||
|
not_leap:
|
||||||
|
lda #$00 ; Return 0
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Returns the number of days in the current month/year in A
|
||||||
|
get_days_in_month:
|
||||||
|
ldy #tm::tm_mon
|
||||||
|
lda (ptr2),y
|
||||||
|
tax
|
||||||
|
lda months_len,x
|
||||||
|
cpx #FEBRUARY
|
||||||
|
beq :+
|
||||||
|
rts
|
||||||
|
: tax
|
||||||
|
ldy #tm::tm_year ; Adjust for leap years
|
||||||
|
lda (ptr2),y
|
||||||
|
jsr is_leap_year
|
||||||
|
beq :+
|
||||||
|
inx
|
||||||
|
: txa
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Add AX to counter
|
||||||
|
addaxcounter:
|
||||||
|
clc
|
||||||
|
adc Counter
|
||||||
|
sta Counter ; Store in Counter
|
||||||
|
txa
|
||||||
|
adc Counter+1
|
||||||
|
sta Counter+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Helpers for long chain of arithmetic on day counter.
|
||||||
|
; Reload Counter and push it on the stack
|
||||||
|
load_and_push_counter:
|
||||||
|
lda Counter+3
|
||||||
|
sta sreg+1
|
||||||
|
lda Counter+2
|
||||||
|
sta sreg
|
||||||
|
lda Counter
|
||||||
|
ldx Counter+1
|
||||||
|
jsr pusheax
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Store result in AX:sreg to Counter
|
||||||
|
store_counter:
|
||||||
|
sta Counter
|
||||||
|
stx Counter+1
|
||||||
|
lda sreg
|
||||||
|
sta Counter+2
|
||||||
|
lda sreg+1
|
||||||
|
sta Counter+3
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Code
|
||||||
|
|
||||||
|
_mktime:
|
||||||
|
sta ptr2 ; Store struct to ptr2, which arithmetic
|
||||||
|
stx ptr2+1 ; functions won't touch
|
||||||
|
|
||||||
|
; Check pointer validity
|
||||||
|
ora ptr2+1
|
||||||
|
bne :+
|
||||||
|
lda #$FF
|
||||||
|
tax
|
||||||
|
sta sreg
|
||||||
|
sta sreg+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Adjust seconds
|
||||||
|
: ldy #tm::tm_sec
|
||||||
|
lda #N_SEC
|
||||||
|
jsr adjust_field
|
||||||
|
|
||||||
|
; Adjust minutes
|
||||||
|
ldy #tm::tm_min
|
||||||
|
lda #N_MIN
|
||||||
|
jsr adjust_field
|
||||||
|
|
||||||
|
; Adjust hours
|
||||||
|
ldy #tm::tm_hour
|
||||||
|
lda #N_HOUR
|
||||||
|
jsr adjust_field
|
||||||
|
|
||||||
|
;Shift one year as long as tm_mday is more than a year
|
||||||
|
ldy #tm::tm_year
|
||||||
|
lda (ptr2),y
|
||||||
|
|
||||||
|
dec_by_year:
|
||||||
|
jsr is_leap_year ; Compute max numbers of days in year
|
||||||
|
clc
|
||||||
|
adc #<N_DAY_YEAR ; No care about carry,
|
||||||
|
sta Max ; 365+1 doesn't overflow low byte
|
||||||
|
|
||||||
|
ldy #tm::tm_mday+1 ; Do we have more days in store?
|
||||||
|
lda (ptr2),y
|
||||||
|
cmp #>N_DAY_YEAR
|
||||||
|
beq :+ ; High byte equal, check low byte
|
||||||
|
bcs do_year_dec ; High byte greater, decrement
|
||||||
|
bcc dec_by_month ; Low byte lower, we're done
|
||||||
|
: dey
|
||||||
|
lda (ptr2),y
|
||||||
|
cmp Max
|
||||||
|
bcc dec_by_month
|
||||||
|
beq dec_by_month
|
||||||
|
|
||||||
|
do_year_dec:
|
||||||
|
; Decrement days
|
||||||
|
ldy #tm::tm_mday
|
||||||
|
lda (ptr2),y
|
||||||
|
sbc Max ; Carry already set
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
lda (ptr2),y
|
||||||
|
sbc #>N_DAY_YEAR
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
; Increment year
|
||||||
|
ldy #tm::tm_year
|
||||||
|
lda (ptr2),y
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
sta (ptr2),y ; No carry possible here either
|
||||||
|
bcc dec_by_year ; bra, go check next year
|
||||||
|
|
||||||
|
dec_by_month:
|
||||||
|
; We're done decrementing days by full years, now do it
|
||||||
|
; month per month.
|
||||||
|
ldy #tm::tm_mon
|
||||||
|
lda #N_MON
|
||||||
|
jsr adjust_field
|
||||||
|
|
||||||
|
; Get max day for this month
|
||||||
|
jsr get_days_in_month
|
||||||
|
sta Max
|
||||||
|
|
||||||
|
; So, do we have more days than this month?
|
||||||
|
ldy #tm::tm_mday+1
|
||||||
|
lda (ptr2),y
|
||||||
|
bne do_month_dec ; High byte not zero, sure we do
|
||||||
|
dey
|
||||||
|
lda (ptr2),y
|
||||||
|
cmp Max
|
||||||
|
bcc calc_tm_yday ; No
|
||||||
|
beq calc_tm_yday
|
||||||
|
|
||||||
|
do_month_dec:
|
||||||
|
; Decrement days
|
||||||
|
ldy #tm::tm_mday
|
||||||
|
lda (ptr2),y
|
||||||
|
sec
|
||||||
|
sbc Max
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
lda (ptr2),y
|
||||||
|
sbc #$00
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
; Increment month
|
||||||
|
ldy #tm::tm_mon
|
||||||
|
lda (ptr2),y
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
bne dec_by_month ; Check next month
|
||||||
|
|
||||||
|
calc_tm_yday:
|
||||||
|
; We finished decrementing tm_mday and have put it in the correct
|
||||||
|
; year/month range. Now compute the day of the year.
|
||||||
|
ldy #tm::tm_mday ; Get current day of month
|
||||||
|
lda (ptr2),y
|
||||||
|
sta Counter ; Store it in Counter
|
||||||
|
|
||||||
|
lda #$00 ; Init counter high bytes
|
||||||
|
sta Counter+1
|
||||||
|
sta Counter+2
|
||||||
|
sta Counter+3
|
||||||
|
|
||||||
|
ldy #tm::tm_mon ; Get current month
|
||||||
|
lda (ptr2),y
|
||||||
|
asl
|
||||||
|
tax
|
||||||
|
clc
|
||||||
|
lda yday_by_month,x ; Get yday for this month's start
|
||||||
|
adc Counter ; Add it to counter
|
||||||
|
sta Counter
|
||||||
|
inx
|
||||||
|
lda yday_by_month,x
|
||||||
|
adc Counter+1
|
||||||
|
sta Counter+1
|
||||||
|
|
||||||
|
ldy #tm::tm_year ; Adjust for leap years (if after feb)
|
||||||
|
lda (ptr2),y
|
||||||
|
jsr is_leap_year
|
||||||
|
beq dec_counter
|
||||||
|
ldy #tm::tm_mon ; Leap year, get current month
|
||||||
|
lda (ptr2),y
|
||||||
|
cmp #MARCH
|
||||||
|
bcs store_yday
|
||||||
|
|
||||||
|
dec_counter:
|
||||||
|
lda Counter ; Decrease counter by one (yday starts at 0),
|
||||||
|
bne :+ ; unless we're after february in a leap year
|
||||||
|
dec Counter+1
|
||||||
|
: dec Counter
|
||||||
|
|
||||||
|
store_yday:
|
||||||
|
ldy #tm::tm_yday ; Store tm_yday
|
||||||
|
lda Counter
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
lda Counter+1
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
; Now calculate total day count since epoch with the formula:
|
||||||
|
; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970)
|
||||||
|
; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970)
|
||||||
|
; TM->tm_yday (number of days in this year)
|
||||||
|
|
||||||
|
ldy #tm::tm_year ; Get full years
|
||||||
|
lda (ptr2),y
|
||||||
|
sec
|
||||||
|
sbc #70
|
||||||
|
ldx #0
|
||||||
|
jsr pushax
|
||||||
|
lda #<N_DAY_YEAR
|
||||||
|
ldx #>N_DAY_YEAR
|
||||||
|
|
||||||
|
jsr tosumulax
|
||||||
|
jsr addaxcounter
|
||||||
|
|
||||||
|
; Add one day per leap year
|
||||||
|
ldy #tm::tm_year ; Get full years
|
||||||
|
lda (ptr2),y
|
||||||
|
sec
|
||||||
|
sbc #69
|
||||||
|
ldx #0
|
||||||
|
jsr shrax2 ; Divide by 4
|
||||||
|
|
||||||
|
jsr addaxcounter
|
||||||
|
|
||||||
|
; Handle the 2100 exception (which was considered leap by "Add one day
|
||||||
|
; per leap year" just before)
|
||||||
|
ldy #tm::tm_year ; Get full years
|
||||||
|
lda (ptr2),y
|
||||||
|
cmp #201
|
||||||
|
bcc finish_calc ; <= 200, nothing to do
|
||||||
|
|
||||||
|
lda Counter
|
||||||
|
bne :+
|
||||||
|
dec Counter+1
|
||||||
|
: dec Counter
|
||||||
|
|
||||||
|
finish_calc:
|
||||||
|
; Now we can compute the weekday.
|
||||||
|
lda Counter
|
||||||
|
clc
|
||||||
|
adc #JAN_1_1970
|
||||||
|
pha
|
||||||
|
lda Counter+1
|
||||||
|
adc #0
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
lda #7 ; Modulo 7
|
||||||
|
ldx #0
|
||||||
|
jsr tosumodax
|
||||||
|
|
||||||
|
ldy #tm::tm_wday ; Store tm_wday
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
txa
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
; DST
|
||||||
|
lda #$00 ; Store tm_isdst
|
||||||
|
ldy #tm::tm_isdst
|
||||||
|
sta (ptr2),y
|
||||||
|
iny
|
||||||
|
sta (ptr2),y
|
||||||
|
|
||||||
|
; Our struct tm is all fixed and every field calculated.
|
||||||
|
; We can finally count seconds according to this formula:
|
||||||
|
; seconds = (full days since epoch) * 86400UL +
|
||||||
|
; ((unsigned) TM->tm_hour) * 3600UL +
|
||||||
|
; ((unsigned) TM->tm_min) * 60U +
|
||||||
|
; ((unsigned) TM->tm_sec) -
|
||||||
|
; _tz.timezone;
|
||||||
|
|
||||||
|
; We already have the number of days since epoch in our counter,
|
||||||
|
; from just before when we computed tm_wday. Reuse it.
|
||||||
|
jsr load_and_push_counter
|
||||||
|
lda #$00 ; Multiply by 86400
|
||||||
|
sta sreg+1
|
||||||
|
lda #$01
|
||||||
|
sta sreg
|
||||||
|
lda #$80
|
||||||
|
ldx #$51
|
||||||
|
jsr tosumuleax
|
||||||
|
jsr store_counter ; Store into counter
|
||||||
|
|
||||||
|
; Push counter to add 3600 * hours to it
|
||||||
|
jsr load_and_push_counter
|
||||||
|
|
||||||
|
ldx #$00 ; Load hours
|
||||||
|
stx sreg
|
||||||
|
stx sreg+1
|
||||||
|
ldy #tm::tm_hour
|
||||||
|
lda (ptr2),y
|
||||||
|
jsr pusheax ; Push
|
||||||
|
ldx #$00 ; Load 3600
|
||||||
|
stx sreg
|
||||||
|
stx sreg+1
|
||||||
|
lda #<3600
|
||||||
|
ldx #>3600
|
||||||
|
jsr tosumuleax ; Multiply (pops the pushed hours)
|
||||||
|
jsr tosaddeax ; Add to counter (pops the pushed counter)
|
||||||
|
jsr store_counter ; Store counter
|
||||||
|
|
||||||
|
; Push counter to add 60 * min to it
|
||||||
|
jsr load_and_push_counter
|
||||||
|
|
||||||
|
ldy #tm::tm_min ; Load minutes
|
||||||
|
lda (ptr2),y
|
||||||
|
jsr pusha0 ; Push
|
||||||
|
lda #N_MIN
|
||||||
|
ldx #0
|
||||||
|
stx sreg
|
||||||
|
stx sreg+1
|
||||||
|
jsr tosumulax ; Multiply
|
||||||
|
jsr tosaddeax ; Add to pushed counter
|
||||||
|
jsr store_counter ; Store
|
||||||
|
|
||||||
|
; Add seconds
|
||||||
|
jsr load_and_push_counter
|
||||||
|
|
||||||
|
ldy #tm::tm_sec ; Load seconds
|
||||||
|
lda (ptr2),y
|
||||||
|
ldx #0
|
||||||
|
stx sreg
|
||||||
|
stx sreg+1
|
||||||
|
jsr tosaddeax ; Simple addition there
|
||||||
|
|
||||||
|
; No need to store/load/push the counter here, simply to push it
|
||||||
|
; for the last substraction
|
||||||
|
jsr pusheax
|
||||||
|
|
||||||
|
; Substract timezone
|
||||||
|
lda __tz+1+3
|
||||||
|
sta sreg+1
|
||||||
|
lda __tz+1+2
|
||||||
|
sta sreg
|
||||||
|
ldx __tz+1+1
|
||||||
|
lda __tz+1
|
||||||
|
jsr tossubeax
|
||||||
|
|
||||||
|
; And we're done!
|
||||||
|
rts
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
months_len:
|
||||||
|
.byte 31
|
||||||
|
.byte 28
|
||||||
|
.byte 31
|
||||||
|
.byte 30
|
||||||
|
.byte 31
|
||||||
|
.byte 30
|
||||||
|
.byte 31
|
||||||
|
.byte 31
|
||||||
|
.byte 30
|
||||||
|
.byte 31
|
||||||
|
.byte 30
|
||||||
|
.byte 31
|
||||||
|
|
||||||
|
yday_by_month:
|
||||||
|
.word 0
|
||||||
|
.word 31
|
||||||
|
.word 59
|
||||||
|
.word 90
|
||||||
|
.word 120
|
||||||
|
.word 151
|
||||||
|
.word 181
|
||||||
|
.word 212
|
||||||
|
.word 243
|
||||||
|
.word 273
|
||||||
|
.word 304
|
||||||
|
.word 334
|
||||||
|
|
||||||
|
|
||||||
|
.bss
|
||||||
|
|
||||||
|
Max: .res 1 ; We won't need a high byte
|
||||||
|
Counter:
|
||||||
|
.res 4
|
||||||
@@ -50,7 +50,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
|
int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
|
||||||
/* Allocate a block of memory with the given "size", which is aligned to a
|
/* Allocate a block of memory with the given "size", which is aligned to a
|
||||||
** memory address that is a multiple of "alignment". "alignment" MUST NOT be
|
** memory address that is a multiple of "alignment". "alignment" MUST NOT be
|
||||||
@@ -64,20 +63,27 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
|
|||||||
size_t rawsize;
|
size_t rawsize;
|
||||||
size_t uppersize;
|
size_t uppersize;
|
||||||
size_t lowersize;
|
size_t lowersize;
|
||||||
|
char err;
|
||||||
register struct usedblock* b; /* points to raw Block */
|
register struct usedblock* b; /* points to raw Block */
|
||||||
register struct usedblock* u; /* points to User block */
|
register struct usedblock* u; /* points to User block */
|
||||||
register struct usedblock* p; /* Points to upper block */
|
register struct usedblock* p; /* Points to upper block */
|
||||||
|
|
||||||
/* Handle requests for zero-sized blocks */
|
/* Handle requests for zero-sized blocks */
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
err_einval:
|
||||||
|
err = EINVAL;
|
||||||
|
err_out:
|
||||||
*memptr = NULL;
|
*memptr = NULL;
|
||||||
return EINVAL;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test alignment: is it a power of two? There must be only one bit set. */
|
/* Test alignment: is it a power of two? There must be one and only one bit set. */
|
||||||
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
|
if (alignment == 0) {
|
||||||
*memptr = NULL;
|
goto err_einval;
|
||||||
return EINVAL;
|
}
|
||||||
|
|
||||||
|
if (alignment & (alignment - 1)) {
|
||||||
|
goto err_einval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Augment the block size up to the alignment, and allocate memory.
|
/* Augment the block size up to the alignment, and allocate memory.
|
||||||
@@ -90,8 +96,8 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
|
|||||||
|
|
||||||
/* Handle out-of-memory */
|
/* Handle out-of-memory */
|
||||||
if (b == NULL) {
|
if (b == NULL) {
|
||||||
*memptr = NULL;
|
err = ENOMEM;
|
||||||
return ENOMEM;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create (and return) a new pointer that points to the user-visible
|
/* Create (and return) a new pointer that points to the user-visible
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
.import _malloc, _free
|
.import _malloc, _free
|
||||||
.import searchenv, copyenvptr
|
.import searchenv, copyenvptr
|
||||||
.import __environ, __envcount, __envsize
|
.import __environ, __envcount, __envsize
|
||||||
.import return0
|
.import return0, ___directerrno
|
||||||
.import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp
|
.import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp
|
||||||
|
|
||||||
.include "errno.inc"
|
.include "errno.inc"
|
||||||
@@ -169,10 +169,7 @@ addentry:
|
|||||||
; Error entries
|
; Error entries
|
||||||
|
|
||||||
nomem: lda #ENOMEM
|
nomem: lda #ENOMEM
|
||||||
error: jsr ___seterrno
|
error: jmp ___directerrno
|
||||||
lda #$FF ; Return -1
|
|
||||||
tax
|
|
||||||
rts
|
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
@@ -184,5 +181,3 @@ error: jsr ___seterrno
|
|||||||
|
|
||||||
name: .addr 0 ; Pointer to name
|
name: .addr 0 ; Pointer to name
|
||||||
newsize: .byte 0 ; New environment size
|
newsize: .byte 0 ; New environment size
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* realloc.c */
|
|
||||||
/* */
|
|
||||||
/* Change the size of an allocated memory block */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* (C) 1998-2004 Ullrich von Bassewitz */
|
|
||||||
/* Wacholderweg 14 */
|
|
||||||
/* D-70597 Stuttgart */
|
|
||||||
/* EMail: uz@musoftware.de */
|
|
||||||
/* */
|
|
||||||
/* */
|
|
||||||
/* 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. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <_heap.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void* __fastcall__ realloc (void* block, register size_t size)
|
|
||||||
{
|
|
||||||
register struct usedblock* b;
|
|
||||||
struct usedblock* newblock;
|
|
||||||
unsigned oldsize;
|
|
||||||
unsigned newhptr;
|
|
||||||
|
|
||||||
/* Check the block parameter */
|
|
||||||
if (!block) {
|
|
||||||
/* Block is NULL, same as malloc */
|
|
||||||
return malloc (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the size parameter */
|
|
||||||
if (size == 0) {
|
|
||||||
/* Block is not NULL, but size is: free the block */
|
|
||||||
free (block);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make the internal used size from the given size */
|
|
||||||
size += HEAP_ADMIN_SPACE;
|
|
||||||
if (size < sizeof (struct freeblock)) {
|
|
||||||
size = sizeof (struct freeblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The word below the user block contains a pointer to the start of the
|
|
||||||
** raw memory block. The first word of this raw memory block is the full
|
|
||||||
** size of the block. Get a pointer to the real block, get the old block
|
|
||||||
** size.
|
|
||||||
*/
|
|
||||||
b = (((struct usedblock*) block) - 1)->start;
|
|
||||||
oldsize = b->size;
|
|
||||||
|
|
||||||
/* Is the block at the current heap top? */
|
|
||||||
if (((unsigned) b) + oldsize == ((unsigned) __heapptr)) {
|
|
||||||
/* Check if we've enough memory at the heap top */
|
|
||||||
newhptr = ((unsigned) __heapptr) - oldsize + size;
|
|
||||||
if (newhptr <= ((unsigned) __heapend)) {
|
|
||||||
/* Ok, there's space enough */
|
|
||||||
__heapptr = (unsigned*) newhptr;
|
|
||||||
b->size = size;
|
|
||||||
b->start = b;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The given block was not located on top of the heap, or there's no
|
|
||||||
** room left. Try to allocate a new block and copy the data.
|
|
||||||
*/
|
|
||||||
if (newblock = malloc (size)) {
|
|
||||||
|
|
||||||
/* Adjust the old size to the user visible portion */
|
|
||||||
oldsize -= HEAP_ADMIN_SPACE;
|
|
||||||
|
|
||||||
/* If the new block is larger than the old one, copy the old
|
|
||||||
** data only
|
|
||||||
*/
|
|
||||||
if (size > oldsize) {
|
|
||||||
size = oldsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the block data */
|
|
||||||
memcpy (newblock, block, size);
|
|
||||||
free (block);
|
|
||||||
}
|
|
||||||
return newblock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
213
libsrc/common/realloc.s
Normal file
213
libsrc/common/realloc.s
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; void* __fastcall__ realloc (void* block, register size_t size)
|
||||||
|
;
|
||||||
|
|
||||||
|
.importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp
|
||||||
|
.import _malloc, _memcpy, _free
|
||||||
|
.import pushax, popptr1, return0
|
||||||
|
.import incsp2, decsp2
|
||||||
|
.export _realloc
|
||||||
|
|
||||||
|
.include "_heap.inc"
|
||||||
|
|
||||||
|
.macpack generic
|
||||||
|
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; Aliases for clarity
|
||||||
|
|
||||||
|
block = ptr1
|
||||||
|
size = ptr2
|
||||||
|
ublock = ptr3
|
||||||
|
oldsize = ptr4
|
||||||
|
newblock = tmp1 ; (and tmp2)
|
||||||
|
orgblock = tmp3 ; (and tmp4)
|
||||||
|
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; Code
|
||||||
|
|
||||||
|
_realloc:
|
||||||
|
sta size ; Store size
|
||||||
|
stx size+1
|
||||||
|
|
||||||
|
jsr popptr1 ; Pop block
|
||||||
|
|
||||||
|
lda block+1 ; Is block null?
|
||||||
|
tax
|
||||||
|
ora block
|
||||||
|
bne :+
|
||||||
|
|
||||||
|
lda size ; Block is null, just malloc
|
||||||
|
ldx size+1
|
||||||
|
jmp _malloc
|
||||||
|
|
||||||
|
: lda size ; Is size 0?
|
||||||
|
ora size+1
|
||||||
|
bne :+
|
||||||
|
|
||||||
|
lda block ; It is: free block (high byte already in X)
|
||||||
|
jsr _free
|
||||||
|
jmp return0
|
||||||
|
|
||||||
|
: clc ; Add internal used size
|
||||||
|
lda size
|
||||||
|
adc #HEAP_ADMIN_SPACE
|
||||||
|
sta size
|
||||||
|
bcc :+
|
||||||
|
inc size+1
|
||||||
|
bne :+
|
||||||
|
|
||||||
|
lda #$00 ; Size high byte now 0: We overflowed!
|
||||||
|
tax
|
||||||
|
rts
|
||||||
|
|
||||||
|
: ldx size+1 ; Should we round size up?
|
||||||
|
bne :+
|
||||||
|
cmp #.sizeof (freeblock)
|
||||||
|
bcs :+
|
||||||
|
|
||||||
|
lda #.sizeof (freeblock)
|
||||||
|
sta size ; (we presuppose that sizeof (freeblock) is < 256)
|
||||||
|
|
||||||
|
: lda block ; Get pointer to raw memory block
|
||||||
|
sta orgblock ; Store original pointer
|
||||||
|
sec
|
||||||
|
sbc #.sizeof(usedblock)
|
||||||
|
sta ublock
|
||||||
|
lda block+1
|
||||||
|
sta orgblock+1 ; Finish storing original pointer
|
||||||
|
sbc #0
|
||||||
|
sta ublock+1 ; We have our usedblock struct
|
||||||
|
|
||||||
|
; Get block start
|
||||||
|
ldy #usedblock::start+1
|
||||||
|
lda (ublock),y
|
||||||
|
tax ; Backup ublock high
|
||||||
|
dey
|
||||||
|
lda (ublock),y
|
||||||
|
|
||||||
|
sta ublock ; Store ublock
|
||||||
|
stx ublock+1
|
||||||
|
|
||||||
|
; Remember oldsize
|
||||||
|
ldy #usedblock::size+1
|
||||||
|
lda (ublock),y
|
||||||
|
sta oldsize+1
|
||||||
|
dey
|
||||||
|
lda (ublock),y
|
||||||
|
sta oldsize
|
||||||
|
|
||||||
|
clc ; Is the block at heap top?
|
||||||
|
adc ublock
|
||||||
|
tay
|
||||||
|
lda ublock+1
|
||||||
|
adc oldsize+1
|
||||||
|
cmp ___heapptr+1
|
||||||
|
bne must_malloc_new
|
||||||
|
cpy ___heapptr
|
||||||
|
bne must_malloc_new
|
||||||
|
|
||||||
|
tya ; Put ___heapptr back in A
|
||||||
|
sec ; Check if we have enough memory at heap top
|
||||||
|
sbc oldsize ; Substract oldsize
|
||||||
|
sta newblock
|
||||||
|
lda ___heapptr+1
|
||||||
|
sbc oldsize+1
|
||||||
|
sta newblock+1
|
||||||
|
clc
|
||||||
|
lda newblock ; And add size
|
||||||
|
adc size
|
||||||
|
sta newblock
|
||||||
|
lda newblock+1
|
||||||
|
adc size+1
|
||||||
|
sta newblock+1
|
||||||
|
bcs must_malloc_new ; If we have a carry there we overflowed
|
||||||
|
|
||||||
|
cmp ___heapend+1
|
||||||
|
bne :+
|
||||||
|
lda newblock
|
||||||
|
cmp ___heapend
|
||||||
|
: bcc :+
|
||||||
|
bne must_malloc_new
|
||||||
|
|
||||||
|
: lda newblock ; There is enough space
|
||||||
|
sta ___heapptr ; Update heapptr
|
||||||
|
lda newblock+1
|
||||||
|
sta ___heapptr+1
|
||||||
|
|
||||||
|
ldy #usedblock::start+1
|
||||||
|
lda ublock+1
|
||||||
|
sta (ublock),y ; Update block start
|
||||||
|
dey
|
||||||
|
lda ublock
|
||||||
|
sta (ublock),y
|
||||||
|
dey
|
||||||
|
|
||||||
|
.assert usedblock::size = usedblock::start-2, error
|
||||||
|
lda size+1
|
||||||
|
sta (ublock),y ; Update block size
|
||||||
|
dey
|
||||||
|
lda size
|
||||||
|
sta (ublock),y
|
||||||
|
|
||||||
|
lda orgblock ; Return original block
|
||||||
|
ldx orgblock+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
must_malloc_new: ; The block is not at heap top, or too big
|
||||||
|
lda size+1
|
||||||
|
pha ; Backup new size (at this point the only ptr
|
||||||
|
tax ; we'll need after malloc). tmp* are safe
|
||||||
|
lda size ; from malloc, memcpy and free.
|
||||||
|
pha
|
||||||
|
jsr _malloc
|
||||||
|
|
||||||
|
cmp #$00 ; Did malloc succeed?
|
||||||
|
bne :+
|
||||||
|
cpx #$00
|
||||||
|
bne :+
|
||||||
|
pla ; Pop size backup and return NULL
|
||||||
|
pla
|
||||||
|
txa ; X already 0
|
||||||
|
rts ; No
|
||||||
|
|
||||||
|
: sta newblock ; Yes, store newblock
|
||||||
|
stx newblock+1
|
||||||
|
jsr pushax ; Push newblock for memcpy
|
||||||
|
|
||||||
|
lda orgblock ; Push orgblock for memcpy
|
||||||
|
ldx orgblock+1
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
sec ; Remove admin space from oldsize
|
||||||
|
lda oldsize
|
||||||
|
sbc #<HEAP_ADMIN_SPACE
|
||||||
|
sta oldsize
|
||||||
|
lda oldsize+1
|
||||||
|
sbc #>HEAP_ADMIN_SPACE
|
||||||
|
sta oldsize+1
|
||||||
|
|
||||||
|
pla ; Restore new size to AX
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
tya
|
||||||
|
|
||||||
|
cmp oldsize ; Find the smallest size
|
||||||
|
bcc :+
|
||||||
|
cpx oldsize+1
|
||||||
|
bcc :+
|
||||||
|
|
||||||
|
lda oldsize
|
||||||
|
ldx oldsize+1
|
||||||
|
|
||||||
|
: jsr _memcpy ; And copy data
|
||||||
|
|
||||||
|
lda orgblock ; Free old block
|
||||||
|
ldx orgblock+1
|
||||||
|
jsr _free
|
||||||
|
|
||||||
|
lda newblock ; Return new block
|
||||||
|
ldx newblock+1
|
||||||
|
rts
|
||||||
@@ -62,10 +62,6 @@
|
|||||||
; File is not open or the character is invalid
|
; File is not open or the character is invalid
|
||||||
|
|
||||||
error: lda #EINVAL
|
error: lda #EINVAL
|
||||||
jsr ___seterrno
|
jmp ___directerrno
|
||||||
lda #$FF ; Return -1
|
|
||||||
tax
|
|
||||||
rts
|
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy,
|
; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy,
|
||||||
; struct iconpic *myGfx);
|
; struct iconpic *myGfx);
|
||||||
|
|
||||||
; both proc1, proc2 should be: char __fastcall something (void);
|
; both proc1, proc2 should be: char foo (void);
|
||||||
; proc1 is called before reading a byte (.A returns next data)
|
; proc1 is called before reading a byte (.A returns next data)
|
||||||
; proc2 is called before reading each byte which is not pattern (code >219)
|
; proc2 is called before reading each byte which is not pattern (code >219)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
.import ldeaxysp, decsp2, pushax, incsp8
|
.import ldeaxysp, decsp2, pushax, incsp8
|
||||||
.import tosandeax,decax1,tosdiveax,axlong,ldaxysp
|
.import tosandeax,decax1,tosdiveax,axlong,ldaxysp
|
||||||
.import lynxskip0, lynxblock,tosasreax
|
.import lynxskip0, lynxblock,tosasreax
|
||||||
.import __BLOCKSIZE__
|
.import __BANK0BLOCKSIZE__
|
||||||
.importzp _FileCurrBlock
|
.importzp _FileCurrBlock
|
||||||
|
|
||||||
.segment "CODE"
|
.segment "CODE"
|
||||||
@@ -32,15 +32,15 @@
|
|||||||
jsr ldeaxysp
|
jsr ldeaxysp
|
||||||
jsr pusheax
|
jsr pusheax
|
||||||
ldx #$00
|
ldx #$00
|
||||||
lda #<(__BLOCKSIZE__/1024 + 9)
|
lda #<(__BANK0BLOCKSIZE__/1024 + 9)
|
||||||
jsr tosasreax
|
jsr tosasreax
|
||||||
sta _FileCurrBlock
|
sta _FileCurrBlock
|
||||||
jsr lynxblock
|
jsr lynxblock
|
||||||
ldy #$05
|
ldy #$05
|
||||||
jsr ldeaxysp
|
jsr ldeaxysp
|
||||||
jsr pusheax
|
jsr pusheax
|
||||||
lda #<(__BLOCKSIZE__-1)
|
lda #<(__BANK0BLOCKSIZE__-1)
|
||||||
ldx #>(__BLOCKSIZE__-1)
|
ldx #>(__BANK0BLOCKSIZE__-1)
|
||||||
jsr axlong
|
jsr axlong
|
||||||
jsr tosandeax
|
jsr tosandeax
|
||||||
eor #$FF
|
eor #$FF
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
.include "extzp.inc"
|
.include "extzp.inc"
|
||||||
.export lynxskip0, lynxread0
|
.export lynxskip0, lynxread0
|
||||||
.export lynxblock
|
.export lynxblock
|
||||||
.import __BLOCKSIZE__
|
.import __BANK0BLOCKSIZE__
|
||||||
|
|
||||||
.code
|
.code
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ lynxblock:
|
|||||||
lda __iodat
|
lda __iodat
|
||||||
sta IODAT
|
sta IODAT
|
||||||
stz _FileBlockByte
|
stz _FileBlockByte
|
||||||
lda #<($100-(>__BLOCKSIZE__))
|
lda #<($100-(>__BANK0BLOCKSIZE__))
|
||||||
sta _FileBlockByte+1
|
sta _FileBlockByte+1
|
||||||
ply
|
ply
|
||||||
plx
|
plx
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ loop1:
|
|||||||
cont1:
|
cont1:
|
||||||
jsr read_byte
|
jsr read_byte
|
||||||
sta (load_ptr2),y
|
sta (load_ptr2),y
|
||||||
sta PALETTE ; feedback ;-)
|
sta PALETTE + 1 ; feedback ;-)
|
||||||
iny
|
iny
|
||||||
bne loop1
|
bne loop1
|
||||||
inc load_ptr2+1
|
inc load_ptr2+1
|
||||||
@@ -69,6 +69,8 @@ again:
|
|||||||
; last action : clear interrupt
|
; last action : clear interrupt
|
||||||
;
|
;
|
||||||
exit:
|
exit:
|
||||||
|
lda #$10
|
||||||
|
sta INTRST
|
||||||
clc
|
clc
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,18 @@
|
|||||||
.import incsp2
|
.import incsp2
|
||||||
.importzp sp, ptr1
|
.importzp sp, ptr1
|
||||||
|
|
||||||
|
.macpack cpu
|
||||||
|
|
||||||
.proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved)
|
.proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved)
|
||||||
ldy #1
|
ldy #1
|
||||||
lda (sp),y ; get hi byte
|
lda (sp),y ; get hi byte
|
||||||
sta ptr1+1 ; into ptr hi
|
sta ptr1+1 ; into ptr hi
|
||||||
dey ; no optimization for 65C02 here to have Y=0 at exit!
|
dey ; dey even for for 65C02 here to have Y=0 at exit!
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
lda (sp) ; get lo byte
|
||||||
|
.else
|
||||||
lda (sp),y ; get lo byte
|
lda (sp),y ; get lo byte
|
||||||
|
.endif
|
||||||
sta ptr1 ; to ptr lo
|
sta ptr1 ; to ptr lo
|
||||||
jmp incsp2
|
jmp incsp2
|
||||||
.endproc
|
.endproc
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
.export push0, pusha0, pushax
|
.export push0, pusha0, pushax
|
||||||
.importzp sp
|
.importzp sp
|
||||||
|
|
||||||
|
.macpack cpu
|
||||||
|
|
||||||
push0: lda #0
|
push0: lda #0
|
||||||
pusha0: ldx #0
|
pusha0: ldx #0
|
||||||
|
|
||||||
@@ -29,7 +31,11 @@ pusha0: ldx #0
|
|||||||
sta (sp),y ; (27)
|
sta (sp),y ; (27)
|
||||||
pla ; (31)
|
pla ; (31)
|
||||||
dey ; (33)
|
dey ; (33)
|
||||||
|
.if (.cpu .bitand ::CPU_ISET_65SC02)
|
||||||
|
sta (sp) ; (37)
|
||||||
|
.else
|
||||||
sta (sp),y ; (38)
|
sta (sp),y ; (38)
|
||||||
rts ; (44)
|
.endif
|
||||||
|
rts ; (44/43)
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|||||||
14
libsrc/runtime/pushptr1.s
Normal file
14
libsrc/runtime/pushptr1.s
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
;
|
||||||
|
; Colin Leroy-Mira, 2024
|
||||||
|
;
|
||||||
|
; CC65 runtime: Push ptr1 to stack.
|
||||||
|
; A/X destroyed (set to ptr1)
|
||||||
|
|
||||||
|
.export pushptr1
|
||||||
|
.import pushax
|
||||||
|
.importzp ptr1
|
||||||
|
|
||||||
|
pushptr1:
|
||||||
|
lda ptr1
|
||||||
|
ldx ptr1+1
|
||||||
|
jmp pushax
|
||||||
15
libsrc/runtime/returnFFFF.s
Normal file
15
libsrc/runtime/returnFFFF.s
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
;
|
||||||
|
; Ullrich von Bassewitz, 25.10.2000
|
||||||
|
;
|
||||||
|
; CC65 runtime: Return -1 in a/x
|
||||||
|
;
|
||||||
|
|
||||||
|
.export returnFFFF
|
||||||
|
|
||||||
|
.proc returnFFFF
|
||||||
|
|
||||||
|
lda #$FF
|
||||||
|
tax
|
||||||
|
rts
|
||||||
|
|
||||||
|
.endproc
|
||||||
@@ -8,10 +8,15 @@
|
|||||||
;
|
;
|
||||||
|
|
||||||
.export exit, args, _open, _close, _read, _write
|
.export exit, args, _open, _close, _read, _write
|
||||||
|
.export __sysremove, ___osmaperrno
|
||||||
|
|
||||||
|
__sysremove := $FFF2
|
||||||
|
___osmaperrno := $FFF3
|
||||||
_open := $FFF4
|
_open := $FFF4
|
||||||
_close := $FFF5
|
_close := $FFF5
|
||||||
_read := $FFF6
|
_read := $FFF6
|
||||||
_write := $FFF7
|
_write := $FFF7
|
||||||
args := $FFF8
|
args := $FFF8
|
||||||
exit := $FFF9
|
exit := $FFF9
|
||||||
|
|
||||||
|
; $FFFA-FFFF are hardware vectors, extend before not after!
|
||||||
|
|||||||
@@ -1471,7 +1471,7 @@ void CS_Output (CodeSeg* S)
|
|||||||
/* Add line debug info */
|
/* Add line debug info */
|
||||||
if (DebugInfo) {
|
if (DebugInfo) {
|
||||||
WriteOutput ("\t.dbg\tline, \"%s\", %u\n",
|
WriteOutput ("\t.dbg\tline, \"%s\", %u\n",
|
||||||
GetInputName (LI), GetInputLine (LI));
|
GetActualFileName (LI), GetActualLineNum (LI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Output the code */
|
/* Output the code */
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ static void Parse (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Read the declaration specifier */
|
/* Read the declaration specifier */
|
||||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE);
|
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE);
|
||||||
|
|
||||||
/* Don't accept illegal storage classes */
|
/* Don't accept illegal storage classes */
|
||||||
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO ||
|
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO ||
|
||||||
@@ -163,19 +163,19 @@ static void Parse (void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we must reserve storage for the variable. We do this,
|
/* The symbol is now visible in the file scope */
|
||||||
**
|
|
||||||
** - if it is not a typedef or function,
|
|
||||||
** - if we don't had a storage class given ("int i")
|
|
||||||
** - if the storage class is explicitly specified as static,
|
|
||||||
** - or if there is an initialization.
|
|
||||||
**
|
|
||||||
** This means that "extern int i;" will not get storage allocated
|
|
||||||
** in this translation unit.
|
|
||||||
*/
|
|
||||||
if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC &&
|
if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC &&
|
||||||
(Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
(Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
||||||
/* The variable is visible in the file scope */
|
/* Check if we must reserve storage for the variable. We do this,
|
||||||
|
**
|
||||||
|
** - if it is not a typedef or function,
|
||||||
|
** - if we don't had a storage class given ("int i")
|
||||||
|
** - if the storage class is explicitly specified as static,
|
||||||
|
** - or if there is an initialization.
|
||||||
|
**
|
||||||
|
** This means that "extern int i;" will not get storage allocated
|
||||||
|
** in this translation unit.
|
||||||
|
*/
|
||||||
if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE ||
|
if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE ||
|
||||||
(Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC ||
|
(Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC ||
|
||||||
((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN &&
|
((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN &&
|
||||||
@@ -189,7 +189,6 @@ static void Parse (void)
|
|||||||
** or semicolon, it must be followed by a function body.
|
** or semicolon, it must be followed by a function body.
|
||||||
*/
|
*/
|
||||||
if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) {
|
if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) {
|
||||||
/* The function is now visible in the file scope */
|
|
||||||
if (CurTok.Tok == TOK_LCURLY) {
|
if (CurTok.Tok == TOK_LCURLY) {
|
||||||
/* A definition */
|
/* A definition */
|
||||||
Decl.StorageClass |= SC_DEF;
|
Decl.StorageClass |= SC_DEF;
|
||||||
@@ -560,6 +559,10 @@ void Compile (const char* FileName)
|
|||||||
if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
|
if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
|
||||||
Warning ("Static function '%s' used but never defined",
|
Warning ("Static function '%s' used but never defined",
|
||||||
Entry->Name);
|
Entry->Name);
|
||||||
|
} else if ((Entry->Flags & SC_INLINE) != 0) {
|
||||||
|
Warning ("Inline function '%s' %s but never defined",
|
||||||
|
Entry->Name,
|
||||||
|
SymIsRef (Entry) ? "used" : "declared");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ unsigned OptLongAssign (CodeSeg* S)
|
|||||||
L[0] = CS_GetEntry (S, I);
|
L[0] = CS_GetEntry (S, I);
|
||||||
|
|
||||||
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
||||||
|
CodeEntry* N;
|
||||||
if (/* Check the opcode sequence */
|
if (/* Check the opcode sequence */
|
||||||
L[0]->OPC == OP65_LDA &&
|
L[0]->OPC == OP65_LDA &&
|
||||||
L[1]->OPC == OP65_STA &&
|
L[1]->OPC == OP65_STA &&
|
||||||
@@ -119,11 +120,13 @@ unsigned OptLongAssign (CodeSeg* S)
|
|||||||
!RegXUsed (S, I+12) &&
|
!RegXUsed (S, I+12) &&
|
||||||
!CS_RangeHasLabel(S, I, 12)) {
|
!CS_RangeHasLabel(S, I, 12)) {
|
||||||
|
|
||||||
L[1]->AM = L[11]->AM;
|
N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI);
|
||||||
CE_SetArg(L[1], L[11]->Arg);
|
CS_DelEntry (S, I+1);
|
||||||
|
CS_InsertEntry (S, N, I+1);
|
||||||
|
|
||||||
L[3]->AM = L[9]->AM;
|
N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
|
||||||
CE_SetArg(L[3], L[9]->Arg);
|
CS_DelEntry (S, I+3);
|
||||||
|
CS_InsertEntry (S, N, I+3);
|
||||||
|
|
||||||
CS_DelEntries (S, I+8, 4);
|
CS_DelEntries (S, I+8, 4);
|
||||||
|
|
||||||
@@ -179,6 +182,7 @@ unsigned OptLongCopy (CodeSeg* S)
|
|||||||
L[0] = CS_GetEntry (S, I);
|
L[0] = CS_GetEntry (S, I);
|
||||||
|
|
||||||
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
if (CS_GetEntries (S, L+1, I+1, 12)) {
|
||||||
|
CodeEntry *N;
|
||||||
if (L[0]->OPC == OP65_LDA &&
|
if (L[0]->OPC == OP65_LDA &&
|
||||||
!strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) &&
|
!strncmp(L[0]->Arg, L[5]->Arg, strlen(L[5]->Arg)) &&
|
||||||
!strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") &&
|
!strcmp(L[0]->Arg + strlen(L[5]->Arg), "+3") &&
|
||||||
@@ -210,11 +214,13 @@ unsigned OptLongCopy (CodeSeg* S)
|
|||||||
!RegXUsed (S, I+11) &&
|
!RegXUsed (S, I+11) &&
|
||||||
!CS_RangeHasLabel(S, I, 12)) {
|
!CS_RangeHasLabel(S, I, 12)) {
|
||||||
|
|
||||||
L[1]->AM = L[11]->AM;
|
N = NewCodeEntry (OP65_STA, L[11]->AM, L[11]->Arg, 0, L[11]->LI);
|
||||||
CE_SetArg(L[1], L[11]->Arg);
|
CS_DelEntry (S, I+1);
|
||||||
|
CS_InsertEntry (S, N, I+1);
|
||||||
|
|
||||||
L[3]->AM = L[9]->AM;
|
N = NewCodeEntry (OP65_STA, L[9]->AM, L[9]->Arg, 0, L[9]->LI);
|
||||||
CE_SetArg(L[3], L[9]->Arg);
|
CS_DelEntry (S, I+3);
|
||||||
|
CS_InsertEntry (S, N, I+3);
|
||||||
|
|
||||||
CS_DelEntries (S, I+8, 4);
|
CS_DelEntries (S, I+8, 4);
|
||||||
|
|
||||||
|
|||||||
@@ -989,6 +989,24 @@ int IsIncompleteESUType (const Type* T)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int IsAnonESUType (const Type* T)
|
||||||
|
/* Return true if this is an anonymous ESU type */
|
||||||
|
{
|
||||||
|
SymEntry* TagSym = GetESUTagSym (T);
|
||||||
|
|
||||||
|
return TagSym != 0 && SymHasAnonName (TagSym);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int IsAnonStructClass (const Type* T)
|
||||||
|
/* Return true if this is an anonymous struct or union type */
|
||||||
|
{
|
||||||
|
return IsClassStruct (T) && IsAnonESUType (T);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int IsPassByRefType (const Type* T)
|
int IsPassByRefType (const Type* T)
|
||||||
/* Return true if this is a large struct/union type that doesn't fit in the
|
/* Return true if this is a large struct/union type that doesn't fit in the
|
||||||
** primary. This returns false for the void value extension type since it is
|
** primary. This returns false for the void value extension type since it is
|
||||||
|
|||||||
@@ -792,6 +792,12 @@ int IsESUType (const Type* T);
|
|||||||
int IsIncompleteESUType (const Type* T);
|
int IsIncompleteESUType (const Type* T);
|
||||||
/* Return true if this is an incomplete ESU type */
|
/* Return true if this is an incomplete ESU type */
|
||||||
|
|
||||||
|
int IsAnonESUType (const Type* T);
|
||||||
|
/* Return true if this is an anonymous ESU type */
|
||||||
|
|
||||||
|
int IsAnonStructClass (const Type* T);
|
||||||
|
/* Return true if this is an anonymous struct or union type */
|
||||||
|
|
||||||
int IsPassByRefType (const Type* T);
|
int IsPassByRefType (const Type* T);
|
||||||
/* Return true if this is a large struct/union type that doesn't fit in the
|
/* Return true if this is a large struct/union type that doesn't fit in the
|
||||||
** primary. This returns false for the void value extension type since it is
|
** primary. This returns false for the void value extension type since it is
|
||||||
|
|||||||
@@ -124,9 +124,37 @@ static unsigned ParseOneStorageClass (void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned ParseOneFuncSpec (void)
|
||||||
|
/* Parse and return a function specifier */
|
||||||
|
{
|
||||||
|
unsigned FuncSpec = 0;
|
||||||
|
|
||||||
|
/* Check the function specifier given */
|
||||||
|
switch (CurTok.Tok) {
|
||||||
|
|
||||||
|
case TOK_INLINE:
|
||||||
|
FuncSpec = SC_INLINE;
|
||||||
|
NextToken ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOK_NORETURN:
|
||||||
|
FuncSpec = SC_NORETURN;
|
||||||
|
NextToken ();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FuncSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int ParseStorageClass (DeclSpec* Spec)
|
static int ParseStorageClass (DeclSpec* Spec)
|
||||||
/* Parse storage class specifiers. Return true if a specifier is read even if
|
/* Parse storage class specifiers. Return true if a specifier is read even if
|
||||||
** it was duplicated or disallowed. */
|
** it was duplicated or disallowed.
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
/* Check the storage class given */
|
/* Check the storage class given */
|
||||||
unsigned StorageClass = ParseOneStorageClass ();
|
unsigned StorageClass = ParseOneStorageClass ();
|
||||||
@@ -151,6 +179,31 @@ static int ParseStorageClass (DeclSpec* Spec)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int ParseFuncSpecClass (DeclSpec* Spec)
|
||||||
|
/* Parse function specifiers. Return true if a specifier is read even if it
|
||||||
|
** was duplicated or disallowed.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
/* Check the function specifiers given */
|
||||||
|
unsigned FuncSpec = ParseOneFuncSpec ();
|
||||||
|
|
||||||
|
if (FuncSpec == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (FuncSpec != 0) {
|
||||||
|
if ((Spec->StorageClass & FuncSpec) != 0) {
|
||||||
|
Warning ("Duplicate function specifier");
|
||||||
|
}
|
||||||
|
Spec->StorageClass |= FuncSpec;
|
||||||
|
FuncSpec = ParseOneFuncSpec ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void DuplicateQualifier (const char* Name)
|
static void DuplicateQualifier (const char* Name)
|
||||||
/* Print an error message */
|
/* Print an error message */
|
||||||
{
|
{
|
||||||
@@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
TypeCode Q = T_QUAL_NONE;
|
TypeCode Q = T_QUAL_NONE;
|
||||||
int Continue;
|
int HasStorageClass;
|
||||||
|
int HasFuncSpec;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* There may be type qualifiers *before* any storage class specifiers */
|
/* There may be type qualifiers *before* any storage class specifiers */
|
||||||
@@ -311,11 +365,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
|
|||||||
*Qualifiers |= Q;
|
*Qualifiers |= Q;
|
||||||
|
|
||||||
/* Parse storage class specifiers anyway then check */
|
/* Parse storage class specifiers anyway then check */
|
||||||
Continue = ParseStorageClass (Spec);
|
HasStorageClass = ParseStorageClass (Spec);
|
||||||
if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) {
|
if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) {
|
||||||
Error ("Unexpected storage class specified");
|
Error ("Unexpected storage class specified");
|
||||||
}
|
}
|
||||||
} while (Continue || Q != T_QUAL_NONE);
|
|
||||||
|
/* Parse function specifiers anyway then check */
|
||||||
|
HasFuncSpec = ParseFuncSpecClass (Spec);
|
||||||
|
if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) {
|
||||||
|
Error ("Unexpected function specifiers");
|
||||||
|
}
|
||||||
|
} while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -997,7 +1057,7 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||||||
|
|
||||||
/* Parse union fields */
|
/* Parse union fields */
|
||||||
UnionSize = 0;
|
UnionSize = 0;
|
||||||
while (CurTok.Tok != TOK_RCURLY) {
|
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
|
||||||
|
|
||||||
/* Get the type of the entry */
|
/* Get the type of the entry */
|
||||||
DeclSpec Spec;
|
DeclSpec Spec;
|
||||||
@@ -1020,7 +1080,8 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||||
|
|
||||||
/* Check if this is only a type declaration */
|
/* Check if this is only a type declaration */
|
||||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
if (CurTok.Tok == TOK_SEMI &&
|
||||||
|
!(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) {
|
||||||
CheckEmptyDecl (&Spec);
|
CheckEmptyDecl (&Spec);
|
||||||
NextToken ();
|
NextToken ();
|
||||||
continue;
|
continue;
|
||||||
@@ -1061,22 +1122,12 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
|
|||||||
/* In cc65 mode, we allow anonymous structs/unions within
|
/* In cc65 mode, we allow anonymous structs/unions within
|
||||||
** a union.
|
** a union.
|
||||||
*/
|
*/
|
||||||
SymEntry* TagEntry;
|
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount);
|
||||||
if (IS_Get (&Standard) >= STD_CC65 &&
|
|
||||||
IsClassStruct (Decl.Type) &&
|
|
||||||
(TagEntry = GetESUTagSym (Decl.Type)) &&
|
|
||||||
SymHasAnonName (TagEntry)) {
|
|
||||||
/* This is an anonymous struct or union */
|
|
||||||
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount);
|
|
||||||
|
|
||||||
/* Ignore CVR qualifiers */
|
/* Ignore CVR qualifiers */
|
||||||
if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
|
if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
|
||||||
Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
|
Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
|
||||||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Invalid member */
|
|
||||||
goto NextMember;
|
|
||||||
}
|
}
|
||||||
} else if (FieldWidth > 0) {
|
} else if (FieldWidth > 0) {
|
||||||
/* A bit-field without a name will get an anonymous one */
|
/* A bit-field without a name will get an anonymous one */
|
||||||
@@ -1217,7 +1268,7 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||||||
FlexibleMember = 0;
|
FlexibleMember = 0;
|
||||||
StructSize = 0;
|
StructSize = 0;
|
||||||
BitOffs = 0;
|
BitOffs = 0;
|
||||||
while (CurTok.Tok != TOK_RCURLY) {
|
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
|
||||||
|
|
||||||
/* Get the type of the entry */
|
/* Get the type of the entry */
|
||||||
DeclSpec Spec;
|
DeclSpec Spec;
|
||||||
@@ -1240,7 +1291,8 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||||||
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE);
|
||||||
|
|
||||||
/* Check if this is only a type declaration */
|
/* Check if this is only a type declaration */
|
||||||
if (CurTok.Tok == TOK_SEMI && (Spec.Flags & DS_EXTRA_TYPE) == 0) {
|
if (CurTok.Tok == TOK_SEMI &&
|
||||||
|
!(IS_Get (&Standard) >= STD_CC65 && IsAnonStructClass (Spec.Type))) {
|
||||||
CheckEmptyDecl (&Spec);
|
CheckEmptyDecl (&Spec);
|
||||||
NextToken ();
|
NextToken ();
|
||||||
continue;
|
continue;
|
||||||
@@ -1308,22 +1360,12 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
|
|||||||
/* In cc65 mode, we allow anonymous structs/unions within
|
/* In cc65 mode, we allow anonymous structs/unions within
|
||||||
** a struct.
|
** a struct.
|
||||||
*/
|
*/
|
||||||
SymEntry* TagEntry;
|
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount);
|
||||||
if (IS_Get (&Standard) >= STD_CC65 &&
|
|
||||||
IsClassStruct (Decl.Type) &&
|
|
||||||
(TagEntry = GetESUTagSym (Decl.Type)) &&
|
|
||||||
SymHasAnonName (TagEntry)) {
|
|
||||||
/* This is an anonymous struct or union */
|
|
||||||
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount);
|
|
||||||
|
|
||||||
/* Ignore CVR qualifiers */
|
/* Ignore CVR qualifiers */
|
||||||
if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
|
if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
|
||||||
Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
|
Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
|
||||||
Decl.Type[0].C &= ~T_QUAL_CVR;
|
Decl.Type[0].C &= ~T_QUAL_CVR;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Invalid member */
|
|
||||||
goto NextMember;
|
|
||||||
}
|
}
|
||||||
} else if (FieldWidth > 0) {
|
} else if (FieldWidth > 0) {
|
||||||
/* A bit-field without a name will get an anonymous one */
|
/* A bit-field without a name will get an anonymous one */
|
||||||
@@ -1814,7 +1856,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused)))
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* An optional list of type specifications follows */
|
/* An optional list of type specifications follows */
|
||||||
while (CurTok.Tok != TOK_LCURLY) {
|
while (CurTok.Tok != TOK_LCURLY && CurTok.Tok != TOK_CEOF) {
|
||||||
|
|
||||||
DeclSpec Spec;
|
DeclSpec Spec;
|
||||||
int NeedClean;
|
int NeedClean;
|
||||||
@@ -1854,7 +1896,7 @@ static void ParseOldStyleParamDeclList (FuncDesc* F attribute ((unused)))
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Warn about new local type declaration */
|
/* Warn about new local type declaration */
|
||||||
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) {
|
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) {
|
||||||
Warning ("'%s' will be invisible out of this function",
|
Warning ("'%s' will be invisible out of this function",
|
||||||
GetFullTypeName (Spec.Type));
|
GetFullTypeName (Spec.Type));
|
||||||
}
|
}
|
||||||
@@ -1957,7 +1999,7 @@ static void ParseAnsiParamList (FuncDesc* F)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Warn about new local type declaration */
|
/* Warn about new local type declaration */
|
||||||
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0) {
|
if ((Spec.Flags & DS_NEW_TYPE_DECL) != 0 && !IsAnonESUType (Spec.Type)) {
|
||||||
Warning ("'%s' will be invisible out of this function",
|
Warning ("'%s' will be invisible out of this function",
|
||||||
GetFullTypeName (Spec.Type));
|
GetFullTypeName (Spec.Type));
|
||||||
}
|
}
|
||||||
@@ -2375,30 +2417,54 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
|
|||||||
/* Parse attributes for this declarator */
|
/* Parse attributes for this declarator */
|
||||||
ParseAttribute (D);
|
ParseAttribute (D);
|
||||||
|
|
||||||
/* Check a few pre-C99 things */
|
/* Check a few things for the instance (rather than the type) */
|
||||||
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
|
if (D->Ident[0] != '\0') {
|
||||||
/* Check and warn about an implicit int return in the function */
|
/* Check a few pre-C99 things */
|
||||||
if (IsTypeFunc (D->Type) && IsRankInt (GetFuncReturnType (D->Type))) {
|
if ((Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE && IsRankInt (Spec->Type)) {
|
||||||
/* Function has an implicit int return. Output a warning if we don't
|
/* If the standard was not set explicitly to C89, print a warning
|
||||||
** have the C89 standard enabled explicitly.
|
** for typedefs with implicit int type specifier.
|
||||||
*/
|
*/
|
||||||
if (IS_Get (&Standard) >= STD_C99) {
|
if (IS_Get (&Standard) >= STD_C99) {
|
||||||
Warning ("Implicit 'int' return type is an obsolete feature");
|
if ((D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
||||||
|
Warning ("Implicit 'int' type specifier is an obsolete feature");
|
||||||
|
} else {
|
||||||
|
Warning ("Type specifier defaults to 'int' in typedef of '%s'",
|
||||||
|
D->Ident);
|
||||||
|
Note ("Implicit 'int' type specifier is an obsolete feature");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For anything that is not a function or typedef, check for an implicit
|
/* Check other things depending on the "kind" of the instance */
|
||||||
** int declaration.
|
if ((D->StorageClass & SC_TYPEMASK) == SC_FUNC) {
|
||||||
*/
|
/* Special handling for main() */
|
||||||
if (!IsTypeFunc (D->Type) &&
|
if (strcmp (D->Ident, "main") == 0) {
|
||||||
(D->StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
|
/* main() cannot be a fastcall function */
|
||||||
/* If the standard was not set explicitly to C89, print a warning
|
if (IsQualFastcall (D->Type)) {
|
||||||
** for variables with implicit int type.
|
Error ("'main' cannot be declared __fastcall__");
|
||||||
*/
|
}
|
||||||
if (IS_Get (&Standard) >= STD_C99) {
|
|
||||||
Warning ("Implicit 'int' is an obsolete feature");
|
/* main() cannot be an inline function */
|
||||||
|
if ((D->StorageClass & SC_INLINE) == SC_INLINE) {
|
||||||
|
Error ("'main' cannot be declared inline");
|
||||||
|
D->StorageClass &= ~SC_INLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check return type */
|
||||||
|
if (GetUnqualRawTypeCode (GetFuncReturnType (D->Type)) != T_INT) {
|
||||||
|
/* If cc65 extensions aren't enabled, don't allow a main function
|
||||||
|
** that doesn't return an int.
|
||||||
|
*/
|
||||||
|
if (IS_Get (&Standard) != STD_CC65) {
|
||||||
|
Error ("'main' must always return an int");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (Mode != DM_ACCEPT_PARAM_IDENT &&
|
||||||
|
(D->StorageClass & SC_INLINE) == SC_INLINE) {
|
||||||
|
/* 'inline' is only allowed on functions */
|
||||||
|
Error ("'inline' on non-function declaration");
|
||||||
|
D->StorageClass &= ~SC_INLINE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2478,7 +2544,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage)
|
|||||||
Spec->Flags &= ~DS_DEF_STORAGE;
|
Spec->Flags &= ~DS_DEF_STORAGE;
|
||||||
|
|
||||||
/* Parse the type specifiers */
|
/* Parse the type specifiers */
|
||||||
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC);
|
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC);
|
||||||
|
|
||||||
/* If no explicit storage class is given, use the default */
|
/* If no explicit storage class is given, use the default */
|
||||||
if ((Spec->StorageClass & SC_STORAGEMASK) == 0) {
|
if ((Spec->StorageClass & SC_STORAGEMASK) == 0) {
|
||||||
@@ -2495,13 +2561,25 @@ void CheckEmptyDecl (const DeclSpec* Spec)
|
|||||||
** warning if not.
|
** warning if not.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
|
if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) {
|
||||||
|
Error ("'inline' on empty declaration");
|
||||||
|
} else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
|
||||||
/* No declaration at all */
|
/* No declaration at all */
|
||||||
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
|
||||||
Warning ("Declaration does not declare anything");
|
/* Empty declaration of basic types */
|
||||||
} else if (IsClassStruct (Spec->Type) &&
|
Warning ("Useless declaration");
|
||||||
!IsIncompleteESUType (Spec->Type) &&
|
} else if (IsAnonStructClass (Spec->Type)) {
|
||||||
SymHasAnonName (GetESUTagSym (Spec->Type))) {
|
/* This could be that the user made a wrong attempt to declare an
|
||||||
|
** anonymous struct/union field outside a struct/union.
|
||||||
|
*/
|
||||||
Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type));
|
Warning ("Unnamed %s that defines no instances", GetBasicTypeName (Spec->Type));
|
||||||
|
} else if (GetLexicalLevel () == LEX_LEVEL_STRUCT) {
|
||||||
|
/* This could be that the user made a wrong attempt to declare an
|
||||||
|
** anonymous struct/union field inside a struct/union. Perhaps just
|
||||||
|
** paranoid since it is not so uncommon to do forward declarations.
|
||||||
|
*/
|
||||||
|
if (!IsTypeEnum (Spec->Type) || ((Spec->Flags & DS_NEW_TYPE_DEF) == 0)) {
|
||||||
|
Warning ("Declaration defines no instances");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
123
src/cc65/error.c
123
src/cc65/error.c
@@ -120,13 +120,46 @@ Collection DiagnosticStrBufs;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void PrintFileInclusionInfo (const LineInfo* LI)
|
||||||
|
/* Print hierarchy of file inclusion */
|
||||||
|
{
|
||||||
|
if (LI->IncFiles != 0) {
|
||||||
|
unsigned FileCount = CollCount (LI->IncFiles);
|
||||||
|
if (FileCount > 0) {
|
||||||
|
const char* Str = "In file included from %s:%u%c\n";
|
||||||
|
|
||||||
|
while (FileCount-- > 0) {
|
||||||
|
LineInfoFile* LIF = CollAtUnchecked (LI->IncFiles, FileCount);
|
||||||
|
char C = FileCount > 0 ? ',' : ':';
|
||||||
|
|
||||||
|
fprintf (stderr, Str, LIF->Name, LIF->LineNum, C);
|
||||||
|
Str = " from %s:%u%c\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static LineInfo* GetDiagnosticLI (void)
|
||||||
|
/* Get the line info where the diagnostic info refers to */
|
||||||
|
{
|
||||||
|
if (CurTok.LI) {
|
||||||
|
return CurTok.LI;
|
||||||
|
} else {
|
||||||
|
return GetCurLineInfo ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char* GetDiagnosticFileName (void)
|
static const char* GetDiagnosticFileName (void)
|
||||||
/* Get the source file name where the diagnostic info refers to */
|
/* Get the source file name where the diagnostic info refers to */
|
||||||
{
|
{
|
||||||
if (CurTok.LI) {
|
if (CurTok.LI) {
|
||||||
return GetInputName (CurTok.LI);
|
return GetPresumedFileName (CurTok.LI);
|
||||||
} else {
|
} else {
|
||||||
return GetCurrentFilename ();
|
return GetCurrentFileName ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +169,7 @@ static unsigned GetDiagnosticLineNum (void)
|
|||||||
/* Get the source line number where the diagnostic info refers to */
|
/* Get the source line number where the diagnostic info refers to */
|
||||||
{
|
{
|
||||||
if (CurTok.LI) {
|
if (CurTok.LI) {
|
||||||
return GetInputLine (CurTok.LI);
|
return GetPresumedLineNum (CurTok.LI);
|
||||||
} else {
|
} else {
|
||||||
return GetCurrentLineNum ();
|
return GetCurrentLineNum ();
|
||||||
}
|
}
|
||||||
@@ -199,10 +232,18 @@ void Internal (const char* Format, ...)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
|
static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
|
||||||
/* Print an error message - internal function */
|
/* Print an error message - internal function */
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s:%u: Error: ", Filename, LineNo);
|
unsigned LineNo = GetPresumedLineNum (LI);
|
||||||
|
|
||||||
|
/* Print file inclusion if appropriate */
|
||||||
|
if (HasFileInclusionChanged (LI)) {
|
||||||
|
PrintFileInclusionInfo (LI);
|
||||||
|
}
|
||||||
|
RememberCheckedLI (LI);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s:%u: Error: ", GetPresumedFileName (LI), LineNo);
|
||||||
vfprintf (stderr, Msg, ap);
|
vfprintf (stderr, Msg, ap);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
@@ -229,23 +270,23 @@ static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Error (const char* Format, ...)
|
void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...)
|
||||||
/* Print an error message */
|
/* Print an error message with the line info given explicitly */
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntError (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
|
IntError (EC, LI, Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...)
|
void Error (const char* Format, ...)
|
||||||
/* Print an error message with the line info given explicitly */
|
/* Print an error message */
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntError (EC, GetInputName (LI), GetInputLine (LI), Format, ap);
|
IntError (EC_PARSER, GetDiagnosticLI (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +297,7 @@ void PPError (const char* Format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntError (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
|
IntError (EC_PP, GetCurLineInfo (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,17 +309,25 @@ void PPError (const char* Format, ...)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
|
static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
|
||||||
/* Print a warning message - internal function */
|
/* Print a warning message - internal function */
|
||||||
{
|
{
|
||||||
if (IS_Get (&WarningsAreErrors)) {
|
if (IS_Get (&WarningsAreErrors)) {
|
||||||
|
|
||||||
/* Treat the warning as an error */
|
/* Treat the warning as an error */
|
||||||
IntError (EC, Filename, LineNo, Msg, ap);
|
IntError (EC, LI, Msg, ap);
|
||||||
|
|
||||||
} else if (IS_Get (&WarnEnable)) {
|
} else if (IS_Get (&WarnEnable)) {
|
||||||
|
|
||||||
fprintf (stderr, "%s:%u: Warning: ", Filename, LineNo);
|
unsigned LineNo = GetPresumedLineNum (LI);
|
||||||
|
|
||||||
|
/* Print file inclusion if appropriate */
|
||||||
|
if (HasFileInclusionChanged (LI)) {
|
||||||
|
PrintFileInclusionInfo (LI);
|
||||||
|
}
|
||||||
|
RememberCheckedLI (LI);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s:%u: Warning: ", GetPresumedFileName (LI), LineNo);
|
||||||
vfprintf (stderr, Msg, ap);
|
vfprintf (stderr, Msg, ap);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
@@ -297,23 +346,23 @@ static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, cons
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Warning (const char* Format, ...)
|
void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...)
|
||||||
/* Print a warning message */
|
/* Print a warning message with the line info given explicitly */
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntWarning (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
|
IntWarning (EC, LI, Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...)
|
void Warning (const char* Format, ...)
|
||||||
/* Print a warning message with the line info given explicitly */
|
/* Print a warning message */
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntWarning (EC, GetInputName (LI), GetInputLine (LI), Format, ap);
|
IntWarning (EC_PARSER, GetDiagnosticLI (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +373,7 @@ void PPWarning (const char* Format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntWarning (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
|
IntWarning (EC_PP, GetCurLineInfo (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,33 +414,33 @@ void ListWarnings (FILE* F)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void IntNote (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
|
static void IntNote (const LineInfo* LI, const char* Msg, va_list ap)
|
||||||
/* Print a note message - internal function */
|
/* Print a note message - internal function */
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s:%u: Note: ", Filename, LineNo);
|
fprintf (stderr, "%s:%u: Note: ", GetPresumedFileName (LI), GetPresumedLineNum (LI));
|
||||||
vfprintf (stderr, Msg, ap);
|
vfprintf (stderr, Msg, ap);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Note (const char* Format, ...)
|
|
||||||
/* Print a note message */
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start (ap, Format);
|
|
||||||
IntNote (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
|
|
||||||
va_end (ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void LINote (const LineInfo* LI, const char* Format, ...)
|
void LINote (const LineInfo* LI, const char* Format, ...)
|
||||||
/* Print a note message with the line info given explicitly */
|
/* Print a note message with the line info given explicitly */
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntNote (GetInputName (LI), GetInputLine (LI), Format, ap);
|
IntNote (LI, Format, ap);
|
||||||
|
va_end (ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Note (const char* Format, ...)
|
||||||
|
/* Print a note message */
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start (ap, Format);
|
||||||
|
IntNote (GetDiagnosticLI (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +451,7 @@ void PPNote (const char* Format, ...)
|
|||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, Format);
|
va_start (ap, Format);
|
||||||
IntNote (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
|
IntNote (GetDiagnosticLI (), Format, ap);
|
||||||
va_end (ap);
|
va_end (ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ struct StrBuf;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void PrintFileInclusionInfo (const LineInfo* LI);
|
||||||
|
/* Print hierarchy of file inclusion */
|
||||||
|
|
||||||
void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2)));
|
void Fatal (const char* Format, ...) attribute ((noreturn, format (printf, 1, 2)));
|
||||||
/* Print a message about a fatal error and die */
|
/* Print a message about a fatal error and die */
|
||||||
|
|
||||||
@@ -109,7 +112,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1,
|
|||||||
void Error (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
void Error (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
||||||
/* Print an error message */
|
/* Print an error message */
|
||||||
|
|
||||||
void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
|
void LIError (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
|
||||||
/* Print an error message with the line info given explicitly */
|
/* Print an error message with the line info given explicitly */
|
||||||
|
|
||||||
void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
||||||
@@ -118,7 +121,7 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
|||||||
void Warning (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
void Warning (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
||||||
/* Print a warning message */
|
/* Print a warning message */
|
||||||
|
|
||||||
void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
|
void LIWarning (errcat_t EC, LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
|
||||||
/* Print a warning message with the line info given explicitly */
|
/* Print a warning message with the line info given explicitly */
|
||||||
|
|
||||||
void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2)));
|
||||||
|
|||||||
@@ -1407,7 +1407,7 @@ static void Primary (ExprDesc* E)
|
|||||||
} else {
|
} else {
|
||||||
/* Let's see if this is a C99-style declaration */
|
/* Let's see if this is a C99-style declaration */
|
||||||
DeclSpec Spec;
|
DeclSpec Spec;
|
||||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
|
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO);
|
||||||
|
|
||||||
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
|
||||||
Error ("Mixed declarations and code are not supported in cc65");
|
Error ("Mixed declarations and code are not supported in cc65");
|
||||||
|
|||||||
@@ -450,7 +450,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Parse argument declarations and function body. */
|
/* Parse argument declarations and function body. */
|
||||||
{
|
{
|
||||||
int ParamComplete; /* If all paramemters have complete types */
|
int ParamComplete; /* If all paramemters have complete types */
|
||||||
int C99MainFunc = 0;/* Flag for C99 main function returning int */
|
|
||||||
SymEntry* Param;
|
SymEntry* Param;
|
||||||
const Type* RType; /* Real type used for struct parameters */
|
const Type* RType; /* Real type used for struct parameters */
|
||||||
const Type* ReturnType; /* Return type */
|
const Type* ReturnType; /* Return type */
|
||||||
@@ -513,28 +512,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Mark this as the main function */
|
/* Mark this as the main function */
|
||||||
CurrentFunc->Flags |= FF_IS_MAIN;
|
CurrentFunc->Flags |= FF_IS_MAIN;
|
||||||
|
|
||||||
/* Main cannot be a fastcall function */
|
|
||||||
if (IsQualFastcall (Func->Type)) {
|
|
||||||
Error ("'main' cannot be declared as __fastcall__");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check return type */
|
|
||||||
if (GetUnqualRawTypeCode (ReturnType) == T_INT) {
|
|
||||||
/* Determine if this is a main function in a C99 environment that
|
|
||||||
** returns an int.
|
|
||||||
*/
|
|
||||||
if (IS_Get (&Standard) >= STD_C99) {
|
|
||||||
C99MainFunc = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* If cc65 extensions aren't enabled, don't allow a main function
|
|
||||||
** that doesn't return an int.
|
|
||||||
*/
|
|
||||||
if (IS_Get (&Standard) != STD_CC65) {
|
|
||||||
Error ("'main' must always return an int");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a forced import of a symbol that is contained in the startup
|
/* Add a forced import of a symbol that is contained in the startup
|
||||||
** code. This will force the startup code to be linked in.
|
** code. This will force the startup code to be linked in.
|
||||||
*/
|
*/
|
||||||
@@ -659,7 +636,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* If this is the main function in a C99 environment returning an int,
|
/* If this is the main function in a C99 environment returning an int,
|
||||||
** let it always return zero. Otherwise output a warning.
|
** let it always return zero. Otherwise output a warning.
|
||||||
*/
|
*/
|
||||||
if (C99MainFunc) {
|
if (IS_Get (&Standard) >= STD_C99 && GetUnqualRawTypeCode (ReturnType) == T_INT) {
|
||||||
g_getimmed (CF_INT | CF_CONST, 0, 0);
|
g_getimmed (CF_INT | CF_CONST, 0, 0);
|
||||||
} else if (IS_Get (&WarnReturnType)) {
|
} else if (IS_Get (&WarnReturnType)) {
|
||||||
Warning ("Control reaches end of non-void function [-Wreturn-type]");
|
Warning ("Control reaches end of non-void function [-Wreturn-type]");
|
||||||
@@ -685,9 +662,6 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Leave the lexical level */
|
/* Leave the lexical level */
|
||||||
LeaveFunctionLevel ();
|
LeaveFunctionLevel ();
|
||||||
|
|
||||||
/* Eat the closing brace */
|
|
||||||
ConsumeRCurly ();
|
|
||||||
|
|
||||||
/* Restore the old literal pool, remembering the one for the function */
|
/* Restore the old literal pool, remembering the one for the function */
|
||||||
Func->V.F.LitPool = PopLiteralPool ();
|
Func->V.F.LitPool = PopLiteralPool ();
|
||||||
|
|
||||||
@@ -699,6 +673,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Switch back to the old segments */
|
/* Switch back to the old segments */
|
||||||
PopSegContext ();
|
PopSegContext ();
|
||||||
|
|
||||||
|
/* Eat the closing brace after we've done everything with the function
|
||||||
|
** definition. This way we won't have troubles with pragmas right after
|
||||||
|
** the closing brace.
|
||||||
|
*/
|
||||||
|
ConsumeRCurly();
|
||||||
|
|
||||||
/* Reset the current function pointer */
|
/* Reset the current function pointer */
|
||||||
FreeFunction (CurrentFunc);
|
FreeFunction (CurrentFunc);
|
||||||
CurrentFunc = 0;
|
CurrentFunc = 0;
|
||||||
|
|||||||
165
src/cc65/input.c
165
src/cc65/input.c
@@ -91,10 +91,11 @@ struct IFile {
|
|||||||
/* Struct that describes an active input file */
|
/* Struct that describes an active input file */
|
||||||
typedef struct AFile AFile;
|
typedef struct AFile AFile;
|
||||||
struct AFile {
|
struct AFile {
|
||||||
unsigned Line; /* Line number for this file */
|
unsigned LineNum; /* Actual line number for this file */
|
||||||
FILE* F; /* Input file stream */
|
FILE* F; /* Input file stream */
|
||||||
IFile* Input; /* Points to corresponding IFile */
|
IFile* Input; /* Points to corresponding IFile */
|
||||||
int SearchPath; /* True if we've added a path for this file */
|
int SearchPath; /* True if we've added a path for this file */
|
||||||
|
unsigned LineOffs; /* Offset to presumed line number for this file */
|
||||||
char* PName; /* Presumed name of the file */
|
char* PName; /* Presumed name of the file */
|
||||||
PPIfStack IfStack; /* PP #if stack */
|
PPIfStack IfStack; /* PP #if stack */
|
||||||
int MissingNL; /* Last input line was missing a newline */
|
int MissingNL; /* Last input line was missing a newline */
|
||||||
@@ -111,6 +112,7 @@ static Collection* CurrentInputStack;
|
|||||||
|
|
||||||
/* Counter for the __COUNTER__ macro */
|
/* Counter for the __COUNTER__ macro */
|
||||||
static unsigned MainFileCounter;
|
static unsigned MainFileCounter;
|
||||||
|
LineInfo* PrevDiagnosticLI;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -163,10 +165,11 @@ static AFile* NewAFile (IFile* IF, FILE* F)
|
|||||||
AFile* AF = (AFile*) xmalloc (sizeof (AFile));
|
AFile* AF = (AFile*) xmalloc (sizeof (AFile));
|
||||||
|
|
||||||
/* Initialize the fields */
|
/* Initialize the fields */
|
||||||
AF->Line = 0;
|
AF->LineNum = 0;
|
||||||
AF->F = F;
|
AF->F = F;
|
||||||
AF->Input = IF;
|
AF->Input = IF;
|
||||||
AF->PName = 0;
|
AF->LineOffs = 0;
|
||||||
|
AF->PName = 0;
|
||||||
AF->IfStack.Index = -1;
|
AF->IfStack.Index = -1;
|
||||||
AF->MissingNL = 0;
|
AF->MissingNL = 0;
|
||||||
|
|
||||||
@@ -285,7 +288,7 @@ void OpenMainFile (const char* Name)
|
|||||||
/* Update the line infos, so we have a valid line info even at start of
|
/* Update the line infos, so we have a valid line info even at start of
|
||||||
** the main file before the first line is read.
|
** the main file before the first line is read.
|
||||||
*/
|
*/
|
||||||
UpdateLineInfo (MainFile->Input, MainFile->Line, Line);
|
UpdateCurrentLineInfo (Line);
|
||||||
|
|
||||||
/* Initialize the __COUNTER__ counter */
|
/* Initialize the __COUNTER__ counter */
|
||||||
MainFileCounter = 0;
|
MainFileCounter = 0;
|
||||||
@@ -553,7 +556,7 @@ int NextLine (void)
|
|||||||
if (!Input->MissingNL || SB_NotEmpty (Line)) {
|
if (!Input->MissingNL || SB_NotEmpty (Line)) {
|
||||||
|
|
||||||
/* Accept files without a newline at the end */
|
/* Accept files without a newline at the end */
|
||||||
++Input->Line;
|
++Input->LineNum;
|
||||||
|
|
||||||
/* Assume no new line */
|
/* Assume no new line */
|
||||||
Input->MissingNL = 1;
|
Input->MissingNL = 1;
|
||||||
@@ -569,7 +572,7 @@ int NextLine (void)
|
|||||||
if (C == '\n') {
|
if (C == '\n') {
|
||||||
|
|
||||||
/* We got a new line */
|
/* We got a new line */
|
||||||
++Input->Line;
|
++Input->LineNum;
|
||||||
|
|
||||||
/* If the \n is preceeded by a \r, remove the \r, so we can read
|
/* If the \n is preceeded by a \r, remove the \r, so we can read
|
||||||
** DOS/Windows files under *nix.
|
** DOS/Windows files under *nix.
|
||||||
@@ -605,7 +608,7 @@ int NextLine (void)
|
|||||||
InitLine (Line);
|
InitLine (Line);
|
||||||
|
|
||||||
/* Create line information for this line */
|
/* Create line information for this line */
|
||||||
UpdateLineInfo (Input->Input, Input->Line, Line);
|
UpdateCurrentLineInfo (Line);
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
return C != EOF || SB_NotEmpty (Line);
|
return C != EOF || SB_NotEmpty (Line);
|
||||||
@@ -645,15 +648,145 @@ int PreprocessNextLine (void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const char* GetInputFile (const struct IFile* IF)
|
static LineInfoFile* NewLineInfoFile (const AFile* AF)
|
||||||
/* Return a filename from an IFile struct */
|
{
|
||||||
|
const char* Name = AF->PName == 0 ? AF->Input->Name : AF->PName;
|
||||||
|
unsigned Len = strlen (Name);
|
||||||
|
|
||||||
|
/* Allocate memory for the file info and the file name */
|
||||||
|
LineInfoFile* LIF = xmalloc (sizeof (LineInfoFile) + Len);
|
||||||
|
|
||||||
|
/* Copy info */
|
||||||
|
LIF->InputFile = AF->Input;
|
||||||
|
LIF->LineNum = AF->LineNum + AF->LineOffs;
|
||||||
|
memcpy (LIF->Name, Name, Len + 1);
|
||||||
|
|
||||||
|
return LIF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void GetFileInclusionInfo (struct LineInfo* LI)
|
||||||
|
/* Get info about source file inclusion for LineInfo struct */
|
||||||
|
{
|
||||||
|
unsigned FileCount = CollCount (&AFiles);
|
||||||
|
|
||||||
|
CHECK (FileCount > 0);
|
||||||
|
|
||||||
|
/* Get the correct index */
|
||||||
|
--FileCount;
|
||||||
|
|
||||||
|
if (LI->IncFiles != 0) {
|
||||||
|
FreeFileInclusionInfo (LI);
|
||||||
|
}
|
||||||
|
LI->IncFiles = 0;
|
||||||
|
|
||||||
|
if (LI->File != 0) {
|
||||||
|
xfree (LI->File);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy info from the AFile */
|
||||||
|
LI->File = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount));
|
||||||
|
|
||||||
|
/* Remember the actual line number */
|
||||||
|
LI->ActualLineNum = ((AFile*)CollAtUnchecked (&AFiles, FileCount))->LineNum;
|
||||||
|
|
||||||
|
if (FileCount > 0) {
|
||||||
|
/* The file is included from another */
|
||||||
|
|
||||||
|
/* Always use a new collection */
|
||||||
|
LI->IncFiles = NewCollection ();
|
||||||
|
|
||||||
|
while (FileCount-- > 0) {
|
||||||
|
/* Copy info from the AFile */
|
||||||
|
LineInfoFile* LIF = NewLineInfoFile (CollAtUnchecked (&AFiles, FileCount));
|
||||||
|
|
||||||
|
/* Add this file */
|
||||||
|
CollAppend (LI->IncFiles, LIF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void FreeFileInclusionInfo (struct LineInfo* LI)
|
||||||
|
/* Free info about source file inclusion for LineInfo struct */
|
||||||
|
{
|
||||||
|
if (LI->File != 0) {
|
||||||
|
xfree (LI->File);
|
||||||
|
LI->File = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LI->IncFiles != 0) {
|
||||||
|
unsigned I;
|
||||||
|
for (I = 0; I < CollCount (LI->IncFiles); ++I) {
|
||||||
|
CollAtUnchecked (LI->IncFiles, I);
|
||||||
|
}
|
||||||
|
FreeCollection (LI->IncFiles);
|
||||||
|
LI->IncFiles = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int IsDifferentLineInfoFile (const LineInfoFile* Lhs, const LineInfoFile* Rhs)
|
||||||
|
/* Return true if the two files are different */
|
||||||
|
{
|
||||||
|
/* If the input files are the same but their presumed names are different,
|
||||||
|
** we still consider the files same.
|
||||||
|
*/
|
||||||
|
return Lhs->InputFile != Rhs->InputFile || Lhs->LineNum != Rhs->LineNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int HasFileInclusionChanged (const struct LineInfo* LI)
|
||||||
|
/* Return true if file inclusion has changed from last time */
|
||||||
|
{
|
||||||
|
if (LI->File != 0) {
|
||||||
|
LineInfo* PrevLI = GetPrevCheckedLI ();
|
||||||
|
|
||||||
|
if (LI == PrevLI) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PrevLI == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LI->IncFiles != 0) {
|
||||||
|
unsigned I;
|
||||||
|
|
||||||
|
if (PrevLI->IncFiles == 0 ||
|
||||||
|
CollCount (LI->IncFiles) != CollCount (PrevLI->IncFiles)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (I = 0; I < CollCount (LI->IncFiles); ++I) {
|
||||||
|
/* If this refers to a different file, then the inclusion has changed */
|
||||||
|
if (IsDifferentLineInfoFile (CollAtUnchecked (LI->IncFiles, I),
|
||||||
|
CollAtUnchecked (PrevLI->IncFiles, I))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unchanged */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char* GetInputFileName (const struct IFile* IF)
|
||||||
|
/* Return the name of the file from an IFile struct */
|
||||||
{
|
{
|
||||||
return IF->Name;
|
return IF->Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const char* GetCurrentFilename (void)
|
const char* GetCurrentFileName (void)
|
||||||
/* Return the name of the current input file */
|
/* Return the name of the current input file */
|
||||||
{
|
{
|
||||||
unsigned AFileCount = CollCount (&AFiles);
|
unsigned AFileCount = CollCount (&AFiles);
|
||||||
@@ -674,7 +807,7 @@ unsigned GetCurrentLineNum (void)
|
|||||||
unsigned AFileCount = CollCount (&AFiles);
|
unsigned AFileCount = CollCount (&AFiles);
|
||||||
if (AFileCount > 0) {
|
if (AFileCount > 0) {
|
||||||
const AFile* AF = CollLast (&AFiles);
|
const AFile* AF = CollLast (&AFiles);
|
||||||
return AF->Line;
|
return AF->LineNum + AF->LineOffs;
|
||||||
} else {
|
} else {
|
||||||
/* No open file */
|
/* No open file */
|
||||||
return 0;
|
return 0;
|
||||||
@@ -684,18 +817,18 @@ unsigned GetCurrentLineNum (void)
|
|||||||
|
|
||||||
|
|
||||||
void SetCurrentLineNum (unsigned LineNum)
|
void SetCurrentLineNum (unsigned LineNum)
|
||||||
/* Set the line number in the current input file */
|
/* Set the presumed line number in the current input file */
|
||||||
{
|
{
|
||||||
unsigned AFileCount = CollCount (&AFiles);
|
unsigned AFileCount = CollCount (&AFiles);
|
||||||
if (AFileCount > 0) {
|
if (AFileCount > 0) {
|
||||||
AFile* AF = CollLast (&AFiles);
|
AFile* AF = CollLast (&AFiles);
|
||||||
AF->Line = LineNum;
|
AF->LineOffs = LineNum - AF->LineNum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SetCurrentFilename (const char* Name)
|
void SetCurrentFileName (const char* Name)
|
||||||
/* Set the presumed name of the current input file */
|
/* Set the presumed name of the current input file */
|
||||||
{
|
{
|
||||||
unsigned AFileCount = CollCount (&AFiles);
|
unsigned AFileCount = CollCount (&AFiles);
|
||||||
|
|||||||
@@ -52,6 +52,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Forwards */
|
||||||
|
struct IFile;
|
||||||
|
struct LineInfo;
|
||||||
|
|
||||||
/* An enum that describes different types of input files. The members are
|
/* An enum that describes different types of input files. The members are
|
||||||
** choosen so that it is possible to combine them to bitsets
|
** choosen so that it is possible to combine them to bitsets
|
||||||
*/
|
*/
|
||||||
@@ -61,9 +65,6 @@ typedef enum {
|
|||||||
IT_USRINC = 0x04, /* User include file (using "") */
|
IT_USRINC = 0x04, /* User include file (using "") */
|
||||||
} InputType;
|
} InputType;
|
||||||
|
|
||||||
/* Forward for an IFile structure */
|
|
||||||
struct IFile;
|
|
||||||
|
|
||||||
/* The current input line */
|
/* The current input line */
|
||||||
extern StrBuf* Line;
|
extern StrBuf* Line;
|
||||||
|
|
||||||
@@ -125,10 +126,19 @@ int PreprocessNextLine (void);
|
|||||||
** main file.
|
** main file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const char* GetInputFile (const struct IFile* IF);
|
void GetFileInclusionInfo (struct LineInfo* LI);
|
||||||
/* Return a filename from an IFile struct */
|
/* Get info about source file inclusion for LineInfo struct */
|
||||||
|
|
||||||
const char* GetCurrentFilename (void);
|
void FreeFileInclusionInfo (struct LineInfo* LI);
|
||||||
|
/* Free info about source file inclusion for LineInfo struct */
|
||||||
|
|
||||||
|
int HasFileInclusionChanged (const struct LineInfo* LI);
|
||||||
|
/* Return true if file inclusion has changed from last time */
|
||||||
|
|
||||||
|
const char* GetInputFileName (const struct IFile* IF);
|
||||||
|
/* Return the name of the file from an IFile struct */
|
||||||
|
|
||||||
|
const char* GetCurrentFileName (void);
|
||||||
/* Return the name of the current input file */
|
/* Return the name of the current input file */
|
||||||
|
|
||||||
unsigned GetCurrentLineNum (void);
|
unsigned GetCurrentLineNum (void);
|
||||||
@@ -137,7 +147,7 @@ unsigned GetCurrentLineNum (void);
|
|||||||
void SetCurrentLineNum (unsigned LineNum);
|
void SetCurrentLineNum (unsigned LineNum);
|
||||||
/* Set the line number in the current input file */
|
/* Set the line number in the current input file */
|
||||||
|
|
||||||
void SetCurrentFilename (const char* Name);
|
void SetCurrentFileName (const char* Name);
|
||||||
/* Set the presumed name of the current input file */
|
/* Set the presumed name of the current input file */
|
||||||
|
|
||||||
unsigned GetCurrentCounter (void);
|
unsigned GetCurrentCounter (void);
|
||||||
|
|||||||
@@ -56,6 +56,9 @@
|
|||||||
/* Global pointer to line information for the current line */
|
/* Global pointer to line information for the current line */
|
||||||
static LineInfo* CurLineInfo = 0;
|
static LineInfo* CurLineInfo = 0;
|
||||||
|
|
||||||
|
/* Global pointer to previously checked line information about file inclusion hierarchy */
|
||||||
|
static LineInfo* PrevCheckedLI = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -64,7 +67,7 @@ static LineInfo* CurLineInfo = 0;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line)
|
static LineInfo* NewLineInfo (const StrBuf* Line)
|
||||||
/* Create and return a new line info. Ref count will be 1. */
|
/* Create and return a new line info. Ref count will be 1. */
|
||||||
{
|
{
|
||||||
unsigned Len;
|
unsigned Len;
|
||||||
@@ -87,8 +90,9 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L
|
|||||||
|
|
||||||
/* Initialize the fields */
|
/* Initialize the fields */
|
||||||
LI->RefCount = 1;
|
LI->RefCount = 1;
|
||||||
LI->InputFile = F;
|
LI->File = 0;
|
||||||
LI->LineNum = LineNum;
|
LI->IncFiles = 0;
|
||||||
|
GetFileInclusionInfo (LI);
|
||||||
|
|
||||||
/* Copy the line, replacing tabs by spaces in the given line since tabs
|
/* Copy the line, replacing tabs by spaces in the given line since tabs
|
||||||
** will give rather arbitrary results when used in the output later, and
|
** will give rather arbitrary results when used in the output later, and
|
||||||
@@ -117,6 +121,7 @@ static LineInfo* NewLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* L
|
|||||||
static void FreeLineInfo (LineInfo* LI)
|
static void FreeLineInfo (LineInfo* LI)
|
||||||
/* Free a LineInfo structure */
|
/* Free a LineInfo structure */
|
||||||
{
|
{
|
||||||
|
FreeFileInclusionInfo (LI);
|
||||||
xfree (LI);
|
xfree (LI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,8 +161,8 @@ LineInfo* GetCurLineInfo (void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line)
|
void UpdateCurrentLineInfo (const StrBuf* Line)
|
||||||
/* Update the line info - called if a new line is read */
|
/* Update the current line info - called if a new line is read */
|
||||||
{
|
{
|
||||||
/* If a current line info exists, release it */
|
/* If a current line info exists, release it */
|
||||||
if (CurLineInfo) {
|
if (CurLineInfo) {
|
||||||
@@ -172,23 +177,60 @@ void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new line info */
|
/* Create a new line info */
|
||||||
CurLineInfo = NewLineInfo (F, LineNum, Line);
|
CurLineInfo = NewLineInfo (Line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const char* GetInputName (const LineInfo* LI)
|
void RememberCheckedLI (LineInfo* LI)
|
||||||
/* Return the file name from a line info */
|
/* Remember the latest checked line info struct */
|
||||||
|
{
|
||||||
|
if (PrevCheckedLI != LI) {
|
||||||
|
if (PrevCheckedLI != 0) {
|
||||||
|
ReleaseLineInfo (PrevCheckedLI);
|
||||||
|
}
|
||||||
|
PrevCheckedLI = UseLineInfo (LI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LineInfo* GetPrevCheckedLI (void)
|
||||||
|
/* Get the latest checked line info struct */
|
||||||
|
{
|
||||||
|
return PrevCheckedLI;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char* GetPresumedFileName (const LineInfo* LI)
|
||||||
|
/* Return the presumed file name from a line info */
|
||||||
{
|
{
|
||||||
PRECONDITION (LI != 0);
|
PRECONDITION (LI != 0);
|
||||||
return GetInputFile (LI->InputFile);
|
return LI->File->Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned GetInputLine (const LineInfo* LI)
|
unsigned GetPresumedLineNum (const LineInfo* LI)
|
||||||
/* Return the line number from a line info */
|
/* Return the presumed line number from a line info */
|
||||||
{
|
{
|
||||||
PRECONDITION (LI != 0);
|
PRECONDITION (LI != 0);
|
||||||
return LI->LineNum;
|
return LI->File->LineNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char* GetActualFileName (const struct LineInfo* LI)
|
||||||
|
/* Return the actual name of the source file from a line info struct */
|
||||||
|
{
|
||||||
|
return LI->File != 0 ? GetInputFileName (LI->File->InputFile) : "<out of filescope>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unsigned GetActualLineNum (const struct LineInfo* LI)
|
||||||
|
/* Return the actual line number of the source file from a line info struct */
|
||||||
|
{
|
||||||
|
return LI->ActualLineNum;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,15 +60,24 @@ struct IFile;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Struct that describes an input file for line info */
|
||||||
|
typedef struct LineInfoFile LineInfoFile;
|
||||||
|
struct LineInfoFile {
|
||||||
|
struct IFile* InputFile; /* Points to corresponding IFile */
|
||||||
|
unsigned LineNum; /* Presumed line number for this file */
|
||||||
|
char Name[1]; /* Presumed name of the file */
|
||||||
|
};
|
||||||
|
|
||||||
/* The text for the actual line is allocated at the end of the structure, so
|
/* The text for the actual line is allocated at the end of the structure, so
|
||||||
** the size of the structure varies.
|
** the size of the structure varies.
|
||||||
*/
|
*/
|
||||||
typedef struct LineInfo LineInfo;
|
typedef struct LineInfo LineInfo;
|
||||||
struct LineInfo {
|
struct LineInfo {
|
||||||
unsigned RefCount; /* Reference counter */
|
unsigned RefCount; /* Reference counter */
|
||||||
struct IFile* InputFile; /* Input file for this line */
|
LineInfoFile* File; /* Presumed input files for this line */
|
||||||
unsigned LineNum; /* Line number */
|
unsigned ActualLineNum; /* Actual line number for this file */
|
||||||
char Line[1]; /* Source code line */
|
struct Collection* IncFiles; /* Presumed inclusion input files */
|
||||||
|
char Line[1]; /* Text of source code line */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -92,14 +101,26 @@ LineInfo* GetCurLineInfo (void);
|
|||||||
** increased, use UseLineInfo for that purpose.
|
** increased, use UseLineInfo for that purpose.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void UpdateLineInfo (struct IFile* F, unsigned LineNum, const StrBuf* Line);
|
void UpdateCurrentLineInfo (const StrBuf* Line);
|
||||||
/* Update the line info - called if a new line is read */
|
/* Update the current line info - called if a new line is read */
|
||||||
|
|
||||||
const char* GetInputName (const LineInfo* LI);
|
void RememberCheckedLI (struct LineInfo* LI);
|
||||||
/* Return the file name from a line info */
|
/* Remember the latest checked line info struct */
|
||||||
|
|
||||||
unsigned GetInputLine (const LineInfo* LI);
|
LineInfo* GetPrevCheckedLI (void);
|
||||||
/* Return the line number from a line info */
|
/* Get the latest checked line info struct */
|
||||||
|
|
||||||
|
const char* GetPresumedFileName (const LineInfo* LI);
|
||||||
|
/* Return the presumed file name from a line info */
|
||||||
|
|
||||||
|
unsigned GetPresumedLineNum (const LineInfo* LI);
|
||||||
|
/* Return the presumed line number from a line info */
|
||||||
|
|
||||||
|
const char* GetActualFileName (const struct LineInfo* LI);
|
||||||
|
/* Return the actual name of the source file from a line info struct */
|
||||||
|
|
||||||
|
unsigned GetActualLineNum (const struct LineInfo* LI);
|
||||||
|
/* Return the actual line number of the source file from a line info struct */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -551,7 +551,9 @@ void DeclareLocals (void)
|
|||||||
/* A place to store info about potential initializations of auto variables */
|
/* A place to store info about potential initializations of auto variables */
|
||||||
CollAppend (&CurrentFunc->LocalsBlockStack, 0);
|
CollAppend (&CurrentFunc->LocalsBlockStack, 0);
|
||||||
|
|
||||||
/* Loop until we don't find any more variables */
|
/* Loop until we don't find any more variables. EOF is handled in the loop
|
||||||
|
** as well.
|
||||||
|
*/
|
||||||
while (1) {
|
while (1) {
|
||||||
DeclSpec Spec;
|
DeclSpec Spec;
|
||||||
int NeedClean;
|
int NeedClean;
|
||||||
@@ -563,7 +565,7 @@ void DeclareLocals (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Read the declaration specifier */
|
/* Read the declaration specifier */
|
||||||
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
|
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO);
|
||||||
|
|
||||||
/* Check variable declarations. We need distinguish between a default
|
/* Check variable declarations. We need distinguish between a default
|
||||||
** int type and the end of variable declarations. So we will do the
|
** int type and the end of variable declarations. So we will do the
|
||||||
|
|||||||
@@ -842,7 +842,7 @@ static void AddPreLine (StrBuf* Str)
|
|||||||
SB_AppendChar (Str, '\n');
|
SB_AppendChar (Str, '\n');
|
||||||
}
|
}
|
||||||
SB_Printf (&Comment, "#line %u \"%s\"\n",
|
SB_Printf (&Comment, "#line %u \"%s\"\n",
|
||||||
GetCurrentLineNum () - ContinuedLines, GetCurrentFilename ());
|
GetCurrentLineNum () - ContinuedLines, GetCurrentFileName ());
|
||||||
SB_Append (Str, &Comment);
|
SB_Append (Str, &Comment);
|
||||||
} else {
|
} else {
|
||||||
/* Output new lines */
|
/* Output new lines */
|
||||||
@@ -2943,7 +2943,7 @@ static void DoLine (void)
|
|||||||
StrBuf Filename = AUTO_STRBUF_INITIALIZER;
|
StrBuf Filename = AUTO_STRBUF_INITIALIZER;
|
||||||
if (SB_GetString (Line, &Filename)) {
|
if (SB_GetString (Line, &Filename)) {
|
||||||
SB_Terminate (&Filename);
|
SB_Terminate (&Filename);
|
||||||
SetCurrentFilename (SB_GetConstBuf (&Filename));
|
SetCurrentFileName (SB_GetConstBuf (&Filename));
|
||||||
} else {
|
} else {
|
||||||
PPError ("Invalid filename for #line directive");
|
PPError ("Invalid filename for #line directive");
|
||||||
LineNum = 0;
|
LineNum = 0;
|
||||||
@@ -3220,7 +3220,7 @@ void HandleSpecialMacro (Macro* M, const char* Name)
|
|||||||
} else if (strcmp (Name, "__FILE__") == 0) {
|
} else if (strcmp (Name, "__FILE__") == 0) {
|
||||||
/* Replace __FILE__ with the current filename */
|
/* Replace __FILE__ with the current filename */
|
||||||
StrBuf B = AUTO_STRBUF_INITIALIZER;
|
StrBuf B = AUTO_STRBUF_INITIALIZER;
|
||||||
SB_InitFromString (&B, GetCurrentFilename ());
|
SB_InitFromString (&B, GetCurrentFileName ());
|
||||||
SB_Clear (&M->Replacement);
|
SB_Clear (&M->Replacement);
|
||||||
Stringize (&B, &M->Replacement);
|
Stringize (&B, &M->Replacement);
|
||||||
SB_Done (&B);
|
SB_Done (&B);
|
||||||
@@ -3332,7 +3332,7 @@ void Preprocess (void)
|
|||||||
PLine = InitLine (PLine);
|
PLine = InitLine (PLine);
|
||||||
|
|
||||||
if (Verbosity > 1 && SB_NotEmpty (Line)) {
|
if (Verbosity > 1 && SB_NotEmpty (Line)) {
|
||||||
printf ("%s:%u: %.*s\n", GetCurrentFilename (), GetCurrentLineNum (),
|
printf ("%s:%u: %.*s\n", GetCurrentFileName (), GetCurrentLineNum (),
|
||||||
(int) SB_GetLen (Line), SB_GetConstBuf (Line));
|
(int) SB_GetLen (Line), SB_GetConstBuf (Line));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ typedef enum token_t {
|
|||||||
|
|
||||||
/* Function specifiers */
|
/* Function specifiers */
|
||||||
TOK_INLINE,
|
TOK_INLINE,
|
||||||
|
TOK_NORETURN,
|
||||||
TOK_FASTCALL,
|
TOK_FASTCALL,
|
||||||
TOK_CDECL,
|
TOK_CDECL,
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,10 @@ struct CodeEntry;
|
|||||||
#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */
|
#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */
|
||||||
#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */
|
#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */
|
||||||
|
|
||||||
|
/* Function specifiers */
|
||||||
|
#define SC_INLINE 0x10000000U /* Inline function */
|
||||||
|
#define SC_NORETURN 0x20000000U /* Noreturn function */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Label definition or reference */
|
/* Label definition or reference */
|
||||||
|
|||||||
@@ -170,7 +170,8 @@ static void CheckSymTable (SymTable* Tab)
|
|||||||
if (SymIsDef (Entry) && !SymIsRef (Entry) &&
|
if (SymIsDef (Entry) && !SymIsRef (Entry) &&
|
||||||
!SymHasAttr (Entry, atUnused)) {
|
!SymHasAttr (Entry, atUnused)) {
|
||||||
if (Flags & SC_PARAM) {
|
if (Flags & SC_PARAM) {
|
||||||
if (IS_Get (&WarnUnusedParam)) {
|
if (IS_Get (&WarnUnusedParam) &&
|
||||||
|
!IsAnonName (Entry->Name)) {
|
||||||
Warning ("Parameter '%s' is never used", Entry->Name);
|
Warning ("Parameter '%s' is never used", Entry->Name);
|
||||||
}
|
}
|
||||||
} else if ((Flags & SC_TYPEMASK) == SC_FUNC) {
|
} else if ((Flags & SC_TYPEMASK) == SC_FUNC) {
|
||||||
@@ -557,8 +558,10 @@ static SymEntry* FindSymInTable (const SymTable* T, const char* Name, unsigned H
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
|
static SymEntry* FindVisibleSymInTree (const SymTable* Tab, const char* Name)
|
||||||
/* Find the symbol with the given name in the table tree that starts with T */
|
/* Find the visible symbol with the given name in the table tree that starts
|
||||||
|
** with Tab.
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
/* Get the hash over the name */
|
/* Get the hash over the name */
|
||||||
unsigned Hash = HashStr (Name);
|
unsigned Hash = HashStr (Name);
|
||||||
@@ -574,7 +577,7 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Bail out if we found it */
|
/* Bail out if we found it */
|
||||||
if (E != 0) {
|
if (E != 0 && (Tab != SymTab0 || (E->Flags & SC_LOCALSCOPE) == 0)) {
|
||||||
return E;
|
return E;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,9 +592,9 @@ static SymEntry* FindSymInTree (const SymTable* Tab, const char* Name)
|
|||||||
|
|
||||||
|
|
||||||
SymEntry* FindSym (const char* Name)
|
SymEntry* FindSym (const char* Name)
|
||||||
/* Find the symbol with the given name */
|
/* Find with the given name the symbol visible in the current scope */
|
||||||
{
|
{
|
||||||
return FindSymInTree (SymTab, Name);
|
return FindVisibleSymInTree (SymTab, Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -613,9 +616,9 @@ SymEntry* FindLocalSym (const char* Name)
|
|||||||
|
|
||||||
|
|
||||||
SymEntry* FindTagSym (const char* Name)
|
SymEntry* FindTagSym (const char* Name)
|
||||||
/* Find the symbol with the given name in the tag table */
|
/* Find with the given name the tag symbol visible in the current scope */
|
||||||
{
|
{
|
||||||
return FindSymInTree (TagTab, Name);
|
return FindVisibleSymInTree (TagTab, Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -954,7 +957,13 @@ SymEntry* AddStructSym (const char* Name, unsigned Flags, unsigned Size, SymTabl
|
|||||||
TagEntry = 0;
|
TagEntry = 0;
|
||||||
} else if (Size == 0) {
|
} else if (Size == 0) {
|
||||||
/* Empty struct is not supported now */
|
/* Empty struct is not supported now */
|
||||||
Error ("Empty %s type '%s' is not supported", SCType == SC_STRUCT ? "struct" : "union", Name);
|
if (!IsAnonName (Name)) {
|
||||||
|
Error ("Empty %s type '%s' is not supported",
|
||||||
|
SCType == SC_STRUCT ? "struct" : "union", Name);
|
||||||
|
} else {
|
||||||
|
Error ("Empty %s type is not supported",
|
||||||
|
SCType == SC_STRUCT ? "struct" : "union");
|
||||||
|
}
|
||||||
TagEntry = 0;
|
TagEntry = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1356,6 +1365,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
|
|||||||
Name);
|
Name);
|
||||||
Entry = 0;
|
Entry = 0;
|
||||||
} else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) {
|
} else if ((Flags & SC_TYPEMASK) != SC_TYPEDEF) {
|
||||||
|
/* If we are adding the symbol in the file scope, it is now
|
||||||
|
** visible there.
|
||||||
|
*/
|
||||||
|
if (SymTab == SymTab0) {
|
||||||
|
Entry->Flags &= ~SC_LOCALSCOPE;
|
||||||
|
}
|
||||||
|
|
||||||
/* The C standard specifies that the result is undefined if the
|
/* The C standard specifies that the result is undefined if the
|
||||||
** same thing has both internal and external linkage. Most
|
** same thing has both internal and external linkage. Most
|
||||||
** compilers choose to either give an error at compile time, or
|
** compilers choose to either give an error at compile time, or
|
||||||
@@ -1415,6 +1431,13 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Entry == 0) {
|
if (Entry == 0) {
|
||||||
|
/* Hide the symbol in the file scope if we are declaring it in a
|
||||||
|
** local scope.
|
||||||
|
*/
|
||||||
|
if (Tab == SymTab0 && SymTab != SymTab0) {
|
||||||
|
Flags |= SC_LOCALSCOPE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a new entry */
|
/* Create a new entry */
|
||||||
Entry = NewSymEntry (Name, Flags);
|
Entry = NewSymEntry (Name, Flags);
|
||||||
|
|
||||||
@@ -1439,6 +1462,16 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
|
|||||||
Entry->V.F.WrappedCall = WrappedCall;
|
Entry->V.F.WrappedCall = WrappedCall;
|
||||||
Entry->V.F.WrappedCallData = WrappedCallData;
|
Entry->V.F.WrappedCallData = WrappedCallData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A files cope function declaration with the 'extern' storage
|
||||||
|
** class or without the 'inline' specifier ensures that the
|
||||||
|
** function definition (if any) is a non-inline definition.
|
||||||
|
*/
|
||||||
|
if (SymTab == SymTab0 &&
|
||||||
|
((Flags & SC_STORAGEMASK) == SC_EXTERN ||
|
||||||
|
(Flags & SC_INLINE) == 0)) {
|
||||||
|
Entry->Flags |= SC_NOINLINEDEF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add an alias of the global symbol to the local symbol table */
|
/* Add an alias of the global symbol to the local symbol table */
|
||||||
@@ -1575,7 +1608,7 @@ void EmitExternals (void)
|
|||||||
if (SymIsRef (Entry) && !SymIsDef (Entry)) {
|
if (SymIsRef (Entry) && !SymIsDef (Entry)) {
|
||||||
/* An import */
|
/* An import */
|
||||||
g_defimport (Entry->Name, Flags & SC_ZEROPAGE);
|
g_defimport (Entry->Name, Flags & SC_ZEROPAGE);
|
||||||
} else if (SymIsDef (Entry)) {
|
} else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) {
|
||||||
/* An export */
|
/* An export */
|
||||||
g_defexport (Entry->Name, Flags & SC_ZEROPAGE);
|
g_defexport (Entry->Name, Flags & SC_ZEROPAGE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ void LeaveStructLevel (void);
|
|||||||
|
|
||||||
|
|
||||||
SymEntry* FindSym (const char* Name);
|
SymEntry* FindSym (const char* Name);
|
||||||
/* Find the symbol with the given name */
|
/* Find with the given name the symbol visible in the current scope */
|
||||||
|
|
||||||
SymEntry* FindGlobalSym (const char* Name);
|
SymEntry* FindGlobalSym (const char* Name);
|
||||||
/* Find the symbol with the given name in the global symbol table only */
|
/* Find the symbol with the given name in the global symbol table only */
|
||||||
@@ -151,7 +151,7 @@ SymEntry* FindLocalSym (const char* Name);
|
|||||||
/* Find the symbol with the given name in the current symbol table only */
|
/* Find the symbol with the given name in the current symbol table only */
|
||||||
|
|
||||||
SymEntry* FindTagSym (const char* Name);
|
SymEntry* FindTagSym (const char* Name);
|
||||||
/* Find the symbol with the given name in the tag table */
|
/* Find with the given name the tag symbol visible in the current scope */
|
||||||
|
|
||||||
SymEntry FindStructField (const Type* TypeArray, const char* Name);
|
SymEntry FindStructField (const Type* TypeArray, const char* Name);
|
||||||
/* Find a struct/union field in the fields list.
|
/* Find a struct/union field in the fields list.
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ static void PVArgs (CPURegs* Regs)
|
|||||||
|
|
||||||
static void PVOpen (CPURegs* Regs)
|
static void PVOpen (CPURegs* Regs)
|
||||||
{
|
{
|
||||||
char Path[PVOPEN_PATH_SIZE];
|
char Path[PV_PATH_SIZE];
|
||||||
int OFlag = O_INITIAL;
|
int OFlag = O_INITIAL;
|
||||||
int OMode = 0;
|
int OMode = 0;
|
||||||
unsigned RetVal, I = 0;
|
unsigned RetVal, I = 0;
|
||||||
@@ -184,7 +184,7 @@ static void PVOpen (CPURegs* Regs)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++I;
|
++I;
|
||||||
if (I >= PVOPEN_PATH_SIZE) {
|
if (I >= PV_PATH_SIZE) {
|
||||||
Error("PVOpen path too long at address $%04X",Name);
|
Error("PVOpen path too long at address $%04X",Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,6 +253,35 @@ static void PVClose (CPURegs* Regs)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void PVSysRemove (CPURegs* Regs)
|
||||||
|
{
|
||||||
|
char Path[PV_PATH_SIZE];
|
||||||
|
unsigned RetVal, I = 0;
|
||||||
|
|
||||||
|
unsigned Name = GetAX (Regs);
|
||||||
|
|
||||||
|
Print (stderr, 2, "PVSysRemove ($%04X)\n", Name);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++I;
|
||||||
|
if (I >= PV_PATH_SIZE) {
|
||||||
|
Error("PVSysRemove path too long at address $%04X", Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (1);
|
||||||
|
|
||||||
|
Print (stderr, 2, "PVSysRemove (\"%s\")\n", Path);
|
||||||
|
|
||||||
|
RetVal = remove (Path);
|
||||||
|
|
||||||
|
SetAX (Regs, RetVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void PVRead (CPURegs* Regs)
|
static void PVRead (CPURegs* Regs)
|
||||||
{
|
{
|
||||||
unsigned char* Data;
|
unsigned char* Data;
|
||||||
@@ -305,7 +334,17 @@ static void PVWrite (CPURegs* Regs)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void PVOSMapErrno (CPURegs* Regs)
|
||||||
|
{
|
||||||
|
unsigned err = GetAX(Regs);
|
||||||
|
SetAX (Regs, err != 0 ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const PVFunc Hooks[] = {
|
static const PVFunc Hooks[] = {
|
||||||
|
PVSysRemove,
|
||||||
|
PVOSMapErrno,
|
||||||
PVOpen,
|
PVOpen,
|
||||||
PVClose,
|
PVClose,
|
||||||
PVRead,
|
PVRead,
|
||||||
|
|||||||
@@ -44,11 +44,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define PARAVIRT_BASE 0xFFF4
|
#define PARAVIRT_BASE 0xFFF2
|
||||||
/* Lowest address used by a paravirtualization hook */
|
/* Lowest address used by a paravirtualization hook */
|
||||||
|
|
||||||
#define PVOPEN_PATH_SIZE 1024
|
#define PV_PATH_SIZE 1024
|
||||||
/* Maximum path size supported by PVOpen */
|
/* Maximum path size supported by PVOpen/PVSysRemove */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
test/err/bug2304-var-use.c
Normal file
15
test/err/bug2304-var-use.c
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */
|
||||||
|
|
||||||
|
void f1(void)
|
||||||
|
{
|
||||||
|
extern int a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'a' is still invisible in the file scope */
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return a * 0; /* Usage of 'a' should be an error */
|
||||||
|
}
|
||||||
|
|
||||||
|
int a = 42;
|
||||||
@@ -133,6 +133,12 @@ $(WORKDIR)/goto.$1.$2.prg: goto.c $(ISEQUAL) | $(WORKDIR)
|
|||||||
$(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out
|
$(CC65) -t sim$2 -$1 -o $$@ $$< 2>$(WORKDIR)/goto.$1.$2.out
|
||||||
$(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref
|
$(ISEQUAL) $(WORKDIR)/goto.$1.$2.out goto.ref
|
||||||
|
|
||||||
|
# this one requires failure with --std=c89, it fails with --std=cc65 due to
|
||||||
|
# stricter checks
|
||||||
|
$(WORKDIR)/bug2304-implicit-func.$1.$2.prg: bug2304-implicit-func.c | $(WORKDIR)
|
||||||
|
$(if $(QUIET),echo misc/bug2304-implicit-func.$1.$2.prg)
|
||||||
|
$(NOT) $(CC65) --standard c89 -t sim$2 -$1 -o $$@ $$< $(NULLERR)
|
||||||
|
|
||||||
# should not compile until 3-byte struct by value tests are re-enabled
|
# should not compile until 3-byte struct by value tests are re-enabled
|
||||||
$(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR)
|
$(WORKDIR)/struct-by-value.$1.$2.prg: struct-by-value.c | $(WORKDIR)
|
||||||
$(if $(QUIET),echo misc/struct-by-value.$1.$2.prg)
|
$(if $(QUIET),echo misc/struct-by-value.$1.$2.prg)
|
||||||
|
|||||||
21
test/misc/bug2304-implicit-func.c
Normal file
21
test/misc/bug2304-implicit-func.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/* Bug 2304 - Visibility of objects/functions undeclared in file scope but 'extern'-declared in unrelated block scopes */
|
||||||
|
|
||||||
|
/* This one should fail even in C89 */
|
||||||
|
|
||||||
|
void f1(void)
|
||||||
|
{
|
||||||
|
extern unsigned int f();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'f' is still invisible in the file scope */
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
f(); /* Should be a conflict since the implicit function type is incompatible */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int f()
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ CUSTOMSOURCES = \
|
|||||||
# exact error output is required
|
# exact error output is required
|
||||||
ERRORSOURCES = \
|
ERRORSOURCES = \
|
||||||
custom-reference-error.c \
|
custom-reference-error.c \
|
||||||
|
inline-error.c \
|
||||||
bug1889-missing-identifier.c \
|
bug1889-missing-identifier.c \
|
||||||
bug2312-preprocessor-error.c
|
bug2312-preprocessor-error.c
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
|
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
|
||||||
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature
|
bug1889-missing-identifier.c:3: Warning: Implicit 'int' type specifier is an obsolete feature
|
||||||
bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected
|
bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration
|
||||||
bug1889-missing-identifier.c:6: Error: Expression expected
|
bug1889-missing-identifier.c:6: Error: Expression expected
|
||||||
|
|||||||
38
test/ref/inline-error.c
Normal file
38
test/ref/inline-error.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* C99 inline in declarations */
|
||||||
|
|
||||||
|
inline typedef int; /* Error */
|
||||||
|
static inline int; /* Error */
|
||||||
|
inline static int a1; /* Error */
|
||||||
|
int inline (*fp1)(void); /* Error */
|
||||||
|
typedef inline int f1_t(void); /* Error */
|
||||||
|
inline int f1a(void); /* OK here warning later */
|
||||||
|
inline extern int f1b(void); /* OK here warning later */
|
||||||
|
extern inline int f1b(void); /* Same as above */
|
||||||
|
inline static int f1c(void); /* OK here warning later */
|
||||||
|
static inline int f1c(void); /* Same as above */
|
||||||
|
|
||||||
|
void foo(inline int x); /* Error */
|
||||||
|
int a = sizeof (inline int); /* TODO: better error message */
|
||||||
|
int b = sizeof (inline int (int)); /* TODO: better error message */
|
||||||
|
|
||||||
|
inline int main(void) /* Error */
|
||||||
|
{
|
||||||
|
inline typedef int; /* Error */
|
||||||
|
static inline int; /* Error */
|
||||||
|
extern inline int a2; /* Error */
|
||||||
|
int inline (*fp2)(void); /* Error */
|
||||||
|
typedef inline int f2_t(void); /* Error */
|
||||||
|
inline int f2a(void); /* OK here warning later */
|
||||||
|
inline extern int f2b(void); /* OK here warning later */
|
||||||
|
extern inline int f2b(void); /* Same as above */
|
||||||
|
|
||||||
|
f1a(); /* Still imported */
|
||||||
|
f1b(); /* Still imported */
|
||||||
|
f1c(); /* Not imported */
|
||||||
|
f2a(); /* Still imported */
|
||||||
|
f2b(); /* Still imported */
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int main(void); /* Error */
|
||||||
|
|
||||||
|
/* Warning: non-external inline functions declared but undefined in TU */
|
||||||
21
test/ref/inline-error.cref
Normal file
21
test/ref/inline-error.cref
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
inline-error.c:3: Error: 'inline' on empty declaration
|
||||||
|
inline-error.c:4: Error: 'inline' on empty declaration
|
||||||
|
inline-error.c:5: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:6: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:7: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:14: Error: Unexpected function specifiers
|
||||||
|
inline-error.c:15: Error: Mixed declarations and code are not supported in cc65
|
||||||
|
inline-error.c:16: Error: Mixed declarations and code are not supported in cc65
|
||||||
|
inline-error.c:19: Error: 'main' cannot be declared inline
|
||||||
|
inline-error.c:20: Error: 'inline' on empty declaration
|
||||||
|
inline-error.c:21: Error: 'inline' on empty declaration
|
||||||
|
inline-error.c:22: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:23: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:24: Error: 'inline' on non-function declaration
|
||||||
|
inline-error.c:34: Warning: Variable 'fp2' is defined but never used
|
||||||
|
inline-error.c:36: Error: 'main' cannot be declared inline
|
||||||
|
inline-error.c:39: Warning: Inline function 'f1a' used but never defined
|
||||||
|
inline-error.c:39: Warning: Inline function 'f1b' used but never defined
|
||||||
|
inline-error.c:39: Warning: Static function 'f1c' used but never defined
|
||||||
|
inline-error.c:39: Warning: Inline function 'f2a' used but never defined
|
||||||
|
inline-error.c:39: Warning: Inline function 'f2b' used but never defined
|
||||||
67
test/ref/test_fgets.c
Normal file
67
test/ref/test_fgets.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
!!DESCRIPTION!! fgets test
|
||||||
|
!!LICENCE!! Public domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
FILE *in, *out;
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
#define INFILE "cf.in"
|
||||||
|
|
||||||
|
int main(int argc,char **argv)
|
||||||
|
{
|
||||||
|
static char outfile_path[FILENAME_MAX+1];
|
||||||
|
|
||||||
|
sprintf(outfile_path, "%s.test.out", argv[0]);
|
||||||
|
|
||||||
|
out = fopen(outfile_path, "wb");
|
||||||
|
if (out == NULL) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (fgets(buf, sizeof(buf), out) != NULL) {
|
||||||
|
printf("Error, could fgets with write-only file\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!ferror(out)) {
|
||||||
|
printf("Error: file pointer should be in error state\n");
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
unlink(outfile_path);
|
||||||
|
|
||||||
|
in = fopen(INFILE, "rb");
|
||||||
|
if (in == NULL) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(NULL, 0, in) != NULL) {
|
||||||
|
printf("Error, could fgets with zero size\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test ungetc while we're at it */
|
||||||
|
buf[0] = fgetc(in);
|
||||||
|
ungetc(buf[0], in);
|
||||||
|
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), in) != NULL)
|
||||||
|
{
|
||||||
|
printf("%s",buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!feof(in))
|
||||||
|
{
|
||||||
|
printf("We should have EOF!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
46
test/ref/test_gets.c
Normal file
46
test/ref/test_gets.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
!!DESCRIPTION!! gets test
|
||||||
|
!!LICENCE!! Public domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
#define INFILE "cf.in"
|
||||||
|
|
||||||
|
#ifndef __CC65__
|
||||||
|
/* Force declaration on host compiler, as gets() is deprecated for
|
||||||
|
* being dangerous as hell */
|
||||||
|
char *gets (char *__s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef NO_OLD_FUNC_DECL
|
||||||
|
int main(int argc,char **argv)
|
||||||
|
#else
|
||||||
|
main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* Fake stdin with the reference file */
|
||||||
|
fclose(stdin);
|
||||||
|
stdin = fopen(INFILE, "r");
|
||||||
|
if (stdin == NULL) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (gets(buf) != NULL)
|
||||||
|
{
|
||||||
|
printf("%s",buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(stdin);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
test/val/bug2357.c
Normal file
38
test/val/bug2357.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* bug #2357 - Compiler produces invalid code after d8a3938
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long test;
|
||||||
|
|
||||||
|
unsigned long longarray[7];
|
||||||
|
|
||||||
|
void jsr_threebytes(void) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* having replaced two sty $zp with two sta $abs, but forgetting
|
||||||
|
* to update the instruction size, coptlong.c could cause a build
|
||||||
|
* error "Error: Range error (131 not in [-128..127])" if the
|
||||||
|
* computed codesize was under 126, but the real codesize was above
|
||||||
|
* 127.
|
||||||
|
* This tests verifies that the bug is fixed.
|
||||||
|
*/
|
||||||
|
unsigned char __fastcall__ foo (unsigned char res)
|
||||||
|
{
|
||||||
|
if (res == 0) {
|
||||||
|
longarray[1]=test; /* 24 bytes - but the compiler thought 22 */
|
||||||
|
longarray[2]=test; /* 48 bytes - but 44 */
|
||||||
|
longarray[3]=test; /* 72 bytes - 66 */
|
||||||
|
longarray[4]=test; /* 96 bytes - 88 */
|
||||||
|
longarray[6]=test; /* 120 bytes - 110 */
|
||||||
|
jsr_threebytes(); /* 123 - 113 */
|
||||||
|
jsr_threebytes(); /* 126 - 116 */
|
||||||
|
jsr_threebytes(); /* 129 - 119 */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
foo(42);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
20
test/val/inline-func.c
Normal file
20
test/val/inline-func.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* C99 inline */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
inline static int f(int x, ...)
|
||||||
|
{
|
||||||
|
return x * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern inline int g(int x);
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int g(int x)
|
||||||
|
{
|
||||||
|
return x * 3;
|
||||||
|
}
|
||||||
@@ -4,81 +4,107 @@
|
|||||||
|
|
||||||
int fails = 0;
|
int fails = 0;
|
||||||
|
|
||||||
time_t timestamps[] = {
|
typedef struct _test_data {
|
||||||
0,
|
time_t t;
|
||||||
0x2FFFFFFF,
|
char *gmt;
|
||||||
0x6FFFFFFF,
|
char *local;
|
||||||
0xF48656FF,
|
} test_data;
|
||||||
0xF4865700,
|
|
||||||
0xFC5A3EFF,
|
|
||||||
0x6D6739FF,
|
|
||||||
0x6D673A00,
|
|
||||||
0xFFFFFFFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Values checked against glibc 2.37's implementation of ctime() */
|
/* Test data generated using glibc 2.37 */
|
||||||
const char *dates_gmt[] = {
|
test_data data[] = {
|
||||||
"Thu Jan 1 00:00:00 1970\n",
|
/* First year */
|
||||||
"Sun Jul 9 16:12:47 1995\n",
|
{0x00000000, "Thu Jan 1 00:00:00 1970\n", "Thu Jan 1 01:00:00 1970\n"},
|
||||||
"Wed Jul 18 05:49:51 2029\n",
|
{0x004e7970, "Sun Mar 1 12:34:56 1970\n", "Sun Mar 1 13:34:56 1970\n"},
|
||||||
"Thu Dec 31 23:59:59 2099\n",
|
{0x01e1337f, "Thu Dec 31 23:59:59 1970\n", "Fri Jan 1 00:59:59 1971\n"},
|
||||||
"Fri Jan 1 00:00:00 2100\n",
|
|
||||||
"Fri Feb 29 23:59:59 2104\n",
|
|
||||||
"Tue Feb 29 23:59:59 2028\n",
|
|
||||||
"Wed Mar 1 00:00:00 2028\n",
|
|
||||||
"Sun Feb 7 06:28:15 2106\n",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *dates_gmt_plus_one[] = {
|
/* First leap year */
|
||||||
"Thu Jan 1 01:00:00 1970\n",
|
{0x03c26700, "Sat Jan 1 00:00:00 1972\n", "Sat Jan 1 01:00:00 1972\n"},
|
||||||
"Sun Jul 9 17:12:47 1995\n",
|
{0x03c8fe7f, "Wed Jan 5 23:59:59 1972\n", "Thu Jan 6 00:59:59 1972\n"},
|
||||||
"Wed Jul 18 06:49:51 2029\n",
|
{0x041180ff, "Tue Feb 29 23:59:59 1972\n", "Wed Mar 1 00:59:59 1972\n"},
|
||||||
"Fri Jan 1 00:59:59 2100\n",
|
{0x04118100, "Wed Mar 1 00:00:00 1972\n", "Wed Mar 1 01:00:00 1972\n"},
|
||||||
"Fri Jan 1 01:00:00 2100\n",
|
{0x05a4ebff, "Sun Dec 31 23:59:59 1972\n", "Mon Jan 1 00:59:59 1973\n"},
|
||||||
"Sat Mar 1 00:59:59 2104\n",
|
|
||||||
"Wed Mar 1 00:59:59 2028\n",
|
/* A non-leap year */
|
||||||
"Wed Mar 1 01:00:00 2028\n",
|
{0x63b0cd00, "Sun Jan 1 00:00:00 2023\n", "Sun Jan 1 01:00:00 2023\n"},
|
||||||
"Thu Jan 1 00:59:59 1970\n",
|
{0x63fe957f, "Tue Feb 28 23:59:59 2023\n", "Wed Mar 1 00:59:59 2023\n"},
|
||||||
NULL
|
{0x63fe9580, "Wed Mar 1 00:00:00 2023\n", "Wed Mar 1 01:00:00 2023\n"},
|
||||||
|
{0x656d4ec0, "Mon Dec 4 04:00:00 2023\n", "Mon Dec 4 05:00:00 2023\n"},
|
||||||
|
{0x6592007f, "Sun Dec 31 23:59:59 2023\n", "Mon Jan 1 00:59:59 2024\n"},
|
||||||
|
|
||||||
|
/* Another leap year */
|
||||||
|
{0x65920080, "Mon Jan 1 00:00:00 2024\n", "Mon Jan 1 01:00:00 2024\n"},
|
||||||
|
{0x65e11a7f, "Thu Feb 29 23:59:59 2024\n", "Fri Mar 1 00:59:59 2024\n"},
|
||||||
|
{0x65e11a80, "Fri Mar 1 00:00:00 2024\n", "Fri Mar 1 01:00:00 2024\n"},
|
||||||
|
{0x6774857f, "Tue Dec 31 23:59:59 2024\n", "Wed Jan 1 00:59:59 2025\n"},
|
||||||
|
|
||||||
|
/* End of century */
|
||||||
|
{0xf48656ff, "Thu Dec 31 23:59:59 2099\n", "Fri Jan 1 00:59:59 2100\n"},
|
||||||
|
|
||||||
|
/* A non-leap year for exceptional reasons */
|
||||||
|
{0xf4865700, "Fri Jan 1 00:00:00 2100\n", "Fri Jan 1 01:00:00 2100\n"},
|
||||||
|
{0xf4d41f7f, "Sun Feb 28 23:59:59 2100\n", "Mon Mar 1 00:59:59 2100\n"},
|
||||||
|
{0xf4d41f80, "Mon Mar 1 00:00:00 2100\n", "Mon Mar 1 01:00:00 2100\n"},
|
||||||
|
{0xf4fceff0, "Wed Mar 31 23:00:00 2100\n", "Thu Apr 1 00:00:00 2100\n"},
|
||||||
|
{0xf6678a7f, "Fri Dec 31 23:59:59 2100\n", "Sat Jan 1 00:59:59 2101\n"},
|
||||||
|
|
||||||
|
/* First post-2100 leap year */
|
||||||
|
{0xfc0b2500, "Tue Jan 1 00:00:00 2104\n", "Tue Jan 1 01:00:00 2104\n"},
|
||||||
|
{0xfc5a3eff, "Fri Feb 29 23:59:59 2104\n", "Sat Mar 1 00:59:59 2104\n"},
|
||||||
|
{0xfc5a3f00, "Sat Mar 1 00:00:00 2104\n", "Sat Mar 1 01:00:00 2104\n"},
|
||||||
|
{0xfcaa9c70, "Wed Apr 30 23:00:00 2104\n", "Thu May 1 00:00:00 2104\n"},
|
||||||
|
|
||||||
|
/* End of epoch */
|
||||||
|
{0xfdedaa00, "Thu Jan 1 00:00:00 2105\n", "Thu Jan 1 01:00:00 2105\n"},
|
||||||
|
{0xffffffff, "Sun Feb 7 06:28:15 2106\n", "Thu Jan 1 00:59:59 1970\n"}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main (void)
|
int main (void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct tm *tm;
|
||||||
|
char *str;
|
||||||
|
|
||||||
for (i = 0; dates_gmt[i] != NULL; i++) {
|
tm = gmtime(NULL);
|
||||||
struct tm *tm;
|
if (tm != NULL) {
|
||||||
char *str;
|
printf("gmtime should return NULL with a NULL parameter\n");
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check gmtime */
|
tm = localtime(NULL);
|
||||||
tm = gmtime(×tamps[i]);
|
if (tm != NULL) {
|
||||||
|
printf("localtime should return NULL with a NULL parameter\n");
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify conversion both ways */
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
time_t t = data[i].t;
|
||||||
|
|
||||||
|
tm = gmtime(&t);
|
||||||
str = asctime(tm);
|
str = asctime(tm);
|
||||||
if (strcmp(str, dates_gmt[i])) {
|
if (strcmp(data[i].gmt, str)) {
|
||||||
|
printf("0x%lx: gmtime: unexpected result: expected %s, got %s\n", t, data[i].gmt, str);
|
||||||
fails++;
|
fails++;
|
||||||
printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
|
||||||
timestamps[i], dates_gmt[i], str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check localtime with UTC timezone */
|
|
||||||
_tz.timezone = 0;
|
_tz.timezone = 0;
|
||||||
tm = localtime(×tamps[i]);
|
tm = localtime(&t);
|
||||||
str = asctime(tm);
|
str = asctime(tm);
|
||||||
if (strcmp(str, dates_gmt[i])) {
|
if (strcmp(data[i].gmt, str)) {
|
||||||
|
printf("0x%lx: localtime (UTC+0): unexpected result: expected %s, got %s\n", t, data[i].gmt, str);
|
||||||
fails++;
|
fails++;
|
||||||
printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
|
||||||
timestamps[i], dates_gmt[i], str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check localtime at UTC+1 */
|
|
||||||
_tz.timezone = 3600;
|
_tz.timezone = 3600;
|
||||||
tm = localtime(×tamps[i]);
|
tm = localtime(&t);
|
||||||
str = asctime(tm);
|
str = asctime(tm);
|
||||||
if (strcmp(str, dates_gmt_plus_one[i])) {
|
if (strcmp(data[i].local, str)) {
|
||||||
|
printf("0x%lx: localtime (UTC+1): unexpected result: expected %s, got %s\n", t, data[i].local, str);
|
||||||
fails++;
|
fails++;
|
||||||
printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
|
||||||
timestamps[i], dates_gmt_plus_one[i], str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t == 0xFFFFFFFF)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return fails;
|
return fails;
|
||||||
}
|
}
|
||||||
|
|||||||
34
test/val/lib_common_malloc.c
Normal file
34
test/val/lib_common_malloc.c
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "unittest.h"
|
||||||
|
|
||||||
|
TEST
|
||||||
|
{
|
||||||
|
char *buf, *buf2;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
buf = malloc(0);
|
||||||
|
ASSERT_IsTrue (buf == NULL, "malloc (0) returned something");
|
||||||
|
|
||||||
|
for (i = 1; i < 10; i++) {
|
||||||
|
buf = malloc(i);
|
||||||
|
ASSERT_IsTrue (buf != NULL, "small returned nothing");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(4096);
|
||||||
|
ASSERT_IsTrue (buf != NULL, "malloc (4096) returned nothing");
|
||||||
|
|
||||||
|
buf = malloc(61000UL);
|
||||||
|
ASSERT_IsTrue (buf == NULL, "malloc (61000) returned something");
|
||||||
|
|
||||||
|
for (i = 65535UL; i > _heapmaxavail(); i--) {
|
||||||
|
buf = malloc(i);
|
||||||
|
ASSERT_IsTrue (buf == NULL, "malloc returned something but shouldn't have");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(i);
|
||||||
|
ASSERT_IsTrue (buf != NULL, "malloc returned nothing but should have");
|
||||||
|
ASSERT_IsTrue(_heapmaxavail() == 0, "heapmaxavail should be 0");
|
||||||
|
}
|
||||||
|
ENDTEST
|
||||||
@@ -4,58 +4,103 @@
|
|||||||
|
|
||||||
int fails = 0;
|
int fails = 0;
|
||||||
|
|
||||||
time_t timestamps[] = {
|
typedef struct _test_data {
|
||||||
0,
|
time_t t;
|
||||||
0x2FFFFFFF,
|
struct tm tm;
|
||||||
0x6FFFFFFF,
|
char *str;
|
||||||
0xF48656FF,
|
} test_data;
|
||||||
0xF4865700,
|
|
||||||
0xFC5A3EFF,
|
/* Test data generated using glibc 2.37 */
|
||||||
0x6D6739FF,
|
test_data data[] = {
|
||||||
0x6D673A00,
|
/* First year */
|
||||||
0xFFFFFFFF,
|
{0x00000000, {0, 0, 0, 1, 0, 70, 0, 4}, "Thu Jan 1 00:00:00 1970\n"},
|
||||||
|
{0x004e7970, {56, 34, 12, 1, 2, 70, 59, 0}, "Sun Mar 1 12:34:56 1970\n"},
|
||||||
|
{0x01e1337f, {59, 59, 23, 31, 11, 70, 364, 4}, "Thu Dec 31 23:59:59 1970\n"},
|
||||||
|
|
||||||
|
/* First leap year */
|
||||||
|
{0x03c26700, {0, 0, 0, 1, 0, 72, 0, 6}, "Sat Jan 1 00:00:00 1972\n"},
|
||||||
|
{0x03c8fe7f, {59, 59, 23, 5, 0, 72, 4, 3}, "Wed Jan 5 23:59:59 1972\n"},
|
||||||
|
{0x041180ff, {59, 59, 23, 29, 1, 72, 59, 2}, "Tue Feb 29 23:59:59 1972\n"},
|
||||||
|
{0x04118100, {0, 0, 0, 1, 2, 72, 60, 3}, "Wed Mar 1 00:00:00 1972\n"},
|
||||||
|
{0x05a4ebff, {59, 59, 23, 31, 11, 72, 365, 0}, "Sun Dec 31 23:59:59 1972\n"},
|
||||||
|
|
||||||
|
/* A non-leap year */
|
||||||
|
{0x63b0cd00, {0, 0, 0, 1, 0, 123, 0, 0}, "Sun Jan 1 00:00:00 2023\n"},
|
||||||
|
{0x63fe957f, {59, 59, 23, 28, 1, 123, 58, 2}, "Tue Feb 28 23:59:59 2023\n"},
|
||||||
|
{0x63fe9580, {0, 0, 0, 1, 2, 123, 59, 3}, "Wed Mar 1 00:00:00 2023\n"},
|
||||||
|
{0x656d4ec0, {0, 0, 4, 4, 11, 123, 337, 1}, "Mon Dec 4 04:00:00 2023\n"},
|
||||||
|
{0x6592007f, {59, 59, 23, 31, 11, 123, 364, 0}, "Sun Dec 31 23:59:59 2023\n"},
|
||||||
|
|
||||||
|
/* Another leap year */
|
||||||
|
{0x65920080, {0, 0, 0, 1, 0, 124, 0, 1}, "Mon Jan 1 00:00:00 2024\n"},
|
||||||
|
{0x65e11a7f, {59, 59, 23, 29, 1, 124, 59, 4}, "Thu Feb 29 23:59:59 2024\n"},
|
||||||
|
{0x65e11a80, {0, 0, 0, 1, 2, 124, 60, 5}, "Fri Mar 1 00:00:00 2024\n"},
|
||||||
|
{0x6774857f, {59, 59, 23, 31, 11, 124, 365, 2}, "Tue Dec 31 23:59:59 2024\n"},
|
||||||
|
|
||||||
|
/* End of century */
|
||||||
|
{0xf48656ff, {59, 59, 23, 31, 11, 199, 364, 4}, "Thu Dec 31 23:59:59 2099\n"},
|
||||||
|
|
||||||
|
/* A non-leap year for exceptional reasons */
|
||||||
|
{0xf4865700, {0, 0, 0, 1, 0, 200, 0, 5}, "Fri Jan 1 00:00:00 2100\n"},
|
||||||
|
{0xf4d41f7f, {59, 59, 23, 28, 1, 200, 58, 0}, "Sun Feb 28 23:59:59 2100\n"},
|
||||||
|
{0xf4d41f80, {0, 0, 0, 1, 2, 200, 59, 1}, "Mon Mar 1 00:00:00 2100\n"},
|
||||||
|
{0xf4fceff0, {0, 0, 23, 31, 2, 200, 89, 3}, "Wed Mar 31 23:00:00 2100\n"},
|
||||||
|
{0xf6678a7f, {59, 59, 23, 31, 11, 200, 364, 5}, "Fri Dec 31 23:59:59 2100\n"},
|
||||||
|
|
||||||
|
/* First post-2100 leap year */
|
||||||
|
{0xfc0b2500, {0, 0, 0, 1, 0, 204, 0, 2}, "Tue Jan 1 00:00:00 2104\n"},
|
||||||
|
{0xfc5a3eff, {59, 59, 23, 29, 1, 204, 59, 5}, "Fri Feb 29 23:59:59 2104\n"},
|
||||||
|
{0xfc5a3f00, {0, 0, 0, 1, 2, 204, 60, 6}, "Sat Mar 1 00:00:00 2104\n"},
|
||||||
|
{0xfcaa9c70, {0, 0, 23, 30, 3, 204, 120, 3}, "Wed Apr 30 23:00:00 2104\n"},
|
||||||
|
|
||||||
|
/* End of epoch */
|
||||||
|
{0xfdedaa00, {0, 0, 0, 1, 0, 205, 0, 4}, "Thu Jan 1 00:00:00 2105\n"},
|
||||||
|
{0xffffffff, {15, 28, 6, 7, 1, 206, 37, 0}, "Sun Feb 7 06:28:15 2106\n"}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Values checked against glibc 2.37's implementation of ctime() */
|
static int compare_tm(time_t t, struct tm *tm, struct tm *ref) {
|
||||||
const char *dates[] = {
|
if (memcmp(tm, ref, sizeof(tm))) {
|
||||||
"Thu Jan 1 00:00:00 1970\n",
|
printf("0x%lx: unexpected tm from gmtime: "
|
||||||
"Sun Jul 9 16:12:47 1995\n",
|
"expected {%u, %u, %u, %u, %u, %u, %u, %u}, "
|
||||||
"Wed Jul 18 05:49:51 2029\n",
|
"got {%u, %u, %u, %u, %u, %u, %u, %u}\n",
|
||||||
"Thu Dec 31 23:59:59 2099\n",
|
t,
|
||||||
"Fri Jan 1 00:00:00 2100\n",
|
ref->tm_sec, ref->tm_min, ref->tm_hour, ref->tm_mday, ref->tm_mon, ref->tm_year, ref->tm_yday, ref->tm_wday,
|
||||||
"Fri Feb 29 23:59:59 2104\n",
|
tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_yday, tm->tm_wday);
|
||||||
"Tue Feb 29 23:59:59 2028\n",
|
return 1;
|
||||||
"Wed Mar 1 00:00:00 2028\n",
|
}
|
||||||
"Sun Feb 7 06:28:15 2106\n",
|
return 0;
|
||||||
NULL
|
}
|
||||||
};
|
|
||||||
|
|
||||||
int main (void)
|
int main (void)
|
||||||
{
|
{
|
||||||
struct tm tm;
|
|
||||||
time_t t;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Verify conversion both ways */
|
if (mktime(NULL) != (time_t)-1) {
|
||||||
for (t = 0x0FFFFFFF; ; t += 0x10000000) {
|
printf("mktime should return -1 with a NULL parameter\n");
|
||||||
struct tm *tm = gmtime(&t);
|
fails++;
|
||||||
time_t r = mktime(tm);
|
|
||||||
if (t != r) {
|
|
||||||
fails++;
|
|
||||||
printf("Unexpected result for t %lx: %lx\n", t, r);
|
|
||||||
}
|
|
||||||
if (t == 0xFFFFFFFF) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; dates[i] != NULL; i++) {
|
/* Verify conversion both ways */
|
||||||
char *str = ctime(×tamps[i]);
|
for (i = 0; ; i++) {
|
||||||
if (strcmp(str, dates[i])) {
|
time_t t = data[i].t;
|
||||||
|
time_t r;
|
||||||
|
struct tm *tm = gmtime(&t);
|
||||||
|
r = mktime(tm);
|
||||||
|
|
||||||
|
if (t != r) {
|
||||||
|
printf("unexpected timestamp from mktime: expected 0x%lx, got 0x%lx\n", t, r);
|
||||||
fails++;
|
fails++;
|
||||||
printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
|
||||||
timestamps[i], dates[i], str);
|
|
||||||
}
|
}
|
||||||
|
if (compare_tm(t, tm, &data[i].tm)) {
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
if (strcmp(data[i].str, ctime(&t))) {
|
||||||
|
printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t));
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == 0xFFFFFFFF)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return fails;
|
return fails;
|
||||||
}
|
}
|
||||||
|
|||||||
31
test/val/lib_common_pmemalign.c
Normal file
31
test/val/lib_common_pmemalign.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "unittest.h"
|
||||||
|
|
||||||
|
TEST
|
||||||
|
{
|
||||||
|
void *buf;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = posix_memalign(&buf, 123, 1024);
|
||||||
|
ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with wrong alignment");
|
||||||
|
ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with wrong alignment");
|
||||||
|
|
||||||
|
r = posix_memalign(&buf, 0, 1024);
|
||||||
|
ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 alignment");
|
||||||
|
ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 alignment");
|
||||||
|
|
||||||
|
r = posix_memalign(&buf, 256, 0);
|
||||||
|
ASSERT_IsTrue(r == EINVAL, "posix_memalign did not return EINVAL with 0 size");
|
||||||
|
ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL with 0 size");
|
||||||
|
|
||||||
|
r = posix_memalign(&buf, 256, 32768U);
|
||||||
|
ASSERT_IsTrue(r == 0, "posix_memalign did not return 0 on correct call");
|
||||||
|
ASSERT_IsTrue(buf != NULL, "posix_memalign left buf set to NULL on correct call");
|
||||||
|
ASSERT_IsTrue(((unsigned int)buf & 0x00FF) == 0x00, "posix_memalign did not align memory");
|
||||||
|
|
||||||
|
r = posix_memalign(&buf, 256, 32768U);
|
||||||
|
ASSERT_IsTrue(r == ENOMEM, "posix_memalign did not return ENOMEM when no memory is available");
|
||||||
|
ASSERT_IsTrue(buf == NULL, "posix_memalign did not set buf to NULL when no memory is available");
|
||||||
|
}
|
||||||
|
ENDTEST
|
||||||
81
test/val/lib_common_realloc.c
Normal file
81
test/val/lib_common_realloc.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "unittest.h"
|
||||||
|
|
||||||
|
TEST
|
||||||
|
{
|
||||||
|
char *buf, *buf2;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
buf = realloc(NULL, 0);
|
||||||
|
ASSERT_IsTrue (buf == NULL, "realloc (NULL, 0) returned something");
|
||||||
|
|
||||||
|
for (i = 1; i < 10; i++) {
|
||||||
|
buf2 = realloc(buf, i);
|
||||||
|
ASSERT_IsTrue (buf2 != NULL, "small realloc returned nothing");
|
||||||
|
if (i > 1) {
|
||||||
|
ASSERT_IsTrue (buf2 == buf, "buf shouldn't have moved");
|
||||||
|
}
|
||||||
|
buf = buf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = realloc(NULL, 15);
|
||||||
|
ASSERT_IsTrue (buf != NULL, "realloc (NULL, 15) returned nothing");
|
||||||
|
|
||||||
|
buf = realloc(buf, 0);
|
||||||
|
ASSERT_IsTrue (buf == NULL, "realloc (buf, 0) returned something");
|
||||||
|
|
||||||
|
buf = realloc(buf, 32);
|
||||||
|
memset(buf, 'a', 32);
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'a', "wrong contents in buf");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now realloc larger, while there's nothing else in the heap */
|
||||||
|
buf = realloc(buf, 64);
|
||||||
|
memset(buf+32, 'b', 32);
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf");
|
||||||
|
}
|
||||||
|
for (i = 32; i < 64; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now realloc smaller, while there's nothing else in the heap */
|
||||||
|
buf = realloc(buf, 40);
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf");
|
||||||
|
}
|
||||||
|
for (i = 32; i < 40; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate something else, so next realloc has to change block */
|
||||||
|
malloc(50);
|
||||||
|
|
||||||
|
/* Now realloc larger, with something else in the heap */
|
||||||
|
buf = realloc(buf, 128);
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'a', "wrong contents in start of buf");
|
||||||
|
}
|
||||||
|
for (i = 32; i < 40; i++) {
|
||||||
|
ASSERT_IsTrue(buf[i] == 'b', "wrong contents in end of buf");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 129; i < 8192; i++) {
|
||||||
|
buf = realloc(buf, i);
|
||||||
|
ASSERT_IsTrue(buf != NULL, "realloc failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
malloc(4096);
|
||||||
|
|
||||||
|
buf2 = realloc(buf, 58000UL);
|
||||||
|
ASSERT_IsTrue (buf2 == NULL, "realloc (buf, 58000) returned something");
|
||||||
|
|
||||||
|
for (i = 65535UL; i > 65527UL; i--) {
|
||||||
|
buf2 = realloc(buf, i);
|
||||||
|
ASSERT_IsTrue (buf2 == NULL, "realloc returned something but shouldn't have");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ENDTEST
|
||||||
55
test/val/remove.c
Normal file
55
test/val/remove.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int fails = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static void create_out_file(const char *outfile_path) {
|
||||||
|
FILE *out;
|
||||||
|
|
||||||
|
|
||||||
|
out = fopen(outfile_path, "wb");
|
||||||
|
if (out == NULL) {
|
||||||
|
printf("Could not create %s\n", outfile_path);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
static char outfile_path[FILENAME_MAX+1];
|
||||||
|
|
||||||
|
sprintf(outfile_path, "%s.test.out", argv[0]);
|
||||||
|
|
||||||
|
create_out_file(outfile_path);
|
||||||
|
r = remove(outfile_path);
|
||||||
|
if (r != 0) {
|
||||||
|
printf("could not remove() %s\n", outfile_path);
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_out_file(outfile_path);
|
||||||
|
r = unlink(outfile_path);
|
||||||
|
if (r != 0) {
|
||||||
|
printf("could not unlink() %s\n", outfile_path);
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = remove("klsdfjqlsjdflkqjdsoizu");
|
||||||
|
if (r == 0) {
|
||||||
|
printf("remove()ing non-existent file succeeded\n");
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = unlink("klsdfjqlsjdflkqjdsoizu");
|
||||||
|
if (r == 0) {
|
||||||
|
printf("unlink()ing non-existent file succeeded\n");
|
||||||
|
fails++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fails;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user