Tuesday, September 22, 2015

Calling C From Bank 1

One of my goals is to be able to separately compile a program that runs in banks 1 and 2 while the system manages from banks 0 and 3. Since I don't want to have duplicates of the c library, I need a way to let a the compiler use the c library and driver functions that are in bank 0. So this morning, I coded up a system to do that.

I added a crt1.s which is the crt start up for bank 1. It doesn't need to deal with interrupts, and some basic start up code, for example. It just needs to prepare the stack pointer, deal with it's C initialization, and then start main. At the end of main, it has to return to the caller in bank 0.

I haven't tested any of this, but the basic plan is to have a vector of jumps at the top of bank 0. A version will sit at 0x3f11. The vector is jumps to functions in bank 0.

For example. If I want to call printf from the compiled code for bank 1, it will link the vector which will have a:
_printf::
 jp 0
compiled into a location somewhere in the 3f11-3ffd range. Specifically at 0x4000 - sizeof(struct c_vector) + offsetof(struct c_vector, printf).

Because of the way the intel hex programmer works, this will not be burnt into the flash because it is outside the 0x4000-0x7fff range. So a call to printf will go to that location where the jp 0 is. But there won't be a jp 0 there. What will be there is a jp _printf placed there by the compiler that compiled the bank 0 code. The code compiled for bank 1 will, as a result, call the printf compiled separately for bank 0. Of course, the struct c_vector and the jp's have to align perfectly.

If that description was a little complicated, have a look at the code (recall it's not tested yet). It's in progress.

cvector.h:

#ifndef INCLUDE_CVECTORS_H
#define INCLUDE_CVECTORS_H

#ifndef _SDCC_MALLOC_TYPE_MLH
#define _SDCC_MALLOC_TYPE_MLH
#endif

#ifndef __SDCC_BROKEN_STRING_FUNCTIONS
#define __SDCC_BROKEN_STRING_FUNCTIONS
#endif

#ifndef __SDCC_z80
#define __SDCC_z80
#endif

#ifndef _Bool
#define _Bool unsigned char
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "ringBuffer.h"


struct c_vector
{
    // c_ext
    uint8_t jp4e;
    void (*gets_length)(int maxlen);
    uint8_t jp4d;
    void(*gets_echoSuppress)(bool suppressEcho);

    // idle
    uint8_t jp4c;
    void(*idle)(void(*pIdleFn)(void* p), void* p);

    // interrupt
    uint8_t jp4b;
    void(*di)();
    uint8_t jp4a;
    void(*ei)();

    // gpio
    uint8_t jp49;
    uint8_t(*gpioReadDirection)();
    uint8_t jp48;
    void(*gpioWriteDirection)(uint8_t direction, uint8_t mask);
    uint8_t jp447;
    uint8_t(*gpioReadOutLevel)();
    uint8_t jp46;
    void(*gpioWriteLevel)(uint8_t out, uint8_t mask);
    uint8_t jp45;
    void(*gpioWriteBitLevel)(uint8_t bit, bool level);
    uint8_t jp44;
    uint8_t(*gpioReadInLevel)();

    // spi
    uint8_t jp43;
    uint8_t(*spiExchange)(uint8_t b, uint8_t flags, uint8_t count);

    // tick
    uint8_t jp42;
    uint32_t(*getTicks)();

    // timer
    uint8_t jp41;
    void(*timerInit)();
    uint8_t jp40;
    int(*timerGetAvailableTimerId)();
    uint8_t jp3f;
    void(*timerFree)(int timerId);
    uint8_t jp3e;
    void(*timerSet)(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer));
    uint8_t jp3d;
    void(*timerGet)(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer));
    uint8_t jp3c;
    void(*timerStop)(int timerId);
    uint8_t jp3b;
    void(*timerRun)(int timerId);
    uint8_t jp3a;
    void(*timerResetInterval)(int timerId);

    // uart
    uint8_t jp39;
    bool(*uartConfig)(uint8_t configLow);
    uint8_t jp38;
    void(*uartSupressRTS)(bool always, bool exceptRecv);
    uint8_t jp37;
    uint8_t(*uartErrorReadAndClear)(uint8_t clearMask);
    uint8_t jp36;
    RB_INT_TYPE(*send)(uint8_t* pByte, RB_INT_TYPE count);
    uint8_t jp35;
    RB_INT_TYPE(*recv)(uint8_t* pByte, RB_INT_TYPE count);
    uint8_t jp34;
    void(*pend)(void(*pIdleFn)(void* p), void* p);

    // stdio
    uint8_t jp33;
    char(*getchar)();
    uint8_t jp32;
    void(*putchar)(char c);
    uint8_t jp31;
    int(*puts)(const char* s);
    uint8_t jp30;
    char* (*gets)(char* s);
    uint8_t jp2f;
    int(*printf)(char* format, ...);
    uint8_t jp2e;
    int(*vprintf)(char* format, va_list ap);
    uint8_t jp2d;
    int(*sprintf)(char* dest, char* format, ...);
    uint8_t jp2c;
    int(*vsprintf)(char* dest, char* format, va_list ap);

    // stdlib
    uint8_t jp2b;
    void* (*malloc)(size_t size);
    uint8_t jp2a;
    void* (*calloc)(size_t size, size_t count);
    uint8_t jp29;
    void* (*realloc)(void* old, size_t newSize);
    uint8_t jp28;
    void(*free)(void* m);


    // string
    uint8_t jp27;
    void* (*memcpy)(void* dest, const void* src, size_t n);
    uint8_t jp26;
    void* (*memmove)(void *dest, const void *src, size_t n);
    uint8_t jp25;
    char* (*strcpy)(char* dest, const char* src);
    uint8_t jp24;
    char* (*strncpy)(char* dest, const char* src, size_t n);
    uint8_t jp23;
    char* (*strcat)(char* dest, const char* src);
    uint8_t jp22;
    char* (*strncat)(char* dest, const char* src, size_t n);
    uint8_t jp21;
    int(*memcmp)(const void* s1, const void* s2, size_t n);
    uint8_t jp20;
    int(*strcmp)(const char* s1, const char* s2);
    uint8_t jp1f;
    int(*strncmp)(const char* s1, const char* s2, size_t n);
    uint8_t jp1e;
    size_t(*strxfrm)(char *dest, const char* src, size_t n);
    uint8_t jp1d;
    void* (*memchr)(const void *s, int c, size_t n);
    uint8_t jp1c;
    char* (*strchr)(const char *s, char c); /* c should be int according to standard. */
    uint8_t jp1b;
    size_t(*strcspn)(const char *s, const char *reject);
    uint8_t jp1a;
    char* (*strpbrk)(const char *s, const char *accept);
    uint8_t jp19;
    char* (*strrchr)(const char *s, char c); /* c should be int according to standard. */
    uint8_t jp18;
    size_t(*strspn)(const char *s, const char *accept);
    uint8_t jp17;
    char* (*strstr)(const char* haystack, const char *needle);
    uint8_t jp16;
    char* (*strtok)(char* str, const char * delim);
    uint8_t jp15;
    void* (*memset)(void *s, unsigned char c, size_t n); /* c should be int according to standard. */
    uint8_t jp14;
    size_t(*strlen)(const char *s);

    // stdlib
    uint8_t jp13;
    int(*atoi)(const char* s);
    uint8_t jp12;
    long(*atol)(const char* s);
    uint8_t jp11;
    int(*rand)();
    uint8_t jp10;
    void(*srand)(unsigned int seed);
    uint8_t jp0f;
    int(*abs)(int i);
    uint8_t jp0e;
    long(*labs)(long i);

    // ctype
    uint8_t jp0d;
    int(*isblank)(int c);
    uint8_t jp0c;
    int(*isdigit)(int c);
    uint8_t jp0b;
    int(*islower)(int c);
    uint8_t jp0a;
    int(*isupper)(int c);
    uint8_t jp09;
    int(*isalnum)(int c);
    uint8_t jp08;
    int(*isalpha)(int c);
    uint8_t jp07;
    int(*iscntrl)(int c);
    uint8_t jp06;
    int(*isgraph)(int c);
    uint8_t jp05;
    int(*isprint)(int c);
    uint8_t jp04;
    int(*ispunct)(int c);
    uint8_t jp03;
    int(*isspace)(int c);
    uint8_t jp02;
    int(*isxdigit)(int c);
    uint8_t jp01;
    int(*tolower)(int c);
    uint8_t jp00;
    int(*toupper)(int c);

    // version --corresponds to 0x3ffe
    uint16_t _cVectorVersion;
};

