Jelajahi Sumber

Added Fixed-Point BCC library, including a FPCALC application to test it. MULTFP instruction is added to assembler, and some library functions were added during development.

bart 1 tahun lalu
induk
melakukan
ddd3d60235

+ 1 - 0
Assembler/Assembler.py

@@ -215,6 +215,7 @@ def compileLine(line):
         "not"       : CompileInstruction.compileNOT,
         "mults"     : CompileInstruction.compileMULTS,
         "multu"     : CompileInstruction.compileMULTU,
+        "multfp"    : CompileInstruction.compileMULTFP,
         "slt"       : CompileInstruction.compileSLT,
         "sltu"      : CompileInstruction.compileSLTU,
         "load"      : CompileInstruction.compileLoad,

+ 6 - 1
Assembler/CompileInstruction.py

@@ -524,7 +524,8 @@ def compileARITH(line, opcode):
         "1001" : " * (unsigned) ",
         "1010" : " SLT ",
         "1011" : " SLTU ",
-        "1110" : " >> (signed) "
+        "1110" : " >> (signed) ",
+        "1111" : " * (signed FP) "
     }
 
     #create instruction
@@ -576,6 +577,10 @@ def compileMULTS(line):
 def compileMULTU(line):
     return compileARITH(line, "1001")
 
+# compiles MULTFP instruction
+def compileMULTFP(line):
+    return compileARITH(line, "1111")
+
 # compiles SLT instruction
 def compileSLT(line):
     return compileARITH(line, "1010")

+ 11 - 3
BCC/BDOS/lib/stdlib.c

@@ -246,9 +246,17 @@ word strToInt(char* str)
         i--;
     }
 
-    word currentDigit = str[i] - '0';
-    word toAdd = multiplier * currentDigit;
-    retval += toAdd;
+    // Check for negative
+    if (str[i] == '-')
+    {
+        retval *= -1;
+    }
+    else
+    {
+        word currentDigit = str[i] - '0';
+        word toAdd = multiplier * currentDigit;
+        retval += toAdd;
+    }
 
     return retval;
 }

+ 11 - 3
BCC/BareMetal/lib/stdlib.c

@@ -254,9 +254,17 @@ word strToInt(char* str)
         i--;
     }
 
-    word currentDigit = str[i] - '0';
-    word toAdd = multiplier * currentDigit;
-    retval += toAdd;
+    // Check for negative
+    if (str[i] == '-')
+    {
+        retval *= -1;
+    }
+    else
+    {
+        word currentDigit = str[i] - '0';
+        word toAdd = multiplier * currentDigit;
+        retval += toAdd;
+    }
 
     return retval;
 }

+ 11 - 3
BCC/FPGCbuildTools/asm/lib/stdlib.c

@@ -176,9 +176,17 @@ word strToInt(char* str)
         i--;
     }
 
-    word currentDigit = str[i] - '0';
-    word toAdd = multiplier * currentDigit;
-    retval += toAdd;
+    // Check for negative
+    if (str[i] == '-')
+    {
+        retval *= -1;
+    }
+    else
+    {
+        word currentDigit = str[i] - '0';
+        word toAdd = multiplier * currentDigit;
+        retval += toAdd;
+    }
 
     return retval;
 }

+ 190 - 0
BCC/userBDOS/FPCALC.C

