Skip to content

1806 Standard Call and Return Instructions

17-02-19-1806-scal

CDP1806 Standard Call/Return Instructions

The 1802 has no native subroutine call and return operations. For small programs there’s a very quick way of switching program counters that’s arguably better and for more general cases there’s a Standard Call and Return convention(SCRT) that uses registers 4,5 and 6. It’s very well designed though a bit slow and it does tie up 3 registers. The 1806 incorporates two new instructions SCAL(688N) and SRET(689N) that speed up the process and avoids dedicating registers 4,5 and 6 (you still need a register for the return address but it doesn’t have to be 6).

Unfortunately for me the 1806 SCAL/SRET are different from the routines I use in the compiler. They assume that the stack pointer points to the first byte free byte below the stack and can be used freely. The compiler assumes that the stack pointer addresses the last USED byte on the stack and needs to be decremented to point to free memory.

This affects the way parameters and variables are stored and addressed on the stack. One of the parts of LCC that I had the toughest time coming to grips with. I think I could paper over part of them by decrementing SP before each SCAL and incrementing it before each SRET. I would then have to add one to the offset for accessing parameters but variable addressing wouldn’t change. I’ll have to fire up some test cases and try them on an emulator that supports the 1806 instruction set.

For insurance I’ll also make sure that R2 is the X register on each call.  It’s an extra instruction but it has saved me from myself in the past.

;Standard Call routine invoked as D4xxxx - big-endian stack convention
	sep     R3 ;go to subroutine
_call	sex	SP ;make sure X=SP
	glo	retAddr ;save previous return pointer on stack
	dec	sp
	stxd
	ghi	retAddr
	str	sp
	glo	RPC ;copy old PC to retAddr
	plo	retAddr
	ghi	RPC
	phi	retAddr
	lda	retAddr ;pick up subroutine address into RPC
	phi	RPC
	lda	retAddr
	plo	RPC
	br	_call-1

;Standard subroutine return
	sep	RPC	;return to the original program
_return	glo	retAddr	;transfer the current return address to RPC
	plo	RPC
	ghi	retAddr
	phi	RPC
	lda	SP	;pick up old return address
	phi	retAddr
	lda	SP
	plo	retAddr
	br	_return-1

 

Host I/O Updates to the AVR

Over the last week or so i’ve made a bunch of changes to the AVR code to do with host I/O basically to support the xmodem bootloader but more generally to allow bidirectional serial data.

The external changes are:

  • the AVR program can be put in quiet mode by entering ‘p’ quickly after startup, entering ‘q’.  Quiet mode eliminates the run1802 message and shortens the startup delay to 1 second
  • the AVR recognizes STX in the input stream from the 1802 and enables interrupts so host serial data can be handled properly.  ETX disables interrupts so that SPI doesn’t get screwed up.
  • The AVR recognizes 0x12 in the input stream as a request to read serial.available().  The AVR puts the value into the input shift register so the 1802 can read it with an INP(6);
  • The AVR recognizes 0x00 as a request to read a character from the serial buffer
  • The AVR recognizes 0x1B as an escape so that following character is passed on without interpretation.

Additionally, there are a couple of housekeeping changes related to moving to arduino 1.6.5: char1802 is defined unsigned char and output with _BYTE; a #define of MISO was removed because it’s in the standard defines.

ALSO, I increased the size of the input serial buffer from 64 to 256 bytes.  I did that by editing
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h and replacing the 64 with 256.

This will get lost when i upgrade the arduino but there doesn’t seem to be a better option for the moment.

