Commit Graph

207 Commits

Author SHA1 Message Date
Stephen Heumann 32c5fd94a1 Handle out-of-range months in mktime() input. 2022-12-29 23:54:10 -06:00
Stephen Heumann f15caf8096 Make gmtime/localtime properly support times near the limits of the time_t range.
They did not properly handle times in 1969 or 2105 (for the latter, they would infinite-loop).
2022-12-29 23:18:48 -06:00
Stephen Heumann b302a85fd6 Switch time "factor" code over to 0-based month indexing.
This matches both struct tm and ReadTimeHex, so it avoids needing to increment the values.

Also, simplify the time() code a little bit.
2022-12-29 22:53:37 -06:00
Stephen Heumann 17faeda1de Rework time "factor" routine to work for the full 32-bit time range.
ORCA/C uses an unsigned 32-bit time_t which should give a range up to 2105, but calculations on it were being done with signed types, causing them not to work correctly beyond 2036-2038. Now the factor routine, mktime(), and time() should work up to 2105. (In the case of time(), this assumes ReadTimeHex reports the time correctly.)

The factor routine actually computes a 64-bit time value. Currently, the rest of the code only takes the bottom 32 bits of it, but this could be extended if we ever wanted to switch to 64-bit time_t.
2022-12-29 22:31:31 -06:00
Stephen Heumann d30ee1a2e5 Adjust comments in time.asm to reflect actual starting date of time_t.
It was clearly supposed to be 1 Jan 1970, but it's actually not, probably because the number of days from 1 Jan 1900 to 1 Jan 1970 was miscalculated. Changing it now could potentially cause compatibility issues (especially for GNO, which uses time_t in some kernel call interfaces and file formats), so for now it is left as is and just documented appropriately.