#endif



c_vector.c:

#include "c_vector.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include "menuDriver.h"
#include "c_ext.h"
#include "interrupt.h"
#include "gpio.h"
#include "spi.h"
#include "tick.h"
#include "idle.h"
#include "timer.h"
#include "uart.h"
#include "diag.h"

#ifndef __SDCC
#define __at(x)
#endif

void* proxy_memcpy(void* dest, const void* src, size_t n)
{
    return memcpy(dest, src, n);
}

void* proxy_memmove(void *dest, const void *src, size_t n)
{
    return memmove(dest, src, n);
}

char* proxy_strcpy(char* dest, const char* src)
{
    return strcpy(dest, src);
}

char* proxy_strncpy(char* dest, const char* src, size_t n)
{
    return strncpy(dest, src, n);

}

char* proxy_strchr(const char *s, char c)
{
    return strchr(s, c);
}

void* proxy_memset(void *s, unsigned char c, size_t n)
{
    return memset(s, c, n);
}
#define JP_OPCODE (uint8_t)(0xc3)

const struct c_vector __at(0x4000 - sizeof(struct c_vector)) C_VECTOR = {
 JP_OPCODE,
    gets_length,
    JP_OPCODE,
    gets_echoSuppress,

    JP_OPCODE,
    idle,

    JP_OPCODE,
    di,
    JP_OPCODE,
    ei,

    JP_OPCODE,
    gpioReadDirection,
    JP_OPCODE,
    gpioWriteDirection,
    JP_OPCODE,
    gpioReadOutLevel,
    JP_OPCODE,
    gpioWriteLevel,
    JP_OPCODE,
    gpioWriteBitLevel,
    JP_OPCODE,
    gpioReadInLevel,

    JP_OPCODE,
    spiExchange,

    JP_OPCODE,
    getTicks,

    JP_OPCODE,
    timerInit,
    JP_OPCODE,
    timerGetAvailableTimerId,
    JP_OPCODE,
    timerFree,
    JP_OPCODE,
    timerSet,
    JP_OPCODE,
    timerGet,
    JP_OPCODE,
    timerStop,
    JP_OPCODE,
    timerRun,
    JP_OPCODE,
    timerResetInterval,

    JP_OPCODE,
    uartConfig,
    JP_OPCODE,
    uartSupressRTS,
    JP_OPCODE,
    uartErrorReadAndClear,
    JP_OPCODE,
    send,
    JP_OPCODE,
    recv,
    JP_OPCODE,
    pend,

    JP_OPCODE,
    getchar,
    JP_OPCODE,
    putchar,
    JP_OPCODE,
    puts,
    JP_OPCODE,
    gets,
    JP_OPCODE,
    printf,
    JP_OPCODE,
    vprintf,
    JP_OPCODE,
    sprintf,
    JP_OPCODE,
    vsprintf,

    JP_OPCODE,
    malloc,
    JP_OPCODE,
    calloc,
    JP_OPCODE,
    realloc,
    JP_OPCODE,
    free,

    JP_OPCODE,
    proxy_memcpy,
    JP_OPCODE,
    proxy_memmove,
    JP_OPCODE,
    proxy_strcpy,
    JP_OPCODE,
    proxy_strncpy,
    JP_OPCODE,
    strcat,
    JP_OPCODE,
    strncat,
    JP_OPCODE,
    memcmp,
    JP_OPCODE,
    strcmp,
    JP_OPCODE,
    strncmp,
    JP_OPCODE,
    strxfrm,
    JP_OPCODE,
    memchr,
    JP_OPCODE,
    proxy_strchr,
    JP_OPCODE,
    strcspn,
    JP_OPCODE,
    strpbrk,
    JP_OPCODE,
    strrchr,
    JP_OPCODE,
    strspn,
    JP_OPCODE,
    strstr,
    JP_OPCODE,
    strtok,
    JP_OPCODE,
    proxy_memset,
    JP_OPCODE,
    strlen,

    JP_OPCODE,
    atoi,
    JP_OPCODE,
    atol,
    JP_OPCODE,
    rand,
    JP_OPCODE,
    srand,
    JP_OPCODE,
    abs,
    JP_OPCODE,
    labs,

    JP_OPCODE,
    isblank,
    JP_OPCODE,
    isdigit,
    JP_OPCODE,
    islower,
    JP_OPCODE,
    isupper,
    JP_OPCODE,
    isalnum,
    JP_OPCODE,
    isalpha,
    JP_OPCODE,
    iscntrl,
    JP_OPCODE,
    isgraph,
    JP_OPCODE,
    isprint,
    JP_OPCODE,
    ispunct,
    JP_OPCODE,
    isspace,
    JP_OPCODE,
    isxdigit,
    JP_OPCODE,
    tolower,
    JP_OPCODE,
    toupper,

    0x0101
};

const struct MenuItem menuItems[] =
{
 // Banking

 // Flash

 // Running

    {
 NULL,
 NULL
    }
};

void main()
{
    ShowByte(0x00);
    ShowByte(0xff);

    diagInit();
    DiagDir(1);
    tickInit();
    uartInit();
    gpioWriteLevel(0x00, 0x08);
    gpioWriteDirection(0x08, 0x08);
    uartSupressRTS(false, true);

    while (!uartConfig(0 | (uint8_t)UART_BAUD_9600))
    {
        // do nothing
    }

    ei();

    runMenu();
}

crt1.s:

;;

 .module crt1

 .globl _main

 .area _HEADER (ABS)
; This next section must remain in sync with cvectors.h
; This will be part of the intelhex file but will be ignored by the intel hex programmer
; because it is out of the range

; inspect crt1.rel and ensure _cVectorVersion looks like this:
; S _cVectorVersion Def3FFE
; if it is not, adjust this .org accordingly and update _cVectorVersion to reflect a new C vector
 .org 0x3f11