//now needs to be compiled with 1.6.5 and the minicore addon.
//ISPV6 meant for use with olduino_2.0_V4E/V8 hardware AND NEW AVRDUDE
//this sketch turns the Arduino into a loader for the 1802 membership card
//adapted from the arduinoisp sketch included with arduino-022 
//with credit to David Mellis and Randall Bohen
//July 12 2012 - revamped from aduinoisp1802v8
//mostly pin changes to start
//August 1 - dinking around expanding ? code trying to debug verification failure
//aug 9 changing ! to ~ in interface() ddr settings
//aug 10/11 changed run code to make memrd output and low.
//saved back to olduinoispv2_2
//Aug 16 trying shorter timeout delay (1 sec)
//August 18 trying 57600 baud
//Jan 15, 2014 first build for Olduino V@.0 - all serial/spi
//Jan 26, fixing spi clocking logic in justclock
//Mar 10, 2014 speeding up busout/busin for program loading
//April 3, 2014 moving clock pins to PORTC SCK=PC1, /SCK=PC0, circuit olduino_2.0_v2b.sch
//May 25, 2014 pin definitions changed to match olduino_2.0_v4e schematic
//ldsw,clrsw,datapin=mosi,spiclk,/srclk,srclk,clockbvs
//June 12 speeding up clocking and tightening up timing - 1st try @20mhz
//Aug 10 - increasing presstimeu to 20 us
//aug 10 -presstimeu 
//15-01-13 avrdude is requesting version with a 5630 sequence instead of 75'
//17-01-23 experimenting with sending data from host to 1802 program
//         also had to change MISO to MISOX to avoid compile error
//??-??-?? changed char1802 in run1802 to unsigned char cout <<_BYTE(...
//17-02-06 verbose/quites runmode controls run1802 msg
//17-02-14 stx/etx trigger enable/disable interrupts
#define INsw 5 // IN bus pin 27 - normally high
#define presstimeu 20 //us to hold input switch down
#define LDsw 8 // /LOAD(/WAIT) bus pin 29 - arduino PB0 14
#define CLRsw 7 // /CLEAR bus pin 28 - ard pd7 13
#define MEMrd  0 //not connected
//all comm to 1802 is serial.  datapin is connected to the output of MOSR
//MISOX is connected to the input of the 1802's MISR
//sr_sck loads a bit into MISR 
//spi_sck tells other devices to grab top bit of MOSR
//notsr_sck advances MOSR and clocks the data in MISR To the storage register
#define datapin 9 //pin to receive shift register data
#define MISOX 12   //output pin hooked to 1802's input shift register
#define MISOPORT PORTB
#define MISODDR DDRB
#define MISOBIT 4  //miso is on port B bit 4
#define sr_sck 15 //pin to clock the data in the shift registers
#define notsr_sck 16 //inverse of sr_sck
#define spi_sck 14 //clock seen at the spi connector.
#define clockport PORTC
#define clockddr DDRC
#define clockbv 3  //both clocks high
#define notclockbv 4 //clocks low, notclock high
#define N0pin 2    //N0 is the 1802 signalling to do host output
#define OUT2pin 6  //signals the 1802 is writing to MOSR
#include <Streaming.h>
#define cout Serial
#define endl '\n'

#define HWVER 2
#define SWMAJ 5
#define SWMIN 0

// STK Definitions
#define STK_OK 0x10
#define STK_FAILED 0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC 0x14
#define STK_NOSYNC 0x15
#define CRC_EOP 0x20 //ok it is a space...

unsigned long timeoutstart, timeoutinterval=2000; //used to timeout the loader and start the olduino
byte state1802=0;
#define running 1
#define reset 2
#define loading 3
//char runmode; //setting this to 'v' means verbose
char runmode __attribute__ ((section (".noinit")));

void setup() {
  Serial.begin(57600);
  //delay(2000);
  //cout<<"OlduinoII_ISPV6.1 here with runmode="<<runmode<<'\n';
  DDRB=0; //all B pins are inputs (PB5 will have been left as an output by the bootloader)
  digitalWrite(sr_sck,LOW); digitalWrite(spi_sck,LOW);digitalWrite(notsr_sck,HIGH);
  pinMode(sr_sck,OUTPUT); pinMode(notsr_sck,OUTPUT);pinMode(spi_sck,OUTPUT);//set up to drive the shift register
 //doitall(); 
   //while(1); //loop
  timeoutstart=millis(); //if we don't get anything within timeouttime ms we start the 1802 running
  reset1802();

  //while(1){
  //  justclock();
  //}
}
int error=0;
int pmode=0;
int here; // address for reading and writing, set by 'U' command
uint8_t buff[256]; // global block storage

#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
  uint8_t devicecode;
  uint8_t revision;
  uint8_t progtype;
  uint8_t parmode;
  uint8_t polling;
  uint8_t selftimed;
  uint8_t lockbytes;
  uint8_t fusebytes;
  int flashpoll;
  int eeprompoll;
  int pagesize;
  int eepromsize;
  int flashsize;
} 
parameter;

parameter param;  

void loop(void) {
  uint8_t char1802=0x55;
  if (Serial.available()) {
    avrisp();
    timeoutstart=millis();
  }
  if(millis() - timeoutstart > timeoutinterval){
      run1802(); //run - does not return.
  }
}

uint8_t getch() {
  while(!Serial.available());
  return Serial.read();
}
void readbytes(int n) {
  for (int x = 0; x < n; x++) {
    buff[x] = getch(); //was Serial.read();
  }
}


void empty_reply() {
  char gc=getch();
  if (CRC_EOP == gc) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)STK_OK);
  } 
  else {
    Serial.print((char)STK_NOSYNC);
  }
}

void breply(uint8_t b) {
  if (CRC_EOP == getch()) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)b);
    Serial.print((char)STK_OK);
  } 
  else {
    Serial.print((char)STK_NOSYNC);
  }
}

void get_version(uint8_t c) {
  switch(c) {
  case 0x80:
    breply(HWVER);
    break;
  case 0x81:
    breply(SWMAJ);
    break;
  case 0x82:
    breply(SWMIN);
    break;
  case 0x93:
    breply('S'); // serial programmer
    break;
  default:
    breply(0);
  }
}

