mirror of
https://github.com/pruten/shoebill.git
synced 2024-11-17 15:07:18 +00:00
688199cf6d
A/UX doesn't even come close to booting yet with the new FPU, but we're getting there. I don't want to trash master with a code base that doesn't work. Also, I turned off ethernet in the cocoa GUI, since the TAP driver isn't working right on 10.10 yet.
1540 lines
50 KiB
C
1540 lines
50 KiB
C
/*
|
|
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <fenv.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include "../core/shoebill.h"
|
|
|
|
extern struct dis_t dis;
|
|
extern uint16_t dis_op;
|
|
|
|
#define FPU_JUMP_EMU 0
|
|
#define FPU_JUMP_DIS 1
|
|
typedef void (fpu_func_t)(uint16_t, uint16_t);
|
|
|
|
typedef struct {
|
|
fpu_func_t *emu, *dis;
|
|
const char *name;
|
|
} fpu_inst_t;
|
|
|
|
~newmacro(create_fpu_jump_table, 0, {
|
|
my $names = [
|
|
'unknown', 'fabs', 'facos', 'fadd', 'fasin', 'fatan', 'fatanh', 'fbcc', 'fcmp', 'fcos', 'fcosh',
|
|
'fdbcc', 'fdiv', 'fetox', 'fetoxm1', 'fgetexp', 'fgetman', 'fint', 'fintrz', 'flog10', 'flog2',
|
|
'flogn', 'flognp1', 'fmod', 'fmove', 'fmovecr', 'fmovem', 'fmovem_control', 'fmul', 'fneg', 'fnop',
|
|
'frem', 'frestore', 'fsave', 'fscale', 'fscc', 'fsgldiv', 'fsglmul', 'fsin', 'fsincos', 'fsinh',
|
|
'fsqrt', 'fsub', 'ftan', 'ftanh', 'ftentox', 'ftrapcc', 'ftst', 'ftwotox'
|
|
];
|
|
my $fpu_enum = "typedef enum {\n";
|
|
foreach my $n (@$names) {
|
|
$fpu_enum .= "\tfpu_inst_$n,\n";
|
|
}
|
|
$fpu_enum .= "\nfpu_inst_max} fpu_inst_name_t;";
|
|
|
|
my $fpu_table = "fpu_inst_t fpu_inst_table[fpu_inst_max] = {\n";
|
|
foreach my $n (@$names) {
|
|
$fpu_table .= "\t{NULL, NULL, \"" . $n . "\"},\n";
|
|
}
|
|
$fpu_table = substr($fpu_table, 0, -2);
|
|
$fpu_table .= "\n};";
|
|
|
|
my $out = "$fpu_enum \n $fpu_table \n";
|
|
return $out;
|
|
})
|
|
|
|
~create_fpu_jump_table()
|
|
|
|
static fpu_inst_name_t fpu_decode_op(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 ttt MMMMMM);
|
|
|
|
if (t) {
|
|
switch (t) {
|
|
case 1:
|
|
if ((M>>3) == 1)
|
|
return fpu_inst_fdbcc;
|
|
else if ((M>>3) == 7)
|
|
return fpu_inst_ftrapcc;
|
|
return fpu_inst_fscc;
|
|
case 2:
|
|
if (M==0 && ext == 0)
|
|
return fpu_inst_fnop; // same as fbf.w
|
|
// fall through
|
|
case 3:
|
|
return fpu_inst_fbcc;
|
|
case 4:
|
|
return fpu_inst_fsave;
|
|
case 5:
|
|
return fpu_inst_frestore;
|
|
}
|
|
return fpu_inst_unknown;
|
|
}
|
|
|
|
~decompose(ext, ccc xxx yyy eeeeeee)
|
|
|
|
switch (c) {
|
|
case 0: // Reg to reg
|
|
break;
|
|
case 1: // unused
|
|
return fpu_inst_unknown;
|
|
case 2: // Memory->reg & movec
|
|
break;
|
|
|
|
case 3: // reg->mem
|
|
return fpu_inst_fmove;
|
|
|
|
case 4: // mem -> sys ctl registers
|
|
case 5: // sys ctl registers -> mem
|
|
return fpu_inst_fmovem_control;
|
|
|
|
case 6: // movem to fp registers
|
|
case 7: // movem to memory
|
|
return fpu_inst_fmovem;
|
|
}
|
|
|
|
// Here c == 0b000 or 010
|
|
|
|
if (M == 0 && ~bmatch(ext, 010 111 xxx xxxxxxx))
|
|
return fpu_inst_fmovecr;
|
|
|
|
if ((e>>3) == ~b(0110))
|
|
return fpu_inst_fsincos;
|
|
|
|
switch (e) {
|
|
|
|
case ~b(0000000):
|
|
case ~b(1000000):
|
|
case ~b(1000100):
|
|
return fpu_inst_fmove;
|
|
|
|
case ~b(0000001): return fpu_inst_fint;
|
|
case ~b(0000010): return fpu_inst_fsinh;
|
|
case ~b(0000011): return fpu_inst_fintrz;
|
|
case ~b(0000110): return fpu_inst_flognp1;
|
|
case ~b(0001000): return fpu_inst_fetoxm1;
|
|
case ~b(0001001): return fpu_inst_ftanh;
|
|
case ~b(0001010): return fpu_inst_fatan;
|
|
case ~b(0001100): return fpu_inst_fasin;
|
|
case ~b(0001101): return fpu_inst_fatanh;
|
|
case ~b(0001110): return fpu_inst_fsin;
|
|
case ~b(0001111): return fpu_inst_ftan;
|
|
case ~b(0010000): return fpu_inst_fetox;
|
|
case ~b(0010001): return fpu_inst_ftwotox;
|
|
case ~b(0010010): return fpu_inst_ftentox;
|
|
case ~b(0010100): return fpu_inst_flogn;
|
|
case ~b(0010101): return fpu_inst_flog10;
|
|
case ~b(0010110): return fpu_inst_flog2;
|
|
case ~b(0011001): return fpu_inst_fcosh;
|
|
case ~b(0011100): return fpu_inst_facos;
|
|
case ~b(0011101): return fpu_inst_fcos;
|
|
case ~b(0011110): return fpu_inst_fgetexp;
|
|
case ~b(0011111): return fpu_inst_fgetman;
|
|
case ~b(0100001): return fpu_inst_fmod;
|
|
case ~b(0100100): return fpu_inst_fsgldiv;
|
|
case ~b(0100111): return fpu_inst_fsglmul;
|
|
case ~b(0100101): return fpu_inst_frem;
|
|
case ~b(0100110): return fpu_inst_fscale;
|
|
case ~b(0111000): return fpu_inst_fcmp;
|
|
case ~b(0111010): return fpu_inst_ftst;
|
|
|
|
case ~b(0011000):
|
|
case ~b(1011000):
|
|
case ~b(1011100):
|
|
return fpu_inst_fabs;
|
|
|
|
case ~b(0100010):
|
|
case ~b(1100010):
|
|
case ~b(1100110):
|
|
return fpu_inst_fadd;
|
|
|
|
case ~b(0100000):
|
|
case ~b(1100000):
|
|
case ~b(1100100):
|
|
return fpu_inst_fdiv;
|
|
|
|
|
|
case ~b(0100011):
|
|
case ~b(1100011):
|
|
case ~b(1100111):
|
|
return fpu_inst_fmul;
|
|
|
|
case ~b(0011010):
|
|
case ~b(1011010):
|
|
case ~b(1011110):
|
|
return fpu_inst_fneg;
|
|
|
|
case ~b(0000100):
|
|
case ~b(1000001):
|
|
case ~b(1000101):
|
|
return fpu_inst_fsqrt;
|
|
|
|
case ~b(0101000):
|
|
case ~b(1101000):
|
|
case ~b(1101100):
|
|
return fpu_inst_fsub;
|
|
}
|
|
|
|
return fpu_inst_unknown;
|
|
|
|
}
|
|
|
|
#define nextword() ({const uint16_t w=lget(shoe.pc,2); if (shoe.abort) {return;}; shoe.pc+=2; w;})
|
|
#define verify_supervisor() {if (!sr_s()) {throw_privilege_violation(); return;}}
|
|
|
|
void dis_fpu_decode ()
|
|
{
|
|
~decompose(dis_op, 1111 001 xxx 000000);
|
|
|
|
fpu_inst_name_t name;
|
|
uint16_t ext = 0;
|
|
|
|
if (x == 4)
|
|
name = fpu_inst_fsave;
|
|
else if (x == 5)
|
|
name = fpu_inst_frestore;
|
|
else {
|
|
ext = dis_next_word();
|
|
name = fpu_decode_op(dis_op, ext);
|
|
}
|
|
|
|
if (fpu_inst_table[name].dis) {
|
|
(*fpu_inst_table[name].dis)(dis_op, ext);
|
|
return ;
|
|
}
|
|
|
|
sprintf(dis.str, "%s ???", fpu_inst_table[name].name);
|
|
}
|
|
|
|
void inst_fpu_decode ()
|
|
{
|
|
~decompose(shoe.op, 1111 001 xxx 000000);
|
|
|
|
fpu_inst_name_t name;
|
|
uint16_t ext = 0;
|
|
|
|
if (x == 4)
|
|
name = fpu_inst_fsave;
|
|
else if (x == 5)
|
|
name = fpu_inst_frestore;
|
|
else {
|
|
ext = nextword();
|
|
name = fpu_decode_op(shoe.op, ext);
|
|
// "For FPCP instructions that generate FPU exceptions,
|
|
// FPIAR is loaded with the address of an instruction before it's executed,
|
|
// unless all arithmetic exceptions are disabled."
|
|
// My take: set fpiar for all instructions except fsave, frestore, and fmovem_control
|
|
if (name != fpu_inst_fmovem_control)
|
|
shoe.fpiar = shoe.orig_pc;
|
|
}
|
|
|
|
if (fpu_inst_table[name].emu) {
|
|
(*fpu_inst_table[name].emu)(shoe.op, ext);
|
|
return ;
|
|
}
|
|
|
|
slog("inst_fpu_decode: unhandled instruction: %s op=0x%04x ext = 0x%04x pc=0x%08x\n", fpu_inst_table[name].name, shoe.op, ext, shoe.orig_pc);
|
|
assert(!"unknown fpu inst");
|
|
//dbg_state.running = 0;
|
|
|
|
}
|
|
|
|
|
|
void dis_fsave(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 100 MMMMMM);
|
|
~decompose(op, 1111 001 100 mmmrrr);
|
|
|
|
if (m == 4)
|
|
sprintf(dis.str, "fsave -(a%u)", r);
|
|
else
|
|
sprintf(dis.str, "fsave %s", decode_ea_addr(M));
|
|
}
|
|
|
|
void inst_fsave(uint16_t op, uint16_t ext)
|
|
{
|
|
verify_supervisor();
|
|
|
|
~decompose(op, 1111 001 100 MMMMMM);
|
|
~decompose(op, 1111 001 100 mmmrrr);
|
|
|
|
const uint32_t size = 0x1c; // IDLE frame
|
|
const uint16_t frame_header = 0xfd18;
|
|
uint32_t addr;
|
|
|
|
if (m == 4)
|
|
addr = shoe.a[r] - size;
|
|
else {
|
|
call_ea_addr(M);
|
|
addr = shoe.dat;
|
|
}
|
|
|
|
lset(addr, 2, frame_header);
|
|
if (shoe.abort)
|
|
return ;
|
|
|
|
if (m == 4)
|
|
shoe.a[r] = addr;
|
|
}
|
|
|
|
void dis_frestore(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 101 MMMMMM);
|
|
~decompose(op, 1111 001 101 mmmrrr);
|
|
|
|
if (m == 3)
|
|
sprintf(dis.str, "frestore (a%u)+", r);
|
|
else
|
|
sprintf(dis.str, "frestore %s", decode_ea_addr(M));
|
|
}
|
|
|
|
void inst_frestore(uint16_t op, uint16_t ext)
|
|
{
|
|
verify_supervisor();
|
|
|
|
~decompose(op, 1111 001 101 MMMMMM);
|
|
~decompose(op, 1111 001 101 mmmrrr);
|
|
|
|
uint32_t addr, size;
|
|
|
|
if (m == 3)
|
|
addr = shoe.a[r];
|
|
else {
|
|
call_ea_addr(M);
|
|
addr = shoe.dat;
|
|
}
|
|
|
|
const uint16_t word = lget(addr, 2);
|
|
if (shoe.abort) return ;
|
|
|
|
// XXX: These frame sizes are different on 68881/68882/68040
|
|
if ((word & 0xff00) == 0x0000)
|
|
size = 4; // NULL state frame
|
|
else if ((word & 0xff) == 0x0018)
|
|
size = 0x1c; // IDLE state frame
|
|
else if ((word & 0xff) == 0x00b4)
|
|
size = 0xb8; // BUSY state frame
|
|
else {
|
|
slog("Frestore encountered an unknown state frame 0x%04x\n", word);
|
|
assert("inst_frestore: bad state frame");
|
|
return ;
|
|
}
|
|
|
|
if (m==3) {
|
|
shoe.a[r] += size;
|
|
slog("frestore: changing shoe.a[%u] += %u\n", r, size);
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
uint8_t inexact;
|
|
uint8_t dat[4][12];
|
|
} fmovecr_t;
|
|
|
|
fmovecr_t fmovecr_pi = {1, 0x40, 0x00, 0x00, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35, 0x40, 0x00, 0x00, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0x40, 0x00, 0x00, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0x40, 0x00, 0x00, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x35, };
|
|
fmovecr_t fmovecr_log10_2 = {1, 0x3f, 0xfd, 0x00, 0x00, 0x9a, 0x20, 0x9a, 0x84, 0xfb, 0xcf, 0xf7, 0x98, 0x3f, 0xfd, 0x00, 0x00, 0x9a, 0x20, 0x9a, 0x84, 0xfb, 0xcf, 0xf7, 0x98, 0x3f, 0xfd, 0x00, 0x00, 0x9a, 0x20, 0x9a, 0x84, 0xfb, 0xcf, 0xf7, 0x98, 0x3f, 0xfd, 0x00, 0x00, 0x9a, 0x20, 0x9a, 0x84, 0xfb, 0xcf, 0xf7, 0x99, };
|
|
fmovecr_t fmovecr_e = {1, 0x40, 0x00, 0x00, 0x00, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0x40, 0x00, 0x00, 0x00, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0x40, 0x00, 0x00, 0x00, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, 0x40, 0x00, 0x00, 0x00, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9b, };
|
|
fmovecr_t fmovecr_log2_e = {1, 0x3f, 0xff, 0x00, 0x00, 0xb8, 0xaa, 0x3b, 0x29, 0x5c, 0x17, 0xf0, 0xbc, 0x3f, 0xff, 0x00, 0x00, 0xb8, 0xaa, 0x3b, 0x29, 0x5c, 0x17, 0xf0, 0xbb, 0x3f, 0xff, 0x00, 0x00, 0xb8, 0xaa, 0x3b, 0x29, 0x5c, 0x17, 0xf0, 0xbb, 0x3f, 0xff, 0x00, 0x00, 0xb8, 0xaa, 0x3b, 0x29, 0x5c, 0x17, 0xf0, 0xbc, };
|
|
fmovecr_t fmovecr_log10_e = {0, 0x3f, 0xfd, 0x00, 0x00, 0xde, 0x5b, 0xd8, 0xa9, 0x37, 0x28, 0x71, 0x95, 0x3f, 0xfd, 0x00, 0x00, 0xde, 0x5b, 0xd8, 0xa9, 0x37, 0x28, 0x71, 0x95, 0x3f, 0xfd, 0x00, 0x00, 0xde, 0x5b, 0xd8, 0xa9, 0x37, 0x28, 0x71, 0x95, 0x3f, 0xfd, 0x00, 0x00, 0xde, 0x5b, 0xd8, 0xa9, 0x37, 0x28, 0x71, 0x95, };
|
|
fmovecr_t fmovecr_zero = {0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_ln_2 = {1, 0x3f, 0xfe, 0x00, 0x00, 0xb1, 0x72, 0x17, 0xf7, 0xd1, 0xcf, 0x79, 0xac, 0x3f, 0xfe, 0x00, 0x00, 0xb1, 0x72, 0x17, 0xf7, 0xd1, 0xcf, 0x79, 0xab, 0x3f, 0xfe, 0x00, 0x00, 0xb1, 0x72, 0x17, 0xf7, 0xd1, 0xcf, 0x79, 0xab, 0x3f, 0xfe, 0x00, 0x00, 0xb1, 0x72, 0x17, 0xf7, 0xd1, 0xcf, 0x79, 0xac, };
|
|
fmovecr_t fmovecr_ln_10 = {1, 0x40, 0x00, 0x00, 0x00, 0x93, 0x5d, 0x8d, 0xdd, 0xaa, 0xa8, 0xac, 0x17, 0x40, 0x00, 0x00, 0x00, 0x93, 0x5d, 0x8d, 0xdd, 0xaa, 0xa8, 0xac, 0x16, 0x40, 0x00, 0x00, 0x00, 0x93, 0x5d, 0x8d, 0xdd, 0xaa, 0xa8, 0xac, 0x16, 0x40, 0x00, 0x00, 0x00, 0x93, 0x5d, 0x8d, 0xdd, 0xaa, 0xa8, 0xac, 0x17, };
|
|
fmovecr_t fmovecr_10_0 = {0, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_1 = {0, 0x40, 0x02, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_2 = {0, 0x40, 0x05, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_4 = {0, 0x40, 0x0c, 0x00, 0x00, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x9c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_8 = {0, 0x40, 0x19, 0x00, 0x00, 0xbe, 0xbc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x00, 0xbe, 0xbc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x00, 0xbe, 0xbc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x00, 0xbe, 0xbc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_16 = {0, 0x40, 0x34, 0x00, 0x00, 0x8e, 0x1b, 0xc9, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x8e, 0x1b, 0xc9, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x8e, 0x1b, 0xc9, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x8e, 0x1b, 0xc9, 0xbf, 0x04, 0x00, 0x00, 0x00, };
|
|
fmovecr_t fmovecr_10_32 = {1, 0x40, 0x69, 0x00, 0x00, 0x9d, 0xc5, 0xad, 0xa8, 0x2b, 0x70, 0xb5, 0x9e, 0x40, 0x69, 0x00, 0x00, 0x9d, 0xc5, 0xad, 0xa8, 0x2b, 0x70, 0xb5, 0x9d, 0x40, 0x69, 0x00, 0x00, 0x9d, 0xc5, 0xad, 0xa8, 0x2b, 0x70, 0xb5, 0x9d, 0x40, 0x69, 0x00, 0x00, 0x9d, 0xc5, 0xad, 0xa8, 0x2b, 0x70, 0xb5, 0x9e, };
|
|
fmovecr_t fmovecr_10_64 = {1, 0x40, 0xd3, 0x00, 0x00, 0xc2, 0x78, 0x1f, 0x49, 0xff, 0xcf, 0xa6, 0xd5, 0x40, 0xd3, 0x00, 0x00, 0xc2, 0x78, 0x1f, 0x49, 0xff, 0xcf, 0xa6, 0xd5, 0x40, 0xd3, 0x00, 0x00, 0xc2, 0x78, 0x1f, 0x49, 0xff, 0xcf, 0xa6, 0xd5, 0x40, 0xd3, 0x00, 0x00, 0xc2, 0x78, 0x1f, 0x49, 0xff, 0xcf, 0xa6, 0xd6, };
|
|
fmovecr_t fmovecr_10_128 = {1, 0x41, 0xa8, 0x00, 0x00, 0x93, 0xba, 0x47, 0xc9, 0x80, 0xe9, 0x8c, 0xe0, 0x41, 0xa8, 0x00, 0x00, 0x93, 0xba, 0x47, 0xc9, 0x80, 0xe9, 0x8c, 0xdf, 0x41, 0xa8, 0x00, 0x00, 0x93, 0xba, 0x47, 0xc9, 0x80, 0xe9, 0x8c, 0xdf, 0x41, 0xa8, 0x00, 0x00, 0x93, 0xba, 0x47, 0xc9, 0x80, 0xe9, 0x8c, 0xe0, };
|
|
fmovecr_t fmovecr_10_256 = {1, 0x43, 0x51, 0x00, 0x00, 0xaa, 0x7e, 0xeb, 0xfb, 0x9d, 0xf9, 0xde, 0x8e, 0x43, 0x51, 0x00, 0x00, 0xaa, 0x7e, 0xeb, 0xfb, 0x9d, 0xf9, 0xde, 0x8d, 0x43, 0x51, 0x00, 0x00, 0xaa, 0x7e, 0xeb, 0xfb, 0x9d, 0xf9, 0xde, 0x8d, 0x43, 0x51, 0x00, 0x00, 0xaa, 0x7e, 0xeb, 0xfb, 0x9d, 0xf9, 0xde, 0x8e, };
|
|
fmovecr_t fmovecr_10_512 = {1, 0x46, 0xa3, 0x00, 0x00, 0xe3, 0x19, 0xa0, 0xae, 0xa6, 0x0e, 0x91, 0xc7, 0x46, 0xa3, 0x00, 0x00, 0xe3, 0x19, 0xa0, 0xae, 0xa6, 0x0e, 0x91, 0xc6, 0x46, 0xa3, 0x00, 0x00, 0xe3, 0x19, 0xa0, 0xae, 0xa6, 0x0e, 0x91, 0xc6, 0x46, 0xa3, 0x00, 0x00, 0xe3, 0x19, 0xa0, 0xae, 0xa6, 0x0e, 0x91, 0xc7, };
|
|
fmovecr_t fmovecr_10_1024 = {1, 0x4d, 0x48, 0x00, 0x00, 0xc9, 0x76, 0x75, 0x86, 0x81, 0x75, 0x0c, 0x17, 0x4d, 0x48, 0x00, 0x00, 0xc9, 0x76, 0x75, 0x86, 0x81, 0x75, 0x0c, 0x17, 0x4d, 0x48, 0x00, 0x00, 0xc9, 0x76, 0x75, 0x86, 0x81, 0x75, 0x0c, 0x17, 0x4d, 0x48, 0x00, 0x00, 0xc9, 0x76, 0x75, 0x86, 0x81, 0x75, 0x0c, 0x18, };
|
|
fmovecr_t fmovecr_10_2048 = {1, 0x5a, 0x92, 0x00, 0x00, 0x9e, 0x8b, 0x3b, 0x5d, 0xc5, 0x3d, 0x5d, 0xe5, 0x5a, 0x92, 0x00, 0x00, 0x9e, 0x8b, 0x3b, 0x5d, 0xc5, 0x3d, 0x5d, 0xe5, 0x5a, 0x92, 0x00, 0x00, 0x9e, 0x8b, 0x3b, 0x5d, 0xc5, 0x3d, 0x5d, 0xe5, 0x5a, 0x92, 0x00, 0x00, 0x9e, 0x8b, 0x3b, 0x5d, 0xc5, 0x3d, 0x5d, 0xe6, };
|
|
fmovecr_t fmovecr_10_4096 = {1, 0x75, 0x25, 0x00, 0x00, 0xc4, 0x60, 0x52, 0x02, 0x8a, 0x20, 0x97, 0x9b, 0x75, 0x25, 0x00, 0x00, 0xc4, 0x60, 0x52, 0x02, 0x8a, 0x20, 0x97, 0x9a, 0x75, 0x25, 0x00, 0x00, 0xc4, 0x60, 0x52, 0x02, 0x8a, 0x20, 0x97, 0x9a, 0x75, 0x25, 0x00, 0x00, 0xc4, 0x60, 0x52, 0x02, 0x8a, 0x20, 0x97, 0x9b, };
|
|
|
|
const int _fpu_round_map[4] = {FE_TONEAREST, FE_TOWARDZERO, FE_DOWNWARD, FE_UPWARD};
|
|
#define fpu_set_round() assert(0 == fesetround(_fpu_round_map[shoe.fpcr.b.mc_rnd]))
|
|
#define fpu_reset_round() assert(0 == fesetround(FE_TONEAREST))
|
|
|
|
static void fpu_set_cc(long double f)
|
|
{
|
|
// Set condition codes
|
|
shoe.fpsr.raw &= 0x00ffffff;
|
|
shoe.fpsr.b.cc_nan = (0 != isnan(f));
|
|
if (!shoe.fpsr.b.cc_nan) {
|
|
shoe.fpsr.b.cc_n = (0 != signbit(f));
|
|
if (isinf(f))
|
|
shoe.fpsr.b.cc_i = 1;
|
|
else
|
|
shoe.fpsr.b.cc_z = (f == 0.0);
|
|
}
|
|
}
|
|
|
|
static long double fpu_set_reg(long double f, uint8_t r)
|
|
{
|
|
// Round the number according to the mode control byte
|
|
{
|
|
fpu_set_round();
|
|
|
|
if (shoe.fpcr.b.mc_prec == 1) {
|
|
const float tmp = (float)f;
|
|
f = tmp;
|
|
} else if (shoe.fpcr.b.mc_prec == 2) {
|
|
const double tmp = (double)f;
|
|
f = tmp;
|
|
}
|
|
|
|
fpu_reset_round();
|
|
}
|
|
|
|
// Store it
|
|
shoe.fp[r] = f;
|
|
return f;
|
|
}
|
|
|
|
// fpu_set_reg_cc() and fpu_set_ea() set the condition codes. (what else should they set?)
|
|
static void fpu_set_reg_cc(long double f, uint8_t r)
|
|
{
|
|
fpu_set_cc(fpu_set_reg(f, r));
|
|
}
|
|
|
|
static void x87_to_motorola(long double x87, uint8_t motorola[12])
|
|
{
|
|
uint8_t *x87_ptr = (uint8_t*)&x87;
|
|
motorola[0] = x87_ptr[9];
|
|
motorola[1] = x87_ptr[8];
|
|
motorola[2] = 0;
|
|
motorola[3] = 0;
|
|
motorola[4] = x87_ptr[7];
|
|
motorola[5] = x87_ptr[6];
|
|
motorola[6] = x87_ptr[5];
|
|
motorola[7] = x87_ptr[4];
|
|
motorola[8] = x87_ptr[3];
|
|
motorola[9] = x87_ptr[2];
|
|
motorola[10] = x87_ptr[1];
|
|
motorola[11] = x87_ptr[0];
|
|
}
|
|
|
|
static long double motorola_to_x87(const uint8_t motorola[12])
|
|
{
|
|
uint8_t x87[12];
|
|
|
|
x87[11] = 0;
|
|
x87[10] = 0;
|
|
x87[9] = motorola[0];
|
|
x87[8] = motorola[1];
|
|
|
|
x87[7] = motorola[4];
|
|
x87[6] = motorola[5];
|
|
x87[5] = motorola[6];
|
|
x87[4] = motorola[7];
|
|
x87[3] = motorola[8];
|
|
x87[2] = motorola[9];
|
|
x87[1] = motorola[10];
|
|
x87[0] = motorola[11];
|
|
return *(long double*)&x87[0];
|
|
}
|
|
|
|
void inst_fmovecr(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(ext, 010111 rrr xxxxxxx);
|
|
|
|
fmovecr_t *c = &fmovecr_zero;
|
|
|
|
switch (x) {
|
|
case 0x00: c = &fmovecr_pi; break;
|
|
case 0x0b: c = &fmovecr_log10_2; break;
|
|
case 0x0c: c = &fmovecr_e; break;
|
|
case 0x0d: c = &fmovecr_log2_e; break;
|
|
case 0x0e: c = &fmovecr_log10_e; break;
|
|
case 0x0f: c = &fmovecr_zero; break;
|
|
case 0x30: c = &fmovecr_ln_2; break;
|
|
case 0x31: c = &fmovecr_ln_10; break;
|
|
case 0x32: c = &fmovecr_10_0; break;
|
|
case 0x33: c = &fmovecr_10_1; break;
|
|
case 0x34: c = &fmovecr_10_2; break;
|
|
case 0x35: c = &fmovecr_10_4; break;
|
|
case 0x36: c = &fmovecr_10_8; break;
|
|
case 0x37: c = &fmovecr_10_16; break;
|
|
case 0x38: c = &fmovecr_10_32; break;
|
|
case 0x39: c = &fmovecr_10_64; break;
|
|
case 0x3a: c = &fmovecr_10_128; break;
|
|
case 0x3b: c = &fmovecr_10_256; break;
|
|
case 0x3c: c = &fmovecr_10_512; break;
|
|
case 0x3d: c = &fmovecr_10_1024; break;
|
|
case 0x3e: c = &fmovecr_10_2048; break;
|
|
case 0x3f: c = &fmovecr_10_4096; break;
|
|
}
|
|
|
|
// The constants in the 68881's ROM must be in the "intermediate" format, because they're rounded differently based on fpcr.rnd
|
|
const long double f = motorola_to_x87(c->dat[shoe.fpcr.b.mc_rnd]);
|
|
|
|
fpu_set_reg_cc(f, r);
|
|
|
|
slog("inst_fmovecr: set fp%u=%.30Lg\n", r, shoe.fp[r]);
|
|
|
|
// fpu_finalize_exceptions();
|
|
}
|
|
|
|
void dis_fmovecr(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(ext, 010111 rrr xxxxxxx);
|
|
|
|
sprintf(dis.str, "fmovecr.x 0x%02x,fp%u", x, r);
|
|
}
|
|
|
|
void inst_fmovem_control(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(ext, 10d CSI 0000000000);
|
|
|
|
const uint32_t count = C + S + I;
|
|
const uint32_t size = count * 4;
|
|
|
|
if (count == 0) // I don't know if this is even a valid instruction
|
|
return ;
|
|
|
|
if ((m == 0 || m == 1) && (count > 1)) { // data and addr reg modes are valid, but only if count==1
|
|
throw_illegal_instruction();
|
|
return ;
|
|
}
|
|
|
|
uint32_t addr, buf[3];
|
|
uint32_t i;
|
|
|
|
if (d) { // reg to memory
|
|
i=0;
|
|
if (C) buf[i++] = shoe.fpcr.raw;
|
|
if (S) buf[i++] = shoe.fpsr.raw;
|
|
if (I) buf[i++] = shoe.fpiar;
|
|
|
|
if (m == 0) {
|
|
shoe.d[r] = buf[0];
|
|
return ;
|
|
}
|
|
else if (m == 1) {
|
|
shoe.a[r] = buf[0];
|
|
return ;
|
|
}
|
|
else if (m == 3)
|
|
addr = shoe.a[r];
|
|
else if (m == 4)
|
|
addr = shoe.a[r] - size;
|
|
else {
|
|
call_ea_addr(M);
|
|
addr = shoe.dat;
|
|
}
|
|
|
|
for (i=0; i<count; i++) {
|
|
lset(addr + (i*4), 4, buf[i]);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
}
|
|
else { // mem to reg
|
|
if (m == 0) // data reg
|
|
buf[0] = shoe.d[r];
|
|
else if (m == 1) // addr reg
|
|
buf[0] = shoe.a[r];
|
|
else {
|
|
if (m == 3) // post-increment
|
|
addr = shoe.a[r];
|
|
else if (m == 4) // pre-decrement
|
|
addr = shoe.a[r] - size;
|
|
else if (M == 0x3c) // immediate
|
|
addr = shoe.pc;
|
|
else {
|
|
call_ea_addr(M); // call_ea_addr() should work for all other modes
|
|
addr = shoe.dat;
|
|
}
|
|
|
|
for (i=0; i<count; i++) {
|
|
buf[i] = lget(addr + (i*4), 4);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
|
|
if (C) {
|
|
uint8_t round = shoe.fpcr.b.mc_rnd;
|
|
shoe.fpcr.raw = buf[i++];
|
|
uint8_t newround = shoe.fpcr.b.mc_rnd;
|
|
|
|
if (round != newround) {
|
|
slog("inst_fmovem_control: HEY: round %u -> %u\n", round, newround);
|
|
}
|
|
}
|
|
if (S) shoe.fpsr.raw = buf[i++];
|
|
if (I) shoe.fpiar = buf[i++];
|
|
|
|
// Commit immediate-EA-mode PC change
|
|
if (M == 0x3c)
|
|
shoe.pc += size;
|
|
}
|
|
|
|
// Commit pre/post-inc/decrement
|
|
|
|
if (m == 3)
|
|
shoe.a[r] += size;
|
|
if (m == 4)
|
|
shoe.a[r] -= size;
|
|
|
|
|
|
|
|
slog("inst_fmove_control: notice: (EA = %u/%u %08x CSI = %u%u%u)\n", m, r, (uint32_t)shoe.dat, C, S, I);
|
|
}
|
|
|
|
void dis_fmovem_control(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(ext, 10d CSI 0000000000);
|
|
|
|
if (d)
|
|
sprintf(dis.str, "fmovem.l [%u%u%u],%s\n", C, S, I, decode_ea_addr(M)); // <- XXX: decode_ea_addr() is the wrong function to use
|
|
else
|
|
sprintf(dis.str, "fmovem.l %s,[%u%u%u]\n", decode_ea_addr(M), C, S, I); // <- XXX: decode_ea_addr() is the wrong function to use
|
|
}
|
|
|
|
|
|
static uint8_t fpu_test_cc(uint8_t cc)
|
|
{
|
|
const uint8_t z = shoe.fpsr.b.cc_z;
|
|
const uint8_t n = shoe.fpsr.b.cc_n;
|
|
const uint8_t nan = shoe.fpsr.b.cc_nan;
|
|
|
|
switch (cc & 0x0f) {
|
|
case 0: // false
|
|
return 0;
|
|
case 1: // equal
|
|
return z;
|
|
case 2: // greater than
|
|
return !(nan || z || n);
|
|
case 3: // greater than or equal
|
|
return z || !(nan || n);
|
|
case 4: // less than
|
|
return n && !(nan || z);
|
|
case 5: // less than or equal
|
|
return z || (n && !nan);
|
|
case 6: // greater or less than
|
|
return !(nan || z);
|
|
case 7: // ordered
|
|
return !nan;
|
|
case 8: // unordered
|
|
return nan;
|
|
case 9: // not (greater or less than)
|
|
return nan || z;
|
|
case 10: // not (less than or equal)
|
|
return nan || !(n || z);
|
|
case 11: // not (less than)
|
|
return nan || (z || !n);
|
|
case 12: // not (greater than or equal)
|
|
return nan || (n && !z);
|
|
case 13: // not (greater than)
|
|
return nan || z || n;
|
|
case 14: // not equal
|
|
return !z;
|
|
case 15: // true
|
|
return 1;
|
|
}
|
|
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
void inst_fbcc(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 01 s 0bcccc); // b => raise BSUN if NaN
|
|
|
|
uint32_t displacement;
|
|
if (s) {
|
|
const uint16_t ext2 = nextword();
|
|
displacement = (ext << 16) | ext2;
|
|
}
|
|
else {
|
|
const int16_t tmp = ext;
|
|
const int32_t tmp2 = tmp;
|
|
displacement = tmp2;
|
|
}
|
|
|
|
if (b) {
|
|
slog("inst_fbcc: fixme: Got a CC that wants to set BSUN, not implemented\n");
|
|
//assert(0); // FIXME: implement BSUN, or uncomment this
|
|
}
|
|
|
|
if (fpu_test_cc(c)) {
|
|
const uint32_t addr = shoe.orig_pc + 2 + displacement;
|
|
shoe.pc = addr;
|
|
}
|
|
}
|
|
|
|
const char *fpu_cc_names[32] = {
|
|
"f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or",
|
|
"un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t",
|
|
"sf", "seq", "gt", "ge", "lt", "le", "gl", "gle",
|
|
"ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st"
|
|
};
|
|
|
|
void dis_fbcc(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 01 s 0ccccc); // only the low 5 bits of cc are significant
|
|
|
|
uint32_t displacement;
|
|
if (s) {
|
|
const uint16_t ext2 = dis_next_word();
|
|
displacement = (ext << 16) | ext2;
|
|
}
|
|
else {
|
|
const int16_t tmp = ext;
|
|
const int32_t tmp2 = tmp;
|
|
displacement = tmp2;
|
|
}
|
|
|
|
const uint32_t addr = dis.orig_pc + 2 + displacement;
|
|
|
|
sprintf(dis.str, "fb%s.%c *0x%08x", fpu_cc_names[c], "wl"[s], addr);
|
|
}
|
|
|
|
static void reverse_order(uint8_t *buf, const uint32_t size)
|
|
{
|
|
uint32_t i;
|
|
for (i=0; i < (size/2); i++) {
|
|
const uint8_t tmp = buf[i];
|
|
buf[i] = buf[size-(1+i)];
|
|
buf[size-(1+i)] = tmp;
|
|
}
|
|
}
|
|
|
|
void inst_fmovem(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(ext, 11 d ps 000 LLLLLLLL); // Static register mask
|
|
~decompose(ext, 11 0 00 000 0yyy0000); // Register for dynamic mode
|
|
|
|
const uint8_t pre_mask = s ? shoe.d[y] : L; // pre-adjusted mask
|
|
|
|
// Count the number of bits in the mask
|
|
uint32_t count, maskcpy = pre_mask;
|
|
for (count=0; maskcpy; maskcpy >>= 1)
|
|
count += (maskcpy & 1);
|
|
|
|
const uint32_t size = count * 12;
|
|
|
|
// for predecrement mode, the mask is reversed
|
|
uint8_t mask = 0;
|
|
if (m == 4) {
|
|
uint32_t i;
|
|
for (i=0; i < 8; i++) {
|
|
const uint8_t bit = (pre_mask << i) & 0x80;
|
|
mask = (mask >> 1) | bit;
|
|
}
|
|
}
|
|
else
|
|
mask = pre_mask;
|
|
|
|
uint32_t i, addr;
|
|
|
|
// Find the EA
|
|
if (m == 3) {
|
|
addr = shoe.a[r];
|
|
assert(p); // assert post-increment mask
|
|
}
|
|
else if (m == 4) {
|
|
addr = shoe.a[r] - size;
|
|
assert(!p); // assert pre-decrement mask
|
|
}
|
|
else {
|
|
call_ea_addr(M);
|
|
addr = shoe.dat;
|
|
assert(p); // assert post-increment mask
|
|
}
|
|
|
|
slog("inst_fmovem: pre=%08x mask=%08x EA=%u/%u addr=0x%08x size=%u %s\n", pre_mask, mask, m, r, addr, size, d?"to mem":"from mem");
|
|
|
|
if (d) {
|
|
// Write those registers
|
|
for (i=0; i<8; i++) {
|
|
if (!(mask & (0x80 >> i)))
|
|
continue;
|
|
|
|
uint8_t buf[12];
|
|
x87_to_motorola(shoe.fp[i], buf);
|
|
|
|
slog("inst_fmovem: writing %Lf from fp%u", shoe.fp[i], i);
|
|
uint32_t j;
|
|
for (j=0; j<12; j++) {
|
|
slog(" %02x", buf[j]);
|
|
lset(addr, 1, buf[j]);
|
|
addr++;
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
slog("\n");
|
|
}
|
|
}
|
|
else {
|
|
// Read those registers
|
|
for (i=0; i<8; i++) {
|
|
if (!(mask & (0x80 >> i)))
|
|
continue;
|
|
|
|
uint8_t buf[12];
|
|
uint32_t j;
|
|
for (j=0; j<12; j++) {
|
|
buf[j] = lget(addr, 1);
|
|
addr++;
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
shoe.fp[i] = motorola_to_x87(buf);
|
|
|
|
slog("inst_fmovem: read %Lf to fp%u\n", shoe.fp[i], i);
|
|
}
|
|
}
|
|
|
|
// Commit the write for pre/post-inc/decrement
|
|
if (m == 3)
|
|
shoe.a[r] += size;
|
|
else if (m == 4)
|
|
shoe.a[r] -= size;
|
|
|
|
//slog("inst_fmovem: notice: not implemented (EA = %u/%u, mask=0x%02x)\n", m, r, mask);
|
|
|
|
}
|
|
|
|
void dis_fmovem(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(ext, 11 d ps 000 LLLLLLLL); // Static register mask
|
|
~decompose(ext, 11 0 00 000 0yyy0000); // Register for dynamic mode
|
|
|
|
sprintf(dis.str, "fmovem ???");
|
|
}
|
|
|
|
enum {
|
|
format_L = 0,
|
|
format_S = 1,
|
|
format_X = 2,
|
|
format_Ps = 3,
|
|
format_W = 4,
|
|
format_D = 5,
|
|
format_B = 6,
|
|
format_Pd = 7
|
|
} fpu_formats;
|
|
/*
|
|
* 0 L long word integer
|
|
* 1 S single precision real
|
|
* 2 X extended precision real
|
|
* 3 P{#k} packed decimal real with static k factor
|
|
* 4 W word integer
|
|
* 5 D double precision real
|
|
* 6 B byte integer
|
|
* 7 P{Dn} packed decimal real with dynamic k factor
|
|
*/
|
|
|
|
static void fpu_read_ea_commit(uint8_t mr, uint8_t format)
|
|
{
|
|
const uint8_t m = mr >> 3;
|
|
const uint8_t r = mr & 7;
|
|
const uint8_t sizes[8] = {4, 4, 12, 12, 2, 8, 1, 12};
|
|
|
|
if (m == 3)
|
|
shoe.a[r] += sizes[format];
|
|
else if (m == 4)
|
|
shoe.a[r] -= sizes[format];
|
|
}
|
|
|
|
// Note: fpu_read_ea modifies shoe.pc, fpu_read_ea_commit modies shoe.a[r] for pre/post-inc/decrement
|
|
static long double fpu_read_ea(uint8_t mr, uint8_t format)
|
|
{
|
|
const uint8_t m = mr >> 3;
|
|
const uint8_t r = mr & 7;
|
|
const uint8_t sizes[8] = {4, 4, 12, 12, 2, 8, 1, 12};
|
|
|
|
long double data, result;
|
|
uint32_t addr;
|
|
|
|
// If mode==a-reg, or mode==data reg and the size is > 4 bytes, no dice
|
|
if ((m == 1) ||
|
|
((m == 0) && (sizes[format] > 4))) {
|
|
throw_illegal_instruction();
|
|
return 0.0;
|
|
}
|
|
|
|
switch (m) {
|
|
case 0: {
|
|
if (format == format_S) {
|
|
float tmp = shoe.d[r];
|
|
data = tmp;
|
|
}
|
|
else if (format == format_B) {
|
|
int8_t tmp = shoe.d[r];
|
|
data = tmp;
|
|
}
|
|
else if (format == format_W) {
|
|
int16_t tmp = shoe.d[r];
|
|
data = tmp;
|
|
}
|
|
else if (format == format_L) {
|
|
int32_t tmp = shoe.d[r];
|
|
data = tmp;
|
|
}
|
|
|
|
goto got_data;
|
|
}
|
|
|
|
case 3:
|
|
addr = shoe.a[r];
|
|
assert(!( r==7 && sizes[format]==1));
|
|
goto got_address;
|
|
|
|
case 4:
|
|
addr = shoe.a[r] - sizes[format];
|
|
assert(!( r==7 && sizes[format]==1));
|
|
goto got_address;
|
|
|
|
case 7:
|
|
if (r == 4) {
|
|
addr = shoe.pc;
|
|
shoe.pc += sizes[format];
|
|
goto got_address;
|
|
}
|
|
|
|
// fall through to default:
|
|
|
|
default: {
|
|
|
|
shoe.mr=mr;
|
|
ea_addr();
|
|
if (shoe.abort)
|
|
return 0.0;
|
|
|
|
addr = (uint32_t)shoe.dat;
|
|
goto got_address;
|
|
}
|
|
}
|
|
|
|
got_address:
|
|
|
|
{
|
|
uint8_t buf[12];
|
|
uint8_t *ptr = &buf[sizes[format]];
|
|
uint32_t i;
|
|
|
|
slog("inst_f fpu_read_ea: format=%u, data =", format);
|
|
for (i=0; i<sizes[format]; i++) {
|
|
ptr--;
|
|
*ptr = lget(addr+i, 1);
|
|
slog(" %02x", *ptr);
|
|
if (shoe.abort)
|
|
return 0.0;
|
|
}
|
|
|
|
switch (format) {
|
|
case format_B: {
|
|
int8_t tmp = ptr[0];
|
|
data = tmp;
|
|
break;
|
|
}
|
|
case format_W: {
|
|
int16_t tmp = *(int16_t*)ptr;
|
|
data = tmp;
|
|
break;
|
|
}
|
|
case format_L: {
|
|
int32_t tmp = *(int32_t*)ptr;
|
|
data = tmp;
|
|
break;
|
|
}
|
|
case format_S: {
|
|
float tmp = *(float*)ptr;
|
|
data = tmp;
|
|
break;
|
|
}
|
|
case format_D: {
|
|
double tmp = *(double*)ptr;
|
|
data = tmp;
|
|
break;
|
|
}
|
|
case format_X: {
|
|
reverse_order(ptr, 12);
|
|
data = motorola_to_x87(ptr);
|
|
break;
|
|
}
|
|
default: {
|
|
assert(!"unsupported format (packed something)");
|
|
}
|
|
}
|
|
}
|
|
|
|
got_data:
|
|
|
|
fpu_set_round();
|
|
result = data;
|
|
fpu_reset_round();
|
|
slog(" data=%Lf result=%Lf\n", data, result);
|
|
return result;
|
|
}
|
|
|
|
|
|
static void fpu_write_ea(uint8_t mr, uint8_t format, long double orig_data)
|
|
{
|
|
fpu_set_round();
|
|
const long double data = orig_data;
|
|
fpu_reset_round();
|
|
|
|
const uint8_t m = mr >> 3;
|
|
const uint8_t r = mr & 7;
|
|
const uint8_t sizes[8] = {4, 4, 12, 12, 2, 8, 1, 12};
|
|
uint8_t buf[12], *ptr = &buf[0];
|
|
uint32_t addr, i;
|
|
|
|
// If mode==a-reg, or mode==data reg and the size is > 4 bytes, no dice
|
|
if ((m == 1) ||
|
|
((m == 0) && (sizes[format] > 4))) {
|
|
throw_illegal_instruction();
|
|
return ;
|
|
}
|
|
|
|
slog("inst_f fpu_write_ea EA=%u/%u data=%Lf format=%u\n", m, r, data, format);
|
|
|
|
// Convert to the appropriate format
|
|
|
|
switch (format) {
|
|
case format_B: {
|
|
int8_t tmp = data;
|
|
*((int8_t*)ptr) = tmp;
|
|
goto write_to_mem;
|
|
}
|
|
case format_W: {
|
|
int16_t tmp = data;
|
|
*((int16_t*)ptr) = tmp;
|
|
slog("inst_f fpu_write_ea formatted=%u (0x%04x)\n", *((int16_t*)ptr), *((uint16_t*)ptr));
|
|
break;
|
|
}
|
|
case format_L: {
|
|
int32_t tmp = data;
|
|
*((int32_t*)ptr) = tmp;
|
|
break;
|
|
}
|
|
case format_S: {
|
|
float tmp = data;
|
|
*((float*)ptr) = tmp;
|
|
break;
|
|
}
|
|
case format_D: {
|
|
double tmp = data;
|
|
*((double*)ptr) = tmp;
|
|
break;
|
|
}
|
|
case format_X: {
|
|
x87_to_motorola(data, ptr);
|
|
goto write_to_mem; // ptr is already big endian
|
|
}
|
|
default: {
|
|
assert(!"unsupported format (packed something)");
|
|
}
|
|
}
|
|
|
|
swap_order:
|
|
reverse_order(buf, sizes[format]);
|
|
|
|
|
|
write_to_mem:
|
|
// Lookup the EA
|
|
|
|
switch (m) {
|
|
case 0: {
|
|
if (format == format_B) {
|
|
int8_t tmp = data;
|
|
set_d(r, tmp, 1);
|
|
}
|
|
else if (format == format_W) {
|
|
int16_t tmp = data;
|
|
set_d(r, tmp, 2);
|
|
}
|
|
else if (format == format_L) {
|
|
int32_t tmp = data;
|
|
shoe.d[r] = tmp;
|
|
}
|
|
else if (format == format_S) {
|
|
float tmp = data;
|
|
*((float*)&shoe.d[r]) = tmp;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
case 3: // post-increment
|
|
addr = shoe.a[r];
|
|
assert(!( r==7 && sizes[format]==1));
|
|
break;
|
|
case 4: // pre-decrement
|
|
addr = shoe.a[r] - sizes[format];
|
|
assert(!( r==7 && sizes[format]==1));
|
|
break;
|
|
default:
|
|
call_ea_addr(mr);
|
|
addr = (uint32_t)shoe.dat;
|
|
break;
|
|
}
|
|
|
|
// Copy the formatted data into the EA
|
|
slog("inst_f fpu_write_ea: addr=0x%08x\n", addr);
|
|
for (i=0; i < sizes[format]; i++) {
|
|
lset(addr + i, 1, buf[i]);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
|
|
done: // set condition codes and update pre/post-inc/decrement registers
|
|
|
|
// Set condition codes
|
|
shoe.fpsr.raw &= 0x00ffffff;
|
|
shoe.fpsr.b.cc_nan = (0 != isnan(data));
|
|
if (!shoe.fpsr.b.cc_nan) {
|
|
shoe.fpsr.b.cc_n = (0 != signbit(data));
|
|
if (isinf(data))
|
|
shoe.fpsr.b.cc_i = 1;
|
|
else
|
|
shoe.fpsr.b.cc_z = (data == 0.0);
|
|
}
|
|
|
|
if (m == 3)
|
|
shoe.a[r] += sizes[format];
|
|
else if (m == 4)
|
|
shoe.a[r] -= sizes[format];
|
|
}
|
|
|
|
void inst_fmove(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(ext, 0 E V aaa zzz KKKKKKK);
|
|
|
|
const uint8_t format = a;
|
|
|
|
if (K == ~b(1000100) || K == ~b(1000000)) {
|
|
assert(!"inst_fmove: This is either a K-value, or somebody called fmove and specified the secret precision bits");
|
|
}
|
|
|
|
// E==0 => Don't use EA (reg->reg)
|
|
// E==1 => Use EA
|
|
// V==0 => reg->reg or mem->reg
|
|
// V==1 => reg->mem
|
|
|
|
// Load the source value into 'data'
|
|
|
|
long double data;
|
|
|
|
if (E && !V) { // mem -> reg
|
|
data = fpu_read_ea(M, format);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
else if (!E) { // reg -> mem
|
|
data = shoe.fp[a];
|
|
}
|
|
else { // reg -> reg
|
|
data = shoe.fp[z];
|
|
}
|
|
|
|
|
|
// XXX: Check for exceptions?
|
|
|
|
// Write the result
|
|
|
|
if (E && V) { // reg -> mem
|
|
fpu_write_ea(M, format, data);
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
else if (!V) { // mem -> reg
|
|
fpu_set_reg_cc(data, z);
|
|
fpu_read_ea_commit(M, format);
|
|
}
|
|
else { // reg -> reg
|
|
fpu_set_reg_cc(data, z);
|
|
}
|
|
|
|
const uint8_t sizes[8] = {4, 4, 12, 12, 2, 8, 1, 12};
|
|
slog("inst_fmove src=%Lf size=%u a=%u z=%u to-mem=%u useEA=%u EA = %u/%u\n", data, sizes[format], a, z, V, E, m, r);
|
|
}
|
|
|
|
void dis_fnop(uint16_t op, uint16_t ext)
|
|
{
|
|
sprintf(dis.str, "fnop");
|
|
}
|
|
|
|
void inst_fnop(uint16_t op, uint16_t ext)
|
|
{
|
|
}
|
|
|
|
void dis_fmove(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(op, 1111 001 000 mmmrrr);
|
|
~decompose(ext, 0 E V aaa bbb KKKKKKK);
|
|
|
|
// E==0 => reg to reg
|
|
// E==1 => mem to reg / reg to mem
|
|
// V==0 => reg->reg or mem->reg
|
|
// V==1 => reg->mem
|
|
|
|
|
|
sprintf(dis.str, "fmove ???");
|
|
|
|
}
|
|
|
|
void dis_fmath(uint16_t op, uint16_t ext)
|
|
{
|
|
sprintf(dis.str, "fmath ??");
|
|
}
|
|
|
|
static void fpu_set_fpsr_quotient(long double a, long double b, long double result)
|
|
{
|
|
// Thanks for being super vague on the meaning of this register, 68881 documentation
|
|
|
|
const long double quo = truncl((a - result) / b);
|
|
const uint8_t sign = signbit(quo);
|
|
const uint64_t quo_int = fabsl(quo);
|
|
|
|
shoe.fpsr.b.qu_quotient = quo_int & 0x7f;
|
|
shoe.fpsr.b.qu_s = sign;
|
|
}
|
|
|
|
void inst_fmath(uint16_t op, uint16_t ext)
|
|
{
|
|
~decompose(op, 1111 001 000 MMMMMM);
|
|
~decompose(ext, 0 a 0 sss ddd eeeeeee);
|
|
|
|
const uint8_t src_in_ea = a;
|
|
const uint8_t source_specifier = s;
|
|
const uint8_t dest_register = d;
|
|
const uint8_t extension = e;
|
|
|
|
uint8_t do_write_back_result = 1;
|
|
|
|
long double source, dest, result;
|
|
|
|
if (src_in_ea) {
|
|
source = fpu_read_ea(M, source_specifier);
|
|
slog("inst_fmath: source = %u/%u = %Lf", M>>3, M&7, source);
|
|
if ((M>>3) == 3)
|
|
slog(" a[%u]=0x%08x", M&7, shoe.a[M&7]);
|
|
|
|
if (shoe.abort)
|
|
return ;
|
|
}
|
|
else {
|
|
source = shoe.fp[source_specifier];
|
|
slog("inst_fmath: source = fp%u = %Lf", source_specifier, source);
|
|
}
|
|
|
|
dest = shoe.fp[dest_register];
|
|
slog(" dest = fp%u = %Lf\n", dest_register, dest);
|
|
|
|
switch (e) {
|
|
case ~b(0000001): {// fpu_inst_fint
|
|
const uint8_t dir = shoe.fpcr.b.mc_rnd;
|
|
|
|
// {FE_TONEAREST, FE_TOWARDZERO, FE_DOWNWARD, FE_UPWARD};
|
|
|
|
if (dir == 0)
|
|
result = roundl(source);
|
|
else if (dir == 1)
|
|
result = truncl(source);
|
|
else if (dir == 2)
|
|
result = floorl(source);
|
|
else
|
|
result = ceill(source);
|
|
|
|
slog("inst_fint: source = %Lf result = %Lf round=%u\n", source, result, dir);
|
|
|
|
break;
|
|
}
|
|
case ~b(0000010): assert(!"fpu_inst_fsinh;");
|
|
case ~b(0000011): // fpu_inst_fintrz
|
|
slog("inst_fintrz dest = %Lf source = %Lf\n", dest, source);
|
|
result = truncl(source);
|
|
break;
|
|
|
|
case ~b(0000110): // flognp1
|
|
slog("inst_flognp1 dest = %Lf source = %Lf\n", dest, source);
|
|
assert(source > -1.0);
|
|
result = log1pl(source);
|
|
break;
|
|
case ~b(0001000): assert(!"fpu_inst_fetoxm1;");
|
|
case ~b(0001001): assert(!"fpu_inst_ftanh;");
|
|
case ~b(0001010): // fatan
|
|
slog("inst_fatan dest = %Lf source = %Lf\n", dest, source);
|
|
result = atanl(source);
|
|
break;
|
|
|
|
case ~b(0001100): assert(!"fpu_inst_fasin;");
|
|
case ~b(0001101): assert(!"fpu_inst_fatanh;");
|
|
case ~b(0001110): // fsin
|
|
slog("inst_fsin dest = %Lf source = %Lf\n", dest, source);
|
|
result = sinl(source);
|
|
break;
|
|
case ~b(0001111): assert(!"fpu_inst_ftan;");
|
|
case ~b(0010000): // fetox
|
|
slog("inst_fetox dest = %Lf source = %Lf\n", dest, source);
|
|
result = expl(source);
|
|
break;
|
|
case ~b(0010001): assert(!"fpu_inst_ftwotox;");
|
|
case ~b(0010010): assert(!"fpu_inst_ftentox;");
|
|
case ~b(0010100): assert(!"fpu_inst_flogn;");
|
|
case ~b(0010101): assert(!"fpu_inst_flog10;");
|
|
case ~b(0010110): assert(!"fpu_inst_flog2;");
|
|
case ~b(0011001): assert(!"fpu_inst_fcosh;");
|
|
case ~b(0011100): assert(!"fpu_inst_facos;");
|
|
case ~b(0011101): // fcos
|
|
slog("fpu_inst_fcos dest = %Lf source = %Lf\n", dest, source);
|
|
result = cosl(source);
|
|
break;
|
|
|
|
case ~b(0011110): {// fpu_inst_fgetexp
|
|
if (!((source > 0) || (source < 0)))
|
|
result = source; // positive or negative zero
|
|
else if (!isfinite(source)) {
|
|
assert(!"fgetexp: isinfl(source)");
|
|
// returns NAN and an exception bit - not implemented for the moment
|
|
}
|
|
else {
|
|
// Extract the debiased exponent from the 80-bit source
|
|
uint8_t motorola[12];
|
|
x87_to_motorola(source, motorola);
|
|
int32_t exp = (motorola[0] & 0x7f) << 8;
|
|
exp |= motorola[1];
|
|
exp -= 16383; // debias
|
|
result = exp;
|
|
}
|
|
break;
|
|
}
|
|
case ~b(0011111): assert(!"fpu_inst_fgetman;");
|
|
case ~b(0100001):
|
|
// don't forget to set fpu_set_fpsr_quotient();
|
|
assert(!"fpu_inst_fmod;");
|
|
|
|
case ~b(0100100): assert(!"fpu_inst_fsgldiv");
|
|
|
|
case ~b(0100101): { // fpu_inst_frem
|
|
assert(source != 0.0);
|
|
result = remainderl(dest, source);
|
|
fpu_set_fpsr_quotient(dest, source, result);
|
|
slog("inst_frem: dest = %Lf source = %Lf quot = %u result = %Lf\n", dest, source, shoe.fpsr.b.qu_quotient, result);
|
|
break;
|
|
}
|
|
case ~b(0100110): assert(!"fpu_inst_fscale;");
|
|
|
|
case ~b(0111000): { // fpu_inst_fcmp
|
|
const long double diff = dest - source;
|
|
slog("inst_fcmp: dest = %Lf source = %Lf\n", dest, source);
|
|
fpu_set_cc(diff);
|
|
do_write_back_result = 0; // don't write result back to register
|
|
break;
|
|
}
|
|
case ~b(0111010): { // fpu_inst_ftst
|
|
slog("fpu_inst_ftst: dest = %Lf\n");
|
|
fpu_set_cc(source);
|
|
do_write_back_result = 0; // don't write result back to register
|
|
break;
|
|
}
|
|
|
|
case ~b(1011100):
|
|
case ~b(1011000):
|
|
assert(!"inst_fabs: can't handle");
|
|
case ~b(0011000):// fpu_inst_fabs
|
|
result = fabsl(source);
|
|
slog("inst_fabs: source=%Lf result=%Lf\n", source, result);
|
|
break;
|
|
|
|
case ~b(1100010):
|
|
case ~b(1100110):
|
|
assert(!"can't handle");
|
|
case ~b(0100010): { // fpu_inst_fadd
|
|
slog("inst_fadd dest = %Lf source = %Lf\n", dest, source);
|
|
result = dest + source;
|
|
break;
|
|
}
|
|
|
|
case ~b(1100000):
|
|
case ~b(1100100):
|
|
assert(!"can't handle");
|
|
case ~b(0100000): { // fpu_inst_fdiv
|
|
assert(source != 0.0);
|
|
slog("inst_fdiv dest = %Lf source = %Lf\n", dest, source);
|
|
|
|
result = dest / source;
|
|
break;
|
|
}
|
|
|
|
|
|
case ~b(1100011):
|
|
case ~b(1100111):
|
|
assert(!"can't handle");
|
|
case ~b(0100011): { // fpu_inst_fmul
|
|
slog("inst_fmul dest = %Lf source = %Lf\n", dest, source);
|
|
result = source * dest;
|
|
break;
|
|
}
|
|
|
|
case ~b(1011010):
|
|
case ~b(1011110):
|
|
assert(!"fneg: can't handle");
|
|
case ~b(0011010): // fneg
|
|
slog("inst_fneg dest = %Lf source = %Lf\n", dest, source);
|
|
result = -source;
|
|
break;
|
|
|
|
case ~b(1000001):
|
|
case ~b(1000101):
|
|
assert(!"can't handle");
|
|
case ~b(0000100): { // fpu_inst_fsqrt
|
|
slog("inst_fsqrt dest = %Lf source = %Lf\n", dest, source);
|
|
result = sqrtl(source);
|
|
break;
|
|
}
|
|
|
|
case ~b(1101000):
|
|
case ~b(1101100):
|
|
assert(!"can't handle");
|
|
case ~b(0101000): { // fpu_inst_fsub
|
|
slog("inst_fsub dest = %Lf source = %Lf\n", dest, source);
|
|
result = dest - source;
|
|
break;
|
|
}
|
|
|
|
case ~b(0110000) ... ~b(0110111):
|
|
assert(!"fpu_inst_fsincos;");
|
|
|
|
default:
|
|
assert(!"inst_fmath: unknown instruction");
|
|
}
|
|
|
|
// Finalize the read, if source was in memory
|
|
if (src_in_ea) {
|
|
fpu_read_ea_commit(M, source_specifier);
|
|
}
|
|
|
|
// Only write back the result if necessary (fcmp doesn't do this)
|
|
if (do_write_back_result) {
|
|
slog("inst_fmath: result = %Lf\n", result);
|
|
fpu_set_reg_cc(result, dest_register);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup the jump table for fpu instructions
|
|
// XXX: come up with a better, unified system for decoding instructions
|
|
void fpu_setup_jump_table()
|
|
{
|
|
uint32_t i;
|
|
|
|
|
|
fpu_inst_table[fpu_inst_fnop].emu = inst_fnop;
|
|
fpu_inst_table[fpu_inst_fnop].dis = dis_fnop;
|
|
|
|
fpu_inst_table[fpu_inst_fbcc].emu = inst_fbcc;
|
|
fpu_inst_table[fpu_inst_fbcc].dis = dis_fbcc;
|
|
|
|
fpu_inst_table[fpu_inst_fmovecr].emu = inst_fmovecr;
|
|
fpu_inst_table[fpu_inst_fmovecr].dis = dis_fmovecr;
|
|
|
|
fpu_inst_table[fpu_inst_fmove].emu = inst_fmove;
|
|
fpu_inst_table[fpu_inst_fmove].dis = dis_fmove;
|
|
|
|
fpu_inst_table[fpu_inst_fmovem].emu = inst_fmovem;
|
|
fpu_inst_table[fpu_inst_fmovem].dis = dis_fmovem;
|
|
|
|
fpu_inst_table[fpu_inst_fmovem_control].emu = inst_fmovem_control;
|
|
fpu_inst_table[fpu_inst_fmovem_control].dis = dis_fmovem_control;
|
|
|
|
fpu_inst_table[fpu_inst_frestore].emu = inst_frestore;
|
|
fpu_inst_table[fpu_inst_frestore].dis = dis_frestore;
|
|
|
|
fpu_inst_table[fpu_inst_fsave].emu = inst_fsave;
|
|
fpu_inst_table[fpu_inst_fsave].dis = dis_fsave;
|
|
|
|
const fpu_inst_name_t _fmath[] = {
|
|
fpu_inst_fsincos,
|
|
fpu_inst_fint,
|
|
fpu_inst_fsinh,
|
|
fpu_inst_fintrz,
|
|
fpu_inst_flognp1,
|
|
fpu_inst_fetoxm1,
|
|
fpu_inst_ftanh,
|
|
fpu_inst_fatan,
|
|
fpu_inst_fatanh,
|
|
fpu_inst_fsin,
|
|
fpu_inst_ftan,
|
|
fpu_inst_fetox,
|
|
fpu_inst_ftwotox,
|
|
fpu_inst_ftentox,
|
|
fpu_inst_flogn,
|
|
fpu_inst_flog10,
|
|
fpu_inst_flog2,
|
|
fpu_inst_fcosh,
|
|
fpu_inst_facos,
|
|
fpu_inst_fcos,
|
|
fpu_inst_fgetexp,
|
|
fpu_inst_fgetman,
|
|
fpu_inst_fmod,
|
|
fpu_inst_fsgldiv,
|
|
fpu_inst_fsglmul,
|
|
fpu_inst_frem,
|
|
fpu_inst_fscale,
|
|
fpu_inst_fcmp,
|
|
fpu_inst_ftst,
|
|
fpu_inst_fabs,
|
|
fpu_inst_fadd,
|
|
fpu_inst_fdiv,
|
|
fpu_inst_fmul,
|
|
fpu_inst_fneg,
|
|
fpu_inst_fsqrt,
|
|
fpu_inst_fsub
|
|
};
|
|
|
|
for (i=0; i < sizeof(_fmath) / sizeof(fpu_inst_name_t); i++) {
|
|
fpu_inst_table[_fmath[i]].emu = inst_fmath;
|
|
fpu_inst_table[_fmath[i]].dis = dis_fmath;
|
|
}
|
|
}
|
|
|
|
|
|
|