/*
 * Velleman K8048 Programmer for FreeBSD and others.
 *
 * Copyright (c) 2005-2007 Darron Broad
 * All rights reserved.
 *
 * Licensed under the terms of the BSD license, see file LICENSE
 * for details.
 *
 * $Id: pic.c,v 1.18 2007/05/15 01:06:21 darron Exp $
 */

#include "k8048.h"

/*
 * READ PROGRAM MEMORY BLOCK
 *
 *  RETURN PROGRAM BASE ADDRESS (>=0) OR ERROR (<0)
 */
long
pic_read_program_memory_block(struct k8048 *k, unsigned short *data, int max)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        return pic16f_read_program_memory_block(k, data, max);
        case ARCH16BIT: pic18f_read_config_memory(k);
                        return pic18f_read_program_memory_block(k, data, max);
    }
    return -1;
}

/*
 * READ DATA MEMORY BLOCK
 *
 *  RETURN EEPROM BASE ADDRESS (>=0) OR ERROR (<0)
 */
long
pic_read_data_memory_block(struct k8048 *k, unsigned char *data, int max)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        return pic16f_read_data_memory_block(k, data, max);
        case ARCH16BIT: pic18f_read_config_memory(k);
                        return pic18f_read_data_memory_block(k, data, max);
    }
    return -1;
}
 
/*
 * PROGRAM/VERIFY DEVICE
 *
 *  RETURN NUMBER OF VERIFY ERRORS
 */
int
pic_program_verify(struct k8048 *k, const char *fname, int mode)
{
    char *filename= getarg(fname);
    int fail=0;
    
    if(filename==NULL)
    {
        printf("%s: error: invalid filename\n", __FUNCTION__);
        return 0;
    }

    inhx32(filename);
    if(inhx32_count<=0)
    {
        free(filename);
        return 0;
    }

    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        break;
        case ARCH16BIT: pic18f_read_config_memory(k);
                        break;
    }
    if(mode==PROGRAM)   switch(k->arch)
    {
        case ARCH14BIT: pic16f_program(k);
                        break;
        case ARCH16BIT: pic18f_program(k);
                        break;
    }
    else /* VERIFY */   switch(k->arch)
    {
        case ARCH14BIT: fail= pic16f_verify(k);
                        break;
        case ARCH16BIT: fail= pic18f_verify(k);
                        break;
    }
    
    inhx32_free();
    free(filename);

    return fail;
}

/*
 * UNPROTECT A DEVICE
 */
void
pic_unprotect(struct k8048 *k)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        pic16f_disableprotection(k, 0, 0);
                        break;
        case ARCH16BIT: printf("%s: information: currently unimplemented on this device\n", __FUNCTION__);
                        break;
    }
}

/*
 * BLANK A DEVICE
 */
void
pic_blank(struct k8048 *k)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        pic16f_bulkerase(k);
                        break;
        case ARCH16BIT: pic18f_read_config_memory(k);
                        io_init_program_verify(k);
                        pic18f_bulk_erase(k);
                        io_standby(k);
                        break;
    }
}

/*
 * DUMP DEVICE ID
 */
void
pic_dumpdeviceid(struct k8048 *k)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);
                        pic16f_dumpdeviceid(k);
                        break;
        case ARCH16BIT: pic18f_read_config_memory(k);
                        pic18f_dumpdeviceid(k);
                        break;
    }
}

/*
 * DUMP CONFIGURATION
 */
void
pic_dumpconfig(struct k8048 *k)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);                
                        pic16f_dumpconfig(k);
                        break;
        case ARCH16BIT: pic18f_read_config_memory(k);
                        pic18f_dumpconfig(k);
                        break;
    }
}

/*
 * WRITE CONFIGURATION
 */
void
pic_writeconfig(struct k8048 *k, unsigned short config)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);                
                        pic16f_disableprotection(k, 0, config);
                        break;
        case ARCH16BIT: printf("%s: information: currently unimplemented on this device\n", __FUNCTION__);
                        break;
    }
}
  
/*
 * DUMP OSCILLATOR CALIBRATION
 */
void
pic_dumposccal(struct k8048 *k)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);   
                        pic16f_dumposccal(k);
                        break;
        case ARCH16BIT: printf("%s: information: osccal is not supported on this device\n", __FUNCTION__);
                        break;
    }
}

/*
 * WRITE OSCILLATOR CALIBRATION
 */
void
pic_writeosccal(struct k8048 *k, unsigned short osccal)
{
    switch(k->arch)
    {
        case ARCH14BIT: pic16f_read_config_memory(k);   
                        pic16f_disableprotection(k, osccal, 0);
                        break;
        case ARCH16BIT: printf("%s: information: osccal is not supported on this device\n", __FUNCTION__);
                        break;
    }
}

/*
 * DUMP MAX WORDS OF CODE MEMORY IN FLASH
 */
void
pic_dumpflash(struct k8048 *k, unsigned short max)
{
    int i, skip, lines=0;
    unsigned short *code, addr=0, compare;
    long baseaddr;

    code=(unsigned short *)calloc(max, sizeof(unsigned short));
    if(code==NULL)
    {
        printf("%s: fatal error: calloc failed\n", __FUNCTION__);
        exit(-1);
    }

    /* read data */
    baseaddr= pic_read_program_memory_block(k, code, max);
    if(baseaddr<0)
        goto dump_code_fail;

    /* dump data */
    while( addr < max )
    {
        skip=0;

        /* detect blank row */
        for(i=0; i<CODEWIDTH; i++)
        {
            compare= k->arch; /* arch mask = compare mask */
            if(code[addr+i] == compare)
                skip++;
        }

        /* display row if not blank */
        if(skip<CODEWIDTH)
        {
            lines++;
            printf("[%04lx] ", baseaddr);
            for(i=0; i<CODEWIDTH; i++)
                printf("%04x ", code[ addr + i ] );
            printf("\n");
        }

        /* move to next row */
        addr    +=CODEWIDTH;
        baseaddr+=CODEWIDTH;

        if(k->arch==ARCH16BIT)
            baseaddr+=CODEWIDTH;
    }
    if(lines==0)
        printf("%s: information: device empty\n", __FUNCTION__);

dump_code_fail:
    free(code);
}

/*
 * DUMP MAX WORDS OF DATA MEMORY IN EEPROM
 */
void
pic_dumpeeprom(struct k8048 *k, unsigned short max)
{
    int i;
    unsigned char *chars, ch, *data;
    unsigned short addr=0;
    long baseaddr;

    data=(unsigned char *)calloc(max, sizeof(unsigned char));
    if(data==NULL)
    {
        printf("%s: fatal error: calloc failed\n", __FUNCTION__);
        exit(-1);
    }
    chars=(unsigned char *)calloc(DATAWIDTH+1, sizeof(unsigned char));
    if(chars==NULL)
    {
        printf("%s: fatal error: calloc failed\n", __FUNCTION__);
        exit(-1);
    }
    chars[DATAWIDTH]='\0';

    /* read data */
    baseaddr= pic_read_data_memory_block(k, data, max);
    if(baseaddr<0)
        goto dump_data_fail;

    /* dump data */
    while( addr < max )
    {
        printf("[%04lx] ", baseaddr);
        for(i=0; i<DATAWIDTH; i++)
        {
            ch= data[ addr + i ];
            if(ch>=' ' && ch<127)
                chars[i]=ch;
            else
                chars[i]='.';
            printf("%02x ", ch);
        }
        printf("%s\n", chars);
        addr    +=DATAWIDTH;
        baseaddr+=DATAWIDTH;
    }

dump_data_fail:
    free(chars);
    free(data);
}