_gets_length::
 jp 0
_gets_echoSuppress::
 jp 0
_idle::
 jp 0
_di::
 jp 0
_e::
 jp 0
_gpioReadDirection::
 jp 0
_gpioWriteDirection::
 jp 0
_gpioReadOutLevel::
 jp 0
_gpioWriteLevel::
 jp 0
_gpioWriteBitLevel::
 jp 0
_gpioReadInLevel::
 jp 0
_spiExchange::
 jp 0
_getTicks::
 jp 0
_timerInit::
 jp 0
_timerGetAvailableTimerId::
 jp 0
_timerFree::
 jp 0
_timerSet::
 jp 0
_timerGet::
 jp 0
_timerStop::
 jp 0
_timerRun::
 jp 0
_timerResetInterval::
 jp 0
_uartConfig::
 jp 0
_uartSupressRTS::
 jp 0
_uartErrorReadAndClear::
 jp 0
_send::
 jp 0
_recv::
 jp 0
_pend::
 jp 0
_getchar::
 jp 0
_putchar::
 jp 0
_puts::
 jp 0
_gets::
 jp 0
_printf::
 jp 0
_vprintf::
 jp 0
_sprintf::
 jp 0
_vsprintf::
 jp 0
_malloc::
 jp 0
_calloc::
 jp 0
_realloc::
 jp 0
_free::
 jp 0
_memcpy::
 jp 0
_memmove::
 jp 0
_strcpy::
 jp 0
_strncpy::
 jp 0
_strcat::
 jp 0
_strncat::
 jp 0
_memcmp::
 jp 0
_strcmp::
 jp 0
_strncmp::
 jp 0
_strxfrm::
 jp 0
_memchr::
 jp 0
_strchr::
 jp 0
_strcspn::
 jp 0
_strpbrk::
 jp 0
_strrchr::
 jp 0
_strspn::
 jp 0
_strstr::
 jp 0
_strtok::
 jp 0
_memset::
 jp 0
_strlen::
 jp 0
_atoi::
 jp 0
_atol::
 jp 0
_rand::
 jp 0
_randSeed::
 jp 0
_abs::
 jp 0
_labs::
 jp 0
_isblank::
 jp 0
_isdigit::
 jp 0
_islower::
 jp 0
_isupper::
 jp 0
_isalnum::
 jp 0
_isalpha::
 jp 0
_iscntrl::
 jp 0
_isgraph::
 jp 0
_isprint::
 jp 0
_ispunct::
 jp 0
_isspace::
 jp 0
_isxdigit::
 jp 0
_tolower::
 jp 0
_toupper::
 jp 0
_cVectorVersion::
 .dw 0x0101

 ; 4000-4020 unused for now since intel Hex might overlap 4000

 ;; required c vector version the applet expects
 ;; C program must provide a const uint16_t  CVECTOR_VERSION = 0x0101; // adjust accordingly
 .globl _CVECTOR_VERSION

 .org 0x4020
 .dw _CVECTOR_VERSION
 
 ;; pointer to name of the applet
 ;; C program must provide a const char* const APPLET_NAME = "applet name here";
 .globl _APPLET_NAME
 .org 0x4022
 .dw _APPLET_NAME

 ;; pointer to name of the applet
 ;; C program must provide a const char* const APPLET_NAME = "applet name here";
 .globl _APPLET_TIMESTAMP
 .org 0x4024
 .dw _APPLET_TIMESTAMP

 ;; applet indicator--is these aren't 1,2,3,4, then the flash block is not an applet
 .org 0x4026
 .dw 0x0102
 .dw 0x0304

 .org 0x4030
init:
 ;; Set stack pointer directly above top of memory.
 ld hl, #0
 add hl, sp
 ld sp,#0xc000
 ;; Create a space to be used by bank switcher
 push hl

 ;; store the SP from the code that called this
 push hl

 ;; Initialise global variables
 call gsinit

 call _main

 ; return to code that called us
 pop hl
 ld sp, hl

 ret

 ;; Ordering of segments for the linker.
 .area _HOME
 .area _CODE
 .area _INITIALIZER
 .area   _GSINIT
 .area   _GSFINAL

 .area _DATA
 .area _INITIALIZED
 .area _BSEG
 .area   _BSS
 .area   _HEAP

 .area   _CODE

 .area   _GSINIT
gsinit::
 ld bc, #l__INITIALIZER
 ld a, b
 or a, c
 jr Z, gsinit_next
 ld de, #s__INITIALIZED
 ld hl, #s__INITIALIZER
 ldir
gsinit_next:

 .area   _GSFINAL
 ret


Here is how it's used. I define APPLET_NAME, APPLET_TIMESTAMP, and CVECTOR_VERSION, and main(). Next it is liniked with a Makefile that links with crt1 instead of crt0 and that uses --code-seg and --data-seg of 0x4020 and 0x8000.

Here is an example that I'm working toward.
clock.c:

#include <stdio.h>
#include "uart.h"
#include "timer.h"

const char* const APPLET_NAME = "clock";
const char* const APPLET_TIMESTAMP = __TIMESTAMP__;
const uint16_t CVECTOR_VERSION = 0x0101;

int clockId = -1;
uint8_t hour;
uint8_t min;
uint8_t sec;

void showClock()
{
    while (sec > 59)
    {
        sec = 0;
        min++;
    }
    while (min > 59)
    {
        min = 0;
        hour++;
    }
    while (hour > 12)
    {
        hour = 1;
    }
    printf("%02hd:%02hd:%02hd\r\n", hour, min, sec);
}

void clockCB(int timerId)
{
    timerId;
    sec++;
    showClock();
}

void main()
{
    char c;
    bool go = false;

    uartSupressRTS(false, false);

    if (clockId == -1)
    {
        clockId = timerGetAvailableTimerId();
    }
    if (clockId == -1)
    {
        return;
    }

    timerSet(clockId, 1000, true, false, clockCB);

    puts("'h' for hour, 'm' for minutes 'g' to go, 's' to stop");
    clockCB(clockId);
    while (!go)
    {
        c = getchar();
        switch (c)
        {
        case 'h':
            hour++;
            break;
        case 'm':
            min++;
            break;
        case 'M':
            min += 10;
            break;
        case 'g':
            timerRun(clockId);
            go = true;
            break;
        case 's':
            timerStop(clockId);
            go = true;
            break;
        }
        showClock();
    }

}


I'll need a menu function that switches a bank 1 and 2 in and then jumps to 0x4020. Also a menu item that lists all eligible banks with bank name and c vector version.

Sunday, September 20, 2015

Clock

I finally got around to starting to implement a clock feature in my menu program. I used the timer driver. It just waits for 1000ms and then outputs a time. To start, the menu goes into a loop that does getchar and then based on the letter pressed, does this:
'm': Increment minute
'M': Add 10 to minutes
'h': increment hour
'g': go--start the clock
's': stop--stops the clock

It works.

Thursday, September 17, 2015

Ticks

