/* * Reading the ST-NICCC megademo data stored in * the SPI flash and streaming it to polygons, * rendered as ANSI character sequences through * the UART. * * The polygon stream is a 640K file, that needs * to be stored in the SPI flash, using: * ICEStick: iceprog -o 1M EXAMPLES/DATA/scene1.dat * ULX3S: cp EXAMPLES/DATA/scene1.dat scene1.img * ujprog -j flash -f 1048576 scene1.img * (using latest version of ujprog compiled from https://github.com/kost/fujprog) * * More details and links in EXAMPLES/DATA/notes.txt */ #include #include #ifdef __linux__ #include #include #else #include "io.h" #endif // when compiling for SPI flash, uncomment to fit some routines in fast BRAM // (but it does not change much, the bottleneck is ANSI RGB encoding and uart. //#define RV32_FASTCODE __attribute((section(".fastcode"))) #define RV32_FASTCODE // when compiling for SPI flash, uncomment to enable wireframe mode (but it is ugly // and it will not fit in BRAM !) // #define WITH_WIREFRAME #ifdef WITH_WIREFRAME int wireframe = 0; #endif #define MIN(x,y) ((x) < (y) ? (x) : (y)) #define MAX(x,y) ((x) > (y) ? (x) : (y)) /**********************************************************************************/ /* Graphics routines */ /**********************************************************************************/ // Map coordinates from file to screen static inline uint8_t map_x(uint8_t x) { return x >> 1; } static inline uint8_t map_y(uint8_t y) { return y >> 2; } void GL_clear() { printf("\033[48;5;16m" // set background color black "\033[2J"); // clear screen } /* * Set background color using 6x6x6 colorcube codes * see https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences */ static inline void GL_setcolor(int color) { static int last_color = -1; if(color != last_color) { printf("\033[48;5;%dm",color); } last_color = color; } static inline void GL_setpixel(int x, int y) { printf("\033[%d;%dH ",y,x); // Goto_XY(x1,y) and print space } #ifdef WITH_WIREFRAME void GL_line(int x1, int y1, int x2, int y2) RV32_FASTCODE; void GL_line(int x1, int y1, int x2, int y2) { int x,y,dx,dy,sy,tmp; // Swap both extremities to ensure x increases if(x2 < x1) { tmp = x2; x2 = x1; x1 = tmp; tmp = y2; y2 = y1; y1 = tmp; } // Bresenham line drawing. dy = y2 - y1; sy = 1; if(dy < 0) { sy = -1; dy = -dy; } dx = x2 - x1; x = x1; y = y1; if(dy > dx) { int ex = (dx << 1) - dy; for(int u=0; u= 0) { x++; ex -= dy << 1; GL_setpixel(x,y); } while(ex >= 0) { x++; ex -= dy << 1; putchar(' '); } ex += dx << 1; } } else { int ey = (dy << 1) - dx; for(int u=0; u= 0) { y += sy; ey -= dx << 1; GL_setpixel(x,y); } ey += dy << 1; } } } #endif void GL_fillpoly(int nb_pts, int* points) RV32_FASTCODE; void GL_fillpoly(int nb_pts, int* points) { static int last_color = -1; char x_left[128]; char x_right[128]; /* Determine clockwise, miny, maxy */ int clockwise = 0; int miny = 256; int maxy = -256; for(int i1=0; i1 0) ^ (y2 > y1)) { GL_line(x1,y1,x2,y2); } continue; } #endif char* x_buffer = ((clockwise > 0) ^ (y2 > y1)) ? x_left : x_right; int dx = x2 - x1; int sx = 1; int dy = y2 - y1; int sy = 1; int x = x1; int y = y1; int ex; if(dx < 0) { sx = -1; dx = -dx; } if(dy < 0) { sy = -1; dy = -dy; } if(y1 == y2) { x_left[y1] = MIN(x1,x2); x_right[y1] = MAX(x1,x2); continue; } ex = (dx << 1) - dy; for(int u=0; u <= dy; ++u) { x_buffer[y] = x; y += sy; while(ex >= 0) { x += sx; ex -= dy << 1; } ex += dx << 1; } } #ifdef WITH_WIREFRAME if(!wireframe) #endif { for(int y = miny; y <= maxy; ++y) { int x1 = x_left[y]; int x2 = x_right[y]; printf("\033[%d;%dH",y,x1); // Goto_XY(x1,y) for(int x=x1; x> 2) { spi_word_addr = spi_addr >> 2; fseek(f, spi_word_addr*4-ADDR_OFFSET, SEEK_SET); fread(&(spi_u.spi_word), 4, 1, f); } result = spi_u.spi_bytes[spi_addr&3]; ++spi_addr; return (uint8_t)(result); } #else # define SPI_FLASH_BASE ((uint32_t*)(1 << 23)) /** * Reads one byte from the SPI flash, using the mapped SPI flash interface. */ static inline uint8_t next_spi_byte() { uint8_t result; if(spi_word_addr != spi_addr >> 2) { spi_word_addr = spi_addr >> 2; spi_u.spi_word = SPI_FLASH_BASE[spi_word_addr]; } result = spi_u.spi_bytes[spi_addr&3]; ++spi_addr; return (uint8_t)(result); } #endif static inline uint16_t next_spi_word() { /* In the ST-NICCC file, * words are stored in big endian format. * (see DATA/scene_description.txt). */ uint16_t hi = (uint16_t)next_spi_byte(); uint16_t lo = (uint16_t)next_spi_byte(); return (hi << 8) | lo; } /* * The colormap, encoded in such a way that it * can be directly sent as ANSI color codes. */ int cmap[16]; /* * Current frame's vertices coordinates (if frame is indexed), * mapped to OLED display dimensions (divide by 2 from file). */ uint8_t X[255]; uint8_t Y[255]; /* * Current polygon vertices, as expected * by GL_fillpoly(): * xi = poly[2*i], yi = poly[2*i+1] */ int poly[30]; /* * Masks for frame flags. */ #define CLEAR_BIT 1 #define PALETTE_BIT 2 #define INDEXED_BIT 4 /* * Reads a frame's polygonal description from * SPI flash and rasterizes the polygons using * FemtoGL. * returns 0 if last frame. * See DATA/scene_description.txt for the * ST-NICCC file format. * See DATA/test_ST_NICCC.c for an example * program. */ int read_frame() RV32_FASTCODE; int read_frame() { uint8_t frame_flags = next_spi_byte(); // Update palette data. if(frame_flags & PALETTE_BIT) { uint16_t colors = next_spi_word(); for(int b=15; b>=0; --b) { if(colors & (1 << b)) { int rgb = next_spi_word(); // Get the three 3-bits per component R,G,B int b3 = (rgb & 0x007); int g3 = (rgb & 0x070) >> 4; int r3 = (rgb & 0x700) >> 8; // Re-encode them as ANSI 8-bits color b3 = b3 * 6 / 8; g3 = g3 * 6 / 8; r3 = r3 * 6 / 8; cmap[15-b] = 16 + b3 + 6*(g3 + 6*r3); } } } if(frame_flags & CLEAR_BIT) { // GL_clear(); } // Update vertices if(frame_flags & INDEXED_BIT) { uint8_t nb_vertices = next_spi_byte(); for(int v=0; v> 4; for(int i=0; i