void set_parameters() {
  // call this after reading paramter packet into buff[]
  param.devicecode = buff[0];
  param.revision = buff[1];
  param.progtype = buff[2];
  param.parmode = buff[3];
  param.polling = buff[4];
  param.selftimed = buff[5];
  param.lockbytes = buff[6];
  param.fusebytes = buff[7];
  param.flashpoll = buff[8]; 
  // ignore buff[9] (= buff[8])
  //getch(); // discard second value
  
  // WARNING: not sure about the byte order of the following
  // following are 16 bits (big endian)
  param.eeprompoll = beget16(&buff[10]);
  param.pagesize = beget16(&buff[12]);
  param.eepromsize = beget16(&buff[14]);

  // 32 bits flashsize (big endian)
  param.flashsize = buff[16] * 0x01000000
    + buff[17] * 0x00010000
    + buff[18] * 0x00000100
    + buff[19];

}

void start_pmode() {
  start_pmode_1802();
  //cout<<"entering program mode";
  pmode = 1;
}

void end_pmode() {
  end_pmode_1802();
  //cout<<"leaving program mode";
  pmode = 0;
}


uint8_t write_flash(int length) {
  write_flash_1802(length);
  return STK_OK;
}

void program_page() {
  char result = (char) STK_FAILED;
  int length = 256 * getch() + getch(); //gets length 1-256
  if (length > 256) {
      Serial.print((char) STK_FAILED);
      return;
  }
  char memtype = getch();
  for (int x = 0; x < length; x++) {
    buff[x] = getch();
  }
  if (CRC_EOP == getch()) {
    Serial.print((char) STK_INSYNC);
    if (memtype == 'F') result = (char)write_flash(length);
    Serial.print(result);
  } 
  else {
    Serial.print((char) STK_NOSYNC);
  }
}

