Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrated 64bit long double into the toolchain #527

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ doxygen/
obj/
release/
build/
CEdev/
cmake-build-debug/
tools/fasmg/fasmg
tools/fasmg/fasmg.exe
Expand Down
88 changes: 46 additions & 42 deletions docs/static/asm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,27 +126,29 @@ For example, if a *short* type is used, the upper byte of the value pushed on th
This table lists the locations relative to *sp* from within the called funciton.
Note that :code:`sp + [0,2]` contains the return address.

+------------+------------+----------------------+
| C/C++ Type | Size | Stack Location |
+============+============+======================+
| char | 1 byte | sp + [3] |
+------------+------------+----------------------+
| short | 2 bytes | sp + [3,4] |
+------------+------------+----------------------+
| int | 3 bytes | sp + [3,5] |
+------------+------------+----------------------+
| long | 4 bytes | sp + [3,6] |
+------------+------------+----------------------+
| (u)int48_t | 6 bytes | sp + [3,8] |
+------------+------------+----------------------+
| long long | 8 bytes | sp + [3,10] |
+------------+------------+----------------------+
| float | 4 bytes | sp + [3,6] |
+------------+------------+----------------------+
| double | 4 bytes | sp + [3,6] |
+------------+------------+----------------------+
| pointer | 3 bytes | sp + [3,5] |
+------------+------------+----------------------+
+-------------+------------+----------------------+
| C/C++ Type | Size | Stack Location |
+=============+============+======================+
| char | 1 byte | sp + [3] |
+-------------+------------+----------------------+
| short | 2 bytes | sp + [3,4] |
+-------------+------------+----------------------+
| int | 3 bytes | sp + [3,5] |
+-------------+------------+----------------------+
| long | 4 bytes | sp + [3,6] |
+-------------+------------+----------------------+
| (u)int48_t | 6 bytes | sp + [3,8] |
+-------------+------------+----------------------+
| long long | 8 bytes | sp + [3,10] |
+-------------+------------+----------------------+
| float | 4 bytes | sp + [3,6] |
+-------------+------------+----------------------+
| double | 4 bytes | sp + [3,6] |
+-------------+------------+----------------------+
| long double | 8 bytes | sp + [3,10] |
+-------------+------------+----------------------+
| pointer | 3 bytes | sp + [3,5] |
+-------------+------------+----------------------+

Returns
^^^^^^^
Expand All @@ -155,24 +157,26 @@ This table lists which registers are used for return values from a function.
The type's sign does not affect the registers used, but may affect the value returned.
The LSB is located in the register on the far right of the expression, e.g. ``E:HL`` indicates register ``L`` stores the LSB.

+------------+-------------------+
| C/C++ Type | Return Register |
+============+===================+
| char | A |
+------------+-------------------+
| short | HL |
+------------+-------------------+
| int | UHL |
+------------+-------------------+
| long | E:UHL |
+------------+-------------------+
| (u)int48_t | UDE:UHL |
+------------+-------------------+
| long long | BC:UDE:UHL |
+------------+-------------------+
| float | E:UHL |
+------------+-------------------+
| double | E:UHL |
+------------+-------------------+
| pointer | UHL |
+------------+-------------------+
+-------------+-------------------+
| C/C++ Type | Return Register |
+=============+===================+
| char | A |
+-------------+-------------------+
| short | HL |
+-------------+-------------------+
| int | UHL |
+-------------+-------------------+
| long | E:UHL |
+-------------+-------------------+
| (u)int48_t | UDE:UHL |
+-------------+-------------------+
| long long | BC:UDE:UHL |
+-------------+-------------------+
| float | E:UHL |
+-------------+-------------------+
| double | E:UHL |
+-------------+-------------------+
| long double | BC:UDE:UHL |
+-------------+-------------------+
| pointer | UHL |
+-------------+-------------------+
2 changes: 1 addition & 1 deletion docs/static/hardware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Therefore, software has to implement that functionality.
Like the Z80, the eZ80 also lacks native support for float-point arithmetic, so :code:`float` is slower than :code:`int`.
Furthermore, in the toolchain, :code:`float` and :code:`double` are actually treated the same (this is allowed by the C standard).