Last night, I decided to wire in the 1ms ticker. Of course, as soon as it wired in, the whole board stopped working. This morning I figured out that I had the INT and SCK lines swapped. So I unswapped them. I have already compiled in the tick driver so it should be grabbing the 3 bits of tick from the ticker. I verified that it is doing that when the UART interrupts occur.

I also verified that the INT pulls a pull-up low. But... I forgot that I had swapped out the 2MHz oscillator for a 4MHz oscillator. So my 1ms tick was actually 500μs. That was easy to fix. I ran home and grabbed my JTAG program and reburned the CPLD.

Now I had a 1ms tick. It wasn't counting correctly, but my code to handle the count wasn't right. Once that was corrected, it counted correctly. I can now make clocks.

However, there was an issue when I tried to program an intel hex file into flash--the tick makes the CPU too busy to keep up with communication. So, I rerouted the timer CPLD INT through a DIP switch. For now, I'll just turn off the timer when I want to program the flash. Until I can figure out a better way. I also have the 8MHz clock timer ready to burn. I may also toy with different tick amounts such as 2ms, or 5ms, or even 10ms.

Just for reference, here is the CPLD verilog:

// This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
// clock in. Scales that down to 125MHz with a 4 bit counter.
// Then it counts 125 of those in a 7 bit counter that resets on 7c.
// When the reset occurs, the pin_nINT line goes from Hi-Z to low.
//
// The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
//
// SCK and Dout are used to read the 3 bit tick counter. This is used
// to ensure that the count is kept synched. Dout is updated on the
// falling edge of SCK and the MSB is shifted first. 
//

`define COUNTER_TOP 11

module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    );

    reg [`COUNTER_TOP:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    wire nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;

    // Not synthesized. Just for testing
    initial begin
        counter = 0;
        ticks = 0;
    end

    // @ 2MHz 1ms, 124
    // @ 4MHz 1ms, 249
    // @ 8MHz 1ms, 499
    //assign nTick = ~(counter ==   11'b11111001111); // 124'15 causes 125'0->0'0
    assign nTick = ~(counter ==  12'b111110011111); // 124'31 causes 250'0->0'0
    //assign nTick = ~(counter == 13'b1111100111111); // 124'63 causes 500'0->0'0
    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

    always @(posedge pin_CLK) begin
        if (nTick == 1'b0) begin
            counter <= 0;
            ticks <= ticks+1;
        end
        else begin
            counter <= counter+1;
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

        // Set interrupt on the CLK. Reset it on the falling edge of
        // pin_nCS+2 clocks
        if (nTick == 1'b0) begin
            nInt <= 1'b0; 
        end
        else if ((nCS1 & (~nCS0)) == 1'b1) begin
            nInt <= 1'b1; 
        end

        // search for falling edge of SCK in CLK
        SCK0 <= pin_SCK;
        if (~pin_nCS) begin
            if (SCK0 & ~pin_SCK) begin
                tickShift[2:0] <= { tickShift[1:0], 1'b0 };
            end
        end
        else begin
            tickShift[2:0] <= ticks[2:0];
        end

    end

endmodule

Here is tick.c:

#include "tick.h"
#include "spi.h"
#include "gpio.h"
#include "idle.h"
#include "uart.h"
#include "timer.h"
#include "interrupt.h"

Z80_IO_PORT(GPIO_OUT, 0x80);
Z80_IO_PORT(SPI_REG, 0x83);
Z80_IO_PORT(SPI_CTRL, 0x84);

uint8_t lastTick = 0;
uint32_t totalTicks = 0;

void tickInit()
{
    // GPIO-0 is UART CS#
    gpioWriteLevel(0x02, 0x02);
    gpioWriteDirection(0x02, 0x02);
    lastTick = 0;
    totalTicks = 0;
}

uint32_t getTicks()
{
    uint32_t ticks;
    di();
    ticks = totalTicks;
    ei();
    return ticks;
}

// called from the actual ISR, so this one doesn't end in reti
void tickISR()
{
    uint8_t tick;
    uint8_t out;

    //tick = spiExchange(0, SPI_SHIFT_TO_MSB, 3);
    out = GPIO_OUT;
    GPIO_OUT = out & ~(1 << 1);
    SPI_REG = 0;
    SPI_CTRL = SPI_SHIFT_TO_MSB | 3;
    __asm__("nop");
    __asm__("nop");
    tick = SPI_REG;
    tick &= 0x07;
    GPIO_OUT = out;

    if (lastTick != tick)
    {
        totalTicks += (uint8_t)((tick - lastTick) & 0x07);
        lastTick = tick;
        timerTick(totalTicks);
        releaseIdle();
    }
}

So, next I have to start working with the timers and idle. One of my short term goals was to make a clock...now that's possible. Also, timeouts for communication.

I haven't used the timer code or idle yet. Here they are--probably buggy as hell since they are totally not tested.

timer.c:

#include "timer.h"
#include "tick.h"
#include <string.h>
#include <assert.h>

#define TIMER_COUNT 2

struct TimerInfo
{
    uint32_t interval;
    uint32_t trigger;
    bool claimed;
    bool repeat;
    bool running;
    void(*callback)(int timer);
};

struct TimerInfo timers[TIMER_COUNT];



void timerInit()
{
    memset(timers, sizeof(timers), 0);
}

// called from inside interrupt
void timerTick(uint32_t totalTicks)
{
    int timerId;
    for (timerId = 0; timerId < TIMER_COUNT; timerId++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (timer->claimed)
        {
            if (timer->running)
            {
                if ((int)(totalTicks - timer->trigger) >= 0)
                {
                    // tick has reached the trigger
                    if (timer->repeat)
                    {
                        timer->trigger += timer->interval;
                    }
                    if (timer->callback)
                    {
                        timer->callback(totalTicks);
                    }
                }
            }
        }
    }
}

int timerGetAvailableTimerId()
{
    int timerId = -1;
    int i;
    for (i = 0; i < TIMER_COUNT; i++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (!timer->claimed)
        {
            timer->claimed = 1;
            timerId = i;
            break;
        }
    }
    return timerId;
}

void timerFree(int timerId)
{
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timers[timerId].claimed = 0;
}

void timerSet(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    assert(timer->claimed);
    timer->interval = interval;
    timer->repeat = repeat;
    timer->running = start;
    timer->callback = cb;
    timer->trigger = getTicks() + interval;
}

void timerGet(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    if (pTrigger)
    {
        *pTrigger = timer->trigger;
    }
    if (pInterval)
    {
        *pInterval = timer->interval;
    }
    if (pRepeat)
    {
        *pRepeat = timer->repeat;
    }
    if (pRunning)
    {
        *pRunning = timer->running;
    }
    if (pCb)
    {
        *pCb = timer->callback;
    }
}

void timerStop(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 0;
}

void timerRun(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 1;
}

void timerResetInterval(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->trigger = getTicks() + timer->interval;
}


idle.c:

#include "idle.h"
#include "interrupt.h"

volatile bool idling;

// DO NOT CALL FROM CRITICAL SECTION
void idle(void(*pIdleFn)(void* p), void* p)
{
    idling = true;
    // assert(interrupts enabled);

    while (idling)
    {
        // spinwait for tick to set idling to false
        if (pIdleFn)
        {
            pIdleFn(p);
        }
    }
}

void releaseIdle()
{
    idling = false;
}



Wednesday, September 16, 2015

UART Driver Improved But Still Slow

I did a lot of scoping of the UART driver. And while I got some minor improvements by hand optimizing some slow parts, the driver is still not super swift. I still have a few questions, about some things, but I also added a tweak--I made it possible to suppress echo on gets. Now when the intel hex file goes across, instead of echoing it back, I just output a '.' to show that something was programmed. This makes the process fairly quick. I still need a to improve the speed of the flash activation.

Also, I have been using the native facilities to program the flash. I no longer use the Arduino. The wires between Arduino and the board are soldered into the board, but I have plans for that. But for now, the Arduino is docked to the side of my board. The computer runs with it not powered up.

Tuesday, September 15, 2015

Bank Switch Works But UART Driver Has An Issue

I got the bank switch stuff working. The interrupt.c wasn't hooking up to the membank.c ISR. So it was naturally doing the bankSwitch, but not the bankSwitchAndJump. Once it was properly hooked up, it started working. Now I can load an image into a flash bank and jump to it. That will be useful.

I also am puzzled that my communication seems so slow. Indeed, when the UART starts to send, it takes 3.2ms before it starts to send the next byte. The interrupt is asserted almost immediately after the send and it is quickly managed. So, somehow, I'm losing the interrupt on transmit buffer empty without filling it and then filling it at some later time...?

Anyway, I'm now bringing all my powers of deduction on that problem. I'm also thinking of not echoing gets for intel hex programming. I already have a 3 extension function for limiting the buffer size. Limiting echo is just one more such function. Combined with an assembly flash activation function call, this should make flash programming much quicker.....

Well, off to solve my problems...

More Menu Goodness

I'm working on making the menu more useable. It's starting to take a while to load the program on the Arduino. But it takes 11 seconds just to copy 16k into flash natively. I could turn the flash activate into assembly to speed it up quite a bit.

I have also added a menu item that just lets me run from RAM. It asks for a RAM bank, puts into bank 1, copies 0-3fff to 4000, then puts it into bank 0 restoring the original bank 1.

The separate copy RAM and copy flash are now one menu item that looks at the banks to decide how to copy.

I added GPIO access.

Also, I am in the process of adding a diagnostic system. This will let me set up diagnostic points that will either output data on the SPI bus or modify GPIO pins. I'll be able to activate and deactivate them. This should help me debug with the scope.

Finally, as can be seen below, I am adding build times for the libraries and application.


Mark Hamann's Z80 Computer

App Build: 21:04:14 Sep 15 2015
BSP Build: 21:04:13 Sep 15 2015
C Lib Build: 08:14:07 Sep 15 2015
Menu

1) run from RAM
2) dump mem
3) copy block
4) erase sectors
5) program byte
6) write byte
7) bank switch
8) program intel hex
9) set diagnostics
10) gpio
> 1

