There was a problem with the fix in commit 0047b755c9660: an instruction should have had an immediate operand, but did not because it was missing the #. This might cause the code to behave incorrectly, depending on memory contents.
This includes changes to fread to fix two problems. The first is that fread would ignore and discard characters put back with ungetc(). The second is that it would generally return the wrong value if reading from stdin with an element size other than 1 (it would return the count of bytes, not elements).
This fixes#9 (the first problem mentioned above).
These are the stdio.asm changes that were present in the beta source code on Opus ][, but had been reverted in commit e3c0c962d4b0f8077. As it turns out, the changes to stdio.asm were OK--the issue was simply that the definitions of stdin/stdout/stderr and the associated initialization code in vars.asm had not been updated to account for the new version of the FILE structure. That has now been done, allowing the changes to work properly.
This fixes#7.
This adds a check for the filename argument being null, in which case it bails out and returns NULL. Previously, it would just dereference the null pointer and treat the contents of memory location 0 as a filename, with unpredictable results. A null filename indicates freopen should try to reopen the same file with a different mode, but the degree of support for that is implementation-defined. We still don't really support it, but at least this will report the failure and avoid unpredictable behavior.
It also removes some unused code at the end, but still forces fputc and fgetc to be linked in. These are needed because there are weak references to them in putchar and getchar, which may need to be used if stdin/stdout have been reopened.
This is what the standards require. Note that the mark is only set to EOF when actually writing to the underlying file, not merely buffering data to write later. This is consistent with the usual POSIX implementation using O_APPEND.
This would seek to the wrong location, and in some cases try to seek before the beginning of the file, leading to an error condition.
The problem stemmed from fseek calling fflush, which sets up the stream's buffer state in a way appropriate for writing but not for reading. It then calls ftell, which (for a read-only stream) would misinterpret this state and miscalculate the current position.
The fix is to make fflush work correctly on a read-only stream, setting up the state for reading. (User code calling fflush on a read-only stream would be undefined behavior, but since fseek does it we need to make it work.)
This fixes#6.
Most files already used spaces, but three used tabs for indentation. These have been converted to use spaces. This allows the files to be displayed with proper formatting in modern editors and on GitHub. It also removes any dependency on SysTabs settings when assembling them.
The spacing in fpextra.asm was also modified to use standard column positions.
There are no non-whitespace changes in this commit.
The C standards require it to do this, in addition to calling fseek.
Here is a test that can show the issue (in a realistic program, the indicator would be set due to an actual IO error, but for testing purposes this just sets it explicitly):
#include <stdio.h>
int main(void) {
FILE *f = tmpfile();
if (!f) return 0;
f->_flag |= _IOERR;
if (!ferror(f)) puts("bad ferror");
rewind(f);
if (ferror(f)) puts("rewind does not reset ferror");
fclose(f);
}
This is similar to the approach recommended in Apple Numerics Manual Ch. 9, except that there is an added case for large values that would otherwise cause an overflow or spuriously report underflow.
The previous simple one could be wrong in several low-order digits due to the inaccuracy in the representation of the exponent (1/3). This version effectively breaks the number up into the form a*8^b, computes the cube root of 8^b exactly (i.e. 2^b), and uses the slightly inaccurate exponentiation only for a.
This uses the obvious calculation, except with scaling to avoid unnecessary overflow/underflow.
There is a discussion of hypot implementations in C. Borges, An Improved Algorithm for hypot(a,b) (https://arxiv.org/pdf/1904.09481.pdf). This implementation is similar to the "Naive (Unfused)" version discussed in that paper. As the paper notes, it is possible to get better accuracy by adding a correction term, but the "naive" version is already reasonably good, so we skip the correction in the interest of code size and speed.
The lrint functions could give the wrong result for negative numbers in upward/downward rounding modes. Casts to comp could also have different rounding behavior.
This is basically the implementation recommended in Apple Numerics Manual Ch. 9, except that there is an added case for large values that would otherwise cause an overflow.
These SANE operations can sometimes return incorrect values for certain negative integers such as -2147483648 and -53021371269120 (numbers with at least 16 low-order zero bits in their two's-complement representation). To work around this, we now avoid calling FX2C or FX2L on negative numbers, generally by saving and restoring the sign separately.
These workarounds are used in several of the new <math.h> rounding functions, and also for code that converts floating-point values to comp or long long. There are some places in SysFloat that should be patched similarly, so we may still hit this problem in certain situations until that is done.
This should work, and mostly does. However, it is affected by a bug in FX2L (and FX2C) which can sometimes give the wrong results for certain negative integers (such as -2147483648). I believe this can occur when at least the lower 16 bits if the integer (in two's-complement representation) are zeros.
This is a bit more complex than other rounding functions, because it rounds to nearest but always away from zero in halfway cases, which is not a rounding direction directly supported by SANE.
This differs from scalbn in that the exponent has type long. When scaling an extended value, exponents slightly outside the range of int can actually be used meaningfully. We address this by doing multiple SCALBX calls (at most 2) in a loop.
Most of these actually just jump to the existing functions, since they really use extended precision anyway. The exception is the modf functions, which need a separate implementation for each type because they store a value through a pointer to that type.
The functions implemented so far are largely the ones that map (nearly) directly to SANE calls.
Note that C99 specifies separate float/double/long double versions of each of these functions, but under ORCA/C they generally use the same code.
These macros differ from the normal comparison operators in that they will not signal invalid due to the operands being unordered. However, this implementation will still signal invalid for SNaNs. That is clearly OK according to the wording in draft C23. C17 and earlier do not mention that possibility, but they do not really specify the behavior of SNaNs in general.
This will be used in conjunction with the new implementation of variable arguments, where they are not removed from the stack until the end of the function.
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.