void read_signature() {
  if (CRC_EOP != getch()) {
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  #define sig_high 0x1e //dummy (code for part ATMEGA644P
  #define sig_middle 0x96 //device
  #define sig_low 0x0A //signature
  Serial.print((char) sig_high);
  Serial.print((char) sig_middle);
  Serial.print((char) sig_low);
  Serial.print((char) STK_OK);
}
void universal() {//15-01-13 this is being used to retrieve the signature
  int w;
  uint8_t ch;

  for (w = 0; w < 4; w++) { //take the data
    buff[w] = getch();
  }
  if ((buff[0]==0x30)  && (buff[2]==0x00)){
    breply(sig_high);
  }
  else if ((buff[0]==0x30)  && (buff[2]==0x01)){
     breply(sig_middle);
  }
  else if ((buff[0]==0x30)  && (buff[2]==0x02)){
    breply(sig_low);
  }
  else{
    breply(STK_FAILED); //see if this gets a rise out of avrdude
  }
}
//////////////////////////////////////////
//////////////////////////////////////////



void read_page() {
  char result = (char)STK_FAILED;
  int length = 256 * getch() + getch();
  char memtype = getch();
  //cout<<"reading "<<length<<" bytes of "<<memtype<<" ";
  if (CRC_EOP != getch()) {
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  if (memtype == 'F') result = read_page_1802(length);
  Serial.print(result);
  return;
}

////////////////////////////////////
////////////////////////////////////


I always want the last word so that wordpress doesn’t eat my code!

int avrisp() { 
  uint8_t data, low, high, gc;
  uint8_t ch =getch();    //cout<<"<"<<_HEX(ch)<<" ";
  switch (ch) {
  case '?': //debug
    doitall();
    break;
  case 'v': //verbose mode
    runmode='v';
    break;
  case 'q': //quiet mode
    runmode='q';
    cout<<"going silent\n";
    break;
  case '!': //run
    cout<<"!=runq\n";
    runmode='q';
    run1802();
    break;
  case '0': // signon
    empty_reply();
    break;
  case '1':
    gc=getch(); //cout<<":"<<_HEX(gc)<<" ";
    if (gc == CRC_EOP) {
      Serial.print((char) STK_INSYNC);
      Serial.print("AVR ISP");
      Serial.print((char) STK_OK);
    }
    break;
  case 'A':
    gc=getch(); //cout<<":"<<_HEX(gc)<<" ";
    get_version(gc);
    break;
  case 'B':
    readbytes(20);
    set_parameters();
    empty_reply();
    break;
  case 'E': // extended parameters - ignore for now
    readbytes(5);
    empty_reply();
    break;

  case 'P':
    start_pmode();
    empty_reply();
    break;
  case 'U':
    here = getch() + 256 * getch();
    sethere_1802();
    //cout<<"here="<<_HEX(here);
    empty_reply();
    break;

  case 0x60: //STK_PROG_FLASH
    low = getch();
    high = getch();
    empty_reply();
    break;
  case 0x61: //STK_PROG_DATA
    data = getch();
    empty_reply();
    break;

  case 0x64: //STK_PROG_PAGE
    program_page();
    break;
    
  case 0x74: //STK_READ_PAGE
    read_page();    
    break;

  case 'V':
    universal();
    break;
  case 'Q':
    error=0;
    end_pmode();
    empty_reply();
    //run1802();
    break;
    
  case 0x75: //STK_READ_SIGN
    read_signature();
    break;

  // expecting a command, not CRC_EOP
  // this is how we can get back in sync
  case CRC_EOP:
    Serial.print((char) STK_NOSYNC);
    break;
    
  // anything else we will return STK_UNKNOWN
  default:
    if (CRC_EOP == getch()) 
      Serial.print((char)STK_UNKNOWN);
    else
      Serial.print((char)STK_NOSYNC);
  }
}

I always want the last word so that wordpress doesn’t eat my code!
 

#define sbi(sfr,bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define cbi(sfr,bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
void busout(uint8_t t){ //put a byte on the bus
  sbi(MISODDR,MISOBIT);
  for (int i=0;i<8;i++){
      if(MISOX, t&0x80){
        sbi(MISOPORT,MISOBIT);
      }else{
        cbi(MISOPORT,MISOBIT);
      }
      clockport=clockbv;//clock up, notclock down
      asm("  nop\n");
      clockport=notclockbv;//clock down, notclock up
      t<<=1;
  }
  cbi(MISODDR,MISOBIT);
}
uint8_t busin(){ //read a byte from the 1802
  uint8_t buschar;
//bit 7 of the byte is already in position to be read, the others have to be clocked in
  buschar=0; //clear the character
  for (uint8_t i = 0; i < 8; ++i) {  //get the 8 bits in sequence
	buschar = (buschar<<1)|digitalRead(datapin); //read bits 0-7 in sequence ad set them in the byte
      clockport=clockbv;//clock up, notclock down
      asm("  nop\n");
      clockport=notclockbv;//clock down, notclock up
  }
  return buschar;
}
void justclock(){ //just run the spi clock
      clockport=clockbv;//clock up, notclock down
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
      asm("nop\n");
      clockport=clockbv;//clock up, notclock down
      asm("nop\n");
      clockport=notclockbv;//clock down, notclock up
}
void run1802(){
  unsigned char char1802=21, pindsave,escmode=false; //char from 1802, captured PIND;
  if ('v'==runmode){
    cout<<" run1802.6.1("<<timeoutinterval<<")"<<"\n";
  }
  //Serial.end(); Serial.begin(9600);
  interface(INPUT); //release the pins(really just INsw)
  digitalWrite(CLRsw,LOW);   digitalWrite(LDsw,LOW);//ensure wait state
  delay(5);
  digitalWrite(LDsw,HIGH); //reset
  delay(5);
  pinMode(MEMrd,OUTPUT); digitalWrite(MEMrd,LOW); //AUG 10 -make sure 1802 can write
  digitalWrite(CLRsw,HIGH); //run
  state1802=running;
  cbi(TIMSK0,TOIE0);
  noInterrupts(); //default is no interrupts
  while(true){ //loop for 1802 program output
    while (PIND&0x40); //wait for SPI signal to go low
    pindsave=PIND; //grab the n0 line while it's valid
    while (0==(PIND&0x40)); //wait for trailing edge - signal goes high again
    if (pindsave&0x04){ //if it's for me  ** have to test this now
      char1802=busin(); //grab the character and clock the SPI bus
      if (escmode==1){//escaping this character
        cout<<_BYTE(char1802); //send it to the host
        escmode=0;
      }else{
        switch(char1802){
          case 0x12: //querying for avail characters
            busout(Serial.available());
            break;
          case 0: //wanting host characters
            busout(getch());
            break;
          case 0x1b: //escape next character
            escmode=1;
            break;
          case 0x02: // allow interrupts
            interrupts(); break;
          case 0x03: // disallow interrupts
            noInterrupts(); break;            
          default:
            cout<<_BYTE(char1802); //send it to the host
        }
      }
   } else{ //not for me, have to do this fast
      justclock(); //just clock the SPI bus
    }
  }
}
void domeall(){
 }
  
void doitall(){
  uint8_t c18;
  cout<<"doing it all\n";
  interface(OUTPUT);
   //start the write sequence
  reset1802();
  digitalWrite(LDsw,LOW);  //then go to load mode
 // busout(0xaa);
  //while(1);
  busout(0x7b);
  pressin();
  busout(0x7a);
  pressin();
  busout(0x7a);
  pressin();
  busout(0x7a);
  pressin();
  busout(0x7a);
  pressin();
  busout(0x30);
  pressin();
  busout(0x00);
  pressin();
  reset1802();
  run1802();
while(1);
}  
void sethere_1802(){
  if (here==0){ //if it wants to read from the beginning, fine
    reset1802();
    digitalWrite(LDsw,LOW);  //then go to load mode
  } //otherwise ignore it
}
void start_pmode_1802() {
  interface(OUTPUT); //set rquired pins to OUTPUT
  reset1802();
  digitalWrite(LDsw,LOW);  //then go to load mode
  //cout<<"entering program mode";
  pmode = 1;
}

void end_pmode_1802() {
  //cout<<"leaving program mode";
  pmode = 0;
}
void pressin(){
  digitalWrite(INsw,LOW); //toggle
  delayMicroseconds(presstimeu);
  digitalWrite(INsw,HIGH);
  delayMicroseconds(presstimeu);
}  
void reset1802(){
  pinMode(CLRsw,OUTPUT); pinMode(LDsw,OUTPUT);
  digitalWrite(CLRsw,LOW);   digitalWrite(LDsw,LOW);//reset
  digitalWrite(LDsw,HIGH); //1802
  state1802=reset;
}

void interface(uint8_t io){ //setting pins to INPUT or OUTPUT
 //really need to rethink this
  digitalWrite(MISOX,LOW);
  digitalWrite(INsw,HIGH);
  digitalWrite(spi_sck,LOW);digitalWrite(sr_sck,LOW);digitalWrite(notsr_sck,HIGH);
  digitalWrite(LDsw,LOW);digitalWrite(CLRsw,LOW);
  pinMode(MISOX,INPUT);//MISO is controlled by busout
  pinMode(spi_sck,OUTPUT);pinMode(sr_sck,OUTPUT);pinMode(notsr_sck,OUTPUT); //clocks are always output
  pinMode(CLRsw,OUTPUT);pinMode(LDsw,OUTPUT);  //ld & clr are always output
  pinMode(INsw,io);  //in controlled here
}



uint8_t read_byte_1802() {//first try
  digitalWrite(INsw,LOW);//push input button
  delayMicroseconds(presstimeu);
  digitalWrite(INsw,HIGH);
  delayMicroseconds(presstimeu);
  uint8_t buschar=busin();  
  return buschar;
}
char read_page_1802(int length) {//first try
  digitalWrite(MEMrd,HIGH); //make sure we're read only
  for (int x = 0; x < length; x+=1) {
    Serial.print((char) read_byte_1802());
  }
  return STK_OK;
}

uint8_t write_flash_1802(int length) {
  int x = 0;
  digitalWrite(MEMrd,LOW); //allow memory write
  while (x < length) {
    write_byte_1802(buff[x++]); //we can only write bytes sequentially from address 0
  }
  digitalWrite(MEMrd,HIGH); //make sure we're read only
  return STK_OK;
}

void write_byte_1802(char data){ //write a byte to the 1802
  busout(data); //put it on the data bus
  digitalWrite(INsw,LOW);//push input button
  delayMicroseconds(presstimeu);
  digitalWrite(INsw,HIGH);
  delayMicroseconds(presstimeu);
}

I always want the last word so that wordpress doesn’t eat my code!

What Fresh Hell Is This?

17-02-16 serial U.jpg

After getting the revised serial bridge code in the AVR working with a new version(olduinoII_ISPV6) I figured I should make sure it was backward compatible with the VF5 that I had been using.  It didn’t work with the little nokia lcd so i decided to reload VF5 in the AVR to make sure it wasn’t an LCD hardware problem.  Of course, VF5 doesn’t work! Running a simple program that sends an ASCII ‘U'(0x55) from the 1802 I get it sending 0x38 0x35 to windows.  The AVR program itself sends text fine but when it’s bridging for the 1802 it Fs it up.

Ahh, I think I know. The character is being held in a uint8_t and the streaming output thingy is converting it so ‘U'(0x55) is coming out as its decimal equivalent 85.  What a waste of time! I see that in V6 I had declared it unsigned char and the output line was cout<<_BYTE(char1802); so i must have solved this a while ago and forgotten.  Dummy.

Xmodem Receiver Working!

17-02-12-stamps17-02-12-error-recoveryWell Thank heavens. The problem came down to the AVR missing the trigger signal that the 1802 was writing to the shift register. I knew this was a risk but I didn’t know what the symptoms would be. As it happens, the most common failure was for the AVR to miss a write that was meant to trigger a read from the host serial buffer. The 1802 would get whatever was in the shift register and the character would stay in the buffer so the 1802 would get extra characters in the block. Because I wasn’t doing any error checking the 1802 ended up trying to read an extra block at the end of transmission. I had been gathering everything i needed to do simple error checking but not using it. A little bit of poking at the code and it’s fine now – the server was always ready to re-transmit so no problem there. I think I could cut down the error frequency a lot by using a flag to tell the 1802 rather than in-band signalling but i’m going to leave it for now. It’s good to have a constant stream of errors to exercise the code. In the dos box above you can see the re-transmission of one block of four in a 512 byte file.

In the horrible code below the readblk routine had been storing the block sequence numbers and the checksum and the main routine displayed them at the end. I changed it to calculate its own checksum and verify that along with the block numbers. The main routine just had to check the return code before ACK-ing the block. There’s an explicit check for the SOH character as well now although it just ignores other characters until it finds one. All of that stuff can come out now along with the places where i had stuck in seq/req sequences to help me trace things with the logic analyzer.

 

//ssxload super simple xmodem loader
//17-02-08 timeout on initial receives
//17-02-09 no timeouts, single start ack, no error checking
//17-02-09 header read moved into main
//17-02-12 block level error checking and NAK
#include <nstdlib.h>
#include <olduino.h>
#include <cpu1802spd4port7.h>
#include "xloader.h"
#define blocksize 128
unsigned char target[1024];
unsigned char blkno, hdr[256],bseq[256],iseq[256],ciseq[256],csum[256],calcsum[256],valid[256];
int readblk(unsigned char * where){//read a block
	unsigned int cnt=blocksize;
	unsigned char ch,calcchk=0,rc=0;
	bseq[blkno]=readch();
	iseq[blkno]=readch();
	while(cnt>0){
		ch=readch();
		*where++=ch;
		calcchk+=ch;
		cnt--;
	}
	ch=readch(); //read the checksum
	csum[blkno]=ch; //save it
	if (calcchk!=ch){//check it
		rc+=4;
	}
	calcsum[blkno]=calcchk;
	if (bseq[blkno]!=(blkno+1)){
		rc+=1;
	}
	ciseq[blkno]=254-blkno;
	if (iseq[blkno]!=(254-blkno)){
		rc+=2;
	}
	return rc;
}


void main(){
	int ch='?',eot=0;
	unsigned char *t=target;
	unsigned int i;
	unsigned char thishdr;
	for(i=0;i<1024;i++){target[i]=0;}
	blkno=0;
	asm(" seq\n nop\n req\n");
	putch(NAK); //send NAK to start
	thishdr=readch(); //get the header character
	while(thishdr!=EOT){
		if (SOH==thishdr){
			hdr[blkno]=thishdr;
			valid[blkno]=readblk(t);
			if (0==valid[blkno]){
				blkno++;
				t+=blocksize;
				putch(ACK);
			}else{
				putch(NAK);
			}

		}
		asm(" seq\n nop\n req\n seq\n nop\n req\n");
		thishdr=getchOrTo(1000);
		asm(" seq\n nop\n req\n");
	}
	putch(ACK);
//and we're done - god willing

	delay(5000);//let python clear out
	printf("terminating header was %d\n",thishdr);
	dump(target,256);
	printf("number of blocks %d\n",blkno);
	printf("bb hh:bn/in c:i r:cs c:cs valid?\n");
	for (i=0;i<blkno;i++){
		printf("%cx %cx:%cx/%cx c:%cx r:%cx c:%cx %d\n",i,hdr[i],bseq[i],iseq[i],ciseq[i],csum[i],calcsum[i],valid[i]);
	}
	while(1);
}
#include <nstdlib.c>
#include <olduino.c>

Xmodem Loader

I have to say I am *very* tired of this thing.  It’s simple bullheadedness that keeps me poking at it.  As of close of day today I am consistently transferring blocks of any size and acknowledging them but I am pretty consistently missing the EOT at the end so my loader hangs. It’s not quite as simple as that because I replaced the failing readch() with one that would time out but it still hangs. I did have a glorious 10 minutes when everything seemed to be working but then it went to sh*t again. Anyway, the biggest change I made today was to eliminate the nointerrupts()/interrupts() statements from the avr code. The rest is just poking around trying stuff.

//ssxload super simple xmodem loader
//loads a single 32 byte block with checksum from a custom host program
//17-02-08 timeout on initial receives
//17-02-09 no timeouts, single start ack, no error checking
//17-02-09 header read moved into main
#include <nstdlib.h>
#include <olduino.h>
#include <cpu1802spd4port7.h>
#include "xloader.h"
#define blocksize 64
unsigned char target[1024];
unsigned char blkno, hdr[256],bseq[256],iseq[256],csum[256];
int readblk(unsigned char * where){//read a block
	unsigned int cnt=blocksize;
	bseq[blkno]=readch();
	iseq[blkno]=readch();
	while(cnt>0){
		*where++=readch();
		cnt--;
	}
	csum[blkno]=readch(); //read the checksum
	return 0;
}


void main(){
	int ch='?',eot=0;
	unsigned char *t=target;
	unsigned int i;
	unsigned char thishdr;
	for(i=0;i<1024;i++){target[i]=0;}
	blkno=0;
	asm(" seq\n nop\n req\n");
	putch(NAK); //send NAK to start
	thishdr=readch(); //get the header character
	while(thishdr!=EOT){
		hdr[blkno]=thishdr;
		readblk(t);
		blkno++;
		t+=blocksize;
		putch(ACK);
		asm(" seq\n nop\n req\n seq\n nop\n req\n");
		thishdr=getchOrTo(1000);
		asm(" seq\n nop\n req\n");
	}
	putch(ACK);
//and we're done - god willing

	delay(5000);//let python clear out
	printf("terminating header was %d\n",thishdr);
	dump(target,128);
	printf("number of blocks %d\n",blkno);
	printf("bb hh:bn/in cs\n");
	for (i=0;i<blkno;i++){
		printf("%cx %cx:%cx/%cx %cx\n",i,hdr[i],bseq[i],iseq[i],csum[i]);
	}
	while(1);
}
#include <nstdlib.c>
#include <olduino.c>
from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from time import sleep
import os
if len(sys.argv)>1:
    filename=sys.argv[1]
else:
    filename="test32.txt"
fileSize=os.path.getsize(filename)
print ("File Size is",fileSize)

def xmodem_send(serial, file):
	blocksize=64
	t=0
	#	t, anim ='|/-\\'
	while 1:
	    if serial.read(1) != 'N':
		t = t + 1
		print ('.')
		if t == 3 : return False
	    else:
		break

	p = 1
	s = file.read(blocksize)
	while s:
	    s = s + '\xFF'*(blocksize - len(s))
	    chk = 0
	    for c in s:
		chk+=ord(c)
		#print (c,ord(c),chk,chk%256)
	    while 1:
		serial.write('S') #SOH)
		serial.write(chr(p))
		serial.write(chr(255 - p))
		serial.write(s)
		serial.write(chr(chk%256))
		serial.flush()
		print ('checksum is ',format(chk%256, '02X'),end=' ')
		answer = serial.read(1)
		if  answer == 'N': continue
		if  answer == 'K': break
		return False
	    s = file.read(blocksize)
	    p = (p + 1)%256
	    print ('.')
	serial.write('T')
	serial.flush()
	return True

#Main program starts here - define the serial port, set RTS off, then open it
#open the file to be loaded
stream = open(filename,'rb')

port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=10,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=57600)
port.rts=False
port.port='COM3'
port.open()
port.rts=True
sleep(0.001)
port.rts=False
port.flushInput()
#transfer the file
result=xmodem_send(port, stream)

stream.close()
port.close()

if result:
    print ("\ntransfer successful")
    sleep(.25)
else:
    print ("\ntransfer unsuccessful")
    #x=raw_input("press enter to continue...");

I always want the last word so that wordpress doesn’t eat my code!
I always want the last word so that wordpress doesn’t eat my code!

Xmodem Loader

At close of day today I have dumbed down the loader further by taking all the error checking and recovery out of the 1802 code(!). The connection between the AVR and the 1802 is not subject to line errors and as long as i keep the blocks small i won’t overrun the AVR’s buffer. This seems to work about 4 out of 5 times, the odd time the 1802 program stalls somewhere but of course i don’t know where because: no error checking, no timeouts, no nothing.  The windows program always thinks everything is fine.  I save all the checksums and block numbers and they look fine when it completes.


//ssxload super simple xmodem loader
//loads a single 32 byte block with checksum from a custom host program
//17-02-08 timeout on initial receives
//17-02-09 no timeouts, single start ack, no error checking
//17-02-09 header read moved into main
#include <nstdlib.h>
#include <olduino.h>
#include <cpu1802spd4port7.h>
#include "xloader.h"
#define blocksize 32
unsigned char target[1024];
unsigned char blkno, hdr[256],bseq[256],iseq[256],csum[256];
int readblk(unsigned char * where){//read a block
unsigned int cnt=blocksize;
bseq[blkno]=readch();
iseq[blkno]=readch();
while(cnt>0){
*where++=readch();
cnt--;
}
csum[blkno]=readch(); //read the checksum
return 0;
}
void main(){
int ch='?',eot=0;
unsigned char *t=target;
unsigned int i;
unsigned char thishdr;
blkno=0;
putch(NAK); //send NAK to start
thishdr=readch(); //get the header character
while(thishdr!=EOT){
hdr[blkno]=thishdr;
readblk(t);
blkno++;
t+=blocksize;
putch(ACK);
thishdr=readch();
}
putch(ACK);
//and we're done - god willing

delay(5000);//let python clear out
dump(target,128);
printf("number of blocks %d\n",blkno);
printf("bb hh:bn/in cs\n");
for (i=0;i<blkno;i++){
printf("%cx %cx:%cx/%cx %cx\n",i,hdr[i],bseq[i],iseq[i],csum[i]);
}
while(1);
}
#include <nstdlib.c>
#include <olduino.c>

There’s an 1802 elf/os specific xmodem receiver here http://www.elf-emulation.com/software.html

 

17-02-09-dumber-xmodem

Xmodem Loader -ick

17-02-08-horrible-xmodemAt close of day today I have a horrible xmodem client and a horrible xmodem sender that work horribly together. I’ve reduced serial speed to 9600 baud once the 1802 is running but i have no idea whether that mattered in the end.  I would much rather be using stolen code but there are just too many moving pieces.  I kind of hope once I debug this i’ll be able to drop back to something pre-made.

//ssxload super simple xmodem loader
//loads a single 32 byte block with checksum from a custom host program
//17-02-08 timeout on initial receives
#include <nstdlib.h>
#include <olduino.h>
#include <cpu1802spd4port7.h>
#include "xloader.h"
unsigned char buff[64];
unsigned char packetno=1;

void main(){
	int chorto='?';
	unsigned int naks=0;
	unsigned char cks=0;
	unsigned int nch=0,i=0, tries=3;
	while (tries-->0 && chorto!=SOH){
		putch(NAK); //send NAK to start
		chorto=getchOrTo(1000);
	}

	while(chorto>0 && chorto!=EOT){
		switch (chorto){
		case SOH:
			nch=0;
			while(nch<35 &chorto>=0){
				chorto=getchorto(500);
				buff[nch++]=chorto;
			}
			if ((buff[0]==packetno)&&(buff[1]==(unsigned char)~packetno)){
				putch(ACK);
				packetno++;
				//naks=0;
			}else{
				putch(NAK);
				//naks++;
			}
			break;
		case EOT:
			putch(ACK);
			break;
		default:
			putch(NAK);
		}
		chorto=getchorto(1000);
	}
	delay(3500);//let python clear out
	dump(buff,35);
	if (chorto<0){
		printf("\ntimeout\n");
	} else{
		printf("\n last rxd %c\n",chorto);
		for (i=2;i<2+32;i++){
			cks+=buff[i];
			//printf("%d %d %d\n",i,buff[i],cks);
		}
		if (buff[2+32]==cks){
			printf("cksum pass\n");
		}else{
			printf("cksum fail read %d calc %d\n",buff[2+32],cks);
		}
	}
	while(1);
}
#include <nstdlib.c>
#include <olduino.c>

 

from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from time import sleep
import os
if len(sys.argv)>1:
    filename=sys.argv[1]
else:
    filename="test32.txt"
fileSize=os.path.getsize(filename)
print ("File Size is",fileSize)

def xmodem_send(serial, file):
	t=0
	#	t, anim ='|/-\\'
	while 1:
	    if serial.read(1) != 'N':
		t = t + 1
		print ('.')
		if t == 60 : return False
	    else:
		break

	p = 1
	s = file.read(32)
	while s:
	    s = s + '\xFF'*(32 - len(s))
	    chk = 0
	    for c in s:
		chk+=ord(c)
		#print (c,ord(c),chk,chk%256)
	    while 1:
		serial.write('S') #SOH)
		serial.write(chr(p))
		serial.write(chr(255 - p))
		serial.write(s)
		serial.write(chr(chk%256))
		serial.flush()
		print ('checksum is ',chk%256,chr(chk%256))
		answer = serial.read(1)
		if  answer == 'N': continue
		if  answer == 'K': break
		return False
	    s = file.read(32)
	    p = (p + 1)%256
	    print ('.')
	serial.write('T')
	return True

#Main program starts here - define the serial port, set RTS off, then open it
#open the file to be loaded
stream = open(filename,'rb')

port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=10,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=9600)
port.rts=False
port.port='COM3'
port.open()
port.rts=True
sleep(0.001)
port.rts=False
port.flushInput()
#transfer the file
result=xmodem_send(port, stream)