And now I have a menu item (not shown above) that lets me swap a flash bank into bank 0 and run from 0. When I do that, I need to hit enter to see the initial menu--so something is still a not quite right, But it's close....

Monday, September 14, 2015

Native Flash Programming Works!

I got native flash programming to work. It's not super fast,. but it's faster than the Arduino. And less complicated---errr it has the potential to be less complicated. Right now I have to manually copy the 0x0000-0x4000 to 0x8000 and then swap that bank into 0x0000. This means that the program is now running out of RAM instead of flash. And I need that if I want to be able to program or erase the flash.

It's also a manual input as to whether the intel hex module should program flash or write RAM. But really, I should be able to use the address to lookup the bank registers and use that.

So I have some tweaking to do.

I'm not sure I want to disconnect my Arduino quite yet. I do want the real estate to mount the SD card socket--but that's for future fun stuff.

I also tried to up the baud rate to 19200, but that didn't work. I'm a bit puzzled. 9600 baud is a character per ms. 19200 is 2 per ms. Can I really not get at the UART in time? Something is strange. If I can figure that out, maybe I can speed up communication.

Anyway, off to improve my menu system before I do too much more. Make it more useful and less cumbersome to use.

Then, I guess it's finally time to start getting the 1ms tick CPLD hooked up and the timers working. Then I just need a crt1 that I'll use for applications that use 4000-7fff in flash and 8000-bfff in RAM but that use C library functions in 0100-3fff.

Flash Programming In System

After last night's discovery that I need to rethink how I program flash, the solution turned out to be pretty painless.

I modified the flash.c to handle the flash in system. Here is flash.c with a new function, activateFlash(uint8_t cmd, bool doCycle4_5):


#include <assert.h>
#include "common.h"
#include "flash.h"
#include "memBank.h"

// reads the flash and waits for the toggle bit to stop toggling.
// returns true if the read byte matched expected. Use 0xff when
// erasing
bool toggle(volatile uint8_t* vaddr, uint8_t expected)
{
    uint8_t b0 = *vaddr;
    uint8_t b1 = *vaddr;
    while (b0 != b1) {
        b0 = b1;
        b1 = *vaddr;
    }
    return b0 == expected;
}

// Flash activation from within the system requires the following:
// 1) program must be running from RAM
// 2) to activate the flash, specific bytes must be written to
//    2aaa and 5555 of the flash AFTER bank switching. This
//    means that banks 0 and 1 must be mapped into memory and
//    2aaa+offset and 5555+offset be programmed
// 3) the bank the user is interested in must be swapped in
//    before the action address is written.
void activateFlash(uint8_t cmd, bool doCycle4_5)
{
    volatile uint8_t* v2aaa = (uint8_t*)(0x2aaa + 0x4000);
    volatile uint8_t* v5555 = (uint8_t*)(0x5555 + 0x4000);
    uint8_t oldBanks[4];
    uint8_t banks[4];

    getBanks(banks);
    getBanks(oldBanks);
    banks[1] = 0;
    banks[2] = 1;
    setBanks(banks);
    switchBanks();

    *v5555 = (uint8_t)0xaa;
    *v2aaa = (uint8_t)0x55;
    *v5555 = cmd;

    if (doCycle4_5)
    {
        *v5555 = (uint8_t)0xaa;
        *v2aaa = (uint8_t)0x55;
    }

    setBanks(oldBanks);
    switchBanks();
}

// Programs a byte to flash. Returns true if the byte matches
// when toggling is finished
bool flashProgramByte(uint8_t b, uint8_t* addr)
{
    volatile uint8_t* vaddr = addr;
    activateFlash(0xa0, false);
    *vaddr = b;
    return toggle(vaddr, b);
}

// Programs a block to flash. Returns true if all the bytes match
// when toggling is finished.
bool flashProgramBlock(uint8_t* b, uint16_t count, uint8_t* addr)
{
    uint16_t i;
    bool ok = true;
    volatile uint8_t* vaddr = addr;
    for (i = 0; i < count; i++)
    {
        ok = ok && flashProgramByte(b[i], vaddr + i);
    }
    return ok;
}

