Below is the code for a full 3d rasterizer in pure C. For now, it displays a single RGB triangle onto the screen via ASCII terminal and then quits. I want you to add more colors to the palette, using the actual extended ASCII escape sequence for colors.
C code:
```c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <limits.h> // For INT32_MAX
#include <unistd.h> // For sleep functions (if needed)
#include <signal.h> // For signal handling

// Define terminal resolution
#define WIDTH 80
#define HEIGHT 40

// Aspect ratio correction factor
#define ASPECT_RATIO_CORRECTION 0.5f

// Define color indices
#define COLOR_BLACK   0
// Colors 1-255: Extended palette
#define COLOR_WHITE   255

// Shade characters ordered from lightest to darkest
const char* SHADES[] = {
    " ", 
    ".", 
    ":", 
    "-", 
    "=", 
    "+", 
    "*", 
    "#", 
    "%", 
    "@",
    "█"
};
#define NUM_SHADES (sizeof(SHADES) / sizeof(SHADES[0]))

// Framebuffer structure storing color and shade for each pixel
typedef struct {
    unsigned char color;  // 0-255 color index
    unsigned char shade;  // 0 to NUM_SHADES-1
} Pixel;

Pixel framebuffer[HEIGHT][WIDTH];

// Vector and Matrix structures
typedef struct {
    float x, y, z, w;
} Vec4;

typedef struct {
    float m[4][4];
} Mat4;

// Vertex and Triangle structures with RGB
typedef struct {
    Vec4 position;
    unsigned char r, g, b;
} Vertex;

typedef struct {
    Vertex v0, v1, v2;
} Triangle;


// Function to create an identity matrix
Mat4 mat4_identity() {
    Mat4 mat = {0};
    for(int i =0; i <4; i++) mat.m[i][i] =1.0f;
    return mat;
}

// Function to multiply two matrices
Mat4 mat4_multiply(Mat4 a, Mat4 b){
    Mat4 result = {0};
    for(int i=0;i<4;i++) {
        for(int j=0;j<4;j++) {
            for(int k=0;k<4;k++) {
                result.m[i][j] += a.m[i][k] * b.m[k][j];
            }
        }
    }
    return result;
}

// Function to create a rotation matrix around Y-axis
Mat4 mat4_rotate_y(float angle){
    Mat4 mat = mat4_identity();
    mat.m[0][0] = cosf(angle);
    mat.m[0][2] = sinf(angle);
    mat.m[2][0] = -sinf(angle);
    mat.m[2][2] = cosf(angle);
    return mat;
}

// Function to create a translation matrix along Z-axis
Mat4 mat4_translate_z(float z){
    Mat4 mat = mat4_identity();
    mat.m[3][2] = z;
    return mat;
}

// Function to create a perspective projection matrix
Mat4 mat4_perspective(float fov, float aspect, float znear, float zfar){
    Mat4 mat = {0};
    float tanHalfFOV = tanf(fov / 2.0f);
    mat.m[0][0] = 1.0f / (aspect * tanHalfFOV);
    mat.m[1][1] = 1.0f / tanHalfFOV;
    mat.m[2][2] = -(zfar + znear) / (zfar - znear);
    mat.m[2][3] = -(2.0f * zfar * znear) / (zfar - znear);
    mat.m[3][2] = -1.0f;
    return mat;
}

// Function to multiply matrix with vector
Vec4 mat4_multiply_vec4(Mat4 mat, Vec4 v){
    Vec4 result;
    result.x = mat.m[0][0]*v.x + mat.m[0][1]*v.y + mat.m[0][2]*v.z + mat.m[0][3]*v.w;
    result.y = mat.m[1][0]*v.x + mat.m[1][1]*v.y + mat.m[1][2]*v.z + mat.m[1][3]*v.w;
    result.z = mat.m[2][0]*v.x + mat.m[2][1]*v.y + mat.m[2][2]*v.z + mat.m[2][3]*v.w;
    result.w = mat.m[3][0]*v.x + mat.m[3][1]*v.y + mat.m[3][2]*v.z + mat.m[3][3]*v.w;
    return result;
}

// Function to handle SIGINT to restore cursor visibility
void handle_sigint(int sig){
    printf("\x1b[?25h"); // Show cursor
    printf("\x1b[0m");   // Reset terminal formatting
    exit(0);
}

// Function to map RGB to nearest 256 color
unsigned char rgb_to_256(unsigned char r, unsigned char g, unsigned char b) {
    // Simple mapping: assume the 6x6x6 color cube and ignore grayscale for simplicity
    // Map 0-255 to 0-5
    int r_level = r * 5 / 255;
    int g_level = g * 5 / 255;
    int b_level = b * 5 / 255;
    
    // Calculate the index in the 6x6x6 color cube
    unsigned char index = 16 + 36 * r_level + 6 * g_level + b_level;
    
    // Clamp the index to 0-255
    if(index > 255) index = 255;
    
    return index;
}

// Function to clear the color buffer
void clear_buffer(){
    for(int y=0; y < HEIGHT; y++){
        for(int x=0; x < WIDTH; x++){
            framebuffer[y][x].color = 0; // Black
            framebuffer[y][x].shade = 0; // Lightest shade
        }
    }
}

// Function to set a pixel's color and shade
void set_pixel(int x, int y, unsigned char color, unsigned char shade){
    if(x <0 || x >= WIDTH || y <0 || y >= HEIGHT) return;
    framebuffer[y][x].color = color;
    framebuffer[y][x].shade = shade;
}

// Function to draw a filled triangle with fixed shading
void draw_triangle(Triangle tri){
    // Find bounding box
    float minX = fminf(fminf(tri.v0.position.x, tri.v1.position.x), tri.v2.position.x);
    float minY = fminf(fminf(tri.v0.position.y, tri.v1.position.y), tri.v2.position.y);
    float maxX = fmaxf(fmaxf(tri.v0.position.x, tri.v1.position.x), tri.v2.position.x);
    float maxY = fmaxf(fmaxf(tri.v0.position.y, tri.v1.position.y), tri.v2.position.y);

    // Clamp to screen
    int x0 = (int)fmaxf(0, floorf(minX));
    int y0 = (int)fmaxf(0, floorf(minY));
    int x1 = (int)fminf(WIDTH-1, ceilf(maxX));
    int y1 = (int)fminf(HEIGHT-1, ceilf(maxY));

    // Triangle setup
    float x0f = tri.v0.position.x;
    float y0f = tri.v0.position.y;
    float x1f_t = tri.v1.position.x;
    float y1f = tri.v1.position.y;
    float x2f = tri.v2.position.x;
    float y2f = tri.v2.position.y;

    // Compute area of the triangle
    float area = (x1f_t - x0f)*(y2f - y0f) - (x2f - x0f)*(y1f - y0f);
    if(area == 0.0f) return;

    // Determine fixed shade index (e.g., middle shade)
    unsigned char fixed_shade = NUM_SHADES - 1; // Adjust as needed

    // Iterate over bounding box
    for(int y = y0; y <= y1; y++){
        for(int x = x0; x <= x1; x++){
            // Barycentric coordinates
            float w1 = ((x1f_t - x0f)*(y - y0f) - (x - x0f)*(y1f - y0f)) / area;
            float w2 = ((x2f - x1f_t)*(y - y1f) - (x - x1f_t)*(y2f - y1f)) / area;
            float w3 = ((x0f - x2f)*(y - y2f) - (x - x2f)*(y0f - y2f)) / area;

            if(w1 >=0 && w2 >=0 && w3 >=0){
                // Interpolate RGB values
                float r = w1 * tri.v0.r + w2 * tri.v1.r + w3 * tri.v2.r;
                float g = w1 * tri.v0.g + w2 * tri.v1.g + w3 * tri.v2.g;
                float b = w1 * tri.v0.b + w2 * tri.v1.b + w3 * tri.v2.b;

                // Map interpolated color to palette index
                unsigned char color_index = rgb_to_256((unsigned char)r, (unsigned char)g, (unsigned char)b);

                // Assign fixed shade index
                unsigned char shade_index = fixed_shade;

                set_pixel(x, y, color_index, shade_index);
            }
        }
    }
}

// Function to clear the terminal screen
void clear_screen(){
    printf("\x1b[2J"); // Clear screen
    printf("\x1b[H");  // Move cursor to home position
}

// Function to render the framebuffer to the terminal
void draw_frame(){
    printf("\x1b[H"); // Move cursor to home
    for(int y=0; y < HEIGHT; y++){
        for(int x=0; x < WIDTH; x++){
            unsigned char color = framebuffer[y][x].color;
            unsigned char shade = framebuffer[y][x].shade;
            if(color != 0){ // If pixel has color
                // Select shade character
                const char* shade_char = SHADES[shade];
                // Use foreground color for better visibility
                printf("\x1b[38;5;%dm%s", color, shade_char);
            }
            else{
                printf(" "); // Background
            }
        }
        printf("\x1b[0m\n"); // Reset color and move to next line
    }
    fflush(stdout);
}

int main(){
    // Register signal handler for graceful exit
    signal(SIGINT, handle_sigint);

    // Hide cursor
    printf("\x1b[?25l");

    // Initialize framebuffer
    clear_buffer();

    // Define a single triangle with distinct vertex colors
    Vertex v0 = {{0.0f,  1.0f, 0.0f, 1.0f}, 255, 0, 0};    // Top vertex (Red)
    Vertex v1 = {{-1.0f, -1.0f, 0.0f, 1.0f}, 0, 255, 0};   // Bottom-left vertex (Green)
    Vertex v2 = {{1.0f, -1.0f, 0.0f, 1.0f}, 0, 0, 255};    // Bottom-right vertex (Blue)

    Triangle tri = {v0, v1, v2};

    // Create transformation matrices
    float angle = 0.0f; // No rotation initially
    Mat4 rot = mat4_rotate_y(angle);
    Mat4 proj = mat4_perspective(M_PI / 3, (float)WIDTH / HEIGHT, 0.1f, 100.0f);
    Mat4 trans = mat4_translate_z(-3.0f); // Move triangle in front of camera

    // Combine transformations: Projection * Rotation * Translation
    Mat4 transform = mat4_multiply(mat4_multiply(proj, rot), trans);

    // Transform the triangle vertices
    Vertex transformed[3];
    for(int j=0; j <3; j++){
        Vec4 pos;
        if(j ==0) pos = tri.v0.position;
        else if(j ==1) pos = tri.v1.position;
        else pos = tri.v2.position;

        Vec4 tp = mat4_multiply_vec4(transform, pos);
        if(tp.w !=0){
            tp.x /= tp.w;
            tp.y /= tp.w;
            tp.z /= tp.w;
        }

        // Scale to screen with aspect ratio correction
        transformed[j].position.x = (tp.x + 1.0f) * 0.5f * (WIDTH -1);
        transformed[j].position.y = (1.0f - (tp.y + 1.0f) * 0.5f * ASPECT_RATIO_CORRECTION) * (HEIGHT -1);
        transformed[j].position.z = tp.z;

        // Assign vertex colors
        if(j ==0){
            transformed[j].r = tri.v0.r;
            transformed[j].g = tri.v0.g;
            transformed[j].b = tri.v0.b;
        }
        else if(j ==1){
            transformed[j].r = tri.v1.r;
            transformed[j].g = tri.v1.g;
            transformed[j].b = tri.v1.b;
        }
        else{
            transformed[j].r = tri.v2.r;
            transformed[j].g = tri.v2.g;
            transformed[j].b = tri.v2.b;
        }
    }

    // Back-face culling
    // Compute edge vectors in screen space
    float edge1_x = transformed[1].position.x - transformed[0].position.x;
    float edge1_y = transformed[1].position.y - transformed[0].position.y;
    float edge2_x = transformed[2].position.x - transformed[0].position.x;
    float edge2_y = transformed[2].position.y - transformed[0].position.y;

    // Compute the normal's Z component (only need Z for 2D screen)
    float normal_z = edge1_x * edge2_y - edge1_y * edge2_x;

    // If the normal is negative, the triangle is facing towards the camera
    if(normal_z < 0){
        draw_triangle( (Triangle){transformed[0], transformed[1], transformed[2]} );
    }

    // Render the frame
    clear_screen();
    draw_frame();

    // Show cursor again
    printf("\x1b[?25h");

    return 0;
}
```