@@ -0,0 +1,190 @@
+// Simple calculator to test floating points
+
+#define word char
+
+#include "LIB/MATH.C"
+#include "LIB/FP.C"
+#include "LIB/STDLIB.C"
+#include "LIB/SYS.C"
+
+#define CALC_BUFFER_MAX_LEN 32
+
+#define CALC_STATE_INPUTSTART 0
+#define CALC_STATE_INPUTA 1
+#define CALC_STATE_INPUTB 2
+#define CALC_STATE_INPUTOP 3
+
+// The current number that is being typed
+char CALC_buffer[CALC_BUFFER_MAX_LEN];
+word CALC_buffer_idx = 0;
+
+fixed_point_t CALC_a = 0;
+fixed_point_t CALC_b = 0;
+
+char CALC_state = CALC_STATE_INPUTSTART;
+
+word calcLoop()
+{
+  if (CALC_state == CALC_STATE_INPUTSTART)
+  {
+    BDOS_PrintConsole("Input A:      ");
+    CALC_state = CALC_STATE_INPUTA;
+  }
+
+
+  if (HID_FifoAvailable())
+  {
+    word c = HID_FifoRead();
+
+    if (CALC_state == CALC_STATE_INPUTOP)
+    {
+      if (c == '+')
+      {
+        BDOS_PrintlnConsole("+");
+        char buffer[24];
+        FP_FPtoString(CALC_a + CALC_b, buffer, 5);
+        BDOS_PrintConsole("Result =      ");
+        BDOS_PrintlnConsole(buffer);
+        CALC_state = CALC_STATE_INPUTA;
+        BDOS_PrintConsole("\n\nInput A:      ");
+      }
+
+      else if (c == '-')
+      {
+        BDOS_PrintlnConsole("-");
+        char buffer[24];
+        FP_FPtoString(CALC_a - CALC_b, buffer, 5);
+        BDOS_PrintConsole("Result =      ");
+        BDOS_PrintlnConsole(buffer);
+        CALC_state = CALC_STATE_INPUTA;
+        BDOS_PrintConsole("\n\nInput A:      ");
+      }
+
+      else if (c == '*')
+      {
+        BDOS_PrintlnConsole("*");
+        char buffer[24];
+        FP_FPtoString(FP_Mult(CALC_a, CALC_b), buffer, 5);
+        BDOS_PrintConsole("Result =      ");
+        BDOS_PrintlnConsole(buffer);
+        CALC_state = CALC_STATE_INPUTA;
+        BDOS_PrintConsole("\n\nInput A:      ");
+      }
+
+      else if (c == 0x1b) // escape
+      {
+        BDOS_PrintcConsole('\n');
+        return 1;
+      }
+    }
+    // number input, dot or sign
+    else if ( (c >= '0' && c <= '9') || c == '.' || c == '-')
+    {
+      if (CALC_state == CALC_STATE_INPUTA || CALC_state == CALC_STATE_INPUTB)
+      {
+        // add to buffer and print character
+        CALC_buffer[CALC_buffer_idx] = c;
+        CALC_buffer_idx++;
+        CALC_buffer[CALC_buffer_idx] = 0; // terminate
+        BDOS_PrintcConsole(c);
+      }
+    }
+    else if (c == 0x8) // backspace
+    {
+      if (CALC_state == CALC_STATE_INPUTA || CALC_state == CALC_STATE_INPUTB)
+      {
+        // replace last char in buffer by 0 (if not at start)
+        if (CALC_buffer_idx != 0)
+        {
+          CALC_buffer_idx--;
+          CALC_buffer[CALC_buffer_idx] = 0;
+          BDOS_PrintcConsole(c);
+        }
+      }
+    }
+    else if (c == 0xa) // newline/enter
+    {
+      switch(CALC_state)
+      {
+        case CALC_STATE_INPUTA:
+          if (CALC_buffer_idx > 0)
+          {
+            BDOS_PrintcConsole('\n');
+
+            CALC_a = FP_StringToFP(CALC_buffer);
+            CALC_buffer_idx = 0;
+
+            BDOS_PrintConsole("Input B:      ");
+            CALC_state = CALC_STATE_INPUTB;
+          }
+          break;
+
+        case CALC_STATE_INPUTB:
+          if (CALC_buffer_idx > 0)
+          {
+            BDOS_PrintcConsole('\n');
+
+            CALC_b = FP_StringToFP(CALC_buffer);
+            CALC_buffer_idx = 0;
+
+            BDOS_PrintConsole("Operation:    ");
+            CALC_state = CALC_STATE_INPUTOP;
+          }
+          break;
+      }
+    }
+    else if (c == 0x1b) // escape
+    {
+      BDOS_PrintcConsole('\n');
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+int main() 
+{
+  BDOS_PrintlnConsole("Fixed-point calculator test\n");
+
+  word stop = 0;
+  while (!stop)
+  {
+    stop = calcLoop();
+  }
+
+  return 'q';
+}
+
+void interrupt()
+{
+  // handle all interrupts
+  word i = getIntID();
+  switch(i)
+  {
+    case INTID_TIMER1:
+      timer1Value = 1; // notify ending of timer1
+      break;
+
+    case INTID_TIMER2:
+      break;
+
+    case INTID_UART0:
+      break;
+
+    case INTID_GPU:
+      break;
+
+    case INTID_TIMER3:
+      break;
+
+    case INTID_PS2:
+      break;
+
+    case INTID_UART1:
+      break;
+
+    case INTID_UART2:
+      break;
+  }
+}

+ 167 - 0
BCC/userBDOS/LIB/FP.C

@@ -0,0 +1,167 @@
+/*
+* Fixed-Point number library
+* Contains functions for decimal numbers encoded in 16.16 format
+*/
+
+#define fixed_point_t char
+
+// Fixed point arithmetic operations using 16.16 format
+#define FP_FRACT_BITS 16
+#define FP_MULTIPLY_CONST 15259
+#define FP_FRACT_MASK 0xFFFF
+
+
+// Convert a fixed-point variable to a string with a given number of decimals (truncated, not rounded)
+void FP_FPtoString(word fixedPointValue, char* outputString, word numDecimals) {
+  word fractMask = FP_FRACT_MASK; // Needed to enforce a load(32) instruction, as constants have limited bits in arith operations
+  word integerPart = fixedPointValue >> FP_FRACT_BITS; // Extract the integer part
+  word fractionalPart = fixedPointValue & fractMask; // Extract the fractional part
+
+  // Convert the integer part, which can be signed, to a string using itoa()
+  if (integerPart < 0)
+  {
+    outputString[0] = '-';
+    if (fractionalPart > 0)
+    {
+      itoa(- (integerPart + 1), &outputString[1]); // integerPart-1 to fix neg numbers
+    }
+    else
+    {
+      itoa(- integerPart, &outputString[1]);
+    }
+  }
+  else
+  {
+    itoa(integerPart, outputString);
+  }
+
+  // Find the end of the integer part string
+  char* endOfString = outputString;
+  while (*endOfString)
+  {
+    endOfString++;
+  }
+
+  // Add a dot to separate the integer and fractional parts
+  *endOfString = '.';
+  endOfString++;
+
+  char* decimalPosition = endOfString;
+
+  char decbuf[10]; // 10 with current multiply constant, as number can be 9 decimal max
+  // Convert the fractional part to intermediate buffer
+  if (integerPart < 0 && fractionalPart > 0)
+  {
+    // Fix for neg numbers (2's complement)
+    fractionalPart = ( (~fractionalPart) & fractMask) + 1;
+  }
+
+  fractionalPart *= FP_MULTIPLY_CONST;
+  itoa(fractionalPart, decbuf);
+
+  // Get length of decimal part string
+  int declen = strlen(decbuf);
+  // Prepend with 9-(length of decimal part) zeros before adding decimal part
+  while (declen < 9)
+  {
+    *endOfString = '0';
+    endOfString++;
+    declen++;
+  }
+  // Terminate string
+  *endOfString = 0;
+
+  // Convert and append the fractional part directly to the result
+  strcat(endOfString, decbuf);
+
+  // Truncate at number of decimals given by placing string terminator
+  decimalPosition[numDecimals] = 0;
+}
+
+
+// Convert a string to a fixed-point number
+fixed_point_t FP_StringToFP(char* decimalString)
+{
+
+  // Get index of the dot, also return int part if no dot
+  char* dotp = strchr(decimalString, '.');
+  if (dotp == 0) return strToInt(decimalString) << FP_FRACT_BITS;
+  word dotIndex = dotp - decimalString;
+
+  // Copy integer part
+  char integerPart[12];
+  memcpy(integerPart, decimalString, dotIndex);
+
+  // Terminate integerPart
+  integerPart[dotIndex] = 0;
+
+  // Copy decimal part
+  char decimalPart[12];
+  strcpy(decimalPart, decimalString + dotIndex + 1);
+
+  // Add integer part to result
+  word result = strToInt(integerPart) << FP_FRACT_BITS;
+
+  // Get decimal part as an integer
+  word decimalPartInt = strToInt(decimalPart);
+
+  // Return if no or invalid decimal part
+  if (decimalPartInt == 0)
+    return result;
+
+  // Get number of decimals of the decimal part string which we need later
+  word lenDecimalPart = strlen(decimalPart);
+
+  // Alternative for 10^lenDecimalPart
+  word divisionNumber = 1;
+  while (lenDecimalPart > 0)
+  {
+    divisionNumber *= 10;
+    lenDecimalPart--;
+  }
+  
+  // Calculate decimal part
+  word decResult = decimalPartInt << FP_FRACT_BITS;
+  decResult = MATH_div(decResult, divisionNumber);
+
+  // Correct for negative numbers (look at string because -0.x)
+  if (decimalString[0] == '-')
+  {
+    result -= (1 << FP_FRACT_BITS);
+    decResult = (1 << FP_FRACT_BITS) - decResult;
+  }
+
+  // Add decimal part to integer part
+  word fractMask = FP_FRACT_MASK; // Needed to enforce a load(32) instruction, as constants have limited bits in arith operations
+  result |= (decResult & fractMask);
+
+  return result;
+}
+
+
+// Convert an integer to a fixed-point number
+fixed_point_t FP_intToFP(word x)
+{
+  return x << FP_FRACT_BITS;
+}
+
+
+// Convert a fixed-point number to an integer (truncate)
+word FP_FPtoInt(fixed_point_t x)
+{
+  return x >> FP_FRACT_BITS;
+}
+
+
+// Multiply two fixed-point numbers using special instruction
+fixed_point_t FP_Mult(fixed_point_t a, fixed_point_t b)
+{
+  // r4: a, r5: b
+  fixed_point_t retval = 0;
+  asm(
+    "multfp r4 r5 r2  ; r2 = a*b (FP signed)\n"
+    "write -4 r14 r2  ; write result to stack for return\n"
+    );
+
+  return retval;
+}

+ 46 - 3
BCC/userBDOS/LIB/STDLIB.C

@@ -166,6 +166,21 @@ word strcmp(char* a, char* b)
 }
 
 
+/*
+Returns a pointer to the first occurrence of the character c in the string s, or 0 if the character is not found.
+*/
+char* strchr (const char *s, char c)
+{
+  do {
+    if (*s == c)
+      {
+        return (char*)s;
+      }
+  } while (*s++);
+  return 0;
+}
+
+
 /*
 Recursive helper function for itoa
 Eventually returns the number of digits in n
@@ -312,14 +327,42 @@ word strToInt(char* str)
     i--;
   }
 
-  word currentDigit = str[i] - '0';
-  word toAdd = multiplier * currentDigit;
-  retval += toAdd;
+  // Check for negative
+  if (str[i] == '-')
+  {
+    retval *= -1;
+  }
+  else
+  {
+    word currentDigit = str[i] - '0';
+    word toAdd = multiplier * currentDigit;
+    retval += toAdd;
+  }
 
   return retval;
 }
 
 
+/*
+Speed optimized function to get the number of decimals for a given digit
+*/
+word numberOfDecimals(word n)
+{
+  if (n < 0) n = -n; // Ignore for now the INT_MIN case where this does not work
+  if (n < 10) return 1;
+  if (n < 100) return 2;
+  if (n < 1000) return 3;
+  if (n < 10000) return 4;
+  if (n < 100000) return 5;
+  if (n < 1000000) return 6;
+  if (n < 10000000) return 7;
+  if (n < 100000000) return 8;
+  if (n < 1000000000) return 9;
+  // Cannot be > 10 for a 32bit integer
+  return 10;
+}
+
+
 /*
 Prints a single char c by writing it to UART_TX_ADDR
 */

+ 16 - 2
BCC/userBDOS/LIB/SYS.C

@@ -111,11 +111,25 @@ void BDOS_PrintlnConsole(char* str)
 
 void BDOS_PrintDecConsole(word i)
 {
-  char buffer[11];
-  itoa(i, buffer);
+  char buffer[12];
+
+  if (i < 0)
+  {
+    buffer[0] = '-';
+    itoa(MATH_abs(i), &buffer[1]);
+  }
+  else
+  {
+    itoa(i, buffer);
+  }
   BDOS_PrintConsole(buffer);
 }
 
+void BDOS_PrintlnDecConsole(word i)
+{
+  BDOS_PrintDecConsole(i);
+  BDOS_PrintcConsole('\n');
+}
 
 void BDOS_PrintHexConsole(word i)
 {