// Erase a 4k sector. Sector must be 0-15
bool flashEraseSector(uint8_t sector)
{
    volatile uint8_t* vaddr = (uint8_t*)(sector<<12);
    assert(sector < 16);
    activateFlash(0x80, true);
    *vaddr = (uint8_t)0x30;
    return toggle(vaddr, 0xff);
}

// Returns true if all bytes in a sector are 0xff
bool flashVerifyErased(uint8_t sector)
{
    volatile uint8_t* vaddr = (uint8_t*)(sector << 12);
    uint16_t i;
    assert(sector < 16);
    for (i = 0; i < 0x1000; i++)
    {
        if (vaddr[i] != 0xff)
        {
            return false;
        }
    }
    return true;
}


I added a new function to activate the flash. It gets the current bank setup, swaps in banks 0 and 1 of flash, activates the flash, and restores the banks.

And here is my menu running:


Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 2
This will copy memory to RAM memory.
Source (hex)? 0
Dest (hex)? 8000
Length (hex)? 4000
Copied
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 8000
Length (hex)? 20
-8000: c3 69 00 ff ff ff ff ff  c3 a3 13 ff ff ff ff ff .i...... ........
-7ff0: c3 b4 13 ff ff ff ff ff  c3 c5 13 ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 6
This allows to switch all 4 banks
Only switch bank 0 when
Use hex where 0-f are flash and 10-17 are RAM
  bank #0 [now==00]? 10
  bank #1 [now==01]? 2
  bank #2 [now==10]? 2
  bank #3 [now==11]? 11
Switching...
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 3ff0
Length (hex)? 10
3ff0: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 5
This will write a byte.
Address (hex)? 3fff
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 3ff0
Length (hex)? 10
3ff0: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff 00 ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 4
This will program a byte.
Address (hex)? 4000
value (hex)? fe
Programmed

Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 4
This will program a byte.
Address (hex)? 4001
value (hex)? fb
Programmed

Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe fb ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
>

The next step was to see if I could erase it--but that took a couple of code changes first... But here is the result:

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 2
This will copy memory to RAM memory.
Source (hex)? 0
Dest (hex)? 8000
Length (hex)? 4000
Copied
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 6
This allows to switch all 4 banks
Only switch bank 0 when
Use hex where 0-f are flash and 10-17 are RAM
  bank #0 [now==00]? 10
  bank #1 [now==01]? 2
  bank #2 [now==10]? 2
  bank #3 [now==11]? 11
Switching...
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe fb ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 3
This allows erase a sector. Ensure the sector is flash
sector (hex: 0-f)? 4
Erased and verified
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
>


As you can see, the fe and fb that I programmed into bank 2 were erased. Yay!!!!

With this, I'm closer to having the ihx file uploading. I just need to do a visual inspection of my ihx upload code and then I'll be ready to load it into my menu. But first, I need a program block menu item that will copy a block into flash.

I also need to handle backspace better. It seems to work on the screen, but my hex conversion returns an error if I use the gets string where backspace happened. Also, the buffer limit for gets isn't working quite right.

Sunday, September 13, 2015

Bank Switching and Native Flash Programming

I think I got my bank switching stuff working. In fact, it worked right out of the box--no debugging necessary. Which is a little worrisome. But, I added some menu items for programming flash and writing RAM and bank switching is working as expected.

But, then I ran into a problem.... To put the flash chip into the modes for programming and/or erasing, I need to write to 2aaa and 5555 in specific sequences. But those addresses are from the flash chip's point of view--after any bank switching. It's obvious now, but it didn't occur to me before.

That means, I need the lowest 32K of flash to live in 32k of addressable memory for that brief time. And once in that mode, I can't touch the flash except for whatever programming/erasing I need to do.

Since my bank granularity is 16k, this is a bit of a problem. It's going to take a little creativity.

Bit 15 doesn't matter. But bit 14 does. So when I write to 2aaa, it has to be a to bank where bit 14 is 0. And when I write 5555, it has to be to a bank where bit 14 is 1. Right now, the flash code just writes 2aaa and 5555, but actually, I'm going to need to ensure than either 1) 2 banks are loaded in and the flash programmer has the appropriate addresses that map to post-bank switch of 2aaa and 5555 or 2) that the banks are switched between the writes to 2aaa, 5555, and whatever the user wants to write.

Obviously the first one is easier.

Basically, the steps I need to follow are these:

  1. copy the flash programming code into a RAM bank
  2. swap that RAM bank into bank 0 and run from there
  3. swap the flash banks 00 and 01 into banks 1 and 2
  4. activate the flash mode with 2aaa+4000 and 5555+4000. These map to 2aaa and 5555
  5. swap the flash bank to be programmed into bank 1
  6. perform the write that will program or erase
So back to the drawing board on my flash driver. It's not going to be too bad. Unless there is something else I'm overlooking.

Menu Doesn't Work Yet

My program that just does gets and puts what was sent worked. But not my latest effort to get a menu program up and running. It doesn't even seem to get to main().

Hmmm... I noticed that the compiler is inserting it's own mult and div functions which it needs for atoi. Those were being located in RAM. That turned out to because I removed the _HOME section (among others) from crt0.s when I was trying to get things working. Now, I added it back in and those automatically added arithmetic functions are going to the right place.

Then, all of a sudden the flash programmer started failing. I traced that to the fact I was using the start rather than the iterator in the for loop that erases the sectors in the Arduino, so it was just erasing the first sector over and over. Which was fine when my program fit into one sector.... Literal growing pains.

And.....I seem to be in main() now executing an endless loop of basic pin manipulation that is appearing on the scope as hoped..... Whew!

By the way, I added a "C extension" which lets me specify a maximum buffer length for gets. If the user goes too high, it putchars a BEL. That will either beep or make puTTY flash.

And, now the puts works, but my printf with a %s is spamming a zero. Since the string's first character is 0, that's progress.....

There, fixed that bug.....

Now, I'm getting my first menu item spammed over and over....

That was easy enough to fix.....

And now it shows the menu and takes a value. But it didn't seem to execute the code it's supposed to.....

OK, my prompt code actually returns the zero based index and I was decrementing it....

And one more bug in my prompt interpreter.....

And voila!


Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
> 1
Read: 0
Executing...

Address (hex)? 0
Length (hex)? 20
000: c3 69 0 ff ff ff ff ff  c3 72 c ff ff ff ff ff .i...... .r......
c3 83 c ff ff ff ff ff  c3 94 c ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
>


So I've got a couple of issues to take care of, but from having the program not working a few hours ago to this isn't too bad.

Time to publish this post....

Saturday, September 12, 2015

Taming The Make System

I've never enjoyed gnu make. Most people I know don't, but it's a necessary evil. So I took some time out to finally get it into some shape. So, now I have a new SW structure. Here is my new directory structure:


And here are my makefiles and includes:

SW/Makefile:


SW_DIR= $(shell pwd | sed 's/\/cygdrive\/c\//C:\\\\/' | sed 's/\//\\\\/g')
SW_DIR_CYG= $(shell pwd)
CFLAGS_Z80=-mz80
CFLAGS_GENERIC=-c -DNDEBUG
AFLAGS=-o
LFLAGS= -mz80 --no-std-crt0 --nostdlib --code-loc 0x100 --data-loc 0x8000
INCLUDES=-I${SW_DIR}/include -I${SW_DIR}/app/include
INCLUDES_CYG=-I${SW_DIR_CYG}/include -I${SW_DIR_CYG}/app/include
TOOLDIR=/cygdrive/c/Program\ Files/SDCC/bin
LIBDIR=lib