In the toolchain, :code:`float` is the standard IEEE 32-bit float.
In the toolchain, :code:`float` is the standard IEEE 32-bit float, and :code:`long double` is the standard IEEE 64-bit float.
However, IEEE floats have limited precision and have unintuitive rouding behavior.
So when TI designed the TI-81, they wrote their own special floating-point number format, which is much more suitable for precision scientific calculations.
Unfortunately, it also much slower.
Expand Down
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ PREFIX = CEdev
include $(CURDIR)/src/common.mk

LIBS := libload graphx fontlibc keypadc fileioc usbdrvce srldrvce msddrvce fatdrvce
SRCS := ce crt libc libcxx
SRCS := ce crt libc libcxx softfloat
TOOLS := fasmg convbin convimg convfont cedev-config

ifeq ($(OS),Windows_NT)
Expand Down
1 change: 1 addition & 0 deletions src/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ INSTALL_LIB := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/libload))
INSTALL_CRT := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/crt))
INSTALL_LIBC := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/libc))
INSTALL_LIBCXX := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/libcxx))
INSTALL_SOFTFLOAT := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/softfloat))
INSTALL_CE := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/lib/ce))
INSTALL_BIN := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/bin))
INSTALL_H := $(call QUOTE_ARG,$(call NATIVEPATH,$(INSTALL_DIR)/include))
Expand Down
40 changes: 40 additions & 0 deletions src/crt/dcmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>

typedef union F64_pun {
long double flt;
uint64_t bin;
} F64_pun;

#define Float64_inf_lsh_1 UINT64_C(0xFFE0000000000000)

#define F64_CMP_EQUAL 0
#define F64_CMP_LESS -1
#define F64_CMP_GREATER 1
#define F64_CMP_UNORDERED 1

// assumes no NaN
int _dcmp_c(const long double* x, const long double *y) {
F64_pun arg_x, arg_y;
arg_x.flt = *x;
arg_y.flt = *y;

// if (isunordered(x, y)) {
// return F64_CMP_UNORDERED;
// }

bool x_sign = signbit(*x);
bool y_sign = signbit(*y);
if (x_sign != y_sign) {
if (iszero(*x) && iszero(*y)) {
return F64_CMP_EQUAL;
}
return (x_sign ? F64_CMP_LESS : F64_CMP_GREATER);
}

if (arg_x.bin == arg_y.bin) {
return F64_CMP_EQUAL;
}
return ((arg_x.bin < arg_y.bin) != x_sign) ? F64_CMP_LESS : F64_CMP_GREATER;
}
50 changes: 50 additions & 0 deletions src/crt/dcmp.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
assume adl=1

section .text

public __dcmp

#if 0

; This version of _dcmp does not save/restore the state of BC/DE/HL, the state
; of these registers may by modified by the C function

; int _dcmp_c(long double x, const long double *y)
__dcmp:
; f64_f64*_ret_i24
push af, iy
ld iy, 9
add iy, sp
push iy, bc, de, hl
call __dcmp_c
pop af, af, af, af, iy, af
; Set the comparison flags
add hl, de
or a, a
sbc hl, de
ret

#else

; Properly saves/restores the state of BC/DE/HL

; int _dcmp_c(const long double *x, const long double *y)
__dcmp:
; f64*_f64*_ret_i24
push bc, de, hl, af, iy
ld iy, 6
add iy, sp
pea iy + 12
push iy
call __dcmp_c
pop af, af, iy, af
; Set the comparison flags
add hl, de
or a, a
sbc hl, de
pop hl, de, bc
ret

#end if

extern __dcmp_c
16 changes: 16 additions & 0 deletions src/crt/dneg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <stdint.h>

typedef union F64_pun {
long double flt;
uint64_t bin;
} F64_pun;

#define Float64_signbit UINT64_C(0x8000000000000000)

