sw: add printf
We'll use this to print debugging information out the serial port. Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
		
							
								
								
									
										117
									
								
								sw/include/printf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								sw/include/printf.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// \author (c) Marco Paland (info@paland.com)
 | 
			
		||||
//             2014-2019, PALANDesign Hannover, Germany
 | 
			
		||||
//
 | 
			
		||||
// \license The MIT License (MIT)
 | 
			
		||||
//
 | 
			
		||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
// of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
// in the Software without restriction, including without limitation the rights
 | 
			
		||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
// copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
// furnished to do so, subject to the following conditions:
 | 
			
		||||
// 
 | 
			
		||||
// The above copyright notice and this permission notice shall be included in
 | 
			
		||||
// all copies or substantial portions of the Software.
 | 
			
		||||
// 
 | 
			
		||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
// THE SOFTWARE.
 | 
			
		||||
//
 | 
			
		||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
 | 
			
		||||
//        embedded systems with a very limited resources.
 | 
			
		||||
//        Use this instead of bloated standard/newlib printf.
 | 
			
		||||
//        These routines are thread safe and reentrant.
 | 
			
		||||
//
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#ifndef _PRINTF_H_
 | 
			
		||||
#define _PRINTF_H_
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Output a character to a custom device like UART, used by the printf() function
 | 
			
		||||
 * This function is declared here only. You have to write your custom implementation somewhere
 | 
			
		||||
 * \param character Character to output
 | 
			
		||||
 */
 | 
			
		||||
void _putchar(char character);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tiny printf implementation
 | 
			
		||||
 * You have to implement _putchar if you use printf()
 | 
			
		||||
 * To avoid conflicts with the regular printf() API it is overridden by macro defines
 | 
			
		||||
 * and internal underscore-appended functions like printf_() are used
 | 
			
		||||
 * \param format A string that specifies the format of the output
 | 
			
		||||
 * \return The number of characters that are written into the array, not counting the terminating null character
 | 
			
		||||
 */
 | 
			
		||||
#define printf printf_
 | 
			
		||||
int printf_(const char* format, ...);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tiny sprintf implementation
 | 
			
		||||
 * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
 | 
			
		||||
 * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
 | 
			
		||||
 * \param format A string that specifies the format of the output
 | 
			
		||||
 * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
 | 
			
		||||
 */
 | 
			
		||||
#define sprintf sprintf_
 | 
			
		||||
int sprintf_(char* buffer, const char* format, ...);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tiny snprintf/vsnprintf implementation
 | 
			
		||||
 * \param buffer A pointer to the buffer where to store the formatted string
 | 
			
		||||
 * \param count The maximum number of characters to store in the buffer, including a terminating null character
 | 
			
		||||
 * \param format A string that specifies the format of the output
 | 
			
		||||
 * \param va A value identifying a variable arguments list
 | 
			
		||||
 * \return The number of characters that COULD have been written into the buffer, not counting the terminating
 | 
			
		||||
 *         null character. A value equal or larger than count indicates truncation. Only when the returned value
 | 
			
		||||
 *         is non-negative and less than count, the string has been completely written.
 | 
			
		||||
 */
 | 
			
		||||
#define snprintf  snprintf_
 | 
			
		||||
#define vsnprintf vsnprintf_
 | 
			
		||||
int  snprintf_(char* buffer, size_t count, const char* format, ...);
 | 
			
		||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tiny vprintf implementation
 | 
			
		||||
 * \param format A string that specifies the format of the output
 | 
			
		||||
 * \param va A value identifying a variable arguments list
 | 
			
		||||
 * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
 | 
			
		||||
 */
 | 
			
		||||
#define vprintf vprintf_
 | 
			
		||||
int vprintf_(const char* format, va_list va);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * printf with output function
 | 
			
		||||
 * You may use this as dynamic alternative to printf() with its fixed _putchar() output
 | 
			
		||||
 * \param out An output function which takes one character and an argument pointer
 | 
			
		||||
 * \param arg An argument pointer for user data passed to output function
 | 
			
		||||
 * \param format A string that specifies the format of the output
 | 
			
		||||
 * \return The number of characters that are sent to the output function, not counting the terminating null character
 | 
			
		||||
 */
 | 
			
		||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif  // _PRINTF_H_
 | 
			
		||||
@@ -7,6 +7,25 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
struct usb_setup_request;
 | 
			
		||||
 | 
			
		||||
enum epfifo_response {
 | 
			
		||||
    EPF_ACK = 0,
 | 
			
		||||
    EPF_NAK = 1,
 | 
			
		||||
    EPF_NONE = 2,
 | 
			
		||||
    EPF_STALL = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Note that our PIDs are only bits 2 and 3 of the token,
 | 
			
		||||
// since all other bits are effectively redundant at this point.
 | 
			
		||||
enum USB_PID {
 | 
			
		||||
    USB_PID_OUT   = 0,
 | 
			
		||||
    USB_PID_SOF   = 1,
 | 
			
		||||
    USB_PID_IN    = 2,
 | 
			
		||||
    USB_PID_SETUP = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define USB_EV_ERROR 1
 | 
			
		||||
#define USB_EV_PACKET 2
 | 
			
		||||
 | 
			
		||||
void usb_isr(void);
 | 
			
		||||
void usb_init(void);
 | 
			