MAKEARGS="CFLAGS_Z80=${CFLAGS_Z80}" "CFLAGS_GENERIC=${CFLAGS_GENERIC}" "INCLUDES=${INCLUDES}" "AFLAGS=${AFLAGS}" "TOOLDIR=${TOOLDIR}" "LIBDIR=../${LIBDIR}" "BASEDIR=.." "LFLAGS=${LFLAGS}" "INCLUDES_CYG=${INCLUDES_CYG}"


# This makefile is the top level makefile that calls down to all the submakes
# It will get the current directory, basic flags, and then drill down
# includes are 
#

#

.PHONY: all c bsp clean depends clean_bsp clean_app clean_c clean_lib

all:
 cd app ; ${MAKE} all ${MAKEARGS} "DIRS=test" "PROG=mainRW"
# cd app ; ${MAKE} all ${MAKEARGS} "DIRS=menu" "PROG=menu"

c:
 cd c_src ; ${MAKE} all ${MAKEARGS}

bsp:
 cd bsp ; ${MAKE} all ${MAKEARGS} "DIRS=crt0  diag  flash  gpio  interrupt  membank  spi  tick  uart" "LIB=bsp"
 cd app ; ${MAKE} all ${MAKEARGS} "DIRS=idle intelHex ringBuffer timer" "LIB=bsp"

clean: clean_bsp clean_app clean_c clean_lib

clean_bsp:
 cd bsp ; ${MAKE} clean ${MAKEARGS} "DIRS=crt0  diag  flash  gpio  interrupt  membank  spi  tick  uart" "LIB=bsp"

clean_app:
 cd app ; ${MAKE} clean ${MAKEARGS} "DIRS=idle intelHex ringBuffer  test  timer menu test" "LIB=bsp"

clean_c:
 cd c_src; ${MAKE} clean ${MAKEARGS}

clean_lib:
 cd lib ; rm -f *.lib; rm -f *.rel
 
depends:
 cd app ; ${MAKE} depends ${MAKEARGS}

test:
 cd bsp; pwd; make test ${MAKEARGS}



SW/common.inc:


COMMON=INCUDED

MAKEARGS="CFLAGS_Z80=${CFLAGS_Z80}" "CFLAGS_GENERIC=${CFLAGS_GENERIC}" "INCLUDES=${INCLUDES}" "AFLAGS=${AFLAGS}" "TOOLDIR=${TOOLDIR}" "LIBDIR=../${LIBDIR}" "BASEDIR=../${BASEDIR}"


SW/subdirs.inc:


SUBDIRS=INCUDED

.PHONY: all clean

all:
 echo ${DIRS}
 @for i in ${DIRS} ; do \
        cd $$i; \
  make all ${MAKEARGS}; \
  cd ..; \
    done

clean:
 echo ${DIRS}
 @for i in ${DIRS} ; do \
        cd $$i; \
  make clean ${MAKEARGS}; \
  cd ..; \
    done


SW/generate.inc:


CC=${TOOLDIR}/sdcc.exe
AS=${TOOLDIR}/sdasz80.exe
AR=${TOOLDIR}/sdcclib.exe

C_SOURCES=$(wildcard *.c)
S_SOURCES=$(wildcard *.s)
REL_C=$(patsubst %.c,%.rel, ${C_SOURCES})
REL_S=$(patsubst %.s,%.rel ,${S_SOURCES})

all: ${REL_C} ${REL_S}

# - keeps it from crashing out if no *.d file
-include *.d

clean:
 rm -f *.d
 rm -f *.lib
 rm -f *.rel
 rm -f *.asm
 rm -f *.lst
 rm -f *.sym

%.rel : %.s
 ${AS} -g ${AFLAGS} $<
 ${AR} r ${LIBDIR}/${LIB}.lib $@

%.rel : %.c
 ${CC} ${CFLAGS_Z80} ${CFLAGS_GENERIC} ${INCLUDES} $<
 ${AR} r ${LIBDIR}/${LIB}.lib $@

%.d : %.c
 gcc ${CFLAGS_GENERIC} ${INCLUDES_CYG} -MM $< > $*.d


The Makefiles found in directories with no code:


include ${BASEDIR}/common.inc
include ${BASEDIR}/subdirs.inc


The Makefiles found in directories with code:


include ${BASEDIR}/common.inc
include ${BASEDIR}/generate.inc


SW/bsp/crt0 has a special makefile since crt0 isn't archived but copied to SW/lib:


include ${BASEDIR}/common.inc

# This makefile is unique because the crt0 isn't archived. Rather it must be the first item in the list of rel/ar given to the linker
CC=${TOOLDIR}/sdcc.exe
AS=${TOOLDIR}/sdasz80.exe
AR=${TOOLDIR}/sdcclib.exe

C_SOURCES=$(wildcard *.c)
S_SOURCES=$(wildcard *.s)
REL_C=$(patsubst %.c,%.rel, ${C_SOURCES})
REL_S=$(patsubst %.s,%.rel ,${S_SOURCES})

all: ${REL_C} ${REL_S}

clean:
 rm -f *.d
 rm -f *.lib
 rm -f *.rel
 rm -f *.asm
 rm -f *.lst
 rm -f *.sym

%.rel : %.s
 ${AS} -g ${AFLAGS} $<
# ${AR} rc ${LIBDIR}/${LIB} $@
 cp crt0.rel ${LIBDIR}/

%.rel : %.c
 ${CC} ${CFLAGS_Z80} ${CFLAGS_GENERIC} ${INCLUDES} $<
# ${AR} rc ${LIBDIR}/${LIB}.ar $@

And the common C directory also has a special Makefile:


include ${BASEDIR}/common.inc

CC=${TOOLDIR}/sdcc.exe
AS=${TOOLDIR}/sdasz80.exe
AR=${TOOLDIR}/sdcclib.exe


C_SOURCES= _calloc.c _divslong.c _divulong.c _free.c _heap.c _itoa.c _ltoa.c _malloc.c _memchr.c _memcmp.c _memcpy.c _memset.c _modslong.c _modulong.c _mullong.c _realloc.c _startup.c _strcat.c _strchr.c _strcmp.c _strcspn.c _strncat.c _strncmp.c _strncpy.c _strpbrk.c _strrchr.c _strspn.c _strstr.c _strtok.c assert.c atoi.c atol.c errno.c gets.c isalnum.c isalpha.c isblank.c iscntrl.c isdigit.c isgraph.c islower.c isprint.c ispunct.c isspace.c isupper.c isxdigit.c labs.c puts.c rand.c sprintf.c strxfrm.c time.c tolower.c toupper.c vprintf.c 

# _divschar.c _modschar.c _mulschar.c _setjmp.c _strcpy.c abs.c _memmove.c _strlen.c _modsint.c _moduint.c _mulint.c _divsint.c _divuint.c

