URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/forwardcom/libraries
- from Rev 100 to Rev 101
- ↔ Reverse comparison
Rev 100 → Rev 101
/printf_light.as
0,0 → 1,646
/**************************** printf_light.as ******************************** |
* Author: Agner Fog |
* date created: 2021-05-24 |
* Last modified: 2021-05-25 |
* Version: 1.11 |
* Project: ForwardCom library libc_light.li |
* Description: printf, sprintf, snprintf: Print formatted output |
* This version is for small CPUs with limited capabilities |
* The following instructions are avoided: mul, div, push, pop, sys_call. |
* Output to stdout goes directly to output port 10. It does not wait if |
* the output buffer is full. |
* |
* C declarations: |
* int printf (const char * format, ... ); |
* int sprintf (char * string, const char * format, ... ); |
* int snprintf (char * string, size_t max_length, const char * format, ... ); |
* |
* printf: print to stdout |
* sprintf: print to string |
* snprintf: print to string with length limit |
* fprintf: print to file. not implemented yet |
* |
* These functions are following the definitions in the C standard. |
* All standard features are supported, except the following: |
* - cannot print floating point numbers |
* - cannot print octal |
* - cannot print decimal integers with more than 32 bits |
* |
* Copyright 2021 GNU General Public License http://www.gnu.org/licenses |
*****************************************************************************/ |
|
const section read ip |
|
// call table for dispatching format string specifiers |
call_table: int8 (specif_space-def)/4 // ' ': print space or minus |
int8 0 // ! |
int8 0 // " |
int8 (specif_hash-def)/4 // #: prepend 0x |
int8 0 // $ |
int8 (specif_percent-def)/4 // %: print percent sign |
int8 0 // & |
int8 0 // ' |
int8 0 // ( |
int8 0 // ) |
int8 (specif_star-def)/4 // *: width specified by parameter |
int8 (specif_plus-def)/4 // +: print sign |
int8 0 // , |
int8 (specif_minus-def)/4 // -: left justified |
int8 0 // . |
int8 0 // / |
int8 (specif_number-def)/4 // 0 |
int8 (specif_number-def)/4 // 1 |
int8 (specif_number-def)/4 // 2 |
int8 (specif_number-def)/4 // 3 |
int8 (specif_number-def)/4 // 4 |
int8 (specif_number-def)/4 // 5 |
int8 (specif_number-def)/4 // 6 |
int8 (specif_number-def)/4 // 7 |
int8 (specif_number-def)/4 // 8 |
int8 (specif_number-def)/4 // 9 |
int8 0 // : |
int8 0 // ; |
int8 0 // < |
int8 0 // = |
int8 0 // > |
int8 0 // ? |
int8 0 // @ |
int8 (specif_hex-def)/4 // A: hexadecimal float |
int8 0 // B |
int8 0 // C |
int8 0 // D |
int8 (specif_float-def)/4 // E: float |
int8 (specif_float-def)/4 // F: float |
int8 (specif_float-def)/4 // G: float |
int8 0 // H |
int8 0 // I |
int8 0 // J |
int8 0 // K |
int8 (specif_l-def)/4 // L: long (long double) |
int8 0 // M |
int8 0 // N |
int8 0 // O |
int8 0 // P |
int8 0 // Q |
int8 0 // R |
int8 0 // S |
int8 0 // T |
int8 0 // U |
int8 0 // V |
int8 0 // W |
int8 (specif_hex-def)/4 // X: hexadecimal |
int8 0 // Y |
int8 0 // Z |
int8 0 // [ |
int8 0 // \ |
int8 0 // ] |
int8 0 // ^ |
int8 0 // _ |
int8 0 // ` |
int8 (specif_hex-def)/4 // a: hexadecimal float |
int8 0 // b |
int8 (specif_char-def)/4 // c: character |
int8 (specif_dec-def)/4 // d: signed decimal |
int8 (specif_float-def)/4 // e: float |
int8 (specif_float-def)/4 // f: float |
int8 (specif_float-def)/4 // g: float |
int8 (specif_h-def)/4 // h: short |
int8 (specif_dec-def)/4 // i: signed decimal |
int8 (specif_l-def)/4 // j: intmax_t |
int8 0 // k |
int8 (specif_l-def)/4 // l: long |
int8 0 // m |
int8 (specif_num-def)/4 // n: read number of characters printed so far |
int8 0 // o |
int8 (specif_hex-def)/4 // p: pointer, print as hexadecimal |
int8 0 // q |
int8 0 // r |
int8 (specif_string-def)/4 // s: string |
int8 (specif_l-def)/4 // t: ptrdiff_t |
int8 (specif_uns-def)/4 // u: unsigned decimal |
int8 0 // v |
int8 0 // w |
int8 (specif_hex-def)/4 // x: hexadecimal |
int8 0 // y |
int8 (specif_l-def)/4 // z: size_t |
const end |
|
code section execute align = 4 // code section |
|
|
// _printf: print formatted string to stdout |
// parameters: r0: format string, r1: parameter list |
_printf function public reguse = 0xF, 0 |
int64 r2 = r0 // format string |
int64 r3 = r1 // parameter list |
int64 sp -= 10*8 // start saving registers |
int64 [sp+0x30] = r10 // save r10. The rest are saved under printf_generic |
int64 r10 = address ([char_to_stdout]) |
jump printf_generic |
|
|
// _sprintf: print formatted string to string buffer |
// parameters: r0: destination string, r1: format string, r2: parameter list |
_sprintf function public reguse = 0xF, 0 |
int64 r3 = r2 // parameter list |
int64 r2 = r1 // format string |
int64 r1 = -1 // character count limit = UINT_MAX |
int64 sp -= 10*8 // start saving registers |
int64 [sp+0x30] = r10 // save r10. The rest are saved under printf_generic |
int64 r10 = address ([char_to_string]) |
jump printf_generic |
|
|
// _snprintf: print formatted string to string buffer with limit |
// parameters: r0: destination string, r1: length limit, r2: format string, r3: parameter list |
_snprintf function public reguse = 0xF, 0 |
int64 r1-- // character count limit. make space for terminating zero |
int64 sp -= 10*8 // start saving registers |
int64 [sp+0x30] = r10 // save r10. The rest are saved under printf_generic |
int64 r10 = address ([char_to_string]) |
// continue in printf_generic |
|
printf_generic: // common procedure for all printf variants |
// save r4 - r13 |
//int64 sp -= 10*8 // this is done above |
int64 [sp+0x00] = r4 |
int64 [sp+0x08] = r5 |
int64 [sp+0x10] = r6 |
int64 [sp+0x18] = r7 |
int64 [sp+0x20] = r8 |
int64 [sp+0x28] = r9 |
// int64 [sp+0x30] = r10 // r10 saved above |
int64 [sp+0x38] = r11 |
int64 [sp+0x40] = r12 |
int64 [sp+0x48] = r13 |
|
if (int64 r2 == 0) {jump finish} // format string pointer is null |
int r13 = 0 // reset field width |
int r11 = 0 // reset modifiers |
int r12 = 0 // state start |
|
// loop through format string |
while (true) { |
int8 r8 = [r2] // read character from format string |
if (int8+ r8 == 0) {break} // end of format string |
int64 r2++ // increment format string pointer |
|
if (int r12 == 0) { // state start |
|
if (int8+ r8 == '%') { |
int r12 = 1 // state after '%' |
} |
else { |
call (r10) // print character from format string |
} |
nop |
|
} |
else { // after '%' or modifier |
int r6 = r8 - ' ' // table index |
int64 r4 = address ([unknown_character]) |
if (uint r6 > 91) { |
call (r4) // call unknown_character |
} |
else { |
int64 r5 = address ([call_table]) |
int8 call_relative (r4, [r5 + r6*1]) // dispatch format specifier or modifier |
} |
} |
} |
finish: |
|
// if printing to string, insert terminating zero |
int64 r4 = address ([char_to_string]) |
if (uint64 r10 >= r4) { |
int r4 = 0 |
int8 [r0] = r4 |
} |
int64 r0 = r9 // return number of characters written |
|
// restore r4 - r13 |
int64 r4 = [sp+0x00] |
int64 r5 = [sp+0x08] |
int64 r6 = [sp+0x10] |
int64 r7 = [sp+0x18] |
int64 r8 = [sp+0x20] |
int64 r9 = [sp+0x28] |
int64 r10 = [sp+0x30] |
int64 r11 = [sp+0x38] |
int64 r12 = [sp+0x40] |
int64 r13 = [sp+0x48] |
int64 sp += 10*8 |
return // return from _printf, etc. |
|
|
///////////////////////////////////////////////////////////// |
// subfunctions for different characters in format string |
///////////////////////////////////////////////////////////// |
|
specif_space: // ' ': print space or minus |
if (int r12 != 1) {jump unknown_character} |
int r11 |= 8 // set modifier flag |
return |
|
specif_plus: // +: print sign |
if (int r12 != 1) {jump unknown_character} |
int r11 |= 4 // set modifier flag |
return |
|
specif_minus: // -: left justified |
if (int r12 != 1) {jump unknown_character} |
int r11 |= 1 // set modifier flag |
return |
|
specif_hash: // hash sign: print prefix 0x |
if (int r12 != 1) {jump unknown_character} |
int r11 |= 0x10 // set modifier flag |
return |
|
specif_h: // h: short |
int r4 = test_bits_or(r11, 0x40) // is there a preceding h? |
int r11 |= 0x40 // set modifier flag h |
int r11 |= 0x80, mask = r4 // set modifier flag hh |
int r12 = 3 // state after sub_specifier |
return |
|
specif_l: // l: long |
int r11 |= 0x20 // set modifier flag l |
int r12 = 3 // state after sub_specifier |
return |
|
specif_percent: // %: print percent sign |
jump unknown_character // just print % sign and reset state |
|
specif_star: // *: width specified by parameter |
if (int r12 != 1) {jump unknown_character} |
int r13 = [r3] // read width from paramter |
int64 r3 += 8 |
int r12 = 2 // state after width |
int r11 |= 0x100 // set modifier flag |
return |
|
specif_number: // 0-9 |
if (int r12 == 1) { |
if (int r8 == '0') { // leading '0' means print leading zeroes |
int r11 |= 2 // set modifier flag 2 |
return |
} |
} |
// number specifies width |
// width = previous_width * 10 + new_digit |
int r4 = r13 << 3 // multiply width by 10 |
int r13 <<= 1 |
int r13 += r4 |
int r8 -= '0' // convert from ASCII |
int r13 += r8 // new width |
int r12 = 2 // state after width |
int r11 |= 0x100 // set modifier flag |
return |
|
// subfunction for writing to stdout |
char_to_stdout: |
int8 output(r8, r8, 10) // write to stdout |
int64 r9++ // count characters written |
return |
|
// subfunction for writing to string |
char_to_string: |
int64 r9++ // count characters written or potentially written |
if (uint64 r9 > r1) { // compare with string length limit |
jump char_to_string9 // stop printing |
} |
int8 [r0] = r8 // write to string |
int64 r0++ // increment string pointer |
char_to_string9: |
return |
|
|
// %n: read number of characters printed so far |
specif_num: // n: read number of characters printed so far |
int64 r4 = [r3] // read pointer from parameter list |
int64 r3 += 8 // increment parameter list |
int32 [r4] = r9 // save number of characters written |
jump reset_state |
|
|
// %c: Print character |
specif_char: // c: print character |
int r13-- // number of leading or trailing spaces to pring |
if (int !(r11 & 1)) { // field is right justified. print leading spaces |
int r8 = ' ' // leading spaces |
for (int; r13 > 0; r13--) { |
call (r10) // print space |
} |
} |
int8 r8 = [r3] // read character from parameter list |
int64 r3 += 8 // increment parameter list |
call (r10) // print character |
int r8 = ' ' // print any trailing spaces |
for (int; r13 > 0; r13--) { |
call (r10) // print space |
} |
jump reset_state |
|
|
// %s: Print string |
specif_string: // s: string |
int64 r6 = [r3] // read string pointer from parameter list |
int64 r3 += 8 // increment parameter list |
if (int64 r6 == 0) {jump unknown_character} // null string |
int r4 = r11 ^ 1 // check if right justified |
int16+ test_bits_and(r4, 0x101), jump_false specif_string2 |
// string is right justified with specified width. check if leading spaces are needed |
for (int r5 = 0; r5 < r13; r5++) { |
int8 r8 = [r6+r5] // read string character |
if (int8+ r8 == 0) {break} // string is shorter. leading spaces needed |
} |
// print leading spaces |
int r8 = ' ' |
for (int ; r5 < r13; r5++) { |
call (r10) // print space |
} |
int r13 = 0 // avoid printing trailing spaces also |
specif_string2: |
while (true) { |
int8 r8 = [r6] // read character from string |
if (int8+ r8 == 0) {break} // end of string |
int64 r6++ // increment string pointer |
call (r10) // print character |
int r13-- // count down field width |
} |
// print any trailing space |
int r8 = ' ' |
while (int r13 > 0) { |
call (r10) // print character |
int r13-- // count down field width |
} |
jump reset_state |
|
|
// %e, %f, %g: Print floating point number |
specif_float: |
//jump specif_float1 // (intermediate jump target specif_float placed here to avoid overflow in call table) |
|
// float not implemented. print as %#LX |
int r11 |= 0x30 // print 0x prefix, 64 bits |
jump specif_hex |
|
|
// reference point, default in call table, print unknown character: |
def: // default, reference point in call table |
unknown_character: // unknown character after '%' |
call r10 // print character |
//jump reset_state |
reset_state: // reset state after '%' command |
int r11 = 0 // reset modifiers |
int r12 = 0 // state start |
int r13 = 0 // reset field width |
return |
|
|
// %x: Print hexadecimal |
specif_hex: // X: hexadecimal |
int r7 = r8 & 0x20 // is lower case |
int64 r4 = [r3] // read integer from parameter list |
int64 r3 += 8 // increment parameter list |
if (int !(r11 & 0x20)) { // check if long int |
int32 r4 = r4 // not long. truncate to 32 bits |
} |
specif_hex2: // entry from %i (decimal) if number too big |
int64 r5 = bitscan(r4, 1) // find number of bits |
uint32 r5 >>= 2 |
int32 r5++ // number of digits to print |
int r13 -= r5 // number of leading or trailing spaces to print |
int r6 = test_bit(r11, 4) // '#' option |
int r13 -= 2, mask = r6 // make space of 0x prefix |
|
if (int !(r11 & 1)) { // field is right justified. print leading spaces |
int r8 = ' ' // leading spaces |
int r6 = test_bit(r11, 1) // get bit 1: leading zeroes |
int r8 = '0', mask=r6, fallback=r8 // leading zeroes instead of leading spaces |
while (int r13 > 0) { // print r13 leading spaces or zeroes |
call r10 |
int r13-- |
} |
} |
|
if (int r11 & 0x10) { // '#' flag. Print 0x prefix |
int r8 = '0' |
call r10 |
int r8 = 'X' | r7 // 'x' or 'X' |
call r10 |
} |
|
// convert to hexadecimal |
if (int r5 > 8) { // must use 64 bits |
int r6 = 16 - r5 // number of digits to skip |
int r6 <<= 2 // number of bits to skip |
uint64 r4 <<= r6 // remove leading zero bits |
for (int ; r5 > 0; r5--) { // loop for r5 digits |
uint64 r4 = rotate(r4, 4) // get digit into low position |
int r8 = r4 & 0xF // get digit |
int r8 += '0' // convert to ASCII |
int r6 = r8 > '9' // digit is A - F |
int r8 += 7, mask = r6 // add 7 to get letter A - F |
int r8 |= r7 // lower case |
call r10 // print character |
} |
} |
else { // use 32 bits. CPU may not support 64 bits |
int r6 = 8 - r5 // number of digits to skip |
int r6 <<= 2 // number of bits to skip |
uint32 r4 <<= r6 // remove leading zero bits |
for (int ; r5 > 0; r5--) { // loop for r5 digits |
uint32 r4 = rotate(r4, 4) // get digit into low position |
int r8 = r4 & 0xF // get digit |
int r8 += '0' // convert to ASCII |
int r6 = r8 > '9' // digit is A - F |
int r8 += 7, mask = r6 // add 7 to get letter A - F |
int r8 |= r7 // lower case |
call r10 // print character |
} |
} |
// print any trailing spaces |
int r8 = ' ' // trailing spaces |
while (int r13 > 0) { // print r13 trailing spaces |
call r10 |
int r13-- |
} |
jump reset_state |
|
|
// %i, %d: Print signed decimal integer |
specif_dec: // i: signed decimal |
int64 r4 = [r3] // read integer from parameter list |
int64 r3 += 8 // increment parameter list |
if (int r11 & 0x20) { // %li: 64-bit integer |
int64 r5 = test_bit(r4, 63) // get sign bit |
int64 r4 = -r4, mask = r5 // change sign if negative |
int r11 |= 0x200, mask = r5 // set bit to remember negative |
int64 r6 = bitscan(r4, 1) // find number of bits |
if (int r6 > 31) { // cannot handle 64 bits decimal. Do hexadecimal instead |
int r11 |= 0x11 // prepare for hexadecimal escape. get 0x prefix and left justify |
int r7 = 0 // use upper case for hexadecimal escape |
if (int8+ r5 & 1) { |
int r8 = '-' // print '-' |
call r10 |
} |
jump specif_hex2 // print as hexadecimal |
} |
} |
else { |
if (int r11 & 0x80) { // %hhi: int8 |
int32 r5 = test_bit(r4, 7) // get sign bit |
int8 r4 = -r4, mask = r5 // change sign if negative. truncate to 8 bits |
int r11 |= 0x200, mask = r5 // set bit to remember negative |
} |
else { |
if (int r11 & 0x40) { // %hhi: int16 |
int32 r5 = test_bit(r4, 15) // get sign bit |
int16 r4 = -r4, mask = r5 // change sign if negative. truncate to 16 bits |
int r11 |= 0x200, mask = r5 // set bit to remember negative |
} |
else { |
// %i: int32 |
int32 r5 = test_bit(r4, 31) // get sign bit |
int32 r4 = -r4, mask = r5 // change sign if negative. truncate to 16 bits |
int r11 |= 0x200, mask = r5 // set bit to remember negative |
} |
} |
} |
jump specif_uns2 // sign has been stored in r11. continue in unsigned |
|
|
// %u: Print unsigned decimal integer |
specif_uns: // u: unsigned decimal |
int64 r4 = [r3] // read integer from parameter list |
int64 r3 += 8 // increment parameter list |
if (int r11 & 0x20) { // %li: 64-bit integer |
int64 r6 = bitscan(r4, 1) // find number of bits |
int r8 |= 0x10 // prepare for hexadecimal escape. get 0x prefix |
int r7 = 0 // use upper case for hexadecimal escape |
if (int r6 > 31) {jump specif_hex2}// cannot handle 64 bits decimal. Do hexadecimal instead |
} |
|
specif_uns2: |
int64 sp -= 8 // save r14 |
int64 [sp] = r14 |
|
// 32 bit decimal signed or unsigned |
// First two BCD digits are made with simple subtraction to avoid the need for a larger bit field |
int r5 = 0 // upper two BCD digits |
|
while (uint32 r4 >= 1000000000) { |
nop |
uint32 r4 -= 1000000000 |
uint32 r5 += 0x10 |
nop |
} |
while (uint32 r4 >= 100000000) { |
uint32 r4 -= 100000000 |
uint32 r5 += 0x01 |
} |
|
// Generate 8 BCD digits using double dabble algorithm |
int32 r14 = 0 // generate BCD in r14 |
for (int r6 = 0; r6 < 32; r6++) { // loop for 32 bits |
int32 r7 = r14 + 0x33333333 // digit values 5-9 will set bit 3 in each 4-bit nibble |
int32 r7 &= 0x88888888 // isolate bit 3 in each nibble |
int32 r8 = r7 >> 3 // generate value 3 in nibbles with value 5-9 |
int32 r7 >>= 2 |
int32 r7 |= r8 // this will have 3 for each nibble with a value 5-9 |
int32 r14 += r7 // add 3 to nibble values 5-9 to generate 8-12 |
int32 r14 = funnel_shift(r4, r14, 31) // shift most significant bit of r4 into r14 |
int32 r4 <<= 1 |
} |
// r5:r14 = BCD value |
|
// determine width |
int r6 = bitscan(r14, 1) // number of significant bits |
uint r6 >>= 2 // number of significant digits in low part |
int r6++ // number of digits in low part |
if (int r5 != 0) { // high part is nonzero |
int r6 = bitscan(r5, 1) // find number of digits in high part |
uint r6 >>= 2 |
int r6 += 9 // number of digits total |
} |
int r7 = test_bits_or(r11, 0x20C) // leading sign needed |
int r6 += r7 // number of characters needed |
int r13 -= r6 // number of leading or trailing spaces needed |
|
if (int !(r11 & 1)) { // right justified |
int r6 = r13 > 0 && r7 // leading sign && leading spaces |
int r4 = test_bit(r11, 1), options=1, fallback=r6 // leading zeroes and leading sign. sign must come first |
int r8 = ' ' // leading sign to print |
int r6 = test_bit(r11, 2) // flag for '+' prefix |
int r8 = '+', mask=r6, fallback=r8 // leading + required if not negative |
int r6 = test_bit(r11, 9) // flag for negative |
int r8 = '-', mask=r6, fallback=r8 // leading -. value is negative |
if (int r4 & 1) { // sign must come before leading zeroes |
call r10 // print leading sign |
int r7 = 0 // remember leading sign has been written |
} |
// print leading spaces or zeroes |
int r8 = ' ' // space |
int r6 = test_bit(r11, 1) // flag for leading zeroes |
int r8 = '0', mask=r6, fallback=r8 |
while (int r13 > 0) { // write leading spaces or zeroes |
call r10 |
int r13-- |
} |
} |
if (int r7 & 1) { // sign after leading spaces |
int r8 = ' ' // space |
int r6 = test_bit(r11, 2) // flag for '+' prefix |
int r8 = '+', mask=r6, fallback=r8 // leading + required if not negative |
int r6 = test_bit(r11, 9) // flag for negative |
int r8 = '-', mask=r6, fallback=r8 // leading -. value is negative |
call r10 // write sign |
} |
|
int r4 = 0 // remember if first digit has been printed |
|
// print high two digits |
uint32 r5 <<= 24 // left justify high part |
for (int r6 = 2; r6 > 0; r6--) { // print two high digits if any |
int32 r5 = rotate(r5, 4) // get most significant digit first |
int8 r8 = r5 & 0x0F |
int r4 = (r8 != 0) || r4 // digit has been printed |
int8 r8 += '0' // convert to ASCII |
if (int r4 != 0) { |
call r10 // print character to stdout |
} |
} |
|
// print low 8 decimal digits |
for (int r6 = 8; r6 > 0; r6--) { |
int32 r14 = rotate(r14, 4) // get most significant digit first |
int8 r8 = r14 & 0x0F |
int r4 = (r8 != 0) || r4 // digit has been printed |
int r4 = (r6 == 1) || r4 // last digit must be printed |
int8 r8 += '0' // convert to ASCII |
if (int r4 != 0) { |
call r10 // print character to stdout |
} |
} |
|
// print trailing spaces |
int r8 = ' ' // space |
while (int r13 > 0) { // write trailing spaces |
call r10 |
int r13-- |
} |
|
int64 r14 = [sp] // restore r14 |
int64 sp += 8 |
|
jump reset_state // finished |
|
|
// %e, %f, %g: Print floating point number. not implemented |
specif_float1: |
nop |
jump reset_state // finished |
|
code end |