This is intended to be a complete implementation of strftime as specified in C17, although it lacks meaningful support for time zones or non-C locales.
This affects code where multiple structs or unions are assigned by successive = operators in one expression, e.g. "s1=s2=s3". The middle struct assignment(s) would use the ~Move2 or ~LongMove2 helper functions (for <64k or >=64k moves, respectively). These functions are supposed to leave the destination pointer on the stack so it can be used as the source of a subsequent move, but they both had bugs where they could modify dest and leave that modified value on the stack, which would cause subsequent moves to use the wrong source location. In the case of ~Move2, this only happened if the size was odd.
Here is a program that demonstrated the problems with both functions:
#pragma memorymodel 1
#include <stdio.h>
struct S1 {
char s[80000];
} a,b,c;
int main(void) {
struct S2 {
int x,y;
char z;
} d,e,f;
c.s[66000] = 123;
f.y = 5678;
a = b = c;
d = e = f;
printf("%i %i %i\n", a.s[66000], b.s[66000], c.s[66000]);
printf("%i %i %i\n", d.y, e.y, f.y);
}
The previous code relied on the data bank value at entry being the bank that contained the library code. This might not be the case when called from code using the large memory model, which could lead to the wrong value being returned.
This may be either 50 or 60, depending on the system's video frequency setting (50Hz PAL or 60Hz NTSC). The video setting can be determined by inspecting bit 4 of the LANGSEL soft switch, documented in Appendix E of the Apple IIGS Firmware Reference.
If the upper byte of the int argument was nonzero, it could write the wrong value (the OR of the upper and lower bytes). It should convert the value to unsigned char, i.e. just use the lower byte.
Previously, it could behave incorrectly in some cases where count was 0. This would happen if the address calculation &base[-1] wrapped around and produced a large number, either because base was NULL or because size was larger than the address of base.
This fixes#33.
The core loop uses the 65816's decimal mode to create a BCD representation of the number, then we convert that to ASCII to print it. The idea is based on some code from Kent Dickey, with adjustments to fit this codebase. This technique should be faster, and also should typically result in smaller code, since the long long division routine will not need to be linked into every program that uses printf.
For conversion to signed long long, we can almost just use SANE's FX2C call to convert it to comp. But LLONG_MIN corresponds to the bit pattern of the comp NaN, so it has to be treated specially. (You might think SANE would return the comp NaN when converting out-of-range values, which would give the value we want, but somewhat strangely it does not.)
For conversion to unsigned long long, we check if the value is greater than LLONG_MAX (which is also the max comp value). If so, we subtract LLONG_MAX+1 from the floating-point value before conversion and add it back in to the integer afterward.
This is almost the same as ~CMP8 in SysLib, except that it does not use a static location to save the DP. This makes it slightly smaller/faster, and also makes it thread/interrupt safe.
The new m16.int64 file contains a new "negate8" macro (for 64-bit negation), as well as an improved version of "ph8". These have been added to the individual *.macros files, but if those are regenerated, m16.int64 should be used as an input to macgen ahead of m16.ORCA (which contains the original version of ph8).
SANE does not have built in operations for this, but all the values are exactly representable in the SANE extended format, so we just do the conversion ourselves based on the definition of the extended format.
EOF should not be returned in this case.
I think it shouldn't actually write anything to the destination in this case, but it currently does. That problem remains unfixed for the moment; addressing it would require us to have our own internal buffer.
Conversions using it will not actually work right yet (unless assignment is suppressed), because the necessary changes have not been made in SysFloat, but the infrastructure is now in place to allow for those changes.
This was not being reported unless assignment was suppressed, because the "sta ~eofFound" instruction was too late.
(Found based on test cases by Rich Felker.)
Code in ~scanf would call ~RemoveWord to remove the extra parameters, but ~RemoveWord is designed to be called from one of the ~Scan_... subroutines, which is (in effect) called by JSR and thus has an extra word on the stack for its return address. This stack misalignment caused ~RemoveWord to overwrite a word of the caller's stack when called from the code to remove extra parameters.
This could cause a crash in the following program:
#include <stdio.h>
void f(void) {
int a,b;
sscanf("Z", "%i%i", &a, &b);
}
int main(void) {
f();
}
This may cause scanf to return EOF, if no conversions have been done.
This seems to be the intent of the C standards, although the wording could be clearer. It is also consistent with all the other implementations I tested.
This applies to + or - signs for 'd' or 'i' conversions, and to '0x' or '0X' prefixes for 'i' and 'x' conversions. (The new support for signs on u/o/x conversions did not have the problem.)
These cases were treated as if they successfully matched a number, but now they are correctly treated as a failure, causing no assignment to be done and causing the function to return without processing any further directives.
(Incidentally, the handling of '0x' without following digits is something that differs in modern C libraries: macOS libc, glibc, and musl are all different. Our new behavior should match that of musl, which I believe is what is correct under the C standards.)
The 'll' length modifier is now fully supported for the d, i, o, u, x, and X conversion specifiers. The 'n' conversion specifier can also store to a long long, but the value is still limited to 64k. The 'j' length modifier (for intmax_t) is also now treated as specifying a 64-bit value.
This is a fairly straightforward adaptation of the strtol/strtoul code. The multiplication routine is modified to return the next 16 bits in X so that strtoull can detect overflows.
The ~CDIV8 routine is the same as ~DIV8 in SysLib, except that it negates the remainder if the numerator is negative. This complies with the definition of the % operator in C, which gives negative (or 0) remainders if the numerator is negative, such that (a/b)*b + a%b equals a.
The ~UDIV8 routine is an unsigned version, using the same core division loop but without the sign handling.
This file will be expanded with more support routines as needed.
The unsigned multiply routine uses the core multiplication loop from the 64-bit signed multiply routine in SysLib, with new entry/exit code for the unsigned version.
The 'b' conversion specifier is an ORCA extension to support p-strings, but lower case letters are reserved for use in future C standards, and in fact C23 is likely to use 'b' for integers in binary notation. For the time being, 'b' is still supported with its existing meaning, but it is considered deprecated. It may be removed in the future if we want to support the C23 behavior. Upper-case letters are available to use for extensions, so 'P' should remain available for our use.
For example, the output of the following should have only two leading zeros (and the rest of the field width padded with spaces):
#include <stdio.h>
int main(void) {
printf("%016.5i\n", 123);
}