From 7bb60de74e8bc99006210ae011fc39114f5802ae Mon Sep 17 00:00:00 2001 From: Veikko Sariola Date: Sat, 5 Dec 2020 16:41:07 +0200 Subject: [PATCH] feat(libsointu): make su_render return some error codes, typically due to FPU stack errors. --- include/sointu/sointu.h | 10 +++++++--- render.asm | 4 +++- tests/test_render_samples_api.c | 34 ++++++++++++++++++++------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/include/sointu/sointu.h b/include/sointu/sointu.h index 4ef5867..1899de5 100644 --- a/include/sointu/sointu.h +++ b/include/sointu/sointu.h @@ -83,9 +83,13 @@ void CALLCONV su_load_gmdls(void); // time_out > time_in, as it is modulated and the time could advance by 2 or more, so the loop // exit condition would fire when the current time is already past time_in // -// Returns error code: -// 0 everything ok -// (no actual errors implemented yet) +// Returns an error code, which is actually just masked version of the FPU Status Word +// On a succesful run, the return value should be 0 +// Error code bits: +// bit 0 FPU invalid operation (stack over/underflow OR invalid arithmetic e.g. NaNs) +// bit 2 Divide by zero occurred +// bit 6 Stack overflow or underflow occurred +// bits 11-13 The top pointer of the fpu stack. Any other value than 0 indicates that some values were left on the stack. int CALLCONV su_render(Synth* synth, float* buffer, int* samples, int* time); // Arithmetic opcode ids diff --git a/render.asm b/render.asm index d59a999..133188f 100644 --- a/render.asm +++ b/render.asm @@ -209,7 +209,9 @@ su_render_samples_time_finish: pop _SI ; pop the pointer to samples mov dword [_SI], edx ; *samples = samples rendered mov dword [_BX], eax ; *time = time ticks rendered - xor eax, eax ; TODO: set eax to possible error code, now just 0 + xor eax, eax + fnstsw ax ; store the FPU status flag in ax. + and ax, 0011100001000101b ; mask TOP pointer, stack error, zero divide and invalid operation frstor [_SP] ; restore fpu state add _SP,108 ; rewind the stack allocate for FPU state %if BITS == 32 ; stdcall diff --git a/tests/test_render_samples_api.c b/tests/test_render_samples_api.c index 9a5f382..b696c61 100644 --- a/tests/test_render_samples_api.c +++ b/tests/test_render_samples_api.c @@ -43,18 +43,15 @@ int main(int argc, char* argv[]) { time = 0; errcode = su_render(synth, buffer, &samples, &time); if (errcode != 0) - { - printf("su_render returned error"); goto fail; - } if (samples > 0) { - printf("su_render rendered samples, despite it should not"); + printf("su_render rendered samples, despite it should not\n"); goto fail; } if (time > 0) { - printf("su_render advanced time, despite it should not"); + printf("su_render advanced time, despite it should not\n"); goto fail; } // Then check that when we render using su_render with 0 samples, @@ -62,19 +59,16 @@ int main(int argc, char* argv[]) { samples = 0; time = INT32_MAX; errcode = su_render(synth, buffer, &samples, &time); - if (errcode != 0) - { - printf("su_render returned error"); + if (errcode != 0) goto fail; - } if (samples > 0) { - printf("su_render rendered samples, despite it should not"); + printf("su_render rendered samples, despite it should not\n"); goto fail; } if (time > 0) { - printf("su_render advanced time, despite it should not"); + printf("su_render advanced time, despite it should not\n"); goto fail; } // Then check that each time we call render, only SAMPLES_PER_ROW @@ -84,7 +78,9 @@ int main(int argc, char* argv[]) { // check that buffer full samples = 1; time = INT32_MAX; - su_render(synth, &buffer[totalrendered*2], &samples, &time); + errcode = su_render(synth, &buffer[totalrendered*2], &samples, &time); + if (errcode != 0) + goto fail; totalrendered += samples; if (samples != 1) { @@ -98,7 +94,9 @@ int main(int argc, char* argv[]) { } samples = SAMPLES_PER_ROW - 1; time = INT32_MAX; - su_render(synth, &buffer[totalrendered * 2], &samples, &time); + errcode = su_render(synth, &buffer[totalrendered * 2], &samples, &time); + if (errcode != 0) + goto fail; totalrendered += samples; if (samples != SAMPLES_PER_ROW - 1) { @@ -124,6 +122,16 @@ finish: free(buffer); return retval; fail: + if (errcode > 0) { + if ((errcode & 0xFF00) != 0) + printf("FPU stack was not empty on exit\n"); + if ((errcode & 0x04) != 0) + printf("FPU zero divide\n"); + if ((errcode & 0x01) != 0) + printf("FPU invalid operation\n"); + if ((errcode & 0x40) != 0) + printf("FPU stack error\n"); + } retval = 1; goto finish; }