Shop OBEX P1 Docs P2 Docs Learn Events
Catalina 2.9 - Page 14 — Parallax Forums

Catalina 2.9

1910111214

Comments

  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-18 23:42
    RossH wrote: »
    Yes, you can go to 1K. But I had it working with PC and (I think) a 2K cache. Post your exact options.
    Current options:
    CFLAGS=-x4
    LDFLAGS=$(CFLAGS) -DC3 -DPC -DCR_ON_LF -M256k -DCACHED_2K -lcix -y
    
  • RossHRossH Posts: 5,547
    edited 2011-03-18 23:51
    Hi David,

    Works for me. Did you remember to recompile the utilities folder with the new cache size?

    Ross.
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-18 23:55
    RossH wrote: »
    Hi David,

    Works for me. Did you remember to recompile the utilities folder with the new cache size?

    Ross.

    Yes. "build_all C3 CACHED_2K"?
  • RossHRossH Posts: 5,547
    edited 2011-03-19 00:10
    Yes, that's right. Post your actual binary and I'll try that here.

    Ross.
  • M. K. BorriM. K. Borri Posts: 279
    edited 2011-03-19 00:13
    Sweet! This is quite awesome, thank you!
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-19 00:15
    RossH wrote: »
    Yes, that's right. Post your actual binary and I'll try that here.

    Ross.

    I just tried loading it again and now it seems to work. It's a bit faster than the -x3 version but not enough to make it actually usable. Oh well, it was worth a try. Thanks for your help!
  • RossHRossH Posts: 5,547
    edited 2011-03-19 00:33
    David,

    Maybe there is still a bug or two lurking in there. As I mentioned in post #388, I'm a bit perplexed by some of the benchmark results I'm seeing with the cache, so I think there is still some room for improvement.

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-03-19 04:16
    RossH wrote: »
    David,

    Maybe there is still a bug or two lurking in there. As I mentioned in post #388, I'm a bit perplexed by some of the benchmark results I'm seeing with the cache, so I think there is still some room for improvement.

    Ross.

    David (& Jazzed)

    It appears I have mixed up my OFFSETS with my INDEXES in the SPI cache code. I'm surprised it works at all!

    I'll see about fixing it if I get time tomorrow.

    Ross.
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-19 04:37
    Did you derive your code from my C3 cache code? Is it likely that I have the same problem?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-19 06:08
    Latest cogject code - this replicates the entire simpleserial spin object.

    Some things have ended up a lot simpler in C than in spin eg
    PUB dec(value) | i
    
    '' Print a decimal number
    
      if value < 0
        -value
        tx("-")
    
      i := 1_000_000_000
    
      repeat 10
        if value => i
          tx(value / i + "0")
          value //= i
          result~~
        elseif result or i == 1
          tx("0")
        i /= 10
    

    becomes
    void serial_dec(unsigned long value,unsigned long par[])	// send out decimal value - unsigned
    {
    	char lineoftext[11] = "";					// enough room for a 32 bit long 2^32
      	sprintf(lineoftext, "%u", value);				// convert to a string
    	serial_str(lineoftext,par);					// send out the string
    }
    

    The startup cog code is in an array, and you can either leave it as it is, or overwrite it with a .cog file.

    I have been pushing this code and come across a little problem which may or may not also occur in Spin. If the baud rate is set very slow (1200) and lots of bytes are sent very quickly, the buffer full code does not seem to catch the error. I am not sure if maybe this is pasm updating the head before the tail or some other reason, or maybe the way C reads the variables? I presume that in a "while" loop C is reupdating the parameters? Maybe this is happening because C is faster than Spin?

    I have put a 100ms sleep in this while routine (30ms was not long enough at 1200). I wonder if there is another better solution eg a gap of 2 or 3 between the head and tail, rather than 1?
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == (par[2] + 1 ) & 0xF) 
    	{ 						// par[2] is tx_head, par[3] is tx_tail
    		sleep(100);				// small delay to send out some bytes, should only happen with slow baud rates
    							// printf("head is one less than the tail, buffer full");
    	}
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = par[2];				// get the head value
    	address = par[8] + 16 + tx_head;		// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 			// logical and with 15
    	par[2] = tx_head;				// store it back again
    							// need to add the echo mode?
    }
    

    I now need to think about the "merging" of multiple cogjects - eg things like multiple entries for "stdio", multiple copies of functions (like the cog loader) and other include files, and merging things into the "main" function. I might see if I can find another object to work on...
    /* PASM demo for use with Catalina IDE Precompiler and Compiler */
    
    #include <stdio.h>
    
    // PASM blocks of code and associated arrays
    
    /* PASM Code for compilation using SpinC and inclusion as a data file
    PASM Start serial.spin
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
                                                                         ' Start of C hub constants
                                                                         ' End of C constants
    PUB Main
        coginit(1,@entry,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
    
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     txcode,#transmit      'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive                 jmpret  rxcode,txcode         'run a chunk of transmit code, then return
    
                            test    rxtxmode,#%001  wz    'wait for start bit on rx pin
                            test    rxmask,ina      wc
            if_z_eq_c       jmp     #receive
    
                            mov     rxbits,#9             'ready to receive byte
                            mov     rxcnt,bitticks
                            shr     rxcnt,#1
                            add     rxcnt,cnt                          
    
    :bit                    add     rxcnt,bitticks        'ready next bit period
    
    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            test    rxmask,ina      wc    'receive bit on rx pin
                            rcr     rxdata,#1
                            djnz    rxbits,#:bit
    
                            shr     rxdata,#32-9          'justify and trim received byte
                            and     rxdata,#$FF
                            test    rxtxmode,#%001  wz    'if rx inverted, invert byte
            if_nz           xor     rxdata,#$FF
    
                            rdlong  t2,par                'save received byte and inc head
                            add     t2,rxbuff
                            wrbyte  rxdata,t2
                            sub     t2,rxbuff
                            add     t2,#1
                            and     t2,#$0F
                            wrlong  t2,par
    
                            jmp     #receive              'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
                            wrlong  t3,t1
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    PASM End
    */ 
    	unsigned long serial_cog[511] = 
           {
               0xa0bca9f0, 0x80fca810, 0x08bcaa54, 0xa0fcb201, 
               0x2cbcb255, 0x80fca804, 0x08bcaa54, 0xa0fcbe01, 
               0x2cbcbe55, 0x80fca804, 0x08bcae54, 0x80fca804, 
               0x08bcb054, 0x80fca804, 0x08bcb454, 0xa0bcc05a, 
               0x80fcc010, 0x627cae04, 0x617cae02, 0x689be85f, 
               0x68abec5f, 0xa0fcc833, 0x5cbcbc64, 0x627cae01, 
               0x613cb3f2, 0x5c640016, 0xa0fcb809, 0xa0bcba58, 
               0x28fcba01, 0x80bcbbf1, 0x80bcba58, 0x5cbcbc64, 
               0xa0bca85d, 0x84bca9f1, 0xc17ca800, 0x5c4c001f, 
               0x613cb3f2, 0x30fcb601, 0xe4fcb81e, 0x28fcb617, 
               0x60fcb6ff, 0x627cae01, 0x6cd4b6ff, 0x08bcabf0, 
               0x80bcaa5a, 0x003cb655, 0x84bcaa5a, 0x80fcaa01, 
               0x60fcaa0f, 0x083cabf0, 0x5c7c0016, 0x5cbcc85e, 
               0xa0bca9f0, 0x80fca808, 0x08bcaa54, 0x80fca804, 
               0x08bcac54, 0x863caa56, 0x5c680033, 0x80bcac60, 
               0x00bcc256, 0x84bcac60, 0x80fcac01, 0x60fcac0f, 
               0x083cac54, 0x68fcc300, 0x2cfcc202, 0x68fcc201, 
               0xa0fcc40b, 0xa0bcc7f1, 0x627cae04, 0x617cae02, 
               0x6ce0c201, 0x29fcc201, 0x70abe85f, 0x7497ec5f, 
               0x80bcc658, 0x5cbcc85e, 0xa0bca863, 0x84bca9f1, 
               0xc17ca800, 0x5c4c004d, 0xe4fcc446, 0x5c7c0033
           };
    
    // start of C functions
    
    void external_cog_load(int cognumber, unsigned long cogdata[], unsigned long *parameters_array)    	//  load a cog from external memory
    {
    	unsigned long hubcog[511];		// create a local array, this is in hub ram, not external ram	
    	int i;	
    	for(i=0;i<512;i++)								
    	{
    		hubcog[i]=cogdata[i];					// move from external memory to a local array in hub
    	}
     	_coginit((int)parameters_array>>2, (int)hubcog>>2, cognumber);		// load the cog
    }                                  
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    unsigned long serial_start(unsigned long rxpin,unsigned long txpin,unsigned long mode, unsigned long baudrate, unsigned long par[], unsigned long cogdata[])
    {
    /*
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    */
    
    	unsigned long okay;
    	unsigned long bit_ticks;
    	unsigned long buffer_ptr;
    	par[0] = 0;						// rx_head   longfill(@rx_head, 0, 4)
    	par[1] = 0;						// rx_tail
    	par[2] = 0;						// tx_head
    	par[3] = 0;						// tx_tail
    	par[4] = rxpin;					//   longmove(@rx_pin, @rxpin, 3)
    	par[5] = txpin;					// note - if rewrite the pasm code could save a couple of hub longs here
    	par[6] = mode;					// as rxpin and txpin are not used anywhere else
    	bit_ticks = _clockfreq() / baudrate;   		//   bit_ticks := clkfreq / baudrate
    	par[7] = bit_ticks;
    	buffer_ptr = (unsigned long)&par[9];		//   buffer_ptr := @rx_buffer  points to start of circular buffer
    	par[8] = buffer_ptr;					// pointer to the start of the circular buffers
    								// rx buffer is 9 to 12 and tx buffer is 13 to 16 (16 bytes =4 longs)
    	external_cog_load(7,cogdata,par);			// load from external ram
    	// okay returns the cog number or -1 if a fail page 119 manual. Ignored here
    	printf("par array is at %u \n",(unsigned long)&par[0]);
    	printf("par array entry 1 is at %u \n",(unsigned long)&par[1]);
    	printf("par array entry 7 is at %u \n",(unsigned long)&par[7]);
    	printf("rx_head is at %u \n",(unsigned long)&par[9]);
    	printf("buffer_ptr is %u \n",par[8]);
    	return okay;
    }
    
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == (par[2] + 1 ) & 0xF) 
    	{ 						// par[2] is tx_head, par[3] is tx_tail
    		sleep(100);				// small delay to send out some bytes, should only happen with slow baud rates
    							// printf("head is one less than the tail, buffer full");
    	}
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = par[2];				// get the head value
    	address = par[8] + 16 + tx_head;		// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 			// logical and with 15
    	par[2] = tx_head;				// store it back again
    							// need to add the echo mode?
    }
    
    unsigned long serial_rxcheck(unsigned long par[])
    {
    /*
    PUB rxcheck : rxbyte
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    */
    	unsigned long rxbyte;			// actually is a long, so can return -1 FFFFFFFF if nothing and 0-FF if a byte
    	int address;					// hub address
    	rxbyte = 0;					// set explicitly to zero
    	rxbyte = rxbyte - 1;				// return ffffffff if nothing
    	if (par[1] != par[0])
    	{
    		address = par[8] + par[1];		// par[8] is the rx buffer, par[1] is rx_tail
    		rxbyte = peek(address);		// get the return byte from the buffer
    		par[1] = (par[1] +1) & 0xF;		// add one to tail
    	}
    	return rxbyte;
    }
    
    unsigned long serial_rx(unsigned long par[])
    {
    /*
    PUB rx : rxbyte
    '' Receive byte (may wait for byte)
    '' returns $00..$FF
      repeat while (rxbyte := rxcheck) < 0	
    */
    	unsigned long rxbyte;					// actually is a long, not a byte
    	while ((rxbyte = serial_rxcheck(par)) == -1) {} 	// 0xffffffff and -1 works, but " < 0" gives a compiler error
    	return rxbyte;						// return the value
    }
    
    void serial_rxflush(unsigned long par[])				// flush receive buffer
    {
    	while (serial_rxcheck(par) != -1) {}			// keep checking until buffer clear
    }
    
    unsigned long serial_rxtime(unsigned long ms,unsigned long par[]) // wait ms milliseconds for byte, -1 if nothing
    {
    	unsigned long rxbyte = -1;
    	unsigned long counter = 0;					// start a counter, 10ms ticks
    	ms = ms / 10;							// internal delay for 1ms ticks is too high
    	while (((rxbyte = serial_rxcheck(par)) == -1) & (counter < ms))	// wait until a byte or counter times out
    	{
    		_waitcnt(_cnt()+(10*(_clockfreq()/1000))-4296);		// wait 10 milliseconds
    		counter +=1; 						// add one to counter
    	}
    	return rxbyte;
    }
    
    void serial_str(char lineoftext[],unsigned long par[])		// send out the string
    {
    /*
    '' Send string                    
    
      repeat strsize(stringptr)
        tx(byte[stringptr++])
    */
    	int i;
    	for(i=0; i<strlen(lineoftext);i++)
    	{
    		serial_tx(lineoftext[i],par);			// send out the bytes one at a time
    	}
    }
    
    void serial_dec(unsigned long value,unsigned long par[])	// send out decimal value - unsigned
    {
    /*
    '' Print a decimal number
    
      if value < 0
        -value
        tx("-")
    
      i := 1_000_000_000
    
      repeat 10
        if value => i
          tx(value / i + "0")
          value //= i
          result~~
        elseif result or i == 1
          tx("0")
        i /= 10
    */
    	char lineoftext[11] = "";					// enough room for a 32 bit long 2^32
      	sprintf(lineoftext, "%u", value);				// convert to a string
      									// printf  ("lineoftext is now: %s\n", lineoftext);
    	serial_str(lineoftext,par);					// send out the string
    }
    
    void serial_hex(unsigned long value, unsigned long par[])	// send out a hex value
    /*
    '' Print a hexadecimal number
      value <<= (8 - digits) << 2
      repeat digits
        tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
    */
    {
    	char lineoftext[8] = "";						// enough room for FFFFFFFF
    	sprintf(lineoftext,"%x",value);				// convert to hex value
    	serial_str(lineoftext,par);					// send it out
    }
    
    void serial_crlf(unsigned long par[])				// send a crlf
    {
    	serial_tx(13,par);						// cr
    	serial_tx(10,par);						// lf
    }
    
    int EoF (FILE* stream)
    {
      	register int c, status = ((c = fgetc(stream)) == EOF);
      	ungetc(c,stream);
      	return status;
    }
    
    void readcog(char *filename,unsigned long external_cog[])		// read in a .cog file into external memory array 
    {
    	int i;
    	FILE *FP1;
    	i = 0;
    	if((FP1=fopen(filename,"rb"))==0)					// open the file
       	{
      		fprintf(stderr,"Can't open file %s\n",filename);
    		exit(1);
       	}
      	fseek(FP1,0,0);
      	while(!EoF(FP1) & (i<511))							// run until end of file
    	{
    		external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24);	// get the long
    		// printf("%u ",external_cog[i]);
    		i+=1;
    	}
    	if(FP1)
           {
         		fclose(FP1);							// close the file
         		FP1=NULL;
       	}
    	printf("external array cog 0 = %u \n",external_cog[0]);
    }
    
    
    void main ()
    {
    	int i;
    	char lineoftext[80];							// for string testing
    	unsigned long received_byte;					// actually a long, not a byte
    	unsigned long serial_parameters[16];				// reserve hub space for buffer, head tail pointers
           clearscreen();							// white on blue vga
           printf("Clock speed %u \n",_clockfreq());                     // see page 28 of the propeller manual for other useful commands
           printf("Catalina running in cog number %i \n",_cogid());      // integer
    	printf("internal array cog 0 = %u \n",serial_cog[0]);
    	readcog("serial.cog",serial_cog);				// read in serial.cog to external memory overwrites existing data
    	serial_start(31,30,0,1200,serial_parameters,serial_cog);	// start serial cog pins 31,30, mode 0, 1200 baud
           printf("Started serial driver\n");
    	for(i=0; i<10; i++)
    	{
    		serial_tx(65+i,serial_parameters); 			// test sending a byte 10x (delay for starting a serial terminal program)
    		sleep(500);
    		printf("send byte %u \n",65+i);
    	}
    	strcpy(lineoftext,"This is a really long string test with a slow baud rate to check buffer overruns");			// store a string
    	serial_str(lineoftext,serial_parameters);				// send it out
    	serial_crlf(serial_parameters);
    	serial_dec(102030405,serial_parameters);				// send out a big decimal number
    	serial_crlf(serial_parameters);
    	serial_str("Hex value is ",serial_parameters);
    	serial_hex(102030405,serial_parameters);					// send out a hex value
    	serial_crlf(serial_parameters);
    	serial_rxflush(serial_parameters);					// flush the receive buffer
    	printf("Type a character within the next 3 seconds \n");	// test the timeout
    	received_byte = serial_rxtime(3000,serial_parameters);		// get a byte with a timeout
    	printf("character was ascii %u \n",received_byte);
    	printf("type some characters, will return that character plus 1 \n");
    	for (i=0;i<19;i++)							// test 19 times, so tests buffer restarting
    	{
    		received_byte = serial_rx(serial_parameters);		// get a byte
    		serial_tx(received_byte,serial_parameters);		// echo it back
    		printf("sent back byte %u \n",received_byte);
    	}
    
    	printf("program finished \n");
    	while (1); // Prop reboots on exit from main()
    }
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-19 17:08
    Dr_Acula wrote: »
    Latest cogject code - this replicates the entire simpleserial spin object.

    Some things have ended up a lot simpler in C than in spin eg ... dec()
    Why did you change the behaviour? The SPIN method outputs a signed decimal, don't you think C should match that?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-19 17:10
    Good point kuoneko. Actually I did that because I was not sure how to convert an unsigned number to a signed number.

    Is that something easy to do?
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-19 17:25
    Try this
    void serial_dec([COLOR="red"]signed[/COLOR] long value,unsigned long par[])
    {
    	char lineoftext[[COLOR="red"]12[/COLOR]];                       // 10 digits, sign, terminator
      	sprintf(lineoftext, "%[COLOR="red"]d[/COLOR]", value);
    	serial_str(lineoftext,par);
    }
    
    Regarding your tx issue, this may be related to compiler optimisations (volatile variables). You also have two poke calls in there, one with an uninitialised address.
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == (par[2] + 1 ) & 0xF) 
    	{ 					// par[2] is tx_head, par[3] is tx_tail
    		sleep(100);			// small delay to send out some bytes, should only happen with slow baud rates
    						// printf("head is one less than the tail, buffer full");
    	}
    	[COLOR="red"]poke(address,tx);			// poke the tx byte value to hub ram[/COLOR]
    	tx_head = par[2];			// get the head value
    	address = par[8] + 16 + tx_head;	// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);			// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 		// logical and with 15
    	par[2] = tx_head;			// store it back again
    						// need to add the echo mode?
    }
    
    With the compiler I use at work this will give the desired affect, as to whether Catalina supports it is a question for Ross (you could also try yourself).
    void serial_tx(char tx, [COLOR="red"]volatile[/COLOR] unsigned long par[])
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-19 21:38
    Thanks kuroneko
    1) printing negative numbers now
    2) Thanks for spotting the uninitialised poke call. The first one was some leftover code from debugging - I have deleted it. That was a good bug to find - it could have caused all sorts of problems later on!
    3) Ross will hopefully have some advice as to whether the "volatile" would help here.

    I tidied up the code a lot. The "main" now only has one call to the demo function, and it has the one parameter array declaration. This makes it much easier to see how much hub ram a cogject is going to reserve.

    I put all the functions in a "demo" routine. I think this could be a handy addition to any cogject. One reason you might not have a demo routine in Spin is that it uses precious memory, but with this program running from external memory, it does not cost so much to add a demo routine.

    As per a comment from Heater, I got rid of the IDE modifying the C program. What I was finding is that the PASM part hardly needs editing. In fact, it really needs no editing if an existing object is using the PAR method (ones by Chip Gracey tend to conform to this method, I need to do some more research as to which other ones do).

    So now, if you want to build a new program, use Homespun and SPINC to create an array. The vb.net IDE also creates a .cog file, so with one keypress you have a .h array file and a .cog file for the sd card. Copy the .h array back into the program and you probably never will need to edit it.

    I added in the author acknowledgement and the MIT license.

    I also added a quick list of all the functions at the beginning of the code.
    /* PASM serial demo for use with Catalina compiler */
    
    #include <stdio.h>
    
    /* PASM Code - use Homespun and SPINC to create a .h file.The Catalina vb.net IDE can also create a .cog file for the sd card
    Make the array an unsigned long with 511 longs
    Put the array before any functions so it ends up in external memory (arrays within functions end up in hub memory)
    A logical place for the array if there are multiple blocks of pasm code is to place it underneath the pasm code
    
    ''***************************************
    ''*  Full-Duplex Serial Driver v1.1     *
    ''*  Author: Chip Gracey                *
    ''*  Copyright (c) 2006 Parallax, Inc.  *
    ''*  See end of file for terms of use.  *
    ''*  C version by James Moxham          *
    ''***************************************
    
    Functions:
    	serial_start					// start the serial driver
    	serial_tx					// send a byte
    	serial_rx					// receive a byte
    	serial_rxcheck				// check if any bytes present
    	serial_rxflush				// flush the receiver buffer
    	serial_rxtime					// check for a byte, time out after n milliseconds
    	serial_dec					// send out number as a decimal number
    	serial_hex					// send out number as a hex number
    	serial_str					// send out a string
    	serial_crlf					// send a carriage return and line feed
    	readcog					// read a .cog file from the sd card to external memory
    
    PASM Start - put 'PASM Start' at the beginning so that the color highlighter knows to highlight the CON PUB DAT. Put 'PASM End' at the end
    
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
                                                                         ' Start of C hub constants
                                                                         ' End of C constants
    PUB Main
        coginit(1,@entry,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
    
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     txcode,#transmit      'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive                 jmpret  rxcode,txcode         'run a chunk of transmit code, then return
    
                            test    rxtxmode,#%001  wz    'wait for start bit on rx pin
                            test    rxmask,ina      wc
            if_z_eq_c       jmp     #receive
    
                            mov     rxbits,#9             'ready to receive byte
                            mov     rxcnt,bitticks
                            shr     rxcnt,#1
                            add     rxcnt,cnt                          
    
    :bit                    add     rxcnt,bitticks        'ready next bit period
    
    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            test    rxmask,ina      wc    'receive bit on rx pin
                            rcr     rxdata,#1
                            djnz    rxbits,#:bit
    
                            shr     rxdata,#32-9          'justify and trim received byte
                            and     rxdata,#$FF
                            test    rxtxmode,#%001  wz    'if rx inverted, invert byte
            if_nz           xor     rxdata,#$FF
    
                            rdlong  t2,par                'save received byte and inc head
                            add     t2,rxbuff
                            wrbyte  rxdata,t2
                            sub     t2,rxbuff
                            add     t2,#1
                            and     t2,#$0F
                            wrlong  t2,par
    
                            jmp     #receive              'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
                            wrlong  t3,t1
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    PASM End
    
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                                   TERMS OF USE: MIT License                                                  &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    */ 
    	unsigned long serial_cog[511] = 
           {
               0xa0bca9f0, 0x80fca810, 0x08bcaa54, 0xa0fcb201, 
               0x2cbcb255, 0x80fca804, 0x08bcaa54, 0xa0fcbe01, 
               0x2cbcbe55, 0x80fca804, 0x08bcae54, 0x80fca804, 
               0x08bcb054, 0x80fca804, 0x08bcb454, 0xa0bcc05a, 
               0x80fcc010, 0x627cae04, 0x617cae02, 0x689be85f, 
               0x68abec5f, 0xa0fcc833, 0x5cbcbc64, 0x627cae01, 
               0x613cb3f2, 0x5c640016, 0xa0fcb809, 0xa0bcba58, 
               0x28fcba01, 0x80bcbbf1, 0x80bcba58, 0x5cbcbc64, 
               0xa0bca85d, 0x84bca9f1, 0xc17ca800, 0x5c4c001f, 
               0x613cb3f2, 0x30fcb601, 0xe4fcb81e, 0x28fcb617, 
               0x60fcb6ff, 0x627cae01, 0x6cd4b6ff, 0x08bcabf0, 
               0x80bcaa5a, 0x003cb655, 0x84bcaa5a, 0x80fcaa01, 
               0x60fcaa0f, 0x083cabf0, 0x5c7c0016, 0x5cbcc85e, 
               0xa0bca9f0, 0x80fca808, 0x08bcaa54, 0x80fca804, 
               0x08bcac54, 0x863caa56, 0x5c680033, 0x80bcac60, 
               0x00bcc256, 0x84bcac60, 0x80fcac01, 0x60fcac0f, 
               0x083cac54, 0x68fcc300, 0x2cfcc202, 0x68fcc201, 
               0xa0fcc40b, 0xa0bcc7f1, 0x627cae04, 0x617cae02, 
               0x6ce0c201, 0x29fcc201, 0x70abe85f, 0x7497ec5f, 
               0x80bcc658, 0x5cbcc85e, 0xa0bca863, 0x84bca9f1, 
               0xc17ca800, 0x5c4c004d, 0xe4fcc446, 0x5c7c0033
           };
    
    // start of C functions
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    void external_memory_cog_load(int cognumber, unsigned long cogdata[], unsigned long parameters_array[])    	//  load a cog from external memory
    {
    	unsigned long hubcog[511];						// create a local array, this is in hub ram, not external ram	
    	int i;	
    	for(i=0;i<512;i++)								
    	{
    		hubcog[i]=cogdata[i];					// move from external memory to a local array in hub
    	}
     	_coginit((int)parameters_array>>2, (int)hubcog>>2, cognumber);		// load the cog
    }  
    
    unsigned long serial_start(unsigned long rxpin,unsigned long txpin,unsigned long mode, unsigned long baudrate, unsigned long par[], unsigned long cogdata[])
    {
    /*
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    */
    
    	unsigned long okay;
    	unsigned long bit_ticks;
    	unsigned long buffer_ptr;
    	par[0] = 0;						// rx_head   longfill(@rx_head, 0, 4)
    	par[1] = 0;						// rx_tail
    	par[2] = 0;						// tx_head
    	par[3] = 0;						// tx_tail
    	par[4] = rxpin;					//   longmove(@rx_pin, @rxpin, 3)
    	par[5] = txpin;					// note - if rewrite the pasm code could save a couple of hub longs here
    	par[6] = mode;					// as rxpin and txpin are not used anywhere else
    	bit_ticks = _clockfreq() / baudrate;   		//   bit_ticks := clkfreq / baudrate
    	par[7] = bit_ticks;
    	buffer_ptr = (unsigned long)&par[9];		//   buffer_ptr := @rx_buffer  points to start of circular buffer
    	par[8] = buffer_ptr;					// pointer to the start of the circular buffers
    								// rx buffer is 9 to 12 and tx buffer is 13 to 16 (16 bytes =4 longs)
    	external_memory_cog_load(7,cogdata,par);		// load from external ram
    	// okay returns the cog number or -1 if a fail page 119 manual. Ignored here
    	// printf("par array is at %u \n",(unsigned long)&par[0]);
    	// printf("par array entry 1 is at %u \n",(unsigned long)&par[1]);
    	// printf("par array entry 7 is at %u \n",(unsigned long)&par[7]);
    	// printf("rx_head is at %u \n",(unsigned long)&par[9]);
    	// printf("buffer_ptr is %u \n",par[8]);
    	return okay;
    }
    
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == ((par[2] + 1 ) & 0xF)) {} // wait if the head has looped right round and is now one less than the tail
    	tx_head = par[2];				// get the head value
    	address = par[8] + 16 + tx_head;		// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 			// logical and with 15
    	par[2] = tx_head;				// store it back again
    							// need to add the echo mode?
    }
    
    unsigned long serial_rxcheck(unsigned long par[])
    {
    /*
    PUB rxcheck : rxbyte
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    */
    	unsigned long rxbyte;			// actually is a long, so can return -1 FFFFFFFF if nothing and 0-FF if a byte
    	int address;					// hub address
    	rxbyte = 0;					// set explicitly to zero
    	rxbyte = rxbyte - 1;				// return ffffffff if nothing
    	if (par[1] != par[0])
    	{
    		address = par[8] + par[1];		// par[8] is the rx buffer, par[1] is rx_tail
    		rxbyte = peek(address);		// get the return byte from the buffer
    		par[1] = (par[1] +1) & 0xF;		// add one to tail
    	}
    	return rxbyte;
    }
    
    unsigned long serial_rx(unsigned long par[])
    {
    /*
    PUB rx : rxbyte
    '' Receive byte (may wait for byte)
    '' returns $00..$FF
      repeat while (rxbyte := rxcheck) < 0	
    */
    	unsigned long rxbyte;					// actually is a long, not a byte
    	while ((rxbyte = serial_rxcheck(par)) == -1) {} 	// 0xffffffff and -1 works, but " < 0" gives a compiler error
    	return rxbyte;						// return the value
    }
    
    void serial_rxflush(unsigned long par[])				// flush receive buffer
    {
    	while (serial_rxcheck(par) != -1) {}			// keep checking until buffer clear
    }
    
    unsigned long serial_rxtime(unsigned long ms,unsigned long par[]) // wait ms milliseconds for byte, -1 if nothing
    {
    	unsigned long rxbyte = -1;
    	unsigned long counter = 0;					// start a counter, 10ms ticks
    	ms = ms / 10;							// internal delay for 1ms ticks is too high
    	while (((rxbyte = serial_rxcheck(par)) == -1) & (counter < ms))	// wait until a byte or counter times out
    	{
    		_waitcnt(_cnt()+(10*(_clockfreq()/1000))-4296);		// wait 10 milliseconds
    		counter +=1; 						// add one to counter
    	}
    	return rxbyte;
    }
    
    void serial_str(char lineoftext[],unsigned long par[])		// send out the string
    {
    /*
    '' Send string                    
    
      repeat strsize(stringptr)
        tx(byte[stringptr++])
    */
    	int i;
    	for(i=0; i<strlen(lineoftext);i++)
    	{
    		serial_tx(lineoftext[i],par);			// send out the bytes one at a time
    	}
    }
    
    void serial_dec(signed long value,unsigned long par[])	// send out decimal value - unsigned
    {
    /*
    '' Print a decimal number
      if value < 0
        -value
        tx("-")
      i := 1_000_000_000
      repeat 10
        if value => i
          tx(value / i + "0")
          value //= i
          result~~
        elseif result or i == 1
          tx("0")
        i /= 10
    */
    	char lineoftext[12] = "";					// enough room for a 32 bit long 2^32 and possibly the minus sign
      	sprintf(lineoftext, "%d", value);				// convert to a string
      									// printf  ("lineoftext is now: %s\n", lineoftext);
    	serial_str(lineoftext,par);					// send out the string
    }
    
    void serial_hex(unsigned long value, unsigned long par[])	// send out a hex value
    /*
    '' Print a hexadecimal number
      value <<= (8 - digits) << 2
      repeat digits
        tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
    */
    {
    	char lineoftext[8] = "";					// enough room for FFFFFFFF
    	sprintf(lineoftext,"%x",value);				// convert to hex value
    	serial_str(lineoftext,par);					// send it out
    }
    
    void serial_crlf(unsigned long par[])				// send a crlf
    {
    	serial_tx(13,par);						// cr
    	serial_tx(10,par);						// lf
    }
    
    int EoF (FILE* stream)
    {
      	register int c, status = ((c = fgetc(stream)) == EOF);
      	ungetc(c,stream);
      	return status;
    }
    
    void readcog(char *filename,unsigned long external_cog[])		// read in a .cog file into external memory array 
    {
    	int i;
    	FILE *FP1;
    	i = 0;
    	if((FP1=fopen(filename,"rb"))==0)					// open the file
       	{
      		fprintf(stderr,"Can't open file %s\n",filename);
    		exit(1);
       	}
      	fseek(FP1,0,0);
      	while(!EoF(FP1) & (i<511))						// run until end of file
    	{
    		external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24);	// get the long
    		// printf("%u ",external_cog[i]);
    		i+=1;
    	}
    	if(FP1)
           {
         		fclose(FP1);							// close the file
         		FP1=NULL;
       	}
    	printf("external array cog first long = 0x%x \n",external_cog[0]);	// hex value
    }
    
    void serial_demo(unsigned long serial_parameters[])			// demonstrate the serial cog code
    {
    	int i;
    	unsigned long value = 0x80000000;					// 80000000 is -1
    	char lineoftext[80];							// for string testing
    	unsigned long received_byte;					// actually a long, not a byte
           clearscreen();							// white on blue vga
           printf("Clock speed %u \n",_clockfreq());                     // see page 28 of the propeller manual for other useful commands
           printf("Catalina running in cog number %i \n",_cogid());      // integer
    	printf("internal array cog first long = 0x%x \n",serial_cog[0]);	// hex value
    //	readcog("serial.cog",serial_cog);					// read in serial.cog to external memory overwrites existing data
    	serial_start(31,30,0,1200,serial_parameters,serial_cog);	// start serial cog pins 31,30, mode 0, 1200 baud
           printf("Started serial driver\n");
    	for(i=0; i<10; i++)
    	{
    		serial_tx(65+i,serial_parameters); 			// test sending a byte 10x (delay for starting a serial terminal program)
    		sleep(500);
    		printf("send byte %u \n",65+i);
    	}
    	serial_crlf(serial_parameters);
    	strcpy(lineoftext,"This is a really long string test with a slow baud rate to check buffer overruns");			// store a string
    	serial_str(lineoftext,serial_parameters);				// send it out
    	serial_crlf(serial_parameters);					// new line
    	serial_dec(value,serial_parameters);				// send out a big decimal number
    	serial_crlf(serial_parameters);					// new line
    	serial_str("Hex value is ",serial_parameters);
    	serial_hex(value,serial_parameters);				// send out a hex value
    	serial_crlf(serial_parameters);
    	serial_rxflush(serial_parameters);					// flush the receive buffer
    	printf("Type a character within the next 3 seconds \n");	// test the timeout
    	received_byte = serial_rxtime(3000,serial_parameters);		// get a byte with a timeout
    	printf("character was ascii %d \n",received_byte);		// %d is signed
    	printf("type some characters \n");
    	for (i=0;i<10;i++)							// test 19 times, so tests buffer restarting
    	{
    		received_byte = serial_rx(serial_parameters);		// get a byte
    		serial_tx(received_byte,serial_parameters);		// echo it back
    		printf("sent back byte %u \n",received_byte);
    	}
    	printf("demo program finished \n");
    }
    
    void main ()
    {
    	unsigned long serial_parameters[16];				// reserve hub space in main for buffer, head tail pointers
    	serial_demo(serial_parameters);					// demo routines       
    	while (1); 								// endless loop as prop reboots on exit from main() 
    }
    
  • RossHRossH Posts: 5,547
    edited 2011-03-19 21:59
    Hi Dr_A,

    Yes, Catalina understands "volatile", but I'this is not the problem (I just tried it and the code generated is the same in both cases, so nothing is being optimized away).

    I think the problem is a missing set of parentheses:
    while ( par[3] == (par[2] + 1 ) & 0xF)
    
    should be
    while ( par[3] == ( (par[2] + 1 ) & 0xF) )
    
    In C, == has higher precednce than &. See here: http://www.difranco.net/cop2220/op-prec.htm

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-03-19 22:16
    David (& Jazzed)

    Yes, my cache code was indeed faulty (embarassingly so!). I've fixed it and will post you both a new pre-release once I have tidied it up and done a little more testing.

    David, you may be interested in the timing - with the corrected cache code, the time to compile your test program has halved - i.e. from 12 seconds to around 6 seconds. The -x4 option is slightly faster than the -x3 option, but not dramatically so - this is probably because of the much smaller cache size (2k instead of 8k).

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-19 22:29
    That has fixed it!

    Ok, I edited the code in the post 2 above. No delay needed now.

    In a general sense, this does bring up a question as to whether there are any precedence differences between Spin and C.

    For long lines of code that have multiple operators in them, and sometimes with no comments, one way to make sense of these is to split them up into individual lines. Maybe you need to add a temporary variable, and maybe it is this need for a temporary variable that is the reason there are these lines of code in some programs (though that doesn't explain the lack of any explanatory comments on some of the longer ones!) Maybe it is just the gentle art of obfuscation?

    Anyway, with a program running in external memory, it does not matter if you add one temp variable to a function. I'm not even sure if this even slows down the code, - is assigning a value to a variable faster or slower or the same speed as pushing a variable onto the stack?

    But the part that has me a little confused is when you come across a complex equation in Spin that is part of a 'repeat' loop. Is there any way to split up the 'while' equation in C into multiple lines?

    Maybe you have a temp variable and you assign its value before the while, and then reassign its value at the end of the while block - with multiple lines? This is a bit messy though as it means you have two assignments to the variable.

    Hopefully not too many of these are going to come up!

    I have thought at times whether one could write a Spin to C translator, but it is the difference in precedence that might be the tricky part.

    Or.... plan B - ask Ross!
  • RossHRossH Posts: 5,547
    edited 2011-03-19 23:17
    Dr_Acula wrote: »
    In a general sense, this does bring up a question as to whether there are any precedence differences between Spin and C.
    Anyone who programs in multiple languages soon learns not to depend on precedence rules. Use lots of parentheses instead. If you compare the page I linked above with the SPIN operator precedence table on page 143 of the Propeller Manual, you will see there are lots of differences, as well as missing operators on both sides. SPIN isn't really "C-like" once you get past a few trivial syntactic similarities.
    Dr_Acula wrote: »
    For long lines of code that have multiple operators in them, and sometimes with no comments, one way to make sense of these is to split them up into individual lines. Maybe you need to add a temporary variable, and maybe it is this need for a temporary variable that is the reason there are these lines of code in some programs (though that doesn't explain the lack of any explanatory comments on some of the longer ones!) Maybe it is just the gentle art of obfuscation?

    Anyway, with a program running in external memory, it does not matter if you add one temp variable to a function. I'm not even sure if this even slows down the code, - is assigning a value to a variable faster or slower or the same speed as pushing a variable onto the stack?
    Judicious use of the "register" keyword can make them the same. For example:
    void main() {
       register int f;
       register int g;
       int a = 1;
       int b = 2;
       int c = 3;
       int d = 4;
       int e = 0;
    
       /* this ... */
       e = (a/b)-(c*d);
    
       /* generates the same code as this ... */
       f = a/b;
       g = c*d;
       e = f - g;
    }
    
    Dr_Acula wrote: »
    But the part that has me a little confused is when you come across a complex equation in Spin that is part of a 'repeat' loop. Is there any way to split up the 'while' equation in C into multiple lines?
    C has do ... while as well as simple for and while loops. It would probably be worth figuring out the equivalent C loop to each of the SPIN repeat options.
    Dr_Acula wrote: »
    Maybe you have a temp variable and you assign its value before the while, and then reassign its value at the end of the while block - with multiple lines? This is a bit messy though as it means you have two assignments to the variable.
    I think you want a do ... while loop.
    Dr_Acula wrote: »
    Hopefully not too many of these are going to come up!

    I have thought at times whether one could write a Spin to C translator, but it is the difference in precedence that might be the tricky part.

    Or.... plan B - ask Ross!
    Not me! But Dave Hein has done some good work on this already.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-20 00:24
    Thanks for all the hints. Brackets it is, and you may see the register keyword in some of my code.

    re do and while, it has taken me years to work out the difference between these, both in C and Basic.

    In a nutshell, one tests the variable at the beginning and one at the end. A 'do' will go through the next code at least once. A 'while' will not if the condition is false.

    Spin has similar rules. I think if I come across a 'repeat' in the future and it needs to be changed to a 'while' and it has complex maths, I'll be adding lots of brackets.

    I have another question now - one of the things I want to code is the highest resolution vga driver and that uses 4 cogs. That means I need to shut down a number of catalina cogs, and if I shut something down, I need to get it working again afterwards.

    So the next object to code is the standard spin keyboard.

    I must say that the second object is much easier than the first - especially Chip's code as it follows the same rules.

    Which cog is the catalina keyboard running in?

    I did some experiments with scanf. I think with two drivers running there are going to be problems and I am seeing the Num Lock led flashing so I am using this as a test. Working through the cogs one at a time and running cogstop, if I shut down cog 4 then the keyboard leds do not flash.

    However, printf then stops working. Is stdio going to get upset if I shut down the keyboard? And is it cog 4 or have I got that wrong?
  • RossHRossH Posts: 5,547
    edited 2011-03-20 00:46
    Dr_Acula wrote: »
    Which cog is the catalina keyboard running in?

    I did some experiments with scanf. I think with two drivers running there are going to be problems and I am seeing the Num Lock led flashing so I am using this as a test. Working through the cogs one at a time and running cogstop, if I shut down cog 4 then the keyboard leds do not flash.

    However, printf then stops working. Is stdio going to get upset if I shut down the keyboard? And is it cog 4 or have I got that wrong?

    This is not so easy to determine, because I was lazy and only registered the HMI plugin itself, not the individual drivers. The way I do it when I need to (e.g. look in demos\multicog\dining_philosphers.c) is just to add one to my own cog id to to get the keyboard driver, since I know it is always the first driver started. But I'll fix this in Catalina 3.0 to explicitly register each driver when it is started, so that you can find it at runtime by searching the registry.

    Nothing will stop working if you shut down the keyboard driver. But you'll have to write your own code to start it back up again!

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-20 01:03
    Lots of experiments and the one that prints all the error messages and does not flash the leds is cog 0.
    	printf("Catalina running in cog number %i \n",_cogid());      // integer
    	printf("stop keyboard cog \n");
    	_cogstop(0);
    	printf("stopped a cog \n");
    

    This reports catalina is running in cog 2.

    Does this mean that the keyboard driver goes into the first free cog and that is cog 0?

    Shutting down cog 3 or 4 gives very odd behaviour. I guess the next test to see if it is cog 0 is to try getting something from the keyboard.

    Addit: yes, cog 0 is the keyboard. This code is incrementing the head counter each keypress and printing 65 when you hit a capital A. Woot!
    	printf("Catalina running in cog number %i \n",_cogid());      // integer
    	_cogstop(0);
    	key_start(26,27,key_parameters,key_cog);
    	printf("keyboard started \n");
    	for (i=0;i<10;i++)
    	{
    		sleep(1000);
    		printf("value of head is %u \n",key_parameters[1]);
    		printf("value of par_present is %u \n",key_parameters[2]);
    		printf("value of buffer 0 is %u \n",key_parameters[11]);
    		printf("value of buffer 1 is %u \n",key_parameters[12]);
    	}
    
  • RossHRossH Posts: 5,547
    edited 2011-03-20 01:29
    Dr_Acula wrote: »
    Addit: yes, cog 0 is the keyboard. This code is incrementing the head counter each keypress and printing 65 when you hit a capital A. Woot!

    How odd. You're probably going to have to wait for Catalina 3.0 to be able to definitively identify the keyboard cog. I'll email you a pre-release as soon as I have one that does what you need.

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-03-20 01:44
    David, Jazzed ...

    I've just emailed you both an updated 3.0 pre-release which fixes the cache problem. It should speed up performance by a factor of two or more.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-20 02:15
    No problem, I can work them out by trial and error at the moment.

    What would be your thoughts on True and False http://forums.parallax.com/showthread.php?129146-TRUE-or-FALSE see post #6 from Dave
    PUB present : truefalse
    
    '' Check if keyboard present - valid ~2s after start
    '' returns t|f
    
      truefalse := -par_present
    

    I converted it to this which has an extra line as it is converting an unsigned array variable to a signed value
    	signed long truefalse;
    	truefalse = par[2];						// 0 = false. In spin, true is -1. In C it is 1, so ? no negative??
    	truefalse = -truefalse;
    	return truefalse;
    

    But - given this is C, should we return 1 instead of -1?
  • RossHRossH Posts: 5,547
    edited 2011-03-20 02:24
    Dr_Acula wrote: »

    But - given this is C, should we return 1 instead of -1?

    Officially, yes. But in reality anything with the least significant bit set will work as "true".

    If in doubt use "if (boolean != 0) ...: instead if "if (boolean) ..." - I do this a lot. It sometimes looks a bit silly, but it is a "safe" way to program when dealing with different languages.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-20 03:01
    I shall use that example right now!

    A small thing
    	printf("hit a key \n");					// needs the \n
    	printf("you hit %u \n",key_getkey(key_parameters));
    

    if I leave out the \n then it does not print the "hit a key" until *after* I hit the key. If I put the \n in, then it prints "hit a key" as expected. Is this a C feature?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-20 03:37
    Keyboard Cogject tested and working.

    Ross, there are 8 cogs - one is being used for catalina (2) , one is for the keyboard (0) and one is spare (7). What are the others being used for?
    /* PASM keyboard object for use with Catalina compiler */
    
    #include <stdio.h>
    
    /*
     PASM Code - use Homespun and SPINC to create a .h file.
    The Catalina vb.net IDE can also create a .cog file for the sd card
    Make the array an unsigned long with 511 longs
    Put the array before any functions so it ends up in external memory (arrays within functions end up in hub memory)
    A logical place for the array if there are multiple blocks of pasm code is to place it underneath the pasm code
    
    ''***************************************
    ''*  PS/2 Keyboard Driver v1.0.1        *
    ''*  Author: Chip Gracey                *
    ''*  Copyright (c) 2004 Parallax, Inc.  *
    ''*  See end of file for terms of use.  *
    ''*  C version by James Moxham.         *
    ''***************************************
    
    Functions
    	key_start				// start the keyboard
    	key_startx				// start keyboard with lock settings
    	key_present				// test if keyboard present - takes 1-2 seconds to boot
    	key_key				// get a key - never waits
    	key_getkey				// get a key - always waits
    	key_newkey				// restart buffer and get a key
    	key_gotkey				// is there a key in the buffer
    	key_clearkeys				// restart buffer
    
    
    {-----------------REVISION HISTORY-----------------
     v1.0.1 - Updated 6/15/2006 to work with Propeller Tool 0.96}
    
    PASM Start - put 'PASM Start' at the beginning so that the color highlighter knows to highlight the CON PUB DAT. Put 'PASM End' at the end
    
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
                                                                         ' Start of C hub constants
                                                                         ' End of C constants
    PUB Main
        coginit(1,@entry,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
    
    '******************************************
    '* Assembly language PS/2 keyboard driver *
    '******************************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   movd    :par,#_dpin             'load input parameters _dpin/_cpin/_locks/_auto
                            mov     x,par
                            add     x,#11*4
                            mov     y,#4
    :par                    rdlong  0,x
                            add     :par,dlsb
                            add     x,#4
                            djnz    y,#:par
    
                            mov     dmask,#1                'set pin masks
                            shl     dmask,_dpin
                            mov     cmask,#1
                            shl     cmask,_cpin
    
                            test    _dpin,#$20      wc      'modify port registers within code
                            muxc    _d1,dlsb
                            muxc    _d2,dlsb
                            muxc    _d3,#1
                            muxc    _d4,#1
                            test    _cpin,#$20      wc
                            muxc    _c1,dlsb
                            muxc    _c2,dlsb
                            muxc    _c3,#1
    
                            mov     _head,#0                'reset output parameter _head
    '
    '
    ' Reset keyboard
    '
    reset                   mov     dira,#0                 'reset directions
                            mov     dirb,#0
    
                            movd    :par,#_present          'reset output parameters _present/_states[8]
                            mov     x,#1+8
    :par                    mov     0,#0
                            add     :par,dlsb
                            djnz    x,#:par
    
                            mov     stat,#8                 'set reset flag
    '
    '
    ' Update parameters
    '
    update                  movd    :par,#_head             'update output parameters _head/_present/_states[8]
                            mov     x,par
                            add     x,#1*4
                            mov     y,#1+1+8
    :par                    wrlong  0,x
                            add     :par,dlsb
                            add     x,#4
                            djnz    y,#:par
    
                            test    stat,#8         wc      'if reset flag, transmit reset command
            if_c            mov     data,#$FF
            if_c            call    #transmit
    '
    '
    ' Get scancode
    '
    newcode                 mov     stat,#0                 'reset state
    
    :same                   call    #receive                'receive byte from keyboard
    
                            cmp     data,#$83+1     wc      'scancode?
    
            if_nc           cmp     data,#$AA       wz      'powerup/reset?
            if_nc_and_z     jmp     #configure
    
            if_nc           cmp     data,#$E0       wz      'extended?
            if_nc_and_z     or      stat,#1
            if_nc_and_z     jmp     #:same
    
            if_nc           cmp     data,#$F0       wz      'released?
            if_nc_and_z     or      stat,#2
            if_nc_and_z     jmp     #:same
    
            if_nc           jmp     #newcode                'unknown, ignore
    '
    '
    ' Translate scancode and enter into buffer
    '
                            test    stat,#1         wc      'lookup code with extended flag
                            rcl     data,#1
                            call    #look
    
                            cmp     data,#0         wz      'if unknown, ignore
            if_z            jmp     #newcode
    
                            mov     t,_states+6             'remember lock keys in _states
    
                            mov     x,data                  'set/clear key bit in _states
                            shr     x,#5
                            add     x,#_states
                            movd    :reg,x
                            mov     y,#1
                            shl     y,data
                            test    stat,#2         wc
    :reg                    muxnc   0,y
    
            if_nc           cmpsub  data,#$F0       wc      'if released or shift/ctrl/alt/win, done
            if_c            jmp     #update
    
                            mov     y,_states+7             'get shift/ctrl/alt/win bit pairs
                            shr     y,#16
    
                            cmpsub  data,#$E0       wc      'translate keypad, considering numlock
            if_c            test    _locks,#%100    wz
            if_c_and_z      add     data,#@keypad1-@table
            if_c_and_nz     add     data,#@keypad2-@table
            if_c            call    #look
            if_c            jmp     #:flags
    
                            cmpsub  data,#$DD       wc      'handle scrlock/capslock/numlock
            if_c            mov     x,#%001_000
            if_c            shl     x,data
            if_c            andn    x,_locks
            if_c            shr     x,#3
            if_c            shr     t,#29                   'ignore auto-repeat
            if_c            andn    x,t             wz
            if_c            xor     _locks,x
            if_c            add     data,#$DD
            if_c_and_nz     or      stat,#4                 'if change, set configure flag to update leds
    
                            test    y,#%11          wz      'get shift into nz
    
            if_nz           cmp     data,#$60+1     wc      'check shift1
            if_nz_and_c     cmpsub  data,#$5B       wc
            if_nz_and_c     add     data,#@shift1-@table
            if_nz_and_c     call    #look
            if_nz_and_c     andn    y,#%11
    
            if_nz           cmp     data,#$3D+1     wc      'check shift2
            if_nz_and_c     cmpsub  data,#$27       wc
            if_nz_and_c     add     data,#@shift2-@table
            if_nz_and_c     call    #look
            if_nz_and_c     andn    y,#%11
    
                            test    _locks,#%010    wc      'check shift-alpha, considering capslock
                            muxnc   :shift,#$20
                            test    _locks,#$40     wc
            if_nz_and_nc    xor     :shift,#$20
                            cmp     data,#"z"+1     wc
            if_c            cmpsub  data,#"a"       wc
    :shift  if_c            add     data,#"A"
            if_c            andn    y,#%11
    
    :flags                  ror     data,#8                 'add shift/ctrl/alt/win flags
                            mov     x,#4                    '+$100 if shift
    :loop                   test    y,#%11          wz      '+$200 if ctrl
                            shr     y,#2                    '+$400 if alt
            if_nz           or      data,#1                 '+$800 if win
                            ror     data,#1
                            djnz    x,#:loop
                            rol     data,#12
    
                            rdlong  x,par                   'if room in buffer and key valid, enter
                            sub     x,#1
                            and     x,#$F
                            cmp     x,_head         wz
            if_nz           test    data,#$FF       wz
            if_nz           mov     x,par
            if_nz           add     x,#11*4
            if_nz           add     x,_head
            if_nz           add     x,_head
            if_nz           wrword  data,x
            if_nz           add     _head,#1
            if_nz           and     _head,#$F
    
                            test    stat,#4         wc      'if not configure flag, done
            if_nc           jmp     #update                 'else configure to update leds
    '
    '
    ' Configure keyboard
    '
    configure               mov     data,#$F3               'set keyboard auto-repeat
                            call    #transmit
                            mov     data,_auto
                            and     data,#%11_11111
                            call    #transmit
    
                            mov     data,#$ED               'set keyboard lock-leds
                            call    #transmit
                            mov     data,_locks
                            rev     data,#-3 & $1F
                            test    data,#%100      wc
                            rcl     data,#1
                            and     data,#%111
                            call    #transmit
    
                            mov     x,_locks                'insert locks into _states
                            and     x,#%111
                            shl     _states+7,#3
                            or      _states+7,x
                            ror     _states+7,#3
    
                            mov     _present,#1             'set _present
    
                            jmp     #update                 'done
    '
    '
    ' Lookup byte in table
    '
    look                    ror     data,#2                 'perform lookup
                            movs    :reg,data
                            add     :reg,#table
                            shr     data,#27
                            mov     x,data
    :reg                    mov     data,0
                            shr     data,x
    
                            jmp     #rand                   'isolate byte
    '
    '
    ' Transmit byte to keyboard
    '
    transmit
    _c1                     or      dira,cmask              'pull clock low
                            movs    napshr,#13              'hold clock for ~128us (must be >100us)
                            call    #nap
    _d1                     or      dira,dmask              'pull data low
                            movs    napshr,#18              'hold data for ~4us
                            call    #nap
    _c2                     xor     dira,cmask              'release clock
    
                            test    data,#$0FF      wc      'append parity and stop bits to byte
                            muxnc   data,#$100
                            or      data,dlsb
    
                            mov     x,#10                   'ready 10 bits
    transmit_bit            call    #wait_c0                'wait until clock low
                            shr     data,#1         wc      'output data bit
    _d2                     muxnc   dira,dmask
                            mov     wcond,c1                'wait until clock high
                            call    #wait
                            djnz    x,#transmit_bit         'another bit?
    
                            mov     wcond,c0d0              'wait until clock and data low
                            call    #wait
                            mov     wcond,c1d1              'wait until clock and data high
                            call    #wait
    
                            call    #receive_ack            'receive ack byte with timed wait
                            cmp     data,#$FA       wz      'if ack error, reset keyboard
            if_nz           jmp     #reset
    
    transmit_ret            ret
    '
    '
    ' Receive byte from keyboard
    '
    receive                 test    _cpin,#$20      wc      'wait indefinitely for initial clock low
                            waitpne cmask,cmask
    receive_ack
                            mov     x,#11                   'ready 11 bits
    receive_bit             call    #wait_c0                'wait until clock low
                            movs    napshr,#16              'pause ~16us
                            call    #nap
    _d3                     test    dmask,ina       wc      'input data bit
                            rcr     data,#1
                            mov     wcond,c1                'wait until clock high
                            call    #wait
                            djnz    x,#receive_bit          'another bit?
    
                            shr     data,#22                'align byte
                            test    data,#$1FF      wc      'if parity error, reset keyboard
            if_nc           jmp     #reset
    rand                    and     data,#$FF               'isolate byte
    
    look_ret
    receive_ack_ret
    receive_ret             ret
    '
    '
    ' Wait for clock/data to be in required state(s)
    '
    wait_c0                 mov     wcond,c0                '(wait until clock low)
    
    wait                    mov     y,tenms                 'set timeout to 10ms
    
    wloop                   movs    napshr,#18              'nap ~4us
                            call    #nap
    _c3                     test    cmask,ina       wc      'check required state(s)
    _d4                     test    dmask,ina       wz      'loop until got state(s) or timeout
    wcond   if_never        djnz    y,#wloop                '(replaced with c0/c1/c0d0/c1d1)
    
                            tjz     y,#reset                'if timeout, reset keyboard
    wait_ret
    wait_c0_ret             ret
    
    
    c0      if_c            djnz    y,#wloop                '(if_never replacements)
    c1      if_nc           djnz    y,#wloop
    c0d0    if_c_or_nz      djnz    y,#wloop
    c1d1    if_nc_or_z      djnz    y,#wloop
    '
    '
    ' Nap
    '
    nap                     rdlong  t,#0                    'get clkfreq
    napshr                  shr     t,#18/16/13             'shr scales time
                            min     t,#3                    'ensure waitcnt won't snag
                            add     t,cnt                   'add cnt to time
                            waitcnt t,#0                    'wait until time elapses (nap)
    
    nap_ret                 ret
    '
    '
    ' Initialized data
    '
    '
    dlsb                    long    1 << 9
    tenms                   long    10_000 / 4
    '
    '
    ' Lookup table
    '                               ascii   scan    extkey  regkey  ()=keypad
    '
    table                   word    $0000   '00
                            word    $00D8   '01             F9
                            word    $0000   '02
                            word    $00D4   '03             F5
                            word    $00D2   '04             F3
                            word    $00D0   '05             F1
                            word    $00D1   '06             F2
                            word    $00DB   '07             F12
                            word    $0000   '08
                            word    $00D9   '09             F10
                            word    $00D7   '0A             F8
                            word    $00D5   '0B             F6
                            word    $00D3   '0C             F4
                            word    $0009   '0D             Tab
                            word    $0060   '0E             `
                            word    $0000   '0F
                            word    $0000   '10
                            word    $F5F4   '11     Alt-R   Alt-L
                            word    $00F0   '12             Shift-L
                            word    $0000   '13
                            word    $F3F2   '14     Ctrl-R  Ctrl-L
                            word    $0071   '15             q
                            word    $0031   '16             1
                            word    $0000   '17
                            word    $0000   '18
                            word    $0000   '19
                            word    $007A   '1A             z
                            word    $0073   '1B             s
                            word    $0061   '1C             a
                            word    $0077   '1D             w
                            word    $0032   '1E             2
                            word    $F600   '1F     Win-L
                            word    $0000   '20
                            word    $0063   '21             c
                            word    $0078   '22             x
                            word    $0064   '23             d
                            word    $0065   '24             e
                            word    $0034   '25             4
                            word    $0033   '26             3
                            word    $F700   '27     Win-R
                            word    $0000   '28
                            word    $0020   '29             Space
                            word    $0076   '2A             v
                            word    $0066   '2B             f
                            word    $0074   '2C             t
                            word    $0072   '2D             r
                            word    $0035   '2E             5
                            word    $CC00   '2F     Apps
                            word    $0000   '30
                            word    $006E   '31             n
                            word    $0062   '32             b
                            word    $0068   '33             h
                            word    $0067   '34             g
                            word    $0079   '35             y
                            word    $0036   '36             6
                            word    $CD00   '37     Power
                            word    $0000   '38
                            word    $0000   '39
                            word    $006D   '3A             m
                            word    $006A   '3B             j
                            word    $0075   '3C             u
                            word    $0037   '3D             7
                            word    $0038   '3E             8
                            word    $CE00   '3F     Sleep
                            word    $0000   '40
                            word    $002C   '41             ,
                            word    $006B   '42             k
                            word    $0069   '43             i
                            word    $006F   '44             o
                            word    $0030   '45             0
                            word    $0039   '46             9
                            word    $0000   '47
                            word    $0000   '48
                            word    $002E   '49             .
                            word    $EF2F   '4A     (/)     /
                            word    $006C   '4B             l
                            word    $003B   '4C             ;
                            word    $0070   '4D             p
                            word    $002D   '4E             -
                            word    $0000   '4F
                            word    $0000   '50
                            word    $0000   '51
                            word    $0027   '52             '
                            word    $0000   '53
                            word    $005B   '54             [
                            word    $003D   '55             =
                            word    $0000   '56
                            word    $0000   '57
                            word    $00DE   '58             CapsLock
                            word    $00F1   '59             Shift-R
                            word    $EB0D   '5A     (Enter) Enter
                            word    $005D   '5B             ]
                            word    $0000   '5C
                            word    $005C   '5D             \
                            word    $CF00   '5E     WakeUp
                            word    $0000   '5F
                            word    $0000   '60
                            word    $0000   '61
                            word    $0000   '62
                            word    $0000   '63
                            word    $0000   '64
                            word    $0000   '65
                            word    $00C8   '66             BackSpace
                            word    $0000   '67
                            word    $0000   '68
                            word    $C5E1   '69     End     (1)
                            word    $0000   '6A
                            word    $C0E4   '6B     Left    (4)
                            word    $C4E7   '6C     Home    (7)
                            word    $0000   '6D
                            word    $0000   '6E
                            word    $0000   '6F
                            word    $CAE0   '70     Insert  (0)
                            word    $C9EA   '71     Delete  (.)
                            word    $C3E2   '72     Down    (2)
                            word    $00E5   '73             (5)
                            word    $C1E6   '74     Right   (6)
                            word    $C2E8   '75     Up      (8)
                            word    $00CB   '76             Esc
                            word    $00DF   '77             NumLock
                            word    $00DA   '78             F11
                            word    $00EC   '79             (+)
                            word    $C7E3   '7A     PageDn  (3)
                            word    $00ED   '7B             (-)
                            word    $DCEE   '7C     PrScr   (*)
                            word    $C6E9   '7D     PageUp  (9)
                            word    $00DD   '7E             ScrLock
                            word    $0000   '7F
                            word    $0000   '80
                            word    $0000   '81
                            word    $0000   '82
                            word    $00D6   '83             F7
    
    keypad1                 byte    $CA, $C5, $C3, $C7, $C0, 0, $C1, $C4, $C2, $C6, $C9, $0D, "+-*", $2F 
    
    keypad2                 byte    "0123456789.", $0D, "+-*",$2F
    
    shift1                  byte    "{|}", 0, 0, "~"
    
    shift2                  byte    $22, 0, 0, 0, 0, "<_>?)!@#$%^&*(", 0, ":", 0, "+"
    '
    '
    ' Uninitialized data
    '
    dmask                   res     1
    cmask                   res     1
    stat                    res     1
    data                    res     1
    x                       res     1
    y                       res     1
    t                       res     1
    
    _head                   res     1       'write-only
    _present                res     1       'write-only
    _states                 res     8       'write-only
    _dpin                   res     1       'read-only at start
    _cpin                   res     1       'read-only at start
    _locks                  res     1       'read-only at start
    _auto                   res     1       'read-only at start
    
    ''
    ''
    ''      _________
    ''      Key Codes
    ''
    ''      00..DF  = keypress and keystate
    ''      E0..FF  = keystate only
    ''
    ''
    ''      09      Tab
    ''      0D      Enter
    ''      20      Space
    ''      21      !
    ''      22      "
    ''      23      #
    ''      24      $
    ''      25      %
    ''      26      &
    ''      27      '
    ''      28      (
    ''      29      )
    ''      2A      *
    ''      2B      +
    ''      2C      ,
    ''      2D      -
    ''      2E      .
    ''      2F      /
    ''      30      0..9
    ''      3A      :
    ''      3B      ;
    ''      3C      <
    ''      3D      =
    ''      3E      >
    ''      3F      ?
    ''      40      @       
    ''      41..5A  A..Z
    ''      5B      [
    ''      5C      \
    ''      5D      ]
    ''      5E      ^
    ''      5F      _
    ''      60      `
    ''      61..7A  a..z
    ''      7B      {
    ''      7C      |
    ''      7D      }
    ''      7E      ~
    ''
    ''      80-BF   (future international character support)
    ''
    ''      C0      Left Arrow
    ''      C1      Right Arrow
    ''      C2      Up Arrow
    ''      C3      Down Arrow
    ''      C4      Home
    ''      C5      End
    ''      C6      Page Up
    ''      C7      Page Down
    ''      C8      Backspace
    ''      C9      Delete
    ''      CA      Insert
    ''      CB      Esc
    ''      CC      Apps
    ''      CD      Power
    ''      CE      Sleep
    ''      CF      Wakeup
    ''
    ''      D0..DB  F1..F12
    ''      DC      Print Screen
    ''      DD      Scroll Lock
    ''      DE      Caps Lock
    ''      DF      Num Lock
    ''
    ''      E0..E9  Keypad 0..9
    ''      EA      Keypad .
    ''      EB      Keypad Enter
    ''      EC      Keypad +
    ''      ED      Keypad -
    ''      EE      Keypad *
    ''      EF      Keypad /
    ''
    ''      F0      Left Shift
    ''      F1      Right Shift
    ''      F2      Left Ctrl
    ''      F3      Right Ctrl
    ''      F4      Left Alt
    ''      F5      Right Alt
    ''      F6      Left Win
    ''      F7      Right Win
    ''
    ''      FD      Scroll Lock State
    ''      FE      Caps Lock State
    ''      FF      Num Lock State
    ''
    ''      +100    if Shift
    ''      +200    if Ctrl
    ''      +400    if Alt
    ''      +800    if Win
    ''
    ''      eg. Ctrl-Alt-Delete = $6C9
    ''
    ''
    '' Note: Driver will buffer up to 15 keystrokes, then ignore overflow.
    PASM End
    
    ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
    ?                                                   TERMS OF USE: MIT License                                                  ?                                                            
    ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
    ?Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    ? 
    ?files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    ?
    ?modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software?
    ?is furnished to do so, subject to the following conditions:                                                                   ?
    ?                                                                                                                              ?
    ?The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.?
    ?                                                                                                                              ?
    ?THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          ?
    ?WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         ?
    ?COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   ?
    ?ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         ?
    ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
    */ 
    
    unsigned long key_cog[511] =
    {
        0x54fc093d, 0xa0be61f0, 0x80fe602c, 0xa0fe6204, 
        0x08bc0130, 0x80bc08d8, 0x80fe6004, 0xe4fe6204, 
        0xa0fe5801, 0x2cbe593d, 0xa0fe5a01, 0x2cbe5b3e, 
        0x617e7a20, 0x70bd3ed8, 0x70bd52d8, 0x70fd7601, 
        0x70fd9401, 0x617e7c20, 0x70bd38d8, 0x70bd44d8, 
        0x70fd9201, 0xa0fe6600, 0xa0ffec00, 0xa0ffee00, 
        0x54fc3534, 0xa0fe6009, 0xa0fc0000, 0x80bc34d8, 
        0xe4fe601a, 0xa0fe5c08, 0x54fc4533, 0xa0be61f0, 
        0x80fe6004, 0xa0fe620a, 0x083c0130, 0x80bc44d8, 
        0x80fe6004, 0xe4fe6222, 0x617e5c08, 0xa0f25eff, 
        0x5cf1689c, 0xa0fe5c00, 0x5cfd88b5, 0x857e5e84, 
        0x864e5eaa, 0x5c480080, 0x864e5ee0, 0x68ca5c01, 
        0x5c48002a, 0x864e5ef0, 0x68ca5c02, 0x5c48002a, 
        0x5c4c0029, 0x617e5c01, 0x34fe5e01, 0x5cfd8894, 
        0x867e5e00, 0x5c680029, 0xa0be653b, 0xa0be612f, 
        0x28fe6005, 0x80fe6135, 0x54bc8530, 0xa0fe6201, 
        0x2cbe632f, 0x617e5c02, 0x74bc0131, 0xe1ce5ef0, 
        0x5c70001e, 0xa0be633c, 0x28fe6210, 0xe1fe5ee0, 
        0x62727e04, 0x80e25f08, 0x80d25f18, 0x5cf18894, 
        0x5c70006a, 0xe1fe5edd, 0xa0f26008, 0x2cb2612f, 
        0x64b2613f, 0x28f26003, 0x28f2641d, 0x66b26132, 
        0x6cb27f30, 0x80f25edd, 0x68d25c04, 0x627e6203, 
        0x85565e61, 0xe1d25e5b, 0x80d25f28, 0x5cd18894, 
        0x64d26203, 0x85565e3e, 0xe1d25e27, 0x80d25f2e, 
        0x5cd18894, 0x64d26203, 0x617e7e02, 0x74fcd020, 
        0x617e7e40, 0x6cc4d020, 0x857e5e7b, 0xe1f25e61, 
        0x80f25e41, 0x64f26203, 0x20fe5e08, 0xa0fe6004, 
        0x627e6203, 0x28fe6202, 0x68d65e01, 0x20fe5e01, 
        0xe4fe606c, 0x24fe5e0c, 0x08be61f0, 0x84fe6001, 
        0x60fe600f, 0x863e6133, 0x62565eff, 0xa09661f0, 
        0x80d6602c, 0x80966133, 0x80966133, 0x04165f30, 
        0x80d66601, 0x60d6660f, 0x617e5c04, 0x5c4c001e, 
        0xa0fe5ef3, 0x5cfd689c, 0xa0be5f40, 0x60fe5e7f, 
        0x5cfd689c, 0xa0fe5eed, 0x5cfd689c, 0xa0be5f3f, 
        0x3cfe5e1d, 0x617e5e04, 0x34fe5e01, 0x60fe5e07, 
        0x5cfd689c, 0xa0be613f, 0x60fe6007, 0x2cfe7803, 
        0x68be7930, 0x20fe7803, 0xa0fe6801, 0x5c7c001e, 
        0x20fe5e02, 0x50bd332f, 0x80fd32da, 0x28fe5e1b, 
        0xa0be612f, 0xa0be5e00, 0x28be5f30, 0x5c7c00c3, 
        0x68bfed2d, 0x50fda60d, 0x5cfdaed2, 0x68bfed2c, 
        0x50fda612, 0x5cfdaed2, 0x6cbfed2d, 0x617e5eff, 
        0x74fe5f00, 0x68be5ed8, 0xa0fe600a, 0x5cfd9ac5, 
        0x29fe5e01, 0x74bfed2c, 0xa0bd96cf, 0x5cfd9ac6, 
        0xe4fe60a7, 0xa0bd96d0, 0x5cfd9ac6, 0xa0bd96d1, 
        0x5cfd9ac6, 0x5cfd88b7, 0x867e5efa, 0x5c540016, 
        0x5c7c0000, 0x617e7c20, 0xf43e5b2d, 0xa0fe600b, 
        0x5cfd9ac5, 0x50fda610, 0x5cfdaed2, 0x613e59f2, 
        0x30fe5e01, 0xa0bd96cf, 0x5cfd9ac6, 0xe4fe60b8, 
        0x28fe5e16, 0x617e5fff, 0x5c4c0016, 0x60fe5eff, 
        0x5c7c0000, 0xa0bd96ce, 0xa0be62d9, 0x50fda612, 
        0x5cfdaed2, 0x613e5bf2, 0x623e59f2, 0xe4c262c7, 
        0xec7e6216, 0x5c7c0000, 0xe4f262c7, 0xe4ce62c7, 
        0xe4f662c7, 0xe4ee62c7, 0x08fe6400, 0x28fe6400, 
        0x48fe6403, 0x80be65f1, 0xf8fe6400, 0x5c7c0000, 
        0x00000200, 0x000009c4, 0x00d80000, 0x00d40000, 
        0x00d000d2, 0x00db00d1, 0x00d90000, 0x00d500d7, 
        0x000900d3, 0x00000060, 0xf5f40000, 0x000000f0, 
        0x0071f3f2, 0x00000031, 0x00000000, 0x0073007a, 
        0x00770061, 0xf6000032, 0x00630000, 0x00640078, 
        0x00340065, 0xf7000033, 0x00200000, 0x00660076, 
        0x00720074, 0xcc000035, 0x006e0000, 0x00680062, 
        0x00790067, 0xcd000036, 0x00000000, 0x006a006d, 
        0x00370075, 0xce000038, 0x002c0000, 0x0069006b, 
        0x0030006f, 0x00000039, 0x002e0000, 0x006cef2f, 
        0x0070003b, 0x0000002d, 0x00000000, 0x00000027, 
        0x003d005b, 0x00000000, 0x00f100de, 0x005deb0d, 
        0x005c0000, 0x0000cf00, 0x00000000, 0x00000000, 
        0x00000000, 0x000000c8, 0xc5e10000, 0xc0e40000, 
        0x0000c4e7, 0x00000000, 0xc9eacae0, 0x00e5c3e2, 
        0xc2e8c1e6, 0x00df00cb, 0x00ec00da, 0x00edc7e3, 
        0xc6e9dcee, 0x000000dd, 0x00000000, 0x00d60000, 
        0xc7c3c5ca, 0xc4c100c0, 0x0dc9c6c2, 0x2f2a2d2b, 
        0x33323130, 0x37363534, 0x0d2e3938, 0x2f2a2d2b, 
        0x007d7c7b, 0x00227e00, 0x3c000000, 0x293f3e5f, 
        0x24234021, 0x2a265e25, 0x003a0028, 0x0000002b
    };
    
    // start of C functions
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    void external_memory_cog_load(int cognumber, unsigned long cogdata[], unsigned long parameters_array[])    	//  load a cog from external memory
    {
    	unsigned long hubcog[511];						// create a local array, this is in hub ram, not external ram	
    	int i;	
    	for(i=0;i<512;i++)								
    	{
    		hubcog[i]=cogdata[i];					// move from external memory to a local array in hub
    	}
     	_coginit((int)parameters_array>>2, (int)hubcog>>2, cognumber);		// load the cog
    }  
    
    unsigned long key_startx(unsigned long dpin,unsigned long cpin, unsigned long keylocks, unsigned long keyauto, int cognumber, unsigned long par[], unsigned long cogdata[])
    
    /*
    PUB startx(dpin, cpin, locks, auto) : okay
    
    '' Like start, but allows you to specify lock settings and auto-repeat
    ''
    ''   locks = lock setup
    ''           bit 6 disallows shift-alphas (case set soley by CapsLock)
    ''           bits 5..3 disallow toggle of NumLock/CapsLock/ScrollLock state
    ''           bits 2..0 specify initial state of NumLock/CapsLock/ScrollLock
    ''           (eg. %0_001_100 = disallow ScrollLock, NumLock initially 'on')
    ''
    ''   auto  = auto-repeat setup
    ''           bits 6..5 specify delay (0=.25s, 1=.5s, 2=.75s, 3=1s)
    ''           bits 4..0 specify repeat rate (0=30cps..31=2cps)
    ''           (eg %01_00000 = .5s delay, 30cps repeat)
    
      stop
      longmove(@par_keys, @dpin, 4)
      okay := cog := cognew(@entry, @par_tail) + 1
    */
    {
    	unsigned long okay;	
    	par[11] = dpin;					//  longmove(@par_keys, @dpin, 4)
    	par[12] = cpin;					// move 4 bytes into array, replaces the longmove
    	par[13] = keylocks;
    	par[14] = keyauto;
    	external_memory_cog_load(cognumber,cogdata,par);		// load from external ram
    	// okay returns the cog number or -1 if a fail page 119 manual. Ignored here
    	return okay;		
    }
    
    unsigned long key_start(unsigned long dpin,unsigned long cpin, int cognumber, unsigned long par[], unsigned long cogdata[])
    /*
    VAR
      long  cog
      long  par_tail        'key buffer tail        read/write      (19 contiguous longs)
      long  par_head        'key buffer head        read-only
      long  par_present     'keyboard present       read-only
      long  par_states[8]   'key states (256 bits)  read-only
      long  par_keys[8]     'key buffer (16 words)  read-only       (also used to pass initial parameters)
    PUB start(dpin, cpin) : okay
    '' Start keyboard driver - starts a cog
    '' returns false if no cog available
    ''
    ''   dpin  = data signal on PS/2 jack
    ''   cpin  = clock signal on PS/2 jack
    ''
    ''     use 100-ohm resistors between pins and jack
    ''     use 10K-ohm resistors to pull jack-side signals to VDD
    ''     connect jack-power to 5V, jack-gnd to VSS
    ''
    '' all lock-keys will be enabled, NumLock will be initially 'on',
    '' and auto-repeat will be set to 15cps with a delay of .5s
      okay := startx(dpin, cpin, %0_000_100, %01_01000)
    */
    {
    	unsigned long okay;						
    	par[0] = 0;							// par_tail
    	par[1] = 0;							// par_head
    	par[2] = 0;							// par_present
      	key_startx(dpin, cpin, 0x4, 0x28, cognumber, par, cogdata);     	// okay := startx(dpin, cpin, %0_000_100, %01_01000)
    	return okay;
    }
    
    unsigned long key_present(unsigned long par[])
    /*
    PUB present : truefalse
    
    '' Check if keyboard present - valid ~2s after start
    '' returns t|f
      truefalse := -par_present
    */
    {
    	signed long truefalse;
    	truefalse = par[2];						// 0 = false. In spin, true is -1. In C it is 1, so ? no negative??
    	truefalse = -truefalse;
    	return truefalse;
    }
    
    unsigned long key_key(unsigned long par[])			// returns keycode, never waits, 0 if nothing
    /*
    PUB key : keycode
    '' Get key (never waits)
    '' returns key (0 if buffer empty)
      if par_tail <> par_head
        keycode := par_keys.word[par_tail]
        par_tail := ++par_tail & $F
    */
    {
    	unsigned long keycode = 0;	
    	unsigned long buffer_ptr;
    	int address;
    	if (par[0] != par[1])					// if par_tail <> par_head
    	{
    		buffer_ptr = par[0] + par[0];			// increments in words, not bytes
    		buffer_ptr += (unsigned long)&par[11];		// pointer to start of buffer
    		address = buffer_ptr;				// convert to integer
    		keycode = peek(address);				// get the key code by peeking it
    		par[0] = (par[0] +1) & 0xF;				// add one to tail
    		// printf("keycode %c address = %i tail %u \n",keycode,address,par[0]);
    	}
    	return keycode;
    }
    
    unsigned long key_getkey(unsigned long par[])
    /*
    PUB getkey : keycode
    '' Get next key (may wait for keypress)
    '' returns key
      repeat until (keycode := key)
    */
    {
    	unsigned long keycode;
    	while ((keycode = key_key(par)) == 0) {}			// keep checking while returns 0
    	return keycode;
    }
    
    unsigned long key_newkey(unsigned long par[])			// clear buffer and get new key
    /*
    PUB newkey : keycode
    '' Clear buffer and get new key (always waits for keypress)
    '' returns key
      par_tail := par_head
      keycode := getkey
    */
    {
    	par[0] = par[1];						// par_tail := par_head
    	return key_getkey(par);					// get the keypress (waits)	
    }
    
    unsigned long key_gotkey(unsigned long par[])			// check if any key in buffer
    /*
    PUB gotkey : truefalse
    '' Check if any key in buffer
    '' returns t|f
      truefalse := par_tail <> par_head
    */
    {
    	unsigned long truefalse = 0;				// 0 is false
    	if (par[0] == par[1])
    	{
    		truefalse = 0xFFFFFFFF;				// -1 is true
    	}
    	return truefalse;
    }
    
    void key_clearkeys(unsigned long par[])
    /*
    PUB clearkeys
    '' Clear key buffer
      par_tail := par_head
    */
    {
    	par[0] = par[1];						// par_tail := par_head
    }
    
    
    
    void key_demo(unsigned long key_parameters[])			// demonstrate the keyboard cog code
    {
    	char name[11];						// input string
    	int i;
    	int j;
    	unsigned long k;
    	clearscreen();						// clear the screen and white on blue
    	printf("Catalina running in cog number %i \n",_cogid());      // integer
    	_cogstop(0);							// keyboard is in cog 0
    	key_start(26,27,7,key_parameters,key_cog);		// start in cog 7
    	printf("keyboard started \n");
    	sleep(1000);							// delay to start up the keyboard
    	// printf("Keyboard not present =0, present = -1 (or 1) %d \n",key_present(key_parameters));
    	// printf("hit a key \n");					// needs the \n
    	// printf("you hit %u \n",key_getkey(key_parameters));	// test getkey
    	// printf("hit another key \n");				
    	// printf("you hit %u \n",key_newkey(key_parameters));	// test newkey
    	printf("hit some keys (20 keypresses) \n");
    	for (i=0;i<20;i++)
    	{
    		k = key_getkey(key_parameters);
    		printf("key %c with ascii value %u \n",k,k);
    		// printf("head %u tail %u \n",key_parameters[1],key_parameters[0]);
    	}
    	printf("program finished \n");
    }
    
    
    void main ()
    {
    	unsigned long key_parameters[18];					// reserve hub space in main for buffer, head tail pointers
    	key_demo(key_parameters);						// demo routines       
    	while (1); 								// endless loop as prop reboots on exit from main() 
    }
    
  • RossHRossH Posts: 5,547
    edited 2011-03-20 04:08
    Dr_Acula wrote: »
    Keyboard Cogject tested and working.

    Ross, there are 8 cogs - one is being used for catalina (2) , one is for the keyboard (0) and one is spare (7). What are the others being used for?

    Dr_A,

    You'd have to send me your exact compilation command for me to tell. A mouse? An SD card plugin? One or more floating point plugins? Perhaps a clock? Also, some of the Hi Res VGA drivers actually take two cogs, not one (the Morpheus VGA driver takes three!).

    The only extra cog I can be use of is that at least one is being used for the HMI plugin itself, which adds functionality to the various underlying device drivers. You've probably never realized it, but Catalina adds a visible flashing cursor to all video drivers - even the ones that don't natively support such a thing. The HMI plugin adds a heap of functionality like that to the various native keyboard, mouse and screen drivers.

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-03-20 04:10
    Dr_Acula wrote: »
    I shall use that example right now!

    A small thing
        printf("hit a key \n");                    // needs the \n
        printf("you hit %u \n",key_getkey(key_parameters));
    
    if I leave out the \n then it does not print the "hit a key" until *after* I hit the key. If I put the \n in, then it prints "hit a key" as expected. Is this a C feature?

    I would guess you are compiling with the -lcx library, which buffers all I/O. Recompile your program with -lc and you may see slightly different behaviour. You could probably also call fflush() to force a write. I would have to try this to be sure - but this is quite acceptable C behaviour.

    Ross.
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-20 10:48
    RossH wrote: »
    David, Jazzed ...

    I've just emailed you both an updated 3.0 pre-release which fixes the cache problem. It should speed up performance by a factor of two or more.

    Ross.

    Hi Ross,

    Thanks for the update! I tried this new version and it was not noticably faster at running my xbasic test program. The old version of Catalina using the -x3 memory layout took 12 seconds and this version took 11 seconds. That could easily be explained by errors in pushing the stopwatch button on my watch. In any case, it's good to have the cache bug fixed since I'm sure it could have caused problems.

    Thanks,
    David
Sign In or Register to comment.