Nothing in the C standards requires the time_t epoch to be 1 Jan 1970, so this does not cause any standards-compliance problem for the C standards. (It is different from POSIX, though.)
2022-12-29 14:25:24 -06:00
Stephen Heumann e2de990f4d strftime: use Time Tool Set to get time zone offset.
This is used for the %z conversion specifier (giving the time zone offset in +-HHMM format). The %Z conversion specifier (giving the locale's time zone name or abbreviation) also prints the same thing for now.

As with gmtime, this will only use the Time Tool Set if it has already been started. Otherwise, these conversions simply produce no output.
2022-12-28 19:55:48 -06:00
Stephen Heumann 4019e9f370 gmtime: support time zone adjustment with Time Tool Set.
If the Time Tool Set (tool 56, by Geoff Weiss) is present and active, gmtime will use it (plus the DST flag) to determine the local time offset from UTC, allowing it to produce the correct UTC time. If not, it will still treat local time as being equal to UTC, like it did previously.

The library code will not try to load or start the Time Tool Set, so the program will have to do that before calling gmtime if it wants to use this functionality.
2022-12-28 19:46:49 -06:00
Stephen Heumann 89664d2921 Slightly improve tgamma calculation for x < 8.
Previously, 1-4 low-order bits of the input value were essentially ignored when calculating the numerator, but used to some degree when calculating the denominator. This would lead to the calculated tgamma values decreasing slightly over the range of several consecutive input values (when they should increase). Now, the low-order bits of the input value are effectively just rounded away. This should give slightly more accurate results, and greatly reduces the frequency of cases where consecutive output values go in the wrong direction.
2022-12-24 21:59:52 -06:00
Stephen Heumann 5985e7d774 Implement tgamma (c99).
This uses an approximation based on the Stirling series for large enough x (for which it is highly accurate). For smaller x, identities are used to express gamma(x) in terms of gamma(x+1) or gamma(1-x), ultimately letting the Stirling series approximation be used.
2022-12-24 20:20:40 -06:00
Stephen Heumann 88e764f72d Implement the erf and erfc functions (C99).
This implementation is based on the approximations given in the following paper:

W. J. Cody, Rational Chebyshev Approximations for the Error Function, Mathematics of Computation, Vol. 23, No. 107 (Jul., 1969), pp. 631-637.

Per the paper, the approximations have maximal relative error of 6e-19 or lower (although I have not verified what the figure is for this actual implementation).

See also Cody's FORTRAN implementation based on the same approach:

https://netlib.org/specfun/erf
2022-12-17 22:25:53 -06:00
Stephen Heumann 73ed0778f2 Add cleanup code for CDev calls.
The new CDev root code generated by ORCA/C will now branch to this code after each CDev call, giving it an opportunity to clean up if necessary. Specifically, it will dispose of the user ID allocated for the CDev if it is going away after this call. There are several cases where this occurs, which need to be detected based on the message code passed to the CDev and in some cases other factors.
2022-12-12 18:01:28 -06:00
Stephen Heumann b81b4e1109 Fix several bugs in fgets() and gets().
Bugs fixes:
*fgets() would write 2 bytes in the buffer if called with n=1 (should be 1).
*fgets() would write 2 bytes in the buffer if it encountered EOF before reading any characters, but the EOF flag had not previously been set. (It should not modify the buffer in this case.)
*fgets() and gets() would return NULL if EOF was encountered after reading one or more characters. (They should return the buffer pointer).
2022-10-15 19:01:16 -05:00
Stephen Heumann 505f1c2804 lseek: make seeking to an offset before end of file work properly.
The direction specified by the offset was essentially reversed when calling lseek with whence==2 (seek to offset from end of file). Therefore, specifying a negative offset with whence==2 would fail, rather than seeking before the end of the file as it should.

(The ORCA/C manual is not totally clear about this behavior, but the new behavior is consistent with the POSIX spec and all other implementations I'm aware of, including traditional Unix and APW C. Note that Unix/POSIX allows seeking beyond the end of the file, but GS/OS does not.)

There are also improvements to error handling, so lseek consistently reports EINVAL for invalid offsets.
2022-07-14 18:34:24 -05:00
Stephen Heumann a2bca0df04 Implement O_APPEND mode.
This was documented as supported, but not actually implemented.
2022-07-13 18:34:29 -05:00
Stephen Heumann ad273126dd Remove unnecessary instructions. 2022-07-13 18:27:24 -05:00
Stephen Heumann 7b6cb049b7 Add an 16-bit unsigned multiply routine suitable for use in C.
This differs from the existing ~UMul2 in SysLib in that it gives the low-order 16 bits of the true result in the event of overflow. The C standards require this behavior for computations on unsigned types.
2022-07-06 22:19:32 -05:00
Stephen Heumann 12f8d74c99 Do not use separate segments for __-prefixed versions of functions.
The __-prefixed versions were introduced for use in <stdio.h> macros that have since been removed, so they are not really necessary any more. However, they may be used in old object files, so those symbols are still included for now.
2022-07-05 18:24:18 -05:00
Stephen Heumann 463d24a028 Avoid errors caused by fseek after ungetc on read-only files.
The error could occur because fseek calls fflush followed by ftell. fflush would reset the file position as if the characters in the putback buffer were removed, but ftell would still see them and try to adjust for them (in the case of a read-only file). This could result in trying to seek before the beginning of the file, producing an error.

Here is a program that was affected:

#include <stdio.h>
int main(void) {
        FILE *f = fopen("somefile","r");
        if (!f) return 0;
        fgetc(f);
        ungetc('X', f);
        fseek(f, 0, SEEK_CUR);
        if (ferror(f)) puts("error encountered");
}
2022-07-03 21:58:00 -05:00
Stephen Heumann 219e4352a0 fseek: do not clear read/write flags for read-only/write-only streams.
This maintains the invariant that these flags stay set to reflect the setting of the stream as read-only or write-only, allowing code elsewhere to behave appropriately based on that.
2022-07-03 20:27:19 -05:00
Stephen Heumann 89b501f259 fread: do not try to read if EOF flag is set.
This behavior is implied by the specification of fread in terms of fgetc.
2022-07-03 20:04:12 -05:00
Stephen Heumann ef63f26c4f Allow writing immediately after reading to EOF.
This should be allowed (for read/write files), but it was leading to errors, both because fputc would error out if the EOF flag was set and because the FILE record was not fully reset from its "reading" state. In particular, the _IOREAD flag and the current buffer position pointer need to be reset.

Here is a program that demonstrates the problem:

#include <stdio.h>
int main(void) {
        FILE *f = fopen("somefile","r+");
        if (!f) return 0;
        while (!feof(f)) fgetc(f); /* or: fgetc then fread */
        if (fputc('X', f) == EOF)
                puts("fputc error");
}
2022-07-03 18:54:24 -05:00
Stephen Heumann c877c74b92 In fflush, reset the mark to account for flushing the buffer even on read-only files.
This is needed to keep the file mark consistent with the state of the buffer. Otherwise, subsequent reads may return data from the wrong place. (Using fflush on a read-only stream is undefined behavior, but other things in stdio call it that way, so it must work consistently with what they expect.)

Here is an example that was affected by this:

#include <stdio.h>
#include <string.h>

char buf[100];

int main(void) {
        FILE *f = fopen("somefile","r");
        if (!f) return 0;

        setvbuf(f, 0L, _IOFBF, 14);

        fgets(buf, 10, f);
        printf("first read : %s\n", buf);
        ftell(f);

        memset(buf, 0, sizeof(buf));
        fgets(buf, 10, f);
        printf("second read: %s\n", buf);

        fseek(f, 0, SEEK_CUR);
        memset(buf, 0, sizeof(buf));
        fgets(buf, 10, f);
        printf("third read : %s\n", buf);
}
2022-07-02 23:34:01 -05:00
Stephen Heumann 2d6ca8a7b2 Do not set read/write error indicator if ftell cannot get the file mark.
This is not really an IO operation on the file, and accordingly the C standards do not describe the error indicator as being set here. In particular, you cannot get the mark for a character device, but that is expected behavior, not really an error condition.

errno is still set, indicating that the ftell operation failed.
2022-07-02 22:09:56 -05:00
Stephen Heumann 3eb8a9cb55 Fix bug where ftell would inappropriately change the file mark.
ftell would set the mark as if the buffer and putback were being discarded, but not actually discard them. This resulted in characters being reread once the end of the buffer was reached.

Here is an example that illustrates the problem:

#include <stdio.h>
#include <string.h>
char buf[100];
int main(void) {
        FILE *f = fopen("somefile","r"); // a file with some text
        if (!f) return 0;
        setvbuf(f, 0L, _IOFBF, 14); // to see problem sooner
        fgets(buf, 10, f);
        printf("first read : %s\n", buf);
        ftell(f);
        memset(buf, 0, sizeof(buf));
        fgets(buf, 10, f);
        printf("second read: %s\n", buf);]
}
2022-07-02 22:05:40 -05:00
Stephen Heumann 36808404ca Fix fread bug causing it to discard buffered data.
If you read from a file using fgetc (or another function that calls it internally), and then later read from it using fread, any data left in the buffer from fgetc would be skipped over. The pattern causing this to happen was as follows:

fread called ~SetFilePointer, which (if there was buffered data) called fseek(f, 0, SEEK_CUR). fseek would call fflush, which calls ~InitBuffer. That zeros out the buffer count. fseek then calls ftell, which gets the current mark from GS/OS and then subtracts the buffer count.  But the count was set to 0 in ~InitBuffer, so ftell reflects the position after any previously buffered data. fseek sets the mark to the position returned by ftell, i.e. after any data that was previously read and buffered, so that data would get skipped over. (Before commits 0047b755c9 and c95bfc19fb the behavior would be somewhat different due to the issue with ~InitBuffer that they addressed, but you could still get similar symptoms.)

The fix is simply to use the buffered data (if any), rather than discarding it.

Here is a test program illustrating the problem:

#include <stdio.h>
char buf[BUFSIZ+1];
#define RECSIZE 2
int main(void) {
        FILE *f = fopen("somefile","r"); // a file with some data
        if (!f) return 0;
        fgetc(f);
        size_t count = fread(buf, RECSIZE, BUFSIZ/RECSIZE, f);
        printf("read %zu records: %s\n", count, buf);
}
2022-07-02 18:24:57 -05:00
Stephen Heumann 84f471474a Use newer, more efficient ph2/ph4 macros throughout ORCALib.
These push DP values with pei, rather than lda+pha as in the old versions of the macros.
2022-06-30 19:01:47 -05:00
Stephen Heumann c95bfc19fb Fix a logic error.
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.
2022-06-28 22:25:10 -05:00
Stephen Heumann a2b3d4541a fread fixes.
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).
2022-06-27 18:24:52 -05:00
Stephen Heumann 1f88a38e2e Clear the EOF flag on successful calls to ungetc().
This is specified by the C standards.
2022-06-27 17:59:21 -05:00
Stephen Heumann 38666ee111 Restore changes to allow ungetc of byte values 0x80 through 0xFF.
These are the stdio.asm changes that were present in the beta source code on Opus ][, but had been reverted in commit e3c0c962d4. 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.
2022-06-27 17:58:23 -05:00
Stephen Heumann 7c2fb70c4a freopen improvements
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.
2022-06-26 21:20:23 -05:00
Stephen Heumann 8cfb73a474 Force the file mark to EOF whenever writing to a stream in append mode.
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.
2022-06-26 18:59:57 -05:00
Stephen Heumann 0047b755c9 Fix bug with fseek(..., SEEK_CUR) on read-only streams.
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.
2022-06-26 14:35:56 -05:00
Stephen Heumann 3581d20a7c Standardize indentation using spaces.
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.
2022-06-25 18:27:20 -05:00
Stephen Heumann ab2f17c249 Clear the IO error indicator as part of rewind().
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);
}
2022-06-24 18:36:25 -05:00
Stephen Heumann 997e430562 Implement asinh().
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.
2021-12-24 15:56:36 -06:00
Stephen Heumann b62940404f Implement atanh().
This basically follows the approach recommended in Apple Numerics Manual Ch. 9.
2021-12-23 18:30:52 -06:00
Stephen Heumann 818707ed8c Use a more accurate implementation of cbrt().
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.
2021-12-21 19:11:18 -06:00
Stephen Heumann a45f531fe6 Implement hypot().
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.
2021-12-20 21:52:48 -06:00
Stephen Heumann b01800ff77 Fix rounding issues introduced by SANE bug workarounds.
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.
2021-11-30 20:19:57 -06:00
Stephen Heumann b6690c4826 Implement acosh().
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.
2021-11-30 19:15:54 -06:00
Stephen Heumann eddf778f09 Implement llround(). 2021-11-28 18:30:20 -06:00
Stephen Heumann 66cfa0d406 Remove unnecessary code in lround(). 2021-11-28 18:30:01 -06:00
Stephen Heumann e00c21dd70 Work around bug in FX2C and FX2L.
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.
2021-11-28 14:18:27 -06:00
Stephen Heumann 503182e435 Initial implementation of lround().
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.
2021-11-27 17:52:46 -06:00
Stephen Heumann 88a7bbebcc Implement round().
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.
2021-11-27 15:55:54 -06:00
Stephen Heumann d08773af0d Implement nextafter and nexttoward.
Unlike most of the math functions, these actually have separate implementations for float/double/long double.
2021-11-26 12:47:02 -06:00
Stephen Heumann 6364d0f48f Implement llrint. 2021-11-23 21:16:12 -06:00
Stephen Heumann ce05615a63 Implement fmax and fmin. 2021-11-23 18:54:18 -06:00
Stephen Heumann 14908ebcd6 Implement the nan() function.
This parses the NaN code string itself, but it should give equivalent behavior to the SANE parser.
2021-11-22 21:59:50 -06:00