URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
[/] [forwardcom/] [libraries/] [printf_light.as] - Rev 136
Go to most recent revision | Compare with Previous | Blame | View Log
/**************************** 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 specifierscall_table: int8 (specif_space-def)/4 // ' ': print space or minusint8 0 // !int8 0 // "int8 (specif_hash-def)/4 // #: prepend 0xint8 0 // $int8 (specif_percent-def)/4 // %: print percent signint8 0 // &int8 0 // 'int8 0 // (int8 0 // )int8 (specif_star-def)/4 // *: width specified by parameterint8 (specif_plus-def)/4 // +: print signint8 0 // ,int8 (specif_minus-def)/4 // -: left justifiedint8 0 // .int8 0 // /int8 (specif_number-def)/4 // 0int8 (specif_number-def)/4 // 1int8 (specif_number-def)/4 // 2int8 (specif_number-def)/4 // 3int8 (specif_number-def)/4 // 4int8 (specif_number-def)/4 // 5int8 (specif_number-def)/4 // 6int8 (specif_number-def)/4 // 7int8 (specif_number-def)/4 // 8int8 (specif_number-def)/4 // 9int8 0 // :int8 0 // ;int8 0 // <int8 0 // =int8 0 // >int8 0 // ?int8 0 // @int8 (specif_hex-def)/4 // A: hexadecimal floatint8 0 // Bint8 0 // Cint8 0 // Dint8 (specif_float-def)/4 // E: floatint8 (specif_float-def)/4 // F: floatint8 (specif_float-def)/4 // G: floatint8 0 // Hint8 0 // Iint8 0 // Jint8 0 // Kint8 (specif_l-def)/4 // L: long (long double)int8 0 // Mint8 0 // Nint8 0 // Oint8 0 // Pint8 0 // Qint8 0 // Rint8 0 // Sint8 0 // Tint8 0 // Uint8 0 // Vint8 0 // Wint8 (specif_hex-def)/4 // X: hexadecimalint8 0 // Yint8 0 // Zint8 0 // [int8 0 // \int8 0 // ]int8 0 // ^int8 0 // _int8 0 // `int8 (specif_hex-def)/4 // a: hexadecimal floatint8 0 // bint8 (specif_char-def)/4 // c: characterint8 (specif_dec-def)/4 // d: signed decimalint8 (specif_float-def)/4 // e: floatint8 (specif_float-def)/4 // f: floatint8 (specif_float-def)/4 // g: floatint8 (specif_h-def)/4 // h: shortint8 (specif_dec-def)/4 // i: signed decimalint8 (specif_l-def)/4 // j: intmax_tint8 0 // kint8 (specif_l-def)/4 // l: longint8 0 // mint8 (specif_num-def)/4 // n: read number of characters printed so farint8 0 // oint8 (specif_hex-def)/4 // p: pointer, print as hexadecimalint8 0 // qint8 0 // rint8 (specif_string-def)/4 // s: stringint8 (specif_l-def)/4 // t: ptrdiff_tint8 (specif_uns-def)/4 // u: unsigned decimalint8 0 // vint8 0 // wint8 (specif_hex-def)/4 // x: hexadecimalint8 0 // yint8 (specif_l-def)/4 // z: size_tconst endcode section execute align = 4 // code section// _printf: print formatted string to stdout// parameters: r0: format string, r1: parameter list_printf function public reguse = 0xF, 0int64 r2 = r0 // format stringint64 r3 = r1 // parameter listint64 sp -= 10*8 // start saving registersint64 [sp+0x30] = r10 // save r10. The rest are saved under printf_genericint64 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, 0int64 r3 = r2 // parameter listint64 r2 = r1 // format stringint64 r1 = -1 // character count limit = UINT_MAXint64 sp -= 10*8 // start saving registersint64 [sp+0x30] = r10 // save r10. The rest are saved under printf_genericint64 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, 0int64 r1-- // character count limit. make space for terminating zeroint64 sp -= 10*8 // start saving registersint64 [sp+0x30] = r10 // save r10. The rest are saved under printf_genericint64 r10 = address ([char_to_string])// continue in printf_genericprintf_generic: // common procedure for all printf variants// save r4 - r13//int64 sp -= 10*8 // this is done aboveint64 [sp+0x00] = r4int64 [sp+0x08] = r5int64 [sp+0x10] = r6int64 [sp+0x18] = r7int64 [sp+0x20] = r8int64 [sp+0x28] = r9// int64 [sp+0x30] = r10 // r10 saved aboveint64 [sp+0x38] = r11int64 [sp+0x40] = r12int64 [sp+0x48] = r13if (int64 r2 == 0) {jump finish} // format string pointer is nullint r13 = 0 // reset field widthint r11 = 0 // reset modifiersint r12 = 0 // state start// loop through format stringwhile (true) {int8 r8 = [r2] // read character from format stringif (int8+ r8 == 0) {break} // end of format stringint64 r2++ // increment format string pointerif (int r12 == 0) { // state startif (int8+ r8 == '%') {int r12 = 1 // state after '%'}else {call (r10) // print character from format string}nop}else { // after '%' or modifierint r6 = r8 - ' ' // table indexint64 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 zeroint64 r4 = address ([char_to_string])if (uint64 r10 >= r4) {int r4 = 0int8 [r0] = r4}int64 r0 = r9 // return number of characters written// restore r4 - r13int64 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*8return // return from _printf, etc./////////////////////////////////////////////////////////////// subfunctions for different characters in format string/////////////////////////////////////////////////////////////specif_space: // ' ': print space or minusif (int r12 != 1) {jump unknown_character}int r11 |= 8 // set modifier flagreturnspecif_plus: // +: print signif (int r12 != 1) {jump unknown_character}int r11 |= 4 // set modifier flagreturnspecif_minus: // -: left justifiedif (int r12 != 1) {jump unknown_character}int r11 |= 1 // set modifier flagreturnspecif_hash: // hash sign: print prefix 0xif (int r12 != 1) {jump unknown_character}int r11 |= 0x10 // set modifier flagreturnspecif_h: // h: shortint r4 = test_bits_or(r11, 0x40) // is there a preceding h?int r11 |= 0x40 // set modifier flag hint r11 |= 0x80, mask = r4 // set modifier flag hhint r12 = 3 // state after sub_specifierreturnspecif_l: // l: longint r11 |= 0x20 // set modifier flag lint r12 = 3 // state after sub_specifierreturnspecif_percent: // %: print percent signjump unknown_character // just print % sign and reset statespecif_star: // *: width specified by parameterif (int r12 != 1) {jump unknown_character}int r13 = [r3] // read width from paramterint64 r3 += 8int r12 = 2 // state after widthint r11 |= 0x100 // set modifier flagreturnspecif_number: // 0-9if (int r12 == 1) {if (int r8 == '0') { // leading '0' means print leading zeroesint r11 |= 2 // set modifier flag 2return}}// number specifies width// width = previous_width * 10 + new_digitint r4 = r13 << 3 // multiply width by 10int r13 <<= 1int r13 += r4int r8 -= '0' // convert from ASCIIint r13 += r8 // new widthint r12 = 2 // state after widthint r11 |= 0x100 // set modifier flagreturn// subfunction for writing to stdoutchar_to_stdout:int8 output(r8, r8, 10) // write to stdoutint64 r9++ // count characters writtenreturn// subfunction for writing to stringchar_to_string:int64 r9++ // count characters written or potentially writtenif (uint64 r9 > r1) { // compare with string length limitjump char_to_string9 // stop printing}int8 [r0] = r8 // write to stringint64 r0++ // increment string pointerchar_to_string9:return// %n: read number of characters printed so farspecif_num: // n: read number of characters printed so farint64 r4 = [r3] // read pointer from parameter listint64 r3 += 8 // increment parameter listint32 [r4] = r9 // save number of characters writtenjump reset_state// %c: Print characterspecif_char: // c: print characterint r13-- // number of leading or trailing spaces to pringif (int !(r11 & 1)) { // field is right justified. print leading spacesint r8 = ' ' // leading spacesfor (int; r13 > 0; r13--) {call (r10) // print space}}int8 r8 = [r3] // read character from parameter listint64 r3 += 8 // increment parameter listcall (r10) // print characterint r8 = ' ' // print any trailing spacesfor (int; r13 > 0; r13--) {call (r10) // print space}jump reset_state// %s: Print stringspecif_string: // s: stringint64 r6 = [r3] // read string pointer from parameter listint64 r3 += 8 // increment parameter listif (int64 r6 == 0) {jump unknown_character} // null stringint r4 = r11 ^ 1 // check if right justifiedint16+ test_bits_and(r4, 0x101), jump_false specif_string2// string is right justified with specified width. check if leading spaces are neededfor (int r5 = 0; r5 < r13; r5++) {int8 r8 = [r6+r5] // read string characterif (int8+ r8 == 0) {break} // string is shorter. leading spaces needed}// print leading spacesint r8 = ' 'for (int ; r5 < r13; r5++) {call (r10) // print space}int r13 = 0 // avoid printing trailing spaces alsospecif_string2:while (true) {int8 r8 = [r6] // read character from stringif (int8+ r8 == 0) {break} // end of stringint64 r6++ // increment string pointercall (r10) // print characterint r13-- // count down field width}// print any trailing spaceint r8 = ' 'while (int r13 > 0) {call (r10) // print characterint r13-- // count down field width}jump reset_state// %e, %f, %g: Print floating point numberspecif_float://jump specif_float1 // (intermediate jump target specif_float placed here to avoid overflow in call table)// float not implemented. print as %#LXint r11 |= 0x30 // print 0x prefix, 64 bitsjump specif_hex// reference point, default in call table, print unknown character:def: // default, reference point in call tableunknown_character: // unknown character after '%'call r10 // print character//jump reset_statereset_state: // reset state after '%' commandint r11 = 0 // reset modifiersint r12 = 0 // state startint r13 = 0 // reset field widthreturn// %x: Print hexadecimalspecif_hex: // X: hexadecimalint r7 = r8 & 0x20 // is lower caseint64 r4 = [r3] // read integer from parameter listint64 r3 += 8 // increment parameter listif (int !(r11 & 0x20)) { // check if long intint32 r4 = r4 // not long. truncate to 32 bits}specif_hex2: // entry from %i (decimal) if number too bigint64 r5 = bitscan(r4, 1) // find number of bitsuint32 r5 >>= 2int32 r5++ // number of digits to printint r13 -= r5 // number of leading or trailing spaces to printint r6 = test_bit(r11, 4) // '#' optionint r13 -= 2, mask = r6 // make space of 0x prefixif (int !(r11 & 1)) { // field is right justified. print leading spacesint r8 = ' ' // leading spacesint r6 = test_bit(r11, 1) // get bit 1: leading zeroesint r8 = '0', mask=r6, fallback=r8 // leading zeroes instead of leading spaceswhile (int r13 > 0) { // print r13 leading spaces or zeroescall r10int r13--}}if (int r11 & 0x10) { // '#' flag. Print 0x prefixint r8 = '0'call r10int r8 = 'X' | r7 // 'x' or 'X'call r10}// convert to hexadecimalif (int r5 > 8) { // must use 64 bitsint r6 = 16 - r5 // number of digits to skipint r6 <<= 2 // number of bits to skipuint64 r4 <<= r6 // remove leading zero bitsfor (int ; r5 > 0; r5--) { // loop for r5 digitsuint64 r4 = rotate(r4, 4) // get digit into low positionint r8 = r4 & 0xF // get digitint r8 += '0' // convert to ASCIIint r6 = r8 > '9' // digit is A - Fint r8 += 7, mask = r6 // add 7 to get letter A - Fint r8 |= r7 // lower casecall r10 // print character}}else { // use 32 bits. CPU may not support 64 bitsint r6 = 8 - r5 // number of digits to skipint r6 <<= 2 // number of bits to skipuint32 r4 <<= r6 // remove leading zero bitsfor (int ; r5 > 0; r5--) { // loop for r5 digitsuint32 r4 = rotate(r4, 4) // get digit into low positionint r8 = r4 & 0xF // get digitint r8 += '0' // convert to ASCIIint r6 = r8 > '9' // digit is A - Fint r8 += 7, mask = r6 // add 7 to get letter A - Fint r8 |= r7 // lower casecall r10 // print character}}// print any trailing spacesint r8 = ' ' // trailing spaceswhile (int r13 > 0) { // print r13 trailing spacescall r10int r13--}jump reset_state// %i, %d: Print signed decimal integerspecif_dec: // i: signed decimalint64 r4 = [r3] // read integer from parameter listint64 r3 += 8 // increment parameter listif (int r11 & 0x20) { // %li: 64-bit integerint64 r5 = test_bit(r4, 63) // get sign bitint64 r4 = -r4, mask = r5 // change sign if negativeint r11 |= 0x200, mask = r5 // set bit to remember negativeint64 r6 = bitscan(r4, 1) // find number of bitsif (int r6 > 31) { // cannot handle 64 bits decimal. Do hexadecimal insteadint r11 |= 0x11 // prepare for hexadecimal escape. get 0x prefix and left justifyint r7 = 0 // use upper case for hexadecimal escapeif (int8+ r5 & 1) {int r8 = '-' // print '-'call r10}jump specif_hex2 // print as hexadecimal}}else {if (int r11 & 0x80) { // %hhi: int8int32 r5 = test_bit(r4, 7) // get sign bitint8 r4 = -r4, mask = r5 // change sign if negative. truncate to 8 bitsint r11 |= 0x200, mask = r5 // set bit to remember negative}else {if (int r11 & 0x40) { // %hhi: int16int32 r5 = test_bit(r4, 15) // get sign bitint16 r4 = -r4, mask = r5 // change sign if negative. truncate to 16 bitsint r11 |= 0x200, mask = r5 // set bit to remember negative}else {// %i: int32int32 r5 = test_bit(r4, 31) // get sign bitint32 r4 = -r4, mask = r5 // change sign if negative. truncate to 16 bitsint 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 integerspecif_uns: // u: unsigned decimalint64 r4 = [r3] // read integer from parameter listint64 r3 += 8 // increment parameter listif (int r11 & 0x20) { // %li: 64-bit integerint64 r6 = bitscan(r4, 1) // find number of bitsint r8 |= 0x10 // prepare for hexadecimal escape. get 0x prefixint r7 = 0 // use upper case for hexadecimal escapeif (int r6 > 31) {jump specif_hex2}// cannot handle 64 bits decimal. Do hexadecimal instead}specif_uns2:int64 sp -= 8 // save r14int64 [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 fieldint r5 = 0 // upper two BCD digitswhile (uint32 r4 >= 1000000000) {nopuint32 r4 -= 1000000000uint32 r5 += 0x10nop}while (uint32 r4 >= 100000000) {uint32 r4 -= 100000000uint32 r5 += 0x01}// Generate 8 BCD digits using double dabble algorithmint32 r14 = 0 // generate BCD in r14for (int r6 = 0; r6 < 32; r6++) { // loop for 32 bitsint32 r7 = r14 + 0x33333333 // digit values 5-9 will set bit 3 in each 4-bit nibbleint32 r7 &= 0x88888888 // isolate bit 3 in each nibbleint32 r8 = r7 >> 3 // generate value 3 in nibbles with value 5-9int32 r7 >>= 2int32 r7 |= r8 // this will have 3 for each nibble with a value 5-9int32 r14 += r7 // add 3 to nibble values 5-9 to generate 8-12int32 r14 = funnel_shift(r4, r14, 31) // shift most significant bit of r4 into r14int32 r4 <<= 1}// r5:r14 = BCD value// determine widthint r6 = bitscan(r14, 1) // number of significant bitsuint r6 >>= 2 // number of significant digits in low partint r6++ // number of digits in low partif (int r5 != 0) { // high part is nonzeroint r6 = bitscan(r5, 1) // find number of digits in high partuint r6 >>= 2int r6 += 9 // number of digits total}int r7 = test_bits_or(r11, 0x20C) // leading sign neededint r6 += r7 // number of characters neededint r13 -= r6 // number of leading or trailing spaces neededif (int !(r11 & 1)) { // right justifiedint r6 = r13 > 0 && r7 // leading sign && leading spacesint r4 = test_bit(r11, 1), options=1, fallback=r6 // leading zeroes and leading sign. sign must come firstint r8 = ' ' // leading sign to printint r6 = test_bit(r11, 2) // flag for '+' prefixint r8 = '+', mask=r6, fallback=r8 // leading + required if not negativeint r6 = test_bit(r11, 9) // flag for negativeint r8 = '-', mask=r6, fallback=r8 // leading -. value is negativeif (int r4 & 1) { // sign must come before leading zeroescall r10 // print leading signint r7 = 0 // remember leading sign has been written}// print leading spaces or zeroesint r8 = ' ' // spaceint r6 = test_bit(r11, 1) // flag for leading zeroesint r8 = '0', mask=r6, fallback=r8while (int r13 > 0) { // write leading spaces or zeroescall r10int r13--}}if (int r7 & 1) { // sign after leading spacesint r8 = ' ' // spaceint r6 = test_bit(r11, 2) // flag for '+' prefixint r8 = '+', mask=r6, fallback=r8 // leading + required if not negativeint r6 = test_bit(r11, 9) // flag for negativeint r8 = '-', mask=r6, fallback=r8 // leading -. value is negativecall r10 // write sign}int r4 = 0 // remember if first digit has been printed// print high two digitsuint32 r5 <<= 24 // left justify high partfor (int r6 = 2; r6 > 0; r6--) { // print two high digits if anyint32 r5 = rotate(r5, 4) // get most significant digit firstint8 r8 = r5 & 0x0Fint r4 = (r8 != 0) || r4 // digit has been printedint8 r8 += '0' // convert to ASCIIif (int r4 != 0) {call r10 // print character to stdout}}// print low 8 decimal digitsfor (int r6 = 8; r6 > 0; r6--) {int32 r14 = rotate(r14, 4) // get most significant digit firstint8 r8 = r14 & 0x0Fint r4 = (r8 != 0) || r4 // digit has been printedint r4 = (r6 == 1) || r4 // last digit must be printedint8 r8 += '0' // convert to ASCIIif (int r4 != 0) {call r10 // print character to stdout}}// print trailing spacesint r8 = ' ' // spacewhile (int r13 > 0) { // write trailing spacescall r10int r13--}int64 r14 = [sp] // restore r14int64 sp += 8jump reset_state // finished// %e, %f, %g: Print floating point number. not implementedspecif_float1:nopjump reset_state // finishedcode end
Go to most recent revision | Compare with Previous | Blame | View Log