// Should be easy to implement in assembly
long double _dneg_c(long double x) {
F64_pun ret;
ret.flt = x;
ret.bin ^= Float64_signbit;
return ret.flt;
}
14 changes: 14 additions & 0 deletions src/crt/dneg.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
assume adl=1

section .text

public __dneg

__dneg:
; f64_ret_f64
push af, iy, bc, de, hl
call __dneg_c
pop af, af, af, iy, af
ret

extern __dneg_c
138 changes: 138 additions & 0 deletions src/crt/float64_runtime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <stdint.h>
#include "../softfloat/include/softfloat.h"

//------------------------------------------------------------------------------
// unions
//------------------------------------------------------------------------------

typedef union F64_pun {
long double flt;
float64_t soft;
uint64_t bin;
} F64_pun;

typedef union F32_pun {
float flt;
float32_t soft;
uint32_t bin;
} F32_pun;

//------------------------------------------------------------------------------
// Arithmetic functions
//------------------------------------------------------------------------------

/* long double _dneg_c(long double x) */

long double _dadd_c(long double x, const long double *y) {
F64_pun arg_x, arg_y, ret;
arg_x.flt = x;
arg_y.flt = *y;
ret.soft = f64_add(arg_x.soft, arg_y.soft);
return ret.flt;
}

long double _dsub_c(long double x, const long double *y) {
F64_pun arg_x, arg_y, ret;
arg_x.flt = x;
arg_y.flt = *y;
ret.soft = f64_sub(arg_x.soft, arg_y.soft);
return ret.flt;
}

long double _dmul_c(long double x, const long double *y) {
F64_pun arg_x, arg_y, ret;
arg_x.flt = x;
arg_y.flt = *y;
ret.soft = f64_mul(arg_x.soft, arg_y.soft);
return ret.flt;
}

long double _ddiv_c(long double x, const long double *y) {
F64_pun arg_x, arg_y, ret;
arg_x.flt = x;
arg_y.flt = *y;
ret.soft = f64_div(arg_x.soft, arg_y.soft);
return ret.flt;
}

long double _drem_c(long double x, const long double *y) {
F64_pun arg_x, arg_y, ret;
arg_x.flt = x;
arg_y.flt = *y;
ret.soft = f64_rem(arg_x.soft, arg_y.soft);
return ret.flt;
}

/* int _dcmp_c(const long double *x, const long double* y) */

//------------------------------------------------------------------------------
// Convert to long double
//------------------------------------------------------------------------------

long double _ftod_c(float x) {
F32_pun arg_x;
F64_pun ret;
arg_x.flt = x;
ret.soft = f32_to_f64(arg_x.soft);
return ret.flt;
}

long double _ltod_c(int32_t x) {
F64_pun ret;
ret.soft = i32_to_f64(x);
return ret.flt;
}

long double _lltod_c(int64_t x) {
F64_pun ret;
ret.soft = i64_to_f64(x);
return ret.flt;
}

long double _ultod_c(uint32_t x) {
F64_pun ret;
ret.soft = ui32_to_f64(x);
return ret.flt;
}

long double _ulltod_c(uint64_t x) {
F64_pun ret;
ret.soft = ui64_to_f64(x);
return ret.flt;
}

//------------------------------------------------------------------------------
// Convert from long double
//------------------------------------------------------------------------------

float _dtof_c(long double x) {
F32_pun ret;
F64_pun arg_x;
arg_x.flt = x;
ret.soft = f64_to_f32(arg_x.soft);
return ret.flt;
}

int32_t _dtol_c(long double x) {
F64_pun arg_x;
arg_x.flt = x;
return f64_to_i32_r_minMag(arg_x.soft, false);
}

int64_t _dtoll_c(long double x) {
F64_pun arg_x;
arg_x.flt = x;
return f64_to_i64_r_minMag(arg_x.soft, false);
}

uint32_t _dtoul_c(long double x) {
F64_pun arg_x;
arg_x.flt = x;
return f64_to_ui32_r_minMag(arg_x.soft, false);
}

uint64_t _dtoull_c(long double x) {
F64_pun arg_x;
arg_x.flt = x;
return f64_to_ui64_r_minMag(arg_x.soft, false);
}
Loading