C_LONG_LONG_SOURCES= _divslonglong.c _divulonglong.c _modslonglong.c _modulonglong.c _mullonglong.c _rlslonglong.c _rlulonglong.c _rrslonglong.c _rrulonglong.c atoll.c

C_FLOAT_SOURCES= _atof.c _fs2schar.c _fs2sint.c _fs2slong.c _fs2uchar.c _fs2uint.c _fs2ulong.c _fsadd.c _fscmp.c _fsdiv.c _fseq.c _fsget1arg.c _fsget2args.c _fsgt.c _fslt.c _fsmul.c _fsneq.c _fsnormalize.c _fsreturnval.c _fsrshift.c _fssub.c _fsswapargs.c _logexpf.c _schar2fs.c _sint2fs.c _slong2fs.c _uchar2fs.c _uint2fs.c _ulong2fs.c acosf.c asincosf.c asinf.c atan2f.c atanf.c ceilf.c cosf.c coshf.c cotf.c expf.c fabsf.c floorf.c frexpf.c ldexpf.c log10f.c logf.c modff.c powf.c sincosf.c sincoshf.c sinf.c sinhf.c sqrtf.c tancotf.c tanf.c tanhf.c

REL_C_SOURCES=$(patsubst %.c,%.rel, ${C_SOURCES})
REL_C_LONG_LONG_SOURCES=$(patsubst %.c,%.rel, ${C_LONG_LONG_SOURCES})
REL_C_FLOAT_SOURCES=$(patsubst %.c,%.rel, ${C_FLOAT_SOURCES})
REL_S=$(patsubst %.s,%.rel ,${S_SOURCES})

.PHONY: all z80

all: c_lib float_lib longlong_lib z80

z80:
 cd z80; make all ${MAKEARGS} "LIB=c"

c_lib: ${REL_C_SOURCES} ${REL_S}
 ${AR} r ${LIBDIR}/c.lib ${REL_C_SOURCES} ${REL_S}
 
float_lib: ${REL_C_FLOAT_SOURCES}
 ${AR} r ${LIBDIR}/float.lib ${REL_C_FLOAT_SOURCES}
 
longlong_lib: ${REL_C_LONG_LONG_SOURCES}
 ${AR} r ${LIBDIR}/longlong.lib ${REL_C_LONG_LONG_SOURCES}
 
%.rel : %.s
 ${AS} -g ${AFLAGS} $<

%.rel : %.c
 ${CC} ${CFLAGS_Z80} ${CFLAGS_GENERIC} ${INCLUDES} $<

clean:
 rm -f *.d
 rm -f *.lib
 rm -f *.rel
 rm -f *.asm
 rm -f *.lst
 rm -f *.sym
 cd z80; make clean ${MAKEARGS}


And this is how I make a clean build of the program I'm working with:


$> make clean
$> make all


It's not perfect. It's probably not even good. But it's better than the ad hoc Makefiles I had before because it's more scalable and extensible.

Thursday, September 10, 2015

RTS/CTS Scheme Finally Works

Communication isn't easy. It's easier with computers than with women, but even with computers, the datasheets can be ambiguous.

Getting RTS/CTS flow control working was a bit of a challenge and I had to try a few things before I finally got something that worked.

The problem I had was that the CPU couldn't keep up a long continuous stretch of text. If I'm going to use puTTY to just send over an intel hex file, I need it to keep up. All attempts to get it to stop communication by deasserting RTS weren't fast enough and the internal FIFO in the UART (8 bytes) overflows. This will probably always be a potential problem.

The scheme I finally had to settle on is to keep RTS deasserted (suppressed) except when in getchar and when the receive ring buffer is empty. Immediately upon getting data, the RTS is deasserted. Hopefully an overflow won't happen. It does in 115200 baud, though. And this is after my digikey order arrived and I replaced the 2MHz oscillator with a 4MHz oscillator.

Anyway, I can put a pile of stuff on the clipboard, paste it into puTTY, and watch it echo completely and correctly and long as I don't overflow my buffer I use to call gets.

So a question comes up--what if I wanted to use 115200 baud? I could use a couple of pins on my remaining under-utilized CPLD and combine the IRQ from the UART with the RTS output of the UART. A sort of "assign nRTSout = nRTSin | ~nIRQ;" which would deassert RTS when the interrupt is activated. Of course, the interrupt is open collector wired AND, so the timer tick would also interfere. Though since it's all on the same CPLD, the IRQ from the UART could come to it and then they could combine in the CPLD and an output of the CPLD could drive the INT pin of the Z-80.

But that's something to think about for v2.0.

Anyway, now it's time to start cleaning up the code and making it presentable. Then I can can say that I have ticked off the UART driver sufficiently to start working on the other stuff which is going to enhance my productivity--printf, flash programming, and bank switching. I also got a few other parts that I'll install gradually over the next few days.

But it's 11:30pm so time to call it a night.....

Wednesday, September 9, 2015

Refactoring And Scope Screen Shot

I spent some time refactoring and simplifying the UART driver in hopes of making it easier to understand and also faster. When it's working perfectly, I'll post the code.

Right now it works fine for just typing stuff. But it can't keep up with spam coming in. Even at 9600, it can't keep up. This is the RX and CTS lines. You can see the data is only sent when RTS is low (in RS232 high is low). So I may change the scheme up a bit to make RTS a stricter gatekeeper. I need to ensure reliable communication because I want to send intel hex files to a native flash burner.


Also, I need to start putting in more waveforms. This was my first waveform pulled off the scope.

Tuesday, September 8, 2015

More Orders

I ordered a BNC - banana clips thingy for external triggering. That will, in many cases, free up a probe for something more useful.

Also a SD micro card and adapter--since I eventually want to access it on the SPI bus.

Also, an order into DigiKey:

 14EG4686CT-NDSWITCH SNAP SUB-MINI 70GF 125V4
Immediate
0
0.93000$3.72
 24RPC1316-NDBRD SPT SNAP LOCK NYLON 7/8"4
Immediate
0
0.42000$1.68
 31478-4059-1-NDCONN SD CARD PUSH-PULL R/A SMD1
Immediate
0
3.03000$3.03
 41SER1707-NDOSC XO 8.000MHZ CMOS TTL PC PIN1
Immediate
0
2.21000$2.21
 51SER1703-NDOSC XO 4.000MHZ CMOS TTL PC PIN1
Immediate
0
2.21000$2.21
 62SAM1112-06-NDCONN HEADER .100" 6POS TIN PCB0
Immediate
2
Value Add
1.20000$2.40
 71CP-024B-NDCONN DC POWER RECEPT 5.5 X 2.5MM1
Immediate
0
2.62000$2.62
 81CP-024A-NDCONN DC POWER RECEPT 5.5 X 2.1MM1
Immediate
0
2.84000$2.84
 91G17S0900110EU-NDCONN DSUB RCPT 9POS STR SLDR CUP1
Immediate
0
0.86000$0.86
 102SAM1119-06-NDCONN RCPT .100" 6POS TIN T/H0
Immediate
2
Value Add
0.80000$1.60
 111HS411-NDBOARD LEVEL HEATSINK .375"TO-2201
Immediate
0
0.45000$0.45