stream.close()
port.close()

if result:
    print ("\ntransfer successful")
    sleep(.25)
else:
    print ("\ntransfer unsuccessful")
    #x=raw_input("press enter to continue...");

typedef unsigned char uint8_t;
#define SOH  'S' //0x01
#define STX  0x02
#define EOT  'T' //0x04
#define ACK  'K' //0x06
#define NAK  'N' //0x15
#define CAN  0x18
#define CTRLZ 0x1A
uint8_t getch(){
	uint8_t c;
	out(7,0); //request read of the fifo
	c=inp(7);
	return c;//return the data
}
int seravail(){//see if there's data available
	out(7,0x12); //read the fifo depth
	return inp(7);
}
void putch(uint8_t c){ //filter characters for avr passthru
	if (0==c | 0x27==c){//cases that need to be escaped
		out(7,0x27);
	}
	out(7,c);
}
int getchOrTo(unsigned int retries){//get a character or time out
    while(retries>0){
		if (seravail()==0){
			retries--;
		}else{
			return getch();
		}
	}
	return -1; //return -1 for timeout
}
void dump(unsigned char* data, unsigned int len){
	unsigned int row,col;
	printf("dumping %d bytes at %x\n",len,data);
	for (row=0;row<(len+8);row+=8){
		printf("%x ",row);
		for (col=0;col<8;col++){
			printf("%cx ",data[row+col]);
		}
		printf("\n");
	}
}