diff --git a/litex/soc/software/bios/commands/cmd_sdcard.c b/litex/soc/software/bios/commands/cmd_sdcard.c index 96f9fe64c..d9545e07a 100644 --- a/litex/soc/software/bios/commands/cmd_sdcard.c +++ b/litex/soc/software/bios/commands/cmd_sdcard.c @@ -52,34 +52,92 @@ define_command(sdclk, sdclk, "SDCard set clk frequency (Mhz)", SD_CMDS); * */ #ifdef CSR_SDCORE_BASE -define_command(sdinit, sdinit, "SDCard initialization", SD_CMDS); +define_command(sdinit, sdcard_init, "SDCard initialization", SD_CMDS); #endif /** * Command "sdtest" * - * Perform SDcard access tests + * Perform SDcard read/write tests * */ #ifdef CSR_SDCORE_BASE static void sdtest(int nb_params, char **params) { - unsigned int loops; + unsigned int blocks; char *c; if (nb_params < 1) { - printf("sdtest "); + printf("sdtest "); return; } - loops = strtoul(params[0], &c, 0); + blocks = strtoul(params[0], &c, 0); if (*c != 0) { - printf("Incorrect number of loops"); + printf("Incorrect number of blocks to write"); return; } - sdcard_test(loops); + sdcard_test(blocks); } define_command(sdtest, sdtest, "SDCard test", SD_CMDS); #endif + +/** + * Command "sdtestread" + * + * Perform SDcard read test + * + */ +#ifdef CSR_SDCORE_BASE +static void sdtestread(int nb_params, char **params) +{ + unsigned int block; + char *c; + + if (nb_params < 1) { + printf("sdtestread "); + return; + } + + block = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect number of block to read"); + return; + } + + sdcard_test_read(block); +} + +define_command(sdtestread, sdtestread, "SDCard test read", SD_CMDS); +#endif + +/** + * Command "sdtestwrite" + * + * Perform SDcard write test + * + */ +#ifdef CSR_SDCORE_BASE +static void sdtestwrite(int nb_params, char **params) +{ + unsigned int block; + char *c; + + if (nb_params < 2) { + printf("sdtestread "); + return; + } + + block = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect number of block to write"); + return; + } + + sdcard_test_write(block, params[1]); +} + +define_command(sdtestwrite, sdtestwrite, "SDCard test write", SD_CMDS); +#endif diff --git a/litex/soc/software/bios/sdcard.c b/litex/soc/software/bios/sdcard.c index 314b9e28b..274f3cfac 100644 --- a/litex/soc/software/bios/sdcard.c +++ b/litex/soc/software/bios/sdcard.c @@ -1,6 +1,7 @@ // This file is Copyright (c) 2017 Florent Kermarrec // This file is Copyright (c) 2019 Kees Jongenburger // This file is Copyright (c) 2018 bunnie +// This file is Copyright (c) 2020 Antmicro // License: BSD #include @@ -18,10 +19,30 @@ #define SDCARD_DEBUG -/* clocking */ +#define CHECK_LOOPS_PRINT_THRESHOLD 1000000 + +#define REPEATED_MSG(cnt, thr, fmt, ...) do { \ + const int _cnt = (cnt); \ + if((_cnt) >= (thr)) { \ + if((_cnt) > (thr)) { \ + printf("\033[1A\033[1G"); \ + } \ + printf(fmt "\033[0m\033[K\n", ## __VA_ARGS__); \ + } \ + } while(0); + +#define BLOCK_SIZE 512 +#define NO_RESPONSE 0xFF + +#define SDCARD_RESPONSE_SIZE 5 +unsigned int sdcard_response[SDCARD_RESPONSE_SIZE]; + +volatile char *SDREAD = (char*)(SDREAD_BASE); +volatile char *SDWRITE = (char*)(SDWRITE_BASE); #ifdef CSR_SDCLK_CMD_DATA_ADDR +/* clocking */ static void sdclk_dcm_write(int cmd, int data) { int word; @@ -70,13 +91,13 @@ void sdclk_set_clk(unsigned int freq) { while(!(sdclk_status_read() & CLKGEN_STATUS_LOCKED)); } -#elif CSR_SDCLK_MMCM_DRP_WRITE_ADDR +#elif CSR_MMCM_DRP_WRITE_ADDR static void sdclk_mmcm_write(unsigned int adr, unsigned int data) { - sdclk_mmcm_drp_adr_write(adr); - sdclk_mmcm_drp_dat_w_write(data); - sdclk_mmcm_drp_write_write(1); - while(!sdclk_mmcm_drp_drdy_read()); + mmcm_drp_adr_write(adr); + mmcm_drp_dat_w_write(data); + mmcm_drp_write_write(1); + while(!mmcm_drp_drdy_read()); } @@ -157,40 +178,41 @@ static unsigned int sdtimer_get(void) return sdtimer_value_read(); } -unsigned int sdcard_response[4]; int sdcard_wait_cmd_done(void) { + unsigned check_counter = 0; unsigned int cmdevt; while (1) { cmdevt = sdcore_cmdevt_read(); -#ifdef SDCARD_DEBUG - printf("cmdevt: %08x\n", cmdevt); -#endif + REPEATED_MSG(++check_counter, CHECK_LOOPS_PRINT_THRESHOLD, + "\033[36m cmdevt: %08x (check #%d)", + cmdevt, check_counter); + if(check_counter > CHECK_LOOPS_PRINT_THRESHOLD) { + putchar('\n'); + return NO_RESPONSE; //If we reach threshold, and cmdevt didn't return valid status, return NO_RESPONSE + } if (cmdevt & 0x1) { - if (cmdevt & 0x4) { -#ifdef SDCARD_DEBUG - printf("cmdevt: SD_TIMEOUT\n"); -#endif + if (cmdevt & 0x4) return SD_TIMEOUT; - } - else if (cmdevt & 0x8) { -#ifdef SDCARD_DEBUG - printf("cmdevt: SD_CRCERROR\n"); + else if (cmdevt & 0x8) return SD_CRCERROR; -#endif - } return SD_OK; } } } int sdcard_wait_data_done(void) { + unsigned check_counter = 0; unsigned int dataevt; while (1) { dataevt = sdcore_dataevt_read(); -#ifdef SDCARD_DEBUG - printf("dataevt: %08x\n", dataevt); -#endif + REPEATED_MSG(++check_counter, CHECK_LOOPS_PRINT_THRESHOLD, + "\033[36m dataevt: %08x (check #%d)", + dataevt, check_counter); + if(check_counter > CHECK_LOOPS_PRINT_THRESHOLD) { + putchar('\n'); + return NO_RESPONSE; //If we reach threshold, and cmdevt didn't return valid status, return NO_RESPONSE + } if (dataevt & 0x1) { if (dataevt & 0x4) return SD_TIMEOUT; @@ -202,19 +224,68 @@ int sdcard_wait_data_done(void) { } int sdcard_wait_response(void) { - int status = sdcard_wait_cmd_done(); + int i, j; + int status; + + status = sdcard_wait_cmd_done(); + +#if CONFIG_CSR_DATA_WIDTH == 8 + unsigned int r; + + // LSB is located at RESPONSE_ADDR + (RESPONSE_SIZE - 1) * 4 + int offset; + for(i = 0; i < SDCARD_RESPONSE_SIZE; i++) { + r = 0; + for(j = 0; j < 4; j++) { + // SD card response consists of 17 bytes + // scattered accross 5 32-bit CSRs. + // In a configuration with CONFIG_CSR_DATA_WIDTH == 8 + // this means we need to do 17 32-bit reads + // and group bytes as described below: + // sdcard_response | CSR_SDCORE_RESPONSE_ADDR + // offset | offsets + // ------------------------------------------ + // 0 | [ 0 ] + // 1 | [ 4 8 12 16 ] + // 2 | [ 20 23 28 32 ] + // 3 | [ 36 40 44 48 ] + // 4 | [ 52 56 60 64 ] + // ------------------------------------------ + // | ^ | + // +--- u32 --|--+ + // LS byte + offset = 4 * ((CSR_SDCORE_RESPONSE_SIZE - 1) - j - i * 4); + if(offset >= 0) { + // Read response and move it by 'j' bytes + r |= ((csr_read_simple(CSR_SDCORE_RESPONSE_ADDR + offset) & 0xFF) << (j * 8)); + } + } + sdcard_response[(SDCARD_RESPONSE_SIZE - 1) - i] = r; // NOTE: this is "backwards" but sticking with this because it's compatible with CSR32 + } +#else + volatile unsigned int *buffer = (unsigned int *)CSR_SDCORE_RESPONSE_ADDR; + + for(i = 0; i < SDCARD_RESPONSE_SIZE; i++) { + sdcard_response[i] = buffer[i]; + } +#endif - csr_rd_buf_uint32(CSR_SDCORE_RESPONSE_ADDR, sdcard_response, 4); #ifdef SDCARD_DEBUG - printf("sdcard_response = [%08x, %08x, %08x, %08x];\n", - sdcard_response[0], sdcard_response[1], - sdcard_response[2], sdcard_response[3]); + for(i = 0; i < SDCARD_RESPONSE_SIZE; i++) { + printf("%08x ", sdcard_response[i]); + } + printf("\n"); #endif return status; } /* commands */ +#if CONFIG_CSR_DATA_WIDTH == 8 +#define CSR8_CMD_FIX sdcore_issue_cmd_write(1) +#else +#define CSR8_CMD_FIX +#endif void sdcard_go_idle(void) { #ifdef SDCARD_DEBUG @@ -222,15 +293,18 @@ void sdcard_go_idle(void) { #endif sdcore_argument_write(0x00000000); sdcore_command_write((0 << 8) | SDCARD_CTRL_RESPONSE_NONE); + CSR8_CMD_FIX; } int sdcard_send_ext_csd(void) { + unsigned int arg; + arg = 0x000001aa; #ifdef SDCARD_DEBUG - printf("CMD8: SEND_EXT_CSD\n"); + printf("CMD8: SEND_EXT_CSD, arg: 0x%08x\n", arg); #endif - sdcore_argument_write(0x000001aa); - sdcore_command_write((8 << 8) | SDCARD_CTRL_RESPONSE_NONE); - busy_wait(1); + sdcore_argument_write(arg); + sdcore_command_write((8 << 8) | SDCARD_CTRL_RESPONSE_SHORT); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -240,23 +314,23 @@ int sdcard_app_cmd(int rca) { #endif sdcore_argument_write(rca << 16); sdcore_command_write((55 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } int sdcard_app_send_op_cond(int hcs, int s18r) { unsigned int arg; -#ifdef SDCARD_DEBUG - printf("ACMD41: APP_SEND_OP_COND\n"); -#endif arg = 0x10ff8000; if (hcs) arg |= 0x60000000; if (s18r) arg |= 0x01000000; +#ifdef SDCARD_DEBUG + printf("ACMD41: APP_SEND_OP_COND, arg: %08x\n", arg); +#endif sdcore_argument_write(arg); sdcore_command_write((41 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -266,7 +340,7 @@ int sdcard_all_send_cid(void) { #endif sdcore_argument_write(0x00000000); sdcore_command_write((2 << 8) | SDCARD_CTRL_RESPONSE_LONG); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -276,7 +350,7 @@ int sdcard_set_relative_address(void) { #endif sdcore_argument_write(0x00000000); sdcore_command_write((3 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -286,7 +360,7 @@ int sdcard_send_cid(unsigned int rca) { #endif sdcore_argument_write(rca << 16); sdcore_command_write((10 << 8) | SDCARD_CTRL_RESPONSE_LONG); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -296,7 +370,7 @@ int sdcard_send_csd(unsigned int rca) { #endif sdcore_argument_write(rca << 16); sdcore_command_write((9 << 8) | SDCARD_CTRL_RESPONSE_LONG); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -306,7 +380,7 @@ int sdcard_select_card(unsigned int rca) { #endif sdcore_argument_write(rca << 16); sdcore_command_write((7 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -316,7 +390,7 @@ int sdcard_app_set_bus_width(void) { #endif sdcore_argument_write(0x00000002); sdcore_command_write((6 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -335,9 +409,8 @@ int sdcard_switch(unsigned int mode, unsigned int group, unsigned int value, uns sdcore_blockcount_write(1); sdcore_command_write((6 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_READ << 5)); - busy_wait(1); + CSR8_CMD_FIX; sdcard_wait_response(); - busy_wait(1); return sdcard_wait_data_done(); } @@ -350,9 +423,8 @@ int sdcard_app_send_scr(void) { sdcore_blockcount_write(1); sdcore_command_write((51 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_READ << 5)); - busy_wait(1); + CSR8_CMD_FIX; sdcard_wait_response(); - busy_wait(1); return sdcard_wait_data_done(); } @@ -363,7 +435,7 @@ int sdcard_app_set_blocklen(unsigned int blocklen) { #endif sdcore_argument_write(blocklen); sdcore_command_write((16 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -374,10 +446,11 @@ int sdcard_write_single_block(unsigned int blockaddr) { int cmd_response = -1; while (cmd_response != SD_OK) { sdcore_argument_write(blockaddr); - sdcore_blocksize_write(512); + sdcore_blocksize_write(BLOCK_SIZE); sdcore_blockcount_write(1); sdcore_command_write((24 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_WRITE << 5)); + CSR8_CMD_FIX; cmd_response = sdcard_wait_response(); } return cmd_response; @@ -390,10 +463,11 @@ int sdcard_write_multiple_block(unsigned int blockaddr, unsigned int blockcnt) { int cmd_response = -1; while (cmd_response != SD_OK) { sdcore_argument_write(blockaddr); - sdcore_blocksize_write(512); + sdcore_blocksize_write(BLOCK_SIZE); sdcore_blockcount_write(blockcnt); sdcore_command_write((25 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_WRITE << 5)); + CSR8_CMD_FIX; cmd_response = sdcard_wait_response(); } return cmd_response; @@ -406,10 +480,11 @@ int sdcard_read_single_block(unsigned int blockaddr) { int cmd_response = -1; while (cmd_response != SD_OK) { sdcore_argument_write(blockaddr); - sdcore_blocksize_write(512); + sdcore_blocksize_write(BLOCK_SIZE); sdcore_blockcount_write(1); sdcore_command_write((17 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_READ << 5)); + CSR8_CMD_FIX; cmd_response = sdcard_wait_response(); } return sdcard_wait_data_done(); @@ -422,10 +497,11 @@ int sdcard_read_multiple_block(unsigned int blockaddr, unsigned int blockcnt) { int cmd_response = -1; while (cmd_response != SD_OK) { sdcore_argument_write(blockaddr); - sdcore_blocksize_write(512); + sdcore_blocksize_write(BLOCK_SIZE); sdcore_blockcount_write(blockcnt); sdcore_command_write((18 << 8) | SDCARD_CTRL_RESPONSE_SHORT | (SDCARD_CTRL_DATA_TRANSFER_READ << 5)); + CSR8_CMD_FIX; cmd_response = sdcard_wait_response(); } return cmd_response; @@ -437,7 +513,7 @@ int sdcard_stop_transmission(void) { #endif sdcore_argument_write(0x0000000); sdcore_command_write((12 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -447,7 +523,7 @@ int sdcard_send_status(unsigned int rca) { #endif sdcore_argument_write(rca << 16); sdcore_command_write((13 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -457,7 +533,7 @@ int sdcard_set_block_count(unsigned int blockcnt) { #endif sdcore_argument_write(blockcnt); sdcore_command_write((23 << 8) | SDCARD_CTRL_RESPONSE_SHORT); - busy_wait(1); + CSR8_CMD_FIX; return sdcard_wait_response(); } @@ -467,65 +543,100 @@ void sdcard_decode_cid(void) { "Manufacturer ID: 0x%x\n" "Application ID 0x%x\n" "Product name: %c%c%c%c%c\n", - sdcard_response[0], sdcard_response[1], sdcard_response[2], sdcard_response[3], + sdcard_response[4], - (sdcard_response[0] >> 16) & 0xffff, + (sdcard_response[1] >> 16) & 0xffff, - sdcard_response[0] & 0xffff, + sdcard_response[1] & 0xffff, - (sdcard_response[1] >> 24) & 0xff, - (sdcard_response[1] >> 16) & 0xff, - (sdcard_response[1] >> 8) & 0xff, - (sdcard_response[1] >> 0) & 0xff, - (sdcard_response[2] >> 24) & 0xff + (sdcard_response[2] >> 24) & 0xff, + (sdcard_response[2] >> 16) & 0xff, + (sdcard_response[2] >> 8) & 0xff, + (sdcard_response[2] >> 0) & 0xff, + (sdcard_response[3] >> 24) & 0xff ); + int crc = sdcard_response[4] & 0x000000FF; + int month = (sdcard_response[4] & 0x00000F00) >> 8; + int year = (sdcard_response[4] & 0x000FF000) >> 12; + int psn = ((sdcard_response[4] & 0xFF000000) >> 24) | ((sdcard_response[3] & 0x00FFFFFF) << 8); + printf( "CRC: %02x\n", crc); + printf( "Production date(m/yy): %d/%d\n", month, year); + printf( "PSN: %08x\n", psn); + printf( "OID: %c%c\n", (sdcard_response[1] & 0x00FF0000) >> 16, (sdcard_response[1] & 0x0000FF00) >> 8); } void sdcard_decode_csd(void) { /* FIXME: only support CSR structure version 2.0 */ + + int size = ((sdcard_response[3] & 0xFFFF0000) >> 16) + ((sdcard_response[2] & 0x000000FF) << 16) + 1; printf( "CSD Register: 0x%x%08x%08x%08x\n" "Max data transfer rate: %d MB/s\n" "Max read block length: %d bytes\n" "Device size: %d GB\n", - sdcard_response[0], sdcard_response[1], sdcard_response[2], sdcard_response[3], + sdcard_response[4], (sdcard_response[1] >> 24) & 0xff, - (1 << ((sdcard_response[1] >> 8) & 0xf)), + (1 << ((sdcard_response[2] >> 16) & 0xf)), - ((sdcard_response[2] >> 8) & 0x3fffff)*512/(1024*1024) + size * BLOCK_SIZE / (1024 * 1024) ); } /* bist */ -void sdcard_bist_generator_start(unsigned int blockcnt) { - bist_generator_reset_write(1); - bist_generator_count_write(blockcnt); - bist_generator_start_write(1); +#ifdef CSR_SDDATAWRITER_BASE +void sdcard_sddatawriter_start(void) { + sddatawriter_reset_write(1); + sddatawriter_start_write(1); } -void sdcard_bist_generator_wait(void) { - while((bist_generator_done_read() & 0x1) == 0); +int sdcard_sddatawriter_wait(void) { + unsigned check_counter = 0; + unsigned done = 0; + while(!done) { + done = sddatawriter_done_read(); + REPEATED_MSG(++check_counter, CHECK_LOOPS_PRINT_THRESHOLD, + "\033[36m sddatawriter_done_read: %08x (check #%d)", + done, ++check_counter); + if(check_counter > CHECK_LOOPS_PRINT_THRESHOLD) { + putchar('\n'); + return NO_RESPONSE; //If we reach threshold, and cmdevt didn't return valid status, return NO_RESPONSE + } + } + return 0; +} +#endif + +#ifdef CSR_SDDATAREADER_BASE +void sdcard_sddatareader_start(void) { + sddatareader_reset_write(1); + sddatareader_start_write(1); } - -void sdcard_bist_checker_start(unsigned int blockcnt) { - bist_checker_reset_write(1); - bist_checker_count_write(blockcnt); - bist_checker_start_write(1); -} - -void sdcard_bist_checker_wait(void) { - while((bist_checker_done_read() & 0x1) == 0); +int sdcard_sddatareader_wait(void) { + unsigned check_counter = 0; + unsigned done = 0; + while((done & 1) == 0) { + done = sddatareader_done_read(); + REPEATED_MSG(++check_counter, CHECK_LOOPS_PRINT_THRESHOLD, + "\033[36m sddatareader_done_read: %08x (check #%d)", + done, check_counter); + if(check_counter > CHECK_LOOPS_PRINT_THRESHOLD) { + putchar('\n'); + return NO_RESPONSE; //If we reach threshold, and cmdevt didn't return valid status, return NO_RESPONSE + } + } + return 0; } +#endif /* user */ @@ -542,13 +653,26 @@ int sdcard_init(void) { sdcard_go_idle(); busy_wait(1); sdcard_send_ext_csd(); +#ifdef SDCARD_DEBUG + printf("Accepted voltage: "); + if(sdcard_response[4] & 0x0) + printf("Not defined\n"); + else if(sdcard_response[4] >> 8 & 0x1) + printf("2.7-3.6V\n"); + else if(sdcard_response[4] >> 12 & 0x1) + printf("Reserved\n"); + else if(sdcard_response[4] >> 16 & 0x1) + printf("Reserved\n"); + else + printf("Invalid response\n"); +#endif /* wait for card to be ready */ /* FIXME: 1.8v support */ for(;;) { sdcard_app_cmd(0); sdcard_app_send_op_cond(1, 0); - if (sdcard_response[3] & 0x80000000) { + if (sdcard_response[4] & 0x80000000) { break; } busy_wait(1); @@ -562,7 +686,7 @@ int sdcard_init(void) { /* set relative card address */ sdcard_set_relative_address(); - rca = (sdcard_response[3] >> 16) & 0xffff; + rca = (sdcard_response[4] >> 16) & 0xffff; /* set cid */ sdcard_send_cid(rca); @@ -595,66 +719,195 @@ int sdcard_init(void) { sdcard_app_send_scr(); /* set block length */ - sdcard_app_set_blocklen(512); + sdcard_app_set_blocklen(BLOCK_SIZE); return 0; } -int sdcard_test(unsigned int loops) { - unsigned int i; - unsigned int length; - unsigned int blocks; - unsigned int start; - unsigned int end; - unsigned int errors = 0; - unsigned long write_speed, read_speed; +void hexdump(volatile const char *buf, size_t len) +{ + enum { + BYTES_PER_ROW = 16, + BYTES_PER_GROUP = 4, + ADDR_COL_1 = 1, + HEX_COL_1 = ADDR_COL_1 + 7, + CHAR_COL_1 = HEX_COL_1 + 3 * BYTES_PER_ROW + + (BYTES_PER_ROW/BYTES_PER_GROUP) + 1, + }; - sdcore_cmdtimeout_write(1<<19); - sdcore_datatimeout_write(1<<19); + int i; + unsigned char c; + int printable = 0; + unsigned column, hex_column, char_column; + char cstr[] = "."; - sdtimer_init(); + // Erase line + printf("\033[2K"); - length = 4*1024*1024; - blocks = length/512; + for(i = 0; i < len; ++i) { + c = cstr[0] = buf[i]; + printable = (c >= ' ' && c < 127); + column = i % BYTES_PER_ROW; + hex_column = HEX_COL_1 + 3 * column + (column/BYTES_PER_GROUP); + char_column = CHAR_COL_1 + column + (column/BYTES_PER_GROUP); - for(i=0; i