From f21ce09e2d03df5e2335bac3d04fa79f6551f2df Mon Sep 17 00:00:00 2001 From: Matthew Kilgore Date: Sun, 8 Jan 2023 02:41:54 -0500 Subject: [PATCH] Replace time() with std::chrono, fix startup delay Currently main() includes logic that is intended to sync time() with GetTicks() for the purpose of using GetTicks() to get millisecond accuracy with time(), which only has second accuracy. Unfortunately, the 'syncing' up of these time sources results in an average of a half second delay in starting a QB64-PE program. This logic is easly replaced with std::chrono, which provides a real time clock which is also millisecond accurate. That removes the need to use time() and GetTicks() together to get millisecond accuracy, and means the delay syncing them is no longer necessary. I also separated most of the "delay" and "time" related functions into datetime.cpp, and included the new std::chrono code into that file. Since I needed to call some of the rounding functions in datetime.cpp I also moved that stuff out into its own .cpp and header files to clean things up a bit. Fixes: #282 --- internal/c/libqb.cpp | 321 +----------------- internal/c/libqb/build.mk | 2 + internal/c/libqb/include/datetime.h | 15 + internal/c/libqb/include/event.h | 11 + internal/c/libqb/include/rounding.h | 122 +++++++ internal/c/libqb/src/datetime.cpp | 196 +++++++++++ internal/c/libqb/src/rounding.cpp | 91 +++++ internal/c/qbx.cpp | 140 +------- tests/compile_tests/console_only/image.output | 2 +- .../console_only/screenimage.bas | 2 +- .../console_only/screenimage.output | 2 +- tests/compile_tests/image/test.output | 2 +- tests/compile_tests/screenimage/test.bas | 2 +- tests/compile_tests/screenimage/test.output | 2 +- 14 files changed, 453 insertions(+), 457 deletions(-) create mode 100644 internal/c/libqb/include/datetime.h create mode 100644 internal/c/libqb/include/event.h create mode 100644 internal/c/libqb/include/rounding.h create mode 100644 internal/c/libqb/src/datetime.cpp create mode 100644 internal/c/libqb/src/rounding.cpp diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index 329600ff6..9f258003e 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -25,6 +25,9 @@ #include "http.h" #include "keyhandler.h" #include "glut-thread.h" +#include "datetime.h" +#include "rounding.h" +#include "event.h" int32 disableEvents = 0; @@ -41,102 +44,8 @@ int32 func__getconsoleinput(); // declare here, so we can use with SLEEP and END uint32 rotateLeft(uint32 word, uint32 shift) { return (word << shift) | (word >> (32 - shift)); } #ifndef QB64_WINDOWS -void Sleep(uint32 milliseconds) { - static uint64 sec, nsec; - sec = milliseconds / 1000; - nsec = (milliseconds % 1000) * 1000000; - static timespec ts; - ts.tv_sec = sec; - ts.tv_nsec = nsec; - nanosleep(&ts, NULL); -} - void ZeroMemory(void *ptr, int64 bytes) { memset(ptr, 0, bytes); } #endif -#ifdef QB64_NOT_X86 -int64 qbr(long double f) { - int64 i; - int temp = 0; - if (f > 9223372036854775807) { - temp = 1; - f = f - 9223372036854775808u; - } // if it's too large for a signed int64, make it an unsigned int64 and return that value if possible. - if (f < 0) - i = f - 0.5f; - else - i = f + 0.5f; - if (temp) - return i | 0x8000000000000000; //+9223372036854775808; - return i; -} -uint64 qbr_longdouble_to_uint64(long double f) { - if (f < 0) - return (f - 0.5f); - else - return (f + 0.5f); -} -int32 qbr_float_to_long(float f) { - if (f < 0) - return (f - 0.5f); - else - return (f + 0.5f); -} -int32 qbr_double_to_long(double f) { - if (f < 0) - return (f - 0.5f); - else - return (f + 0.5f); -} -void fpu_reinit() {} // do nothing -#else -// QBASIC compatible rounding via FPU: -// FLDS=load single -// FLDL=load double -// FLDT=load long double -int64 qbr(long double f) { - int64 i; - int temp = 0; - if (f > 9223372036854775807) { - temp = 1; - f = f - 9223372036854775808u; - } // if it's too large for a signed int64, make it an unsigned int64 and return that value if possible. - __asm__("fldt %1;" - "fistpll %0;" - : "=m"(i) - : "m"(f)); - if (temp) - return i | 0x8000000000000000; // if it's an unsigned int64, manually set the bit flag - return i; -} -uint64 qbr_longdouble_to_uint64(long double f) { - uint64 i; - __asm__("fldt %1;" - "fistpll %0;" - : "=m"(i) - : "m"(f)); - return i; -} -int32 qbr_float_to_long(float f) { - int32 i; - __asm__("flds %1;" - "fistpl %0;" - : "=m"(i) - : "m"(f)); - return i; -} -int32 qbr_double_to_long(double f) { - int32 i; - __asm__("fldl %1;" - "fistpl %0;" - : "=m"(i) - : "m"(f)); - return i; -} -void fpu_reinit() { - unsigned int mode = 0x37F; - asm("fldcw %0" : : "m"(*&mode)); -} -#endif // x86 support // bit-array access functions (note: used to be included through 'bit.cpp') uint64 getubits(uint32 bsize, uint8 *base, ptrszint i) { int64 bmask; @@ -220,8 +129,6 @@ void sub__printimage(int32 i); // GUI notification variables int32 force_display_update = 0; -void sub__delay(double seconds); - void *generic_window_handle = NULL; int32 acceptFileDrop = 0; #ifdef QB64_WINDOWS @@ -293,8 +200,6 @@ extern "C" int QB64_Resizable() { return ScreenResize; } int32 sub_gl_called = 0; -extern void evnt(uint32 linenumber, uint32 inclinenumber = 0, const char *incfilename = NULL); - extern "C" int qb64_custom_event(int event, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, void *p1, void *p2); #ifdef QB64_WINDOWS extern "C" LRESULT qb64_os_event_windows(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int *qb64_os_event_info); @@ -1180,40 +1085,6 @@ static uint16 codepage437_to_unicode16[] = { # include "parts/video/font/ttf/src.c" #endif -#ifdef QB64_MACOSX -# include -# define ORWL_NANO (+1.0E-9) -# define ORWL_GIGA UINT64_C(1000000000) -static double orwl_timebase = 0.0; -static uint64_t orwl_timestart = 0; -int64 orwl_gettime(void) { - if (!orwl_timestart) { - mach_timebase_info_data_t tb = {0}; - mach_timebase_info(&tb); - orwl_timebase = tb.numer; - orwl_timebase /= tb.denom; - orwl_timestart = mach_absolute_time(); - } - struct timespec t; - double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase; - t.tv_sec = diff * ORWL_NANO; - t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA); - return t.tv_sec * 1000 + t.tv_nsec / 1000000; -} -#endif - -#ifdef QB64_LINUX -int64 GetTicks() { - struct timespec tp; - clock_gettime(CLOCK_MONOTONIC, &tp); - return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; -} -#elif defined QB64_MACOSX -int64 GetTicks() { return orwl_gettime(); } -#else -int64 GetTicks() { return ((((int64)clock()) * ((int64)1000)) / ((int64)CLOCKS_PER_SEC)); } -#endif - /* Restricted Functionality: (Security focused approach, does not include restricting sound etc) Block while compiling: (ONLY things that cannot be caught at runtime) @@ -1315,7 +1186,6 @@ void sub_shell4(qbs *, int32); //_DONTWAIT & _HIDE int32 func__source(); int32 func_pos(int32 ignore); -double func_timer(double accuracy, int32 passed); int32 func__newimage(int32 x, int32 y, int32 bpp, int32 passed); void display(); void validatepage(int32); @@ -7387,9 +7257,6 @@ extern uint32 cmem_sp; //=65536; extern ptrszint dblock; // 32bit offset of dblock extern uint64 *nothingvalue; -uint32 qb64_firsttimervalue; // based on time of day -uint32 clock_firsttimervalue; // based on program launch time - uint8 wait_needed = 1; int32 full_screen = 0; // 0,1(stretched/closest),2(1:1) @@ -7454,7 +7321,6 @@ extern uint8 stop_program; int32 global_counter = 0; extern double last_line; void end(void); -extern uint32 new_error; extern uint32 error_err; //=0; extern double error_erl; //=0; extern uint32 error_occurred; @@ -16779,78 +16645,6 @@ float func_rnd(float n, int32 passed) { return (double)rnd_seed / 0x1000000; } -double func_timer(double accuracy, int32 passed) { - if (new_error) - return 0; - static uint32 x; - static double d; - static float f; - x = GetTicks(); - x -= clock_firsttimervalue; - x += qb64_firsttimervalue; - // make timer value loop after midnight - // note: there are 86400000 milliseconds in 24hrs(1 day) - x %= 86400000; - d = x; // convert to double - d /= 1000.0; // convert from ms to sec - // reduce accuracy - if (!passed) { - accuracy = 18.2; - } else { - if (accuracy <= 0.0) { - error(5); - return 0; - } - accuracy = 1.0 / accuracy; - } - d *= accuracy; - d = qbr(d); - d /= accuracy; - if (!passed) { - f = d; - d = f; - } - return d; -} - -void sub__delay(double seconds) { - double ms, base, elapsed, prev_now, now; // cannot be static - base = GetTicks(); - if (new_error) - return; - if (seconds < 0) { - error(5); - return; - } - if (seconds > 2147483.647) { - error(5); - return; - } - ms = seconds * 1000.0; - now = base; // force first prev=... assignment to equal base -recalculate: - prev_now = now; - now = GetTicks(); - elapsed = now - base; - if (elapsed < 0) { // GetTicks looped - base = now - (prev_now - base); // calculate new base - } - if (elapsed < ms) { - int64 wait; // cannot be static - wait = ms - elapsed; - if (!wait) - wait = 1; - if (wait >= 10) { - Sleep(9); - evnt(0); // check for new events - // recalculate time - goto recalculate; - } else { - Sleep(wait); - } - } -} - void sub__fps(double fps, int32 passed) { // passed=1 means _AUTO // passed=2 means use fps @@ -16875,60 +16669,6 @@ void sub__fps(double fps, int32 passed) { } } -void sub__limit(double fps) { - if (new_error) - return; - static double prev = 0; - double ms, now, elapsed; // cannot be static - if (fps <= 0.0) { - error(5); - return; - } - ms = 1000.0 / fps; - if (ms > 60000.0) { - error(5); - return; - } // max. 1 min delay between frames allowed to avoid accidental lock-up of program -recalculate: - now = GetTicks(); - if (prev == 0.0) { // first call? - prev = now; - return; - } - if (now < prev) { // value looped? - prev = now; - return; - } - elapsed = now - prev; // elapsed time since prev - - if (elapsed == ms) { - prev = prev + ms; - return; - } - - if (elapsed < ms) { - int64 wait; // cannot be static - wait = ms - elapsed; - if (!wait) - wait = 1; - if (wait >= 10) { - Sleep(9); - evnt(0); // check for new events - } else { - Sleep(wait); - } - // recalculate time - goto recalculate; - } - - // too long since last call, adjust prev to current time - // minor overshoot up to 32ms is recovered, otherwise time is re-seeded - if (elapsed <= (ms + 32.0)) - prev = prev + ms; - else - prev = now; -} - int32 generic_put(int32 i, int32 offset, uint8 *cp, int32 bytes) { // note: generic_put & generic_get have been made largely redundant by gfs_read & gfs_write // offset is a byte-offset from base 0 (-1=current pos) @@ -37309,53 +37049,6 @@ int main(int argc, char *argv[]) { func_command_count = argc; func_command_array = argv; - // struct tm: - // int tm_sec; /* seconds after the minute - [0,59] */ - // int tm_min; /* minutes after the hour - [0,59] */ - // int tm_hour; /* hours since midnight - [0,23] */ - // int tm_mday; /* day of the month - [1,31] */ - // int tm_mon; /* months since January - [0,11] */ - // int tm_year; /* years since 1900 */ - // int tm_wday; /* days since Sunday - [0,6] */ - // int tm_yday; /* days since January 1 - [0,365] */ - // int tm_isdst; /* daylight savings time flag */ - tm *qb64_tm; - time_t qb64_tm_val; - time_t qb64_tm_val_old; - // call both timing routines as close as possible to each other to maximize accuracy - // wait for second "hand" to "tick over"/move - time(&qb64_tm_val_old); - // note: time() returns the time as seconds elapsed since midnight, January 1, 1970, or -1 in the case of an error. - if (qb64_tm_val_old != -1) { - do { - time(&qb64_tm_val); - } while (qb64_tm_val == qb64_tm_val_old); - } else { - qb64_tm_val = 0; // time unknown! (set to midnight, January 1, 1970) - } - clock_firsttimervalue = GetTicks(); - // calculate localtime as milliseconds past midnight - qb64_tm = localtime(&qb64_tm_val); - /* re: localtime() - Return a pointer to the structure result, or NULL if the date passed to the function is: - Before midnight, January 1, 1970. - After 03:14:07, January 19, 2038, UTC (using _time32 and time32_t). - After 23:59:59, December 31, 3000, UTC (using _time64 and __time64_t). - */ - if (qb64_tm) { - qb64_firsttimervalue = qb64_tm->tm_hour * 3600 + qb64_tm->tm_min * 60 + qb64_tm->tm_sec; - qb64_firsttimervalue *= 1000; - } else { - qb64_firsttimervalue = 0; // time unknown! (set to midnight) - } - /* Used as follows for calculating TIMER value: - x=GetTicks(); - x-=clock_firsttimervalue; - x+=qb64_firsttimervalue; - */ - - // init truetype .ttf/.fon font library - #ifdef QB64_WINDOWS // for caps lock, use the state of the lock (=1) // for held keys check against (=-127) @@ -37524,12 +37217,10 @@ main_loop: } // update timer bytes in cmem - static uint32 cmem_ticks; - static double cmem_ticks_double; + uint64_t cmem_ticks; + + cmem_ticks = (uint64_t)(func_timer(0.001, 1) * 1000); - cmem_ticks = GetTicks(); - cmem_ticks -= clock_firsttimervalue; - cmem_ticks += qb64_firsttimervalue; // make timer value loop after midnight // note: there are 86400000 milliseconds in 24hrs(1 day) cmem_ticks %= 86400000; diff --git a/internal/c/libqb/build.mk b/internal/c/libqb/build.mk index 9ffba313b..ddc4c56bc 100644 --- a/internal/c/libqb/build.mk +++ b/internal/c/libqb/build.mk @@ -2,6 +2,8 @@ libqb-objs-y += $(PATH_LIBQB)/src/threading.o libqb-objs-y += $(PATH_LIBQB)/src/buffer.o libqb-objs-y += $(PATH_LIBQB)/src/filepath.o +libqb-objs-y += $(PATH_LIBQB)/src/datetime.o +libqb-objs-y += $(PATH_LIBQB)/src/rounding.o libqb-objs-$(DEP_HTTP) += $(PATH_LIBQB)/src/http.o libqb-objs-y$(DEP_HTTP) += $(PATH_LIBQB)/src/http-stub.o diff --git a/internal/c/libqb/include/datetime.h b/internal/c/libqb/include/datetime.h new file mode 100644 index 000000000..ca7302213 --- /dev/null +++ b/internal/c/libqb/include/datetime.h @@ -0,0 +1,15 @@ +#ifndef INCLUDE_LIBQB_DATETIME_H +#define INCLUDE_LIBQB_DATETIME_H + +int64_t GetTicks(); + +double func_timer(double accuracy, int32_t passed); +void sub__delay(double seconds); +void sub__limit(double fps); + +// We provide a 'Sleep()' function for non-Windows platforms +#ifndef QB64_WINDOWS +void Sleep(uint32_t milliseconds); +#endif + +#endif diff --git a/internal/c/libqb/include/event.h b/internal/c/libqb/include/event.h new file mode 100644 index 000000000..38831418a --- /dev/null +++ b/internal/c/libqb/include/event.h @@ -0,0 +1,11 @@ +#ifndef INCLUDE_LIBQB_EVENT_H +#define INCLUDE_LIBQB_EVENT_H + +#include + +void error(int32_t error_number); +void evnt(uint32_t linenumber, uint32_t inclinenumber = 0, const char *incfilename = NULL); + +extern uint32_t new_error; + +#endif diff --git a/internal/c/libqb/include/rounding.h b/internal/c/libqb/include/rounding.h new file mode 100644 index 000000000..6b6f876ae --- /dev/null +++ b/internal/c/libqb/include/rounding.h @@ -0,0 +1,122 @@ +#ifndef INCLUDE_LIBQB_ROUNDING_H +#define INCLUDE_LIBQB_ROUNDING_H + +#include + +#include "event.h" + +int64_t qbr(long double); +uint64_t qbr_longdouble_to_uint64(long double); +int32_t qbr_float_to_long(float); +int32_t qbr_double_to_long(double); + +void fpu_reinit(); + +// CSNG +static inline double func_csng_float(long double value) { + if ((value <= 3.402823466E38) && (value >= -3.402823466E38)) { + return value; + } + error(6); + return 0; +} +static inline double func_csng_double(double value) { + if ((value <= 3.402823466E38) && (value >= -3.402823466E38)) { + return value; + } + error(6); + return 0; +} + +// CDBL +static inline double func_cdbl_float(long double value) { + if ((value <= 1.7976931348623157E308) && + (value >= -1.7976931348623157E308)) { + return value; + } + error(6); + return 0; +} + +// CINT +// func_cint_single uses func_cint_double +static inline int32_t func_cint_double(double value) { + if ((value < 32767.5) && (value >= -32768.5)) { + return qbr_double_to_long(value); + } + error(6); + return 0; +} +static inline int64_t func_cint_float(long double value) { + if ((value < 32767.5) && (value >= -32768.5)) { + return qbr(value); + } + error(6); + return 0; +} +static inline int16_t func_cint_long(int32_t value) { + if ((value >= -32768) && (value <= 32767)) + return value; + error(6); + return 0; +} +static inline int16_t func_cint_ulong(uint32_t value) { + if (value <= 32767) + return value; + error(6); + return 0; +} +static inline int16_t func_cint_int64(int64_t value) { + if ((value >= -32768) && (value <= 32767)) + return value; + error(6); + return 0; +} +static inline int16_t func_cint_uint64(uint64_t value) { + if (value <= 32767) + return value; + error(6); + return 0; +} + +// CLNG +// func_clng_single uses func_clng_double +//-2147483648 to 2147483647 +static inline int32_t func_clng_double(double value) { + if ((value < 2147483647.5) && (value >= -2147483648.5)) { + return qbr_double_to_long(value); + } + error(6); + return 0; +} +static inline int64_t func_clng_float(long double value) { + if ((value < 2147483647.5) && (value >= -2147483648.5)) { + return qbr(value); + } + error(6); + return 0; +} +static inline int32_t func_clng_ulong(uint32_t value) { + if (value <= 2147483647) + return value; + error(6); + return 0; +} +static inline int32_t func_clng_int64(int64_t value) { + if ((value >= -2147483648) && (value <= 2147483647)) + return value; + error(6); + return 0; +} +static inline int32_t func_clng_uint64(uint64_t value) { + if (value <= 2147483647) + return value; + error(6); + return 0; +} + +//_ROUND (note: round performs no error checking) +static inline int64_t func_round_double(long double value) { return qbr(value); } +static inline int64_t func_round_float(long double value) { return qbr(value); } + +#endif diff --git a/internal/c/libqb/src/datetime.cpp b/internal/c/libqb/src/datetime.cpp new file mode 100644 index 000000000..cddc82aab --- /dev/null +++ b/internal/c/libqb/src/datetime.cpp @@ -0,0 +1,196 @@ + +#include "libqb-common.h" + +#include +#include + +#ifdef QB64_WINDOWS +# include +#endif + +#include "rounding.h" +#include "event.h" +#include "datetime.h" + +#ifdef QB64_MACOSX +# include +# define ORWL_NANO (+1.0E-9) +# define ORWL_GIGA UINT64_C(1000000000) +static double orwl_timebase = 0.0; +static uint64_t orwl_timestart = 0; +static int64_t orwl_gettime(void) { + if (!orwl_timestart) { + mach_timebase_info_data_t tb = {0}; + mach_timebase_info(&tb); + orwl_timebase = tb.numer; + orwl_timebase /= tb.denom; + orwl_timestart = mach_absolute_time(); + } + struct timespec t; + double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase; + t.tv_sec = diff * ORWL_NANO; + t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA); + return t.tv_sec * 1000 + t.tv_nsec / 1000000; +} +#endif + +#ifdef QB64_LINUX +int64_t GetTicks() { + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; +} +#elif defined QB64_MACOSX +int64_t GetTicks() { return orwl_gettime(); } +#else +int64_t GetTicks() { return ((((int64_t)clock()) * ((int64_t)1000)) / ((int64_t)CLOCKS_PER_SEC)); } +#endif + +static uint64_t millis_since_midnight() { + auto currenttime = std::chrono::system_clock::now(); + + // Gives us the number of miliseconds past the current second + uint64_t millis_only = std::chrono::duration_cast(currenttime.time_since_epoch()).count() % 1000; + + // Convert to time_t and then hour/min/sec. localtime() takes the current + // timezone into account for us. + time_t curttime = std::chrono::system_clock::to_time_t(currenttime); + struct tm *local = localtime(&curttime); + + // Compute current time as number of seconds past midnight + uint64_t seconds = local->tm_hour * 3600 + local->tm_min * 60 + local->tm_sec; + + return seconds * 1000 + millis_only; +} + +double func_timer(double accuracy, int32_t passed) { + if (new_error) + return 0; + + double result = (double)millis_since_midnight() / 1000; + + // Adjust result for requested accuracy, or default accuracy. + if (!passed) { + accuracy = 18.2; + } else { + if (accuracy <= 0.0) { + error(5); + return 0; + } + accuracy = 1.0 / accuracy; + } + result *= accuracy; + result = qbr(result); + result /= accuracy; + if (!passed) { + float f = result; + result = f; + } + + return result; +} + +#ifndef QB64_WINDOWS +void Sleep(uint32_t milliseconds) { + static uint64_t sec, nsec; + sec = milliseconds / 1000; + nsec = (milliseconds % 1000) * 1000000; + static timespec ts; + ts.tv_sec = sec; + ts.tv_nsec = nsec; + nanosleep(&ts, NULL); +} +#endif + +void sub__delay(double seconds) { + double ms, base, elapsed, prev_now, now; // cannot be static + base = GetTicks(); + if (new_error) + return; + if (seconds < 0) { + error(5); + return; + } + if (seconds > 2147483.647) { + error(5); + return; + } + ms = seconds * 1000.0; + now = base; // force first prev=... assignment to equal base +recalculate: + prev_now = now; + now = GetTicks(); + elapsed = now - base; + if (elapsed < 0) { // GetTicks looped + base = now - (prev_now - base); // calculate new base + } + if (elapsed < ms) { + int64_t wait; // cannot be static + wait = ms - elapsed; + if (!wait) + wait = 1; + if (wait >= 10) { + Sleep(9); + evnt(0); // check for new events + // recalculate time + goto recalculate; + } else { + Sleep(wait); + } + } +} + +void sub__limit(double fps) { + if (new_error) + return; + static double prev = 0; + double ms, now, elapsed; // cannot be static + if (fps <= 0.0) { + error(5); + return; + } + ms = 1000.0 / fps; + if (ms > 60000.0) { + error(5); + return; + } // max. 1 min delay between frames allowed to avoid accidental lock-up of program +recalculate: + now = GetTicks(); + if (prev == 0.0) { // first call? + prev = now; + return; + } + if (now < prev) { // value looped? + prev = now; + return; + } + elapsed = now - prev; // elapsed time since prev + + if (elapsed == ms) { + prev = prev + ms; + return; + } + + if (elapsed < ms) { + int64_t wait; // cannot be static + wait = ms - elapsed; + if (!wait) + wait = 1; + if (wait >= 10) { + Sleep(9); + evnt(0); // check for new events + } else { + Sleep(wait); + } + // recalculate time + goto recalculate; + } + + // too long since last call, adjust prev to current time + // minor overshoot up to 32ms is recovered, otherwise time is re-seeded + if (elapsed <= (ms + 32.0)) + prev = prev + ms; + else + prev = now; +} + diff --git a/internal/c/libqb/src/rounding.cpp b/internal/c/libqb/src/rounding.cpp new file mode 100644 index 000000000..59be1d0ff --- /dev/null +++ b/internal/c/libqb/src/rounding.cpp @@ -0,0 +1,91 @@ + +#include "libqb-common.h" + +#include "rounding.h" + +#ifdef QB64_NOT_X86 +int64_t qbr(long double f) { + int64_t i; + int temp = 0; + if (f > 9223372036854775807) { + temp = 1; + f = f - 9223372036854775808u; + } // if it's too large for a signed int64, make it an unsigned int64 and return that value if possible. + if (f < 0) + i = f - 0.5f; + else + i = f + 0.5f; + if (temp) + return i | 0x8000000000000000; //+9223372036854775808; + return i; +} +uint64_t qbr_longdouble_to_uint64(long double f) { + if (f < 0) + return (f - 0.5f); + else + return (f + 0.5f); +} +int32_t qbr_float_to_long(float f) { + if (f < 0) + return (f - 0.5f); + else + return (f + 0.5f); +} +int32_t qbr_double_to_long(double f) { + if (f < 0) + return (f - 0.5f); + else + return (f + 0.5f); +} +void fpu_reinit() {} // do nothing +#else +// QBASIC compatible rounding via FPU: +// FLDS=load single +// FLDL=load double +// FLDT=load long double +int64_t qbr(long double f) { + int64_t i; + int temp = 0; + if (f > 9223372036854775807) { + temp = 1; + f = f - 9223372036854775808u; + } // if it's too large for a signed int64, make it an unsigned int64 and return that value if possible. + __asm__("fldt %1;" + "fistpll %0;" + : "=m"(i) + : "m"(f)); + if (temp) + return i | 0x8000000000000000; // if it's an unsigned int64, manually set the bit flag + return i; +} +uint64_t qbr_longdouble_to_uint64(long double f) { + uint64_t i; + __asm__("fldt %1;" + "fistpll %0;" + : "=m"(i) + : "m"(f)); + return i; +} +int32_t qbr_float_to_long(float f) { + int32_t i; + __asm__("flds %1;" + "fistpl %0;" + : "=m"(i) + : "m"(f)); + return i; +} +int32_t qbr_double_to_long(double f) { + int32_t i; + __asm__("fldl %1;" + "fistpl %0;" + : "=m"(i) + : "m"(f)); + return i; +} +void fpu_reinit() { + unsigned int mode = 0x37F; + asm("fldcw %0" : : "m"(*&mode)); +} +#endif // x86 support + + diff --git a/internal/c/qbx.cpp b/internal/c/qbx.cpp index c2910ae3b..643231fd5 100755 --- a/internal/c/qbx.cpp +++ b/internal/c/qbx.cpp @@ -1,6 +1,9 @@ #include "common.h" #include "audio.h" #include "gui.h" +#include "event.h" +#include "rounding.h" +#include "datetime.h" extern int32 func__cinp(int32 toggle, int32 passed); // Console INP scan code reader @@ -56,7 +59,6 @@ qbs *func__inflate(qbs *text, int64 originalsize, int32 passed); #endif */ -extern void error(int32 error_number); extern int32 sub_gl_called; #ifdef QB64_GUI @@ -96,12 +98,8 @@ extern int32 func__scaledheight(); extern qbs *func__cwd(); extern qbs *func__startdir(); -extern void sub__limit(double fps); - extern void sub__fps(double fps, int32 passed); -extern void sub__delay(double seconds); - extern void sub__resize(int32 on_off, int32 stretch_smooth); extern int32 func__resize(); extern int32 func__resizewidth(); @@ -462,7 +460,6 @@ extern long double func_val(qbs *s); extern void sub_out(int32 port, int32 data); extern void sub_randomize(double seed, int32 passed); extern float func_rnd(float n, int32 passed); -extern double func_timer(double accuracy, int32 passed); extern void sub_sound(double frequency, double lengthinclockticks); // following are declared below to allow for inlining // extern double func_abs(double d); @@ -516,22 +513,6 @@ extern qbs *func_time(); extern int32 func_csrlin(); extern int32 func_pos(int32 ignore); extern double func_log(double value); -extern double func_csng_float(long double value); -extern double func_csng_double(double value); -extern double func_cdbl_float(long double value); -extern int32 func_cint_double(double value); -extern int64 func_cint_float(long double value); -extern int16 func_cint_long(int32 value); -extern int16 func_cint_ulong(uint32 value); -extern int16 func_cint_int64(int64 value); -extern int16 func_cint_uint64(uint64 value); -extern int32 func_clng_double(double value); -extern int64 func_clng_float(long double value); -extern int32 func_clng_ulong(uint32 value); -extern int32 func_clng_int64(int64 value); -extern int32 func_clng_uint64(uint64 value); -extern int64 func_round_double(long double value); -extern int64 func_round_float(long double value); extern double func_fix_double(double value); extern long double func_fix_float(long double value); extern double func_exp_single(double value); @@ -702,14 +683,8 @@ uint64 func__setbit(uint64 a1, int b1); uint64 func__resetbit(uint64 a1, int b1); uint64 func__togglebit(uint64 a1, int b1); #ifndef QB64_WINDOWS -extern void Sleep(uint32 milliseconds); extern void ZeroMemory(void *ptr, int64 bytes); #endif -extern int64 qbr(long double f); -extern uint64 qbr_longdouble_to_uint64(long double f); -extern int32 qbr_float_to_long(float f); -extern int32 qbr_double_to_long(double f); -extern void fpu_reinit(void); extern uint64 getubits(uint32 bsize, uint8 *base, ptrszint i); extern int64 getbits(uint32 bsize, uint8 *base, ptrszint i); @@ -870,112 +845,6 @@ template static T qbs_cleanup(uint32 base, T passvalue) { return passvalue; } -// CSNG -inline double func_csng_float(long double value) { - if ((value <= 3.402823466E38) && (value >= -3.402823466E38)) { - return value; - } - error(6); - return 0; -} -inline double func_csng_double(double value) { - if ((value <= 3.402823466E38) && (value >= -3.402823466E38)) { - return value; - } - error(6); - return 0; -} - -// CDBL -inline double func_cdbl_float(long double value) { - if ((value <= 1.7976931348623157E308) && - (value >= -1.7976931348623157E308)) { - return value; - } - error(6); - return 0; -} - -// CINT -// func_cint_single uses func_cint_double -inline int32 func_cint_double(double value) { - if ((value < 32767.5) && (value >= -32768.5)) { - return qbr_double_to_long(value); - } - error(6); - return 0; -} -inline int64 func_cint_float(long double value) { - if ((value < 32767.5) && (value >= -32768.5)) { - return qbr(value); - } - error(6); - return 0; -} -inline int16 func_cint_long(int32 value) { - if ((value >= -32768) && (value <= 32767)) - return value; - error(6); - return 0; -} -inline int16 func_cint_ulong(uint32 value) { - if (value <= 32767) - return value; - error(6); - return 0; -} -inline int16 func_cint_int64(int64 value) { - if ((value >= -32768) && (value <= 32767)) - return value; - error(6); - return 0; -} -inline int16 func_cint_uint64(uint64 value) { - if (value <= 32767) - return value; - error(6); - return 0; -} - -// CLNG -// func_clng_single uses func_clng_double -//-2147483648 to 2147483647 -inline int32 func_clng_double(double value) { - if ((value < 2147483647.5) && (value >= -2147483648.5)) { - return qbr_double_to_long(value); - } - error(6); - return 0; -} -inline int64 func_clng_float(long double value) { - if ((value < 2147483647.5) && (value >= -2147483648.5)) { - return qbr(value); - } - error(6); - return 0; -} -inline int32 func_clng_ulong(uint32 value) { - if (value <= 2147483647) - return value; - error(6); - return 0; -} -inline int32 func_clng_int64(int64 value) { - if ((value >= -2147483648) && (value <= 2147483647)) - return value; - error(6); - return 0; -} -inline int32 func_clng_uint64(uint64 value) { - if (value <= 2147483647) - return value; - error(6); - return 0; -} - -//_ROUND (note: round performs no error checking) -inline int64 func_round_double(long double value) { return qbr(value); } -inline int64 func_round_float(long double value) { return qbr(value); } // force abs to return floating point numbers correctly inline double func_abs(double d) { return fabs(d); } @@ -2395,8 +2264,7 @@ extern int64 display_lock_confirmed; extern int64 display_lock_released; uint32 r; -void evnt(uint32 linenumber, uint32 inclinenumber = 0, - const char *incfilename = NULL) { +void evnt(uint32 linenumber, uint32 inclinenumber, const char *incfilename) { if (disableEvents) return; diff --git a/tests/compile_tests/console_only/image.output b/tests/compile_tests/console_only/image.output index 1f1af952a..f96020217 100644 --- a/tests/compile_tests/console_only/image.output +++ b/tests/compile_tests/console_only/image.output @@ -1 +1 @@ --13 48 72 +-11 48 72 diff --git a/tests/compile_tests/console_only/screenimage.bas b/tests/compile_tests/console_only/screenimage.bas index 3688a1690..65db411dd 100644 --- a/tests/compile_tests/console_only/screenimage.bas +++ b/tests/compile_tests/console_only/screenimage.bas @@ -6,7 +6,7 @@ $ELSE ' We can't do _SCREENIMAGE on Linux or Mac OS build agents, but we can still test ' that it compiles -PRINT -13; +PRINT -11; SYSTEM $END IF diff --git a/tests/compile_tests/console_only/screenimage.output b/tests/compile_tests/console_only/screenimage.output index 032935b5f..c3afb62c1 100644 --- a/tests/compile_tests/console_only/screenimage.output +++ b/tests/compile_tests/console_only/screenimage.output @@ -1 +1 @@ --13 +-11 diff --git a/tests/compile_tests/image/test.output b/tests/compile_tests/image/test.output index 1f1af952a..f96020217 100644 --- a/tests/compile_tests/image/test.output +++ b/tests/compile_tests/image/test.output @@ -1 +1 @@ --13 48 72 +-11 48 72 diff --git a/tests/compile_tests/screenimage/test.bas b/tests/compile_tests/screenimage/test.bas index a886564d9..ed946585e 100644 --- a/tests/compile_tests/screenimage/test.bas +++ b/tests/compile_tests/screenimage/test.bas @@ -7,7 +7,7 @@ $ELSE ' We can't do _SCREENIMAGE on Linux or Mac OS build agents, but we can still ' test that it compiles -PRINT -13; +PRINT -11; SYSTEM $END IF diff --git a/tests/compile_tests/screenimage/test.output b/tests/compile_tests/screenimage/test.output index 032935b5f..c3afb62c1 100644 --- a/tests/compile_tests/screenimage/test.output +++ b/tests/compile_tests/screenimage/test.output @@ -1 +1 @@ --13 +-11