// translate.c — traduce direcciones virtuales con una tabla de páginas lineal.
//
// Uso:   ./translate tamaño_página tabla dirección...
//        tabla: un PFN por página virtual, separados por comas; "-" = inválida.
//        El número de entradas debe ser potencia de 2.
// Ejemplo (el de las diapositivas): páginas de 1 KB, 16 páginas virtuales
//   ./translate 1024 5,12,-,-,30,-,-,-,-,-,-,-,29,-,-,7 0x3229 0x13ff 0x0a0f
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXPAGES 4096

static int log2_exact(unsigned long x) {
    int n = 0;
    while ((1UL << n) < x)
        n++;
    return (1UL << n) == x ? n : -1;
}

// Imprime los nbits menos significativos de x en binario.
static void print_bits(unsigned long x, int nbits) {
    for (int i = nbits - 1; i >= 0; i--)
        putchar((x >> i) & 1 ? '1' : '0');
}

int main(int argc, char *argv[]) {
    if (argc < 4) {
        fprintf(stderr, "uso: %s tamaño_página tabla dirección...\n", argv[0]);
        return 1;
    }
    unsigned long page = strtoul(argv[1], NULL, 0);
    int off_bits = log2_exact(page);
    if (off_bits < 0) {
        fprintf(stderr, "el tamaño de página debe ser potencia de 2\n");
        return 1;
    }

    // Leer la tabla de páginas: pt[vpn] = pfn, o -1 si la entrada es inválida.
    long pt[MAXPAGES];
    int npages = 0;
    long maxpfn = 0;
    char *copy = strdup(argv[2]);
    for (char *t = strtok(copy, ","); t != NULL && npages < MAXPAGES; t = strtok(NULL, ",")) {
        pt[npages] = strcmp(t, "-") == 0 ? -1 : strtol(t, NULL, 0);
        if (pt[npages] > maxpfn)
            maxpfn = pt[npages];
        npages++;
    }
    free(copy);
    int vpn_bits = log2_exact(npages);
    if (vpn_bits < 0) {
        fprintf(stderr, "el número de entradas debe ser potencia de 2\n");
        return 1;
    }
    int pfn_bits = 1;
    while ((1L << pfn_bits) <= maxpfn)
        pfn_bits++;

    printf("página de %lu bytes -> offset de %d bits\n", page, off_bits);
    printf("%d páginas virtuales -> VPN de %d bits (direcciones de %d bits)\n\n",
           npages, vpn_bits, vpn_bits + off_bits);

    for (int a = 3; a < argc; a++) {
        unsigned long va = strtoul(argv[a], NULL, 0);
        printf("VA 0x%04lx = ", va);
        if (va >> (vpn_bits + off_bits)) {
            printf("fuera del espacio de direcciones\n\n");
            continue;
        }
        // Lo mismo que hace la MMU: separar VPN y offset con shift y máscara.
        unsigned long vpn = va >> off_bits;
        unsigned long offset = va & (page - 1);
        print_bits(vpn, vpn_bits);
        printf(" | ");
        print_bits(offset, off_bits);
        printf("\n  VPN = %lu, offset = 0x%lx\n", vpn, offset);
        if (pt[vpn] < 0) {
            printf("  PTE[%lu] inválida -> excepción (fallo de segmentación)\n\n", vpn);
            continue;
        }
        unsigned long pfn = (unsigned long)pt[vpn];
        unsigned long pa = (pfn << off_bits) | offset;
        printf("  PTE[%lu]: PFN = %lu = 0x%lx\n", vpn, pfn, pfn);
        printf("  PA = ");
        print_bits(pfn, pfn_bits);
        printf(" | ");
        print_bits(offset, off_bits);
        printf(" = 0x%04lx\n\n", pa);
    }
    return 0;
}