		||||
void usb_connect(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,6 @@
 | 
			
		||||
 | 
			
		||||
_start:
 | 
			
		||||
  j crt_init
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
  nop
 | 
			
		||||
 | 
			
		||||
.section .text
 | 
			
		||||
.global  trap_entry
 | 
			
		||||
@@ -55,7 +48,6 @@ trap_entry:
 | 
			
		||||
  mret
 | 
			
		||||
  .text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
crt_init:
 | 
			
		||||
  la sp, _fstack + 4
 | 
			
		||||
  la a0, trap_entry
 | 
			
		||||
@@ -87,5 +79,6 @@ bss_done:
 | 
			
		||||
  csrw mie,a0
 | 
			
		||||
 | 
			
		||||
  call main
 | 
			
		||||
infinit_loop:
 | 
			
		||||
  j infinit_loop
 | 
			
		||||
 | 
			
		||||
infinite_loop:
 | 
			
		||||
  j infinite_loop
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								sw/src/div.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								sw/src/div.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
  .text
 | 
			
		||||
  .align 2
 | 
			
		||||
 | 
			
		||||
#ifndef __riscv64
 | 
			
		||||
/* Our RV64 64-bit routines are equivalent to our RV32 32-bit routines.  */
 | 
			
		||||
# define __udivdi3 __udivsi3
 | 
			
		||||
# define __umoddi3 __umodsi3
 | 
			
		||||
# define __divdi3 __divsi3
 | 
			
		||||
# define __moddi3 __modsi3
 | 
			
		||||
#else
 | 
			
		||||
  .globl __udivsi3
 | 
			
		||||
__udivsi3:
 | 
			
		||||
  /* Compute __udivdi3(a0 << 32, a1 << 32); cast result to uint32_t.  */
 | 
			
		||||
  sll    a0, a0, 32
 | 
			
		||||
  sll    a1, a1, 32
 | 
			
		||||
  move   t0, ra
 | 
			
		||||
  jal    __udivdi3
 | 
			
		||||
  sext.w a0, a0
 | 
			
		||||
  jr     t0
 | 
			
		||||
 | 
			
		||||
  .globl __umodsi3
 | 
			
		||||
__umodsi3:
 | 
			
		||||
  /* Compute __udivdi3((uint32_t)a0, (uint32_t)a1); cast a1 to uint32_t.  */
 | 
			
		||||
  sll    a0, a0, 32
 | 
			
		||||
  sll    a1, a1, 32
 | 
			
		||||
  srl    a0, a0, 32
 | 
			
		||||
  srl    a1, a1, 32
 | 
			
		||||
  move   t0, ra
 | 
			
		||||
  jal    __udivdi3
 | 
			
		||||
  sext.w a0, a1
 | 
			
		||||
  jr     t0
 | 
			
		||||
 | 
			
		||||
  .globl __modsi3
 | 
			
		||||
  __modsi3 = __moddi3
 | 
			
		||||
 | 
			
		||||
  .globl __divsi3
 | 
			
		||||
__divsi3:
 | 
			
		||||
  /* Check for special case of INT_MIN/-1. Otherwise, fall into __divdi3.  */
 | 
			
		||||
  li    t0, -1
 | 
			
		||||
  beq   a1, t0, .L20
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  .globl __divdi3
 | 
			
		||||
__divdi3:
 | 
			
		||||
  bltz  a0, .L10
 | 
			
		||||
  bltz  a1, .L11
 | 
			
		||||
  /* Since the quotient is positive, fall into __udivdi3.  */
 | 
			
		||||
 | 
			
		||||
  .globl __udivdi3
 | 
			
		||||
__udivdi3:
 | 
			
		||||
  mv    a2, a1
 | 
			
		||||
  mv    a1, a0
 | 
			
		||||
  li    a0, -1
 | 
			
		||||
  beqz  a2, .L5
 | 
			
		||||
  li    a3, 1
 | 
			
		||||
  bgeu  a2, a1, .L2
 | 
			
		||||
.L1:
 | 
			
		||||
  blez  a2, .L2
 | 
			
		||||
  slli  a2, a2, 1
 | 
			
		||||
  slli  a3, a3, 1
 | 
			
		||||
  bgtu  a1, a2, .L1
 | 
			
		||||
.L2:
 | 
			
		||||
  li    a0, 0
 | 
			
		||||
.L3:
 | 
			
		||||
  bltu  a1, a2, .L4
 | 
			
		||||
  sub   a1, a1, a2
 | 
			
		||||
  or    a0, a0, a3
 | 
			
		||||
.L4:
 | 
			
		||||
  srli  a3, a3, 1
 | 
			
		||||
  srli  a2, a2, 1
 | 
			
		||||
  bnez  a3, .L3
 | 
			
		||||
.L5:
 | 
			
		||||
  ret
 | 
			
		||||
 | 
			
		||||
  .globl __umoddi3
 | 
			
		||||
__umoddi3:
 | 
			
		||||
  /* Call __udivdi3(a0, a1), then return the remainder, which is in a1.  */
 | 
			
		||||
  move  t0, ra
 | 
			
		||||
  jal   __udivdi3
 | 
			
		||||
  move  a0, a1
 | 
			
		||||
  jr    t0
 | 
			
		||||
 | 
			
		||||
  /* Handle negative arguments to __divdi3.  */
 | 
			
		||||
.L10:
 | 
			
		||||
  neg   a0, a0 
 | 
			
		||||
  bgez  a1, .L12      /* Compute __udivdi3(-a0, a1), then negate the result.  */
 | 
			
		||||
  neg   a1, a1
 | 
			
		||||
  j     __divdi3      /* Compute __udivdi3(-a0, -a1).  */
 | 
			
		||||
.L11:                 /* Compute __udivdi3(a0, -a1), then negate the result.  */
 | 
			
		||||
  neg   a1, a1
 | 
			
		||||
.L12:
 | 
			
		||||
  move  t0, ra
 | 
			
		||||
  jal   __divdi3
 | 
			
		||||
  neg   a0, a0
 | 
			
		||||
  jr    t0
 | 
			
		||||
 | 
			
		||||
  .globl __moddi3
 | 
			
		||||
__moddi3:
 | 
			
		||||
  move   t0, ra
 | 
			
		||||
  bltz   a1, .L31
 | 
			
		||||
  bltz   a0, .L32
 | 
			
		||||
.L30:
 | 
			
		||||
  jal    __udivdi3    /* The dividend is not negative.  */
 | 
			
		||||
  move   a0, a1
 | 
			
		||||
  jr     t0
 | 
			
		||||
.L31:
 | 
			
		||||
  neg    a1, a1
 | 
			
		||||
  bgez   a0, .L30
 | 
			
		||||
.L32:
 | 
			
		||||
  neg    a0, a0
 | 
			
		||||
  jal    __udivdi3    /* The dividend is hella negative.  */
 | 
			
		||||
  neg    a0, a1
 | 
			
		||||
  jr     t0
 | 
			
		||||
 | 
			
		||||
#ifdef __riscv64
 | 
			
		||||
  /* continuation of __divsi3 */
 | 
			
		||||
.L20:
 | 
			
		||||
  sll   t0, t0, 31
 | 
			
		||||
  bne   a0, t0, __divdi3
 | 
			
		||||
  ret
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <fomu/csr.h>
 | 
			
		||||
#include <irq.h>
 | 
			
		||||
#include <usb.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <rgb.h>
 | 
			
		||||
#include <spi.h>
 | 
			
		||||
#include <fomu/csr.h>
 | 
			
		||||
#include <tester.h>
 | 
			
		||||
#include <usb-cdc.h>
 | 
			
		||||
 | 
			
		||||
struct ff_spi *spi;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								sw/src/mul.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								sw/src/mul.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
  .text
 | 
			
		||||
  .align 2
 | 
			
		||||
 | 
			
		||||
#ifdef __riscv64
 | 
			
		||||
#define _RISCV_SZPTR 64
 | 
			
		||||
#define _RISCV_SZINT 64
 | 
			
		||||
#else
 | 
			
		||||
/* Our RV64 64-bit routine is equivalent to our RV32 32-bit routine.  */
 | 
			
		||||
# define __muldi3 __mulsi3
 | 
			
		||||
#define _RISCV_SZPTR 32
 | 
			
		||||
#define _RISCV_SZINT 32
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  .globl __muldi3
 | 
			
		||||
__muldi3:
 | 
			
		||||
  mv     a2, a0
 | 
			
		||||
  li     a0, 0
 | 
			
		||||
.L1:
 | 
			
		||||
  slli   a3, a1, _RISCV_SZPTR-1
 | 
			
		||||
  bgez   a3, .L2
 | 
			
		||||
  add    a0, a0, a2
 | 
			
		||||
.L2:
 | 
			
		||||
  srli   a1, a1, 1
 | 
			
		||||
  slli   a2, a2, 1
 | 
			
		||||
  bnez   a1, .L1
 | 
			
		||||
  ret
 | 
			
		||||
							
								
								
									
										914
									
								
								sw/src/printf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										914
									
								
								sw/src/printf.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,914 @@
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// \author (c) Marco Paland (info@paland.com)
 | 
			
		||||
//             2014-2019, PALANDesign Hannover, Germany
 | 
			
		||||
//
 | 
			
		||||
// \license The MIT License (MIT)
 | 
			
		||||
//
 | 
			
		||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
// of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
// in the Software without restriction, including without limitation the rights
 | 
			
		||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
// copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
// furnished to do so, subject to the following conditions:
 | 
			
		||||
//
 | 
			
		||||
// The above copyright notice and this permission notice shall be included in
 | 
			
		||||
// all copies or substantial portions of the Software.
 | 
			
		||||
//
 | 
			
		||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
// THE SOFTWARE.
 | 
			
		||||
//
 | 
			
		||||
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
 | 
			
		||||
//        embedded systems with a very limited resources. These routines are thread
 | 
			
		||||
//        safe and reentrant!
 | 
			
		||||
//        Use this instead of the bloated standard/newlib printf cause these use
 | 
			
		||||
//        malloc for printf (and may not be thread safe).
 | 
			
		||||
//
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include "printf.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
 | 
			
		||||
// printf_config.h header file
 | 
			
		||||
// default: undefined
 | 
			
		||||
// #ifdef PRINTF_INCLUDE_CONFIG_H
 | 
			
		||||
// #include "printf_config.h"
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
 | 
			
		||||
// numeric number including padded zeros (dynamically created on stack)
 | 
			
		||||
// default: 32 byte
 | 
			
		||||
#ifndef PRINTF_NTOA_BUFFER_SIZE
 | 
			
		||||
#define PRINTF_NTOA_BUFFER_SIZE    32U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
 | 
			
		||||
// float number including padded zeros (dynamically created on stack)
 | 
			
		||||
// default: 32 byte
 | 
			
		||||
#ifndef PRINTF_FTOA_BUFFER_SIZE
 | 
			
		||||
#define PRINTF_FTOA_BUFFER_SIZE    32U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// support for the floating point type (%f)
 | 
			
		||||
// default: activated
 | 
			
		||||
// #ifndef PRINTF_DISABLE_SUPPORT_FLOAT
 | 
			
		||||
// #define PRINTF_SUPPORT_FLOAT
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
// support for exponential floating point notation (%e/%g)
 | 
			
		||||
// default: activated
 | 
			
		||||
// #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
 | 
			
		||||
// #define PRINTF_SUPPORT_EXPONENTIAL
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
// define the default floating point precision
 | 
			
		||||
// default: 6 digits
 | 
			
		||||
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
 | 
			
		||||
#define PRINTF_DEFAULT_FLOAT_PRECISION  6U
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// define the largest float suitable to print with %f
 | 
			
		||||
// default: 1e9
 | 
			
		||||
#ifndef PRINTF_MAX_FLOAT
 | 
			
		||||
#define PRINTF_MAX_FLOAT  1e9
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// support for the long long types (%llu or %p)
 | 
			
		||||
// default: activated
 | 
			
		||||
// #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
 | 
			
		||||
// #define PRINTF_SUPPORT_LONG_LONG
 | 
			
		||||
// #endif
 | 
			
		||||
 | 
			
		||||
// support for the ptrdiff_t type (%t)
 | 
			
		||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
 | 
			
		||||
// default: activated
 | 
			
		||||
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
 | 
			
		||||
// #define PRINTF_SUPPORT_PTRDIFF_T
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// internal flag definitions
 | 
			
		||||
#define FLAGS_ZEROPAD   (1U <<  0U)
 | 
			
		||||
#define FLAGS_LEFT      (1U <<  1U)
 | 
			
		||||
#define FLAGS_PLUS      (1U <<  2U)
 | 
			
		||||
#define FLAGS_SPACE     (1U <<  3U)
 | 
			
		||||
#define FLAGS_HASH      (1U <<  4U)
 | 
			
		||||
#define FLAGS_UPPERCASE (1U <<  5U)
 | 
			
		||||
#define FLAGS_CHAR      (1U <<  6U)
 | 
			
		||||
#define FLAGS_SHORT     (1U <<  7U)
 | 
			
		||||
#define FLAGS_LONG      (1U <<  8U)
 | 
			
		||||
#define FLAGS_LONG_LONG (1U <<  9U)
 | 
			
		||||
#define FLAGS_PRECISION (1U << 10U)
 | 
			
		||||
#define FLAGS_ADAPT_EXP (1U << 11U)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// import float.h for DBL_MAX
 | 
			
		||||
#if defined(PRINTF_SUPPORT_FLOAT)
 | 
			
		||||
#include <float.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// output function type
 | 
			
		||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// wrapper (used as buffer) for output function type
 | 
			
		||||
typedef struct {
 | 
			
		||||
  void  (*fct)(char character, void* arg);
 | 
			
		||||
  void* arg;
 | 
			
		||||
} out_fct_wrap_type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal buffer output
 | 
			
		||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
 | 
			
		||||
{
 | 
			
		||||
  if (idx < maxlen) {
 | 
			
		||||
    ((char*)buffer)[idx] = character;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal null output
 | 
			
		||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
 | 
			
		||||
{
 | 
			
		||||
  (void)character; (void)buffer; (void)idx; (void)maxlen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal _putchar wrapper
 | 
			
		||||
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
 | 
			
		||||
{
 | 
			
		||||
  (void)buffer; (void)idx; (void)maxlen;
 | 
			
		||||
  if (character) {
 | 
			
		||||
    _putchar(character);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal output function wrapper
 | 
			
		||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
 | 
			
		||||
{
 | 
			
		||||
  (void)idx; (void)maxlen;
 | 
			
		||||
  if (character) {
 | 
			
		||||
    // buffer is the output fct pointer
 | 
			
		||||
    ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal secure strlen
 | 
			
		||||
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
 | 
			
		||||
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
 | 
			
		||||
{
 | 
			
		||||
  const char* s;
 | 
			
		||||
  for (s = str; *s && maxsize--; ++s);
 | 
			
		||||
  return (unsigned int)(s - str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal test if char is a digit (0-9)
 | 
			
		||||
// \return true if char is a digit
 | 
			
		||||
static inline bool _is_digit(char ch)
 | 
			
		||||
{
 | 
			
		||||
  return (ch >= '0') && (ch <= '9');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal ASCII string to unsigned int conversion
 | 
			
		||||
static unsigned int _atoi(const char** str)
 | 
			
		||||
{
 | 
			
		||||
  unsigned int i = 0U;
 | 
			
		||||
  while (_is_digit(**str)) {
 | 
			
		||||
    i = i * 10U + (unsigned int)(*((*str)++) - '0');
 | 
			
		||||
  }
 | 
			
		||||
  return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// output the specified string in reverse, taking care of any zero-padding
 | 
			
		||||
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  const size_t start_idx = idx;
 | 
			
		||||
 | 
			
		||||
  // pad spaces up to given width
 | 
			
		||||
  if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
 | 
			
		||||
    for (size_t i = len; i < width; i++) {
 | 
			
		||||
      out(' ', buffer, idx++, maxlen);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // reverse string
 | 
			
		||||
  while (len) {
 | 
			
		||||
    out(buf[--len], buffer, idx++, maxlen);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // append pad spaces up to given width
 | 
			
		||||
  if (flags & FLAGS_LEFT) {
 | 
			
		||||
    while (idx - start_idx < width) {
 | 
			
		||||
      out(' ', buffer, idx++, maxlen);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal itoa format
 | 
			
		||||
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  // pad leading zeros
 | 
			
		||||
  if (!(flags & FLAGS_LEFT)) {
 | 
			
		||||
    if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
 | 
			
		||||
      width--;
 | 
			
		||||
    }
 | 
			
		||||
    while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = '0';
 | 
			
		||||
    }
 | 
			
		||||
    while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = '0';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // handle hash
 | 
			
		||||
  if (flags & FLAGS_HASH) {
 | 
			
		||||
    if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
 | 
			
		||||
      len--;
 | 
			
		||||
      if (len && (base == 16U)) {
 | 
			
		||||
        len--;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = 'x';
 | 
			
		||||
    }
 | 
			
		||||
    else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = 'X';
 | 
			
		||||
    }
 | 
			
		||||
    else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = 'b';
 | 
			
		||||
    }
 | 
			
		||||
    if (len < PRINTF_NTOA_BUFFER_SIZE) {
 | 
			
		||||
      buf[len++] = '0';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (len < PRINTF_NTOA_BUFFER_SIZE) {
 | 
			
		||||
    if (negative) {
 | 
			
		||||
      buf[len++] = '-';
 | 
			
		||||
    }
 | 
			
		||||
    else if (flags & FLAGS_PLUS) {
 | 
			
		||||
      buf[len++] = '+';  // ignore the space if the '+' exists
 | 
			
		||||
    }
 | 
			
		||||
    else if (flags & FLAGS_SPACE) {
 | 
			
		||||
      buf[len++] = ' ';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal itoa for 'long' type
 | 
			
		||||
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  char buf[PRINTF_NTOA_BUFFER_SIZE];
 | 
			
		||||
  size_t len = 0U;
 | 
			
		||||
 | 
			
		||||
  // no hash for 0 values
 | 
			
		||||
  if (!value) {
 | 
			
		||||
    flags &= ~FLAGS_HASH;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // write if precision != 0 and value is != 0
 | 
			
		||||
  if (!(flags & FLAGS_PRECISION) || value) {
 | 
			
		||||
    do {
 | 
			
		||||
      const char digit = (char)(value % base);
 | 
			
		||||
      buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
 | 
			
		||||
      value /= base;
 | 
			
		||||
    } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal itoa for 'long long' type
 | 
			
		||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
 | 
			
		||||
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  char buf[PRINTF_NTOA_BUFFER_SIZE];
 | 
			
		||||
  size_t len = 0U;
 | 
			
		||||
 | 
			
		||||
  // no hash for 0 values
 | 
			
		||||
  if (!value) {
 | 
			
		||||
    flags &= ~FLAGS_HASH;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // write if precision != 0 and value is != 0
 | 
			
		||||
  if (!(flags & FLAGS_PRECISION) || value) {
 | 
			
		||||
    do {
 | 
			
		||||
      const char digit = (char)(value % base);
 | 
			
		||||
      buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
 | 
			
		||||
      value /= base;
 | 
			
		||||
    } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
 | 
			
		||||
}
 | 
			
		||||
#endif  // PRINTF_SUPPORT_LONG_LONG
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(PRINTF_SUPPORT_FLOAT)
 | 
			
		||||
 | 
			
		||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
 | 
			
		||||
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
 | 
			
		||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal ftoa for fixed decimal floating point
 | 
			
		||||
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  char buf[PRINTF_FTOA_BUFFER_SIZE];
 | 
			
		||||
  size_t len  = 0U;
 | 
			
		||||
  double diff = 0.0;
 | 
			
		||||
 | 
			
		||||
  // powers of 10
 | 
			
		||||
  static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
 | 
			
		||||
 | 
			
		||||
  // test for special values
 | 
			
		||||
  if (value != value)
 | 
			
		||||
    return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
 | 
			
		||||
  if (value < -DBL_MAX)
 | 
			
		||||
    return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
 | 
			
		||||
  if (value > DBL_MAX)
 | 
			
		||||
    return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
 | 
			
		||||
 | 
			
		||||
  // test for very large values
 | 
			
		||||
  // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
 | 
			
		||||
  if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
 | 
			
		||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
 | 
			
		||||
    return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
 | 
			
		||||
#else
 | 
			
		||||
    return 0U;
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // test for negative
 | 
			
		||||
  bool negative = false;
 | 
			
		||||
  if (value < 0) {
 | 
			
		||||
    negative = true;
 | 
			
		||||
    value = 0 - value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // set default precision, if not set explicitly
 | 
			
		||||
  if (!(flags & FLAGS_PRECISION)) {
 | 
			
		||||
    prec = PRINTF_DEFAULT_FLOAT_PRECISION;
 | 
			
		||||
  }
 | 
			
		||||
  // limit precision to 9, cause a prec >= 10 can lead to overflow errors
 | 
			
		||||
  while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
 | 
			
		||||
    buf[len++] = '0';
 | 
			
		||||
    prec--;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int whole = (int)value;
 | 
			
		||||
  double tmp = (value - whole) * pow10[prec];
 | 
			
		||||
  unsigned long frac = (unsigned long)tmp;
 | 
			
		||||
  diff = tmp - frac;
 | 
			
		||||
 | 
			
		||||
  if (diff > 0.5) {
 | 
			
		||||
    ++frac;
 | 
			
		||||
    // handle rollover, e.g. case 0.99 with prec 1 is 1.0
 | 
			
		||||
    if (frac >= pow10[prec]) {
 | 
			
		||||
      frac = 0;
 | 
			
		||||
      ++whole;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (diff < 0.5) {
 | 
			
		||||
  }
 | 
			
		||||
  else if ((frac == 0U) || (frac & 1U)) {
 | 
			
		||||
    // if halfway, round up if odd OR if last digit is 0
 | 
			
		||||
    ++frac;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (prec == 0U) {
 | 
			
		||||
    diff = value - (double)whole;
 | 
			
		||||
    if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
 | 
			
		||||
      // exactly 0.5 and ODD, then round up
 | 
			
		||||
      // 1.5 -> 2, but 2.5 -> 2
 | 
			
		||||
      ++whole;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    unsigned int count = prec;
 | 
			
		||||
    // now do fractional part, as an unsigned number
 | 
			
		||||
    while (len < PRINTF_FTOA_BUFFER_SIZE) {
 | 
			
		||||
      --count;
 | 
			
		||||
      buf[len++] = (char)(48U + (frac % 10U));
 | 
			
		||||
      if (!(frac /= 10U)) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // add extra 0s
 | 
			
		||||
    while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
 | 
			
		||||
      buf[len++] = '0';
 | 
			
		||||
    }
 | 
			
		||||
    if (len < PRINTF_FTOA_BUFFER_SIZE) {
 | 
			
		||||
      // add decimal
 | 
			
		||||
      buf[len++] = '.';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // do whole part, number is reversed
 | 
			
		||||
  while (len < PRINTF_FTOA_BUFFER_SIZE) {
 | 
			
		||||
    buf[len++] = (char)(48 + (whole % 10));
 | 
			
		||||
    if (!(whole /= 10)) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // pad leading zeros
 | 
			
		||||
  if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
 | 
			
		||||
    if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
 | 
			
		||||
      width--;
 | 
			
		||||
    }
 | 
			
		||||
    while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
 | 
			
		||||
      buf[len++] = '0';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (len < PRINTF_FTOA_BUFFER_SIZE) {
 | 
			
		||||
    if (negative) {
 | 
			
		||||
      buf[len++] = '-';
 | 
			
		||||
    }
 | 
			
		||||
    else if (flags & FLAGS_PLUS) {
 | 
			
		||||
      buf[len++] = '+';  // ignore the space if the '+' exists
 | 
			
		||||
    }
 | 
			
		||||
    else if (flags & FLAGS_SPACE) {
 | 
			
		||||
      buf[len++] = ' ';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
 | 
			
		||||
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
 | 
			
		||||
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  // check for NaN and special values
 | 
			
		||||
  if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
 | 
			
		||||
    return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // determine the sign
 | 
			
		||||
  const bool negative = value < 0;
 | 
			
		||||
  if (negative) {
 | 
			
		||||
    value = -value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // default precision
 | 
			
		||||
  if (!(flags & FLAGS_PRECISION)) {
 | 
			
		||||
    prec = PRINTF_DEFAULT_FLOAT_PRECISION;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // determine the decimal exponent
 | 
			
		||||
  // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t U;
 | 
			
		||||
    double   F;
 | 
			
		||||
  } conv;
 | 
			
		||||
 | 
			
		||||
  conv.F = value;
 | 
			
		||||
  int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023;           // effectively log2
 | 
			
		||||
  conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U);  // drop the exponent so conv.F is now in [1,2)
 | 
			
		||||
  // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
 | 
			
		||||
  int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
 | 
			
		||||
  // now we want to compute 10^expval but we want to be sure it won't overflow
 | 
			
		||||
  exp2 = (int)(expval * 3.321928094887362 + 0.5);
 | 
			
		||||
  const double z  = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
 | 
			
		||||
  const double z2 = z * z;
 | 
			
		||||
  conv.U = (uint64_t)(exp2 + 1023) << 52U;
 | 
			
		||||
  // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
 | 
			
		||||
  conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
 | 
			
		||||
  // correct for rounding errors
 | 
			
		||||
  if (value < conv.F) {
 | 
			
		||||
    expval--;
 | 
			
		||||
    conv.F /= 10;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
 | 
			
		||||
  unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
 | 
			
		||||
 | 
			
		||||
  // in "%g" mode, "prec" is the number of *significant figures* not decimals
 | 
			
		||||
  if (flags & FLAGS_ADAPT_EXP) {
 | 
			
		||||
    // do we want to fall-back to "%f" mode?
 | 
			
		||||
    if ((value >= 1e-4) && (value < 1e6)) {
 | 
			
		||||
      if ((int)prec > expval) {
 | 
			
		||||
        prec = (unsigned)((int)prec - expval - 1);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        prec = 0;
 | 
			
		||||
      }
 | 
			
		||||
      flags |= FLAGS_PRECISION;   // make sure _ftoa respects precision
 | 
			
		||||
      // no characters in exponent
 | 
			
		||||
      minwidth = 0U;
 | 
			
		||||
      expval   = 0;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // we use one sigfig for the whole part
 | 
			
		||||
      if ((prec > 0) && (flags & FLAGS_PRECISION)) {
 | 
			
		||||
        --prec;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // will everything fit?
 | 
			
		||||
  unsigned int fwidth = width;
 | 
			
		||||
  if (width > minwidth) {
 | 
			
		||||
    // we didn't fall-back so subtract the characters required for the exponent
 | 
			
		||||
    fwidth -= minwidth;
 | 
			
		||||
  } else {
 | 
			
		||||
    // not enough characters, so go back to default sizing
 | 
			
		||||
    fwidth = 0U;
 | 
			
		||||
  }
 | 
			
		||||
  if ((flags & FLAGS_LEFT) && minwidth) {
 | 
			
		||||
    // if we're padding on the right, DON'T pad the floating part
 | 
			
		||||
    fwidth = 0U;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // rescale the float value
 | 
			
		||||
  if (expval) {
 | 
			
		||||
    value /= conv.F;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // output the floating part
 | 
			
		||||
  const size_t start_idx = idx;
 | 
			
		||||
  idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
 | 
			
		||||
 | 
			
		||||
  // output the exponent part
 | 
			
		||||
  if (minwidth) {
 | 
			
		||||
    // output the exponential symbol
 | 
			
		||||
    out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
 | 
			
		||||
    // output the exponent value
 | 
			
		||||
    idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
 | 
			
		||||
    // might need to right-pad spaces
 | 
			
		||||
    if (flags & FLAGS_LEFT) {
 | 
			
		||||
      while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return idx;
 | 
			
		||||
}
 | 
			
		||||
#endif  // PRINTF_SUPPORT_EXPONENTIAL
 | 
			
		||||
#endif  // PRINTF_SUPPORT_FLOAT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// internal vsnprintf
 | 
			
		||||
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
 | 
			
		||||
{
 | 
			
		||||
  unsigned int flags, width, precision, n;
 | 
			
		||||
  size_t idx = 0U;
 | 
			
		||||
 | 
			
		||||
  if (!buffer) {
 | 
			
		||||
    // use null output function
 | 
			
		||||
    out = _out_null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  while (*format)
 | 
			
		||||
  {
 | 
			
		||||
    // format specifier?  %[flags][width][.precision][length]
 | 
			
		||||
    if (*format != '%') {
 | 
			
		||||
      // no
 | 
			
		||||
      out(*format, buffer, idx++, maxlen);
 | 
			
		||||
      format++;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // yes, evaluate it
 | 
			
		||||
      format++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // evaluate flags
 | 
			
		||||
    flags = 0U;
 | 
			
		||||
    do {
 | 
			
		||||
      switch (*format) {
 | 
			
		||||
        case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
 | 
			
		||||
        case '-': flags |= FLAGS_LEFT;    format++; n = 1U; break;
 | 
			
		||||
        case '+': flags |= FLAGS_PLUS;    format++; n = 1U; break;
 | 
			
		||||
        case ' ': flags |= FLAGS_SPACE;   format++; n = 1U; break;
 | 
			
		||||
        case '#': flags |= FLAGS_HASH;    format++; n = 1U; break;
 | 
			
		||||
        default :                                   n = 0U; break;
 | 
			
		||||
      }
 | 
			
		||||
    } while (n);
 | 
			
		||||
 | 
			
		||||
    // evaluate width field
 | 
			
		||||
    width = 0U;
 | 
			
		||||
    if (_is_digit(*format)) {
 | 
			
		||||
      width = _atoi(&format);
 | 
			
		||||
    }
 | 
			
		||||
    else if (*format == '*') {
 | 
			
		||||
      const int w = va_arg(va, int);
 | 
			
		||||
      if (w < 0) {
 | 
			
		||||
        flags |= FLAGS_LEFT;    // reverse padding
 | 
			
		||||
        width = (unsigned int)-w;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        width = (unsigned int)w;
 | 
			
		||||
      }
 | 
			
		||||
      format++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // evaluate precision field
 | 
			
		||||
    precision = 0U;
 | 
			
		||||
    if (*format == '.') {
 | 
			
		||||
      flags |= FLAGS_PRECISION;
 | 
			
		||||
      format++;
 | 
			
		||||
      if (_is_digit(*format)) {
 | 
			
		||||
        precision = _atoi(&format);
 | 
			
		||||
      }
 | 
			
		||||
      else if (*format == '*') {
 | 
			
		||||
        const int prec = (int)va_arg(va, int);
 | 
			
		||||
        precision = prec > 0 ? (unsigned int)prec : 0U;
 | 
			
		||||
        format++;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // evaluate length field
 | 
			
		||||
    switch (*format) {
 | 
			
		||||
      case 'l' :
 | 
			
		||||
        flags |= FLAGS_LONG;
 | 
			
		||||
        format++;
 | 
			
		||||
        if (*format == 'l') {
 | 
			
		||||
          flags |= FLAGS_LONG_LONG;
 | 
			
		||||
          format++;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 'h' :
 | 
			
		||||
        flags |= FLAGS_SHORT;
 | 
			
		||||
        format++;
 | 
			
		||||
        if (*format == 'h') {
 | 
			
		||||
          flags |= FLAGS_CHAR;
 | 
			
		||||
          format++;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
 | 
			
		||||
      case 't' :
 | 
			
		||||
        flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
#endif
 | 
			
		||||
      case 'j' :
 | 
			
		||||
        flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      case 'z' :
 | 
			
		||||
        flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      default :
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // evaluate specifier
 | 
			
		||||
    switch (*format) {
 | 
			
		||||
      case 'd' :
 | 
			
		||||
      case 'i' :
 | 
			
		||||
      case 'u' :
 | 
			
		||||
      case 'x' :
 | 
			
		||||
      case 'X' :
 | 
			
		||||
      case 'o' :
 | 
			
		||||
      case 'b' : {
 | 
			
		||||
        // set the base
 | 
			
		||||
        unsigned int base;
 | 
			
		||||
        if (*format == 'x' || *format == 'X') {
 | 
			
		||||
          base = 16U;
 | 
			
		||||
        }
 | 
			
		||||
        else if (*format == 'o') {
 | 
			
		||||
          base =  8U;
 | 
			
		||||
        }
 | 
			
		||||
        else if (*format == 'b') {
 | 
			
		||||
          base =  2U;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          base = 10U;
 | 
			
		||||
          flags &= ~FLAGS_HASH;   // no hash for dec format
 | 
			
		||||
        }
 | 
			
		||||
        // uppercase
 | 
			
		||||
        if (*format == 'X') {
 | 
			
		||||
          flags |= FLAGS_UPPERCASE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // no plus or space flag for u, x, X, o, b
 | 
			
		||||
        if ((*format != 'i') && (*format != 'd')) {
 | 
			
		||||
          flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // ignore '0' flag when precision is given
 | 
			
		||||
        if (flags & FLAGS_PRECISION) {
 | 
			
		||||
          flags &= ~FLAGS_ZEROPAD;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // convert the integer
 | 
			
		||||
        if ((*format == 'i') || (*format == 'd')) {
 | 
			
		||||
          // signed
 | 
			
		||||
          if (flags & FLAGS_LONG_LONG) {
 | 
			
		||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
 | 
			
		||||
            const long long value = va_arg(va, long long);
 | 
			
		||||
            idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
 | 
			
		||||
#endif
 | 
			
		||||
          }
 | 
			
		||||
          else if (flags & FLAGS_LONG) {
 | 
			
		||||
            const long value = va_arg(va, long);
 | 
			
		||||
            idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
 | 
			
		||||
            idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          // unsigned
 | 
			
		||||
          if (flags & FLAGS_LONG_LONG) {
 | 
			
		||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
 | 
			
		||||
            idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
 | 
			
		||||
#endif
 | 
			
		||||
          }
 | 
			
		||||
          else if (flags & FLAGS_LONG) {
 | 
			
		||||
            idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
 | 
			
		||||
            idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
#if defined(PRINTF_SUPPORT_FLOAT)
 | 
			
		||||
      case 'f' :
 | 
			
		||||
      case 'F' :
 | 
			
		||||
        if (*format == 'F') flags |= FLAGS_UPPERCASE;
 | 
			
		||||
        idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
 | 
			
		||||
      case 'e':
 | 
			
		||||
      case 'E':
 | 
			
		||||
      case 'g':
 | 
			
		||||
      case 'G':
 | 
			
		||||
        if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
 | 
			
		||||
        if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
 | 
			
		||||
        idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
#endif  // PRINTF_SUPPORT_EXPONENTIAL
 | 
			
		||||
#endif  // PRINTF_SUPPORT_FLOAT
 | 
			
		||||
      case 'c' : {
 | 
			
		||||
        unsigned int l = 1U;
 | 
			
		||||
        // pre padding
 | 
			
		||||
        if (!(flags & FLAGS_LEFT)) {
 | 
			
		||||
          while (l++ < width) {
 | 
			
		||||
            out(' ', buffer, idx++, maxlen);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // char output
 | 
			
		||||
        out((char)va_arg(va, int), buffer, idx++, maxlen);
 | 
			
		||||
        // post padding
 | 
			
		||||
        if (flags & FLAGS_LEFT) {
 | 
			
		||||
          while (l++ < width) {
 | 
			
		||||
            out(' ', buffer, idx++, maxlen);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case 's' : {
 | 
			
		||||
        const char* p = va_arg(va, char*);
 | 
			
		||||
        unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
 | 
			
		||||
        // pre padding
 | 
			
		||||
        if (flags & FLAGS_PRECISION) {
 | 
			
		||||
          l = (l < precision ? l : precision);
 | 
			
		||||
        }
 | 
			
		||||
        if (!(flags & FLAGS_LEFT)) {
 | 
			
		||||
          while (l++ < width) {
 | 
			
		||||
            out(' ', buffer, idx++, maxlen);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // string output
 | 
			
		||||
        while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
 | 
			
		||||
          out(*(p++), buffer, idx++, maxlen);
 | 
			
		||||
        }
 | 
			
		||||
        // post padding
 | 
			
		||||
        if (flags & FLAGS_LEFT) {
 | 
			
		||||
          while (l++ < width) {
 | 
			
		||||
            out(' ', buffer, idx++, maxlen);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case 'p' : {
 | 
			
		||||
        width = sizeof(void*) * 2U;
 | 
			
		||||
        flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
 | 
			
		||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
 | 
			
		||||
        const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
 | 
			
		||||
        if (is_ll) {
 | 
			
		||||
          idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
#endif
 | 
			
		||||
          idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
 | 
			
		||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case '%' :
 | 
			
		||||
        out('%', buffer, idx++, maxlen);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default :
 | 
			
		||||
        out(*format, buffer, idx++, maxlen);
 | 
			
		||||
        format++;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // termination
 | 
			
		||||
  out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
 | 
			
		||||
 | 
			
		||||
  // return written chars without terminating \0
 | 
			
		||||
  return (int)idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
int printf_(const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list va;
 | 
			
		||||
  va_start(va, format);
 | 
			
		||||
  char buffer[1];
 | 
			
		||||
  const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
 | 
			
		||||
  va_end(va);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int sprintf_(char* buffer, const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list va;
 | 
			
		||||
  va_start(va, format);
 | 
			
		||||
  const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
 | 
			
		||||
  va_end(va);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int snprintf_(char* buffer, size_t count, const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list va;
 | 
			
		||||
  va_start(va, format);
 | 
			
		||||
  const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
 | 
			
		||||
  va_end(va);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int vprintf_(const char* format, va_list va)
 | 
			
		||||
{
 | 
			
		||||
  char buffer[1];
 | 
			
		||||
  return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
 | 
			
		||||
{
 | 
			
		||||
  return _vsnprintf(_out_buffer, buffer, count, format, va);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
  va_list va;
 | 
			
		||||
  va_start(va, format);
 | 
			
		||||
  const out_fct_wrap_type out_fct_wrap = { out, arg };
 | 
			
		||||
  const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
 | 
			
		||||
  va_end(va);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#include <tester.h>
 | 
			
		||||
#include <printf.h>
 | 
			
		||||
 | 
			
		||||
void tester_poll(void) {
 | 
			
		||||
    printf("Hello, world!\n");
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
#include <usb.h>
 | 
			
		||||
#include <usb-cdc.h>
 | 
			
		||||
#include <fomu/csr.h>
 | 
			
		||||
 | 
			
		||||
static int connected = 0;
 | 
			
		||||
 | 
			
		||||
@@ -10,4 +12,13 @@ int cdc_connected(void)
 | 
			
		||||
void cdc_set_connected(int is_connected)
 | 
			
		||||
{
 | 
			
		||||
    connected = is_connected;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _putchar(char character)
 | 
			
		||||
{
 | 
			
		||||
    // Wait for buffer to be empty
 | 
			
		||||
    while (usb_ep_2_in_respond_read() == EPF_ACK)
 | 
			
		||||
        ;
 | 
			
		||||
    usb_ep_2_in_ibuf_head_write(character);
 | 
			
		||||
    usb_ep_2_in_respond_write(EPF_ACK);
 | 
			
		||||
}
 | 
			
		||||
@@ -5,14 +5,23 @@
 | 
			
		||||
 | 
			
		||||
#ifdef CSR_USB_EP_0_OUT_EV_PENDING_ADDR
 | 
			
		||||
 | 
			
		||||
static const int max_byte_length = 64;
 | 
			
		||||
 | 
			
		||||
#define EP0OUT_BUFFERS 4
 | 
			
		||||
#define EP2OUT_BUFFERS 4
 | 
			
		||||
__attribute__((aligned(4)))
 | 
			
		||||
#define EP0OUT_BUFFER_SIZE 256
 | 
			
		||||
static uint8_t volatile usb_ep0out_buffer_len[EP0OUT_BUFFERS];
 | 
			
		||||
static uint8_t volatile usb_ep0out_buffer[EP0OUT_BUFFERS][256];
 | 
			
		||||
static uint8_t volatile usb_ep0out_buffer[EP0OUT_BUFFERS][EP0OUT_BUFFER_SIZE];
 | 
			
		||||
static uint8_t volatile usb_ep0out_last_tok[EP0OUT_BUFFERS];
 | 
			
		||||
static volatile uint8_t usb_ep0out_wr_ptr;
 | 
			
		||||
static volatile uint8_t usb_ep0out_rd_ptr;
 | 
			
		||||
static const int max_byte_length = 64;
 | 
			
		||||
 | 
			
		||||
#define EP2OUT_BUFFER_SIZE 256
 | 
			
		||||
static uint8_t volatile usb_ep2out_buffer_len[EP2OUT_BUFFERS];
 | 
			
		||||
static uint8_t volatile usb_ep2out_buffer[EP2OUT_BUFFERS][EP2OUT_BUFFER_SIZE];
 | 
			
		||||
static volatile uint8_t usb_ep2out_wr_ptr;
 | 
			
		||||
static volatile uint8_t usb_ep2out_rd_ptr;
 | 
			
		||||
 | 
			
		||||
static const uint8_t * volatile current_data;
 | 
			
		||||
static volatile int current_length;
 | 
			
		||||
@@ -20,25 +29,6 @@ static volatile int data_offset;
 | 
			
		||||
static volatile int data_to_send;
 | 
			
		||||
static int next_packet_is_empty;
 | 
			
		||||
 | 
			
		||||
// Note that our PIDs are only bits 2 and 3 of the token,
 | 
			
		||||
// since all other bits are effectively redundant at this point.
 | 
			
		||||
enum USB_PID {
 | 
			
		||||
    USB_PID_OUT   = 0,
 | 
			
		||||
    USB_PID_SOF   = 1,
 | 
			
		||||
    USB_PID_IN    = 2,
 | 
			
		||||
    USB_PID_SETUP = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum epfifo_response {
 | 
			
		||||
    EPF_ACK = 0,
 | 
			
		||||
    EPF_NAK = 1,
 | 
			
		||||
    EPF_NONE = 2,
 | 
			
		||||
    EPF_STALL = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define USB_EV_ERROR 1
 | 
			
		||||
#define USB_EV_PACKET 2
 | 
			
		||||
 | 
			
		||||
void usb_idle(void) {
 | 
			
		||||
    usb_ep_0_out_ev_enable_write(0);
 | 
			
		||||
    usb_ep_0_in_ev_enable_write(0);
 | 
			
		||||
@@ -60,14 +50,22 @@ void usb_disconnect(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_connect(void) {
 | 
			
		||||
 | 
			
		||||
    usb_ep_0_out_ev_pending_write(usb_ep_0_out_ev_enable_read());
 | 
			
		||||
    usb_ep_0_in_ev_pending_write(usb_ep_0_in_ev_pending_read());
 | 
			
		||||
    usb_ep_0_out_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
 | 
			
		||||
    usb_ep_0_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
 | 
			
		||||
 | 
			
		||||
    usb_ep_1_in_ev_pending_write(usb_ep_1_in_ev_enable_read());
 | 
			
		||||
    usb_ep_1_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
 | 
			
		||||
 | 
			
		||||
    usb_ep_2_out_ev_pending_write(usb_ep_2_out_ev_enable_read());
 | 
			
		||||
    usb_ep_2_in_ev_pending_write(usb_ep_2_in_ev_pending_read());
 | 
			
		||||
    usb_ep_2_out_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
 | 
			
		||||
    usb_ep_2_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
 | 
			
		||||
 | 
			
		||||
    // Accept incoming data by default.
 | 
			
		||||
    usb_ep_0_out_respond_write(EPF_ACK);
 | 
			
		||||
    usb_ep_2_out_respond_write(EPF_ACK);
 | 
			
		||||
 | 
			
		||||
    // Reject outgoing data, since we have none to give yet.
 | 
			
		||||
    usb_ep_0_in_respond_write(EPF_NAK);
 | 
			
		||||
@@ -153,24 +151,24 @@ void usb_wait_for_send_done(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_isr(void) {
 | 
			
		||||
    uint8_t ep0o_pending = usb_ep_0_out_ev_pending_read();
 | 
			
		||||
    uint8_t ep0i_pending = usb_ep_0_in_ev_pending_read();
 | 
			
		||||
    uint8_t ep0out_pending = usb_ep_0_out_ev_pending_read();
 | 
			
		||||
    uint8_t ep0in_pending = usb_ep_0_in_ev_pending_read();
 | 
			
		||||
    uint8_t ep1in_pending = usb_ep_1_in_ev_pending_read();
 | 
			
		||||
    uint8_t ep2in_pending = usb_ep_2_in_ev_pending_read();
 | 
			
		||||
    uint8_t ep2out_pending = usb_ep_2_out_ev_pending_read();
 | 
			
		||||
 | 
			
		||||
    // We got an OUT or a SETUP packet.  Copy it to usb_ep0out_buffer
 | 
			
		||||
    // and clear the "pending" bit.
 | 
			
		||||
    if (ep0o_pending) {
 | 
			
		||||
    if (ep0out_pending) {
 | 
			
		||||
        uint8_t last_tok = usb_ep_0_out_last_tok_read();
 | 
			
		||||
        
 | 
			
		||||
        int byte_count = 0;
 | 
			
		||||
        usb_ep0out_last_tok[usb_ep0out_wr_ptr] = last_tok;
 | 
			
		||||
        volatile uint8_t * obuf = usb_ep0out_buffer[usb_ep0out_wr_ptr];
 | 
			
		||||
        if (!usb_ep_0_out_obuf_empty_read()) {
 | 
			
		||||
            while (!usb_ep_0_out_obuf_empty_read()) {
 | 
			
		||||
                obuf[byte_count++] = usb_ep_0_out_obuf_head_read();
 | 
			
		||||
                usb_ep_0_out_obuf_head_write(0);
 | 
			
		||||
            }
 | 
			
		||||
        while (!usb_ep_0_out_obuf_empty_read()) {
 | 
			
		||||
            obuf[byte_count++] = usb_ep_0_out_obuf_head_read();
 | 
			
		||||
            usb_ep_0_out_obuf_head_write(0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (byte_count >= 2)
 | 
			
		||||
            usb_ep0out_buffer_len[usb_ep0out_wr_ptr] = byte_count - 2 /* Strip off CRC16 */;
 | 
			
		||||
        usb_ep0out_wr_ptr = (usb_ep0out_wr_ptr + 1) & (EP0OUT_BUFFERS-1);
 | 
			
		||||
@@ -181,17 +179,43 @@ void usb_isr(void) {
 | 
			
		||||
            current_length = 0;
 | 
			
		||||
            current_data = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        usb_ep_0_out_ev_pending_write(ep0o_pending);
 | 
			
		||||
        usb_ep_0_out_ev_pending_write(ep0out_pending);
 | 
			
		||||
        usb_ep_0_out_respond_write(EPF_ACK);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We just got an "IN" token.  Send data if we have it.
 | 
			
		||||
    if (ep0i_pending) {
 | 
			
		||||
    if (ep0in_pending) {
 | 
			
		||||
        usb_ep_0_in_respond_write(EPF_NAK);
 | 
			
		||||
        usb_ep_0_in_ev_pending_write(ep0i_pending);
 | 
			
		||||
        usb_ep_0_in_ev_pending_write(ep0in_pending);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    if (ep1in_pending) {
 | 
			
		||||
        usb_ep_1_in_respond_write(EPF_NAK);
 | 
			
		||||
        usb_ep_1_in_ev_pending_write(ep1in_pending);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ep2in_pending) {
 | 
			
		||||
        usb_ep_2_in_respond_write(EPF_NAK);
 | 
			
		||||
        usb_ep_2_in_ev_pending_write(ep2in_pending);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ep2out_pending) {
 | 
			
		||||
        volatile uint8_t * obuf = usb_ep2out_buffer[usb_ep2out_wr_ptr];
 | 
			
		||||
        int sz = 0;
 | 
			
		||||
        while (!usb_ep_2_out_obuf_empty_read()) {
 | 
			
		||||
            if (sz < EP2OUT_BUFFER_SIZE)
 | 
			
		||||
                obuf[sz++] = usb_ep_2_out_obuf_head_read() + 1;
 | 
			
		||||
            usb_ep_2_out_obuf_head_write(0);
 | 
			
		||||
        }
 | 
			
		||||
        if (sz > 2) {
 | 
			
		||||
            usb_ep2out_buffer_len[usb_ep2out_wr_ptr] = sz - 2; /* Strip off CRC16 */
 | 
			
		||||
            usb_ep2out_wr_ptr = (usb_ep2out_wr_ptr + 1) & (EP2OUT_BUFFERS-1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        usb_ep_2_out_respond_write(EPF_ACK);
 | 
			
		||||
        usb_ep_2_out_ev_pending_write(ep2out_pending);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user