blob: 5e8d60069b95180a8c053ca3cd2778f1196f88b2 [file] [log] [blame]
/*
* Communications stub for debugging from a host CForth process.
* The host can send a stream of bytes to tell this stub to perform
* various operations including memory access and executing
* subroutines.
* The basic idea is that the host pushes numbers on a stack,
* then sends a command byte that performs an operation using
* the data on the stack. The result of that operation can
* be sent back to the host.
*
* Call tether_loop() from your app to enable the debugging.
*
* Depends on external definition of
* void sendbyte(unsigned char byte);
* unsigned char getbyte(void);
*/
extern void sendbyte(unsigned char byte);
extern unsigned char getbyte(void);
// Protocol byte values:
// Result:
// 10nn.nnnn, [0mmm.mmmm]*, 1100.0000
// 10nn.nnnn Push and set TOS to nnnnnn
// 0mmm.mmmm TOS = TOS << 7 | mmmmmmm
// 11pp.pppp Execute function P
// P=0 : ACK (noop) - used in target to host direction
// P=1 : @
// P=2 : w@
// P=3 : c@
// P=4 : !
// P=5 : w!
// P=6 : l!
// P=7 : Send TOS back to host
// P=8 : EXIT from command processing loop
// P=9 : Push address of scratch buffer 0
// P=10: Push address of scratch buffer 1
// P=11: Read n (TOS) bytes from host and store in target memory at address (SOS)
// P=12: Send n (TOS) bytes from target memory at address (SOS) to host
// P=13: PING - just send ACK
// P=1xxxx0 : Execute TOS with rest of stack as args
// P=1xxxx1 : Execute TOS with rest of stack as args, return result to host
void sendack()
{
sendbyte(0xc0);
}
void send(unsigned int u)
{
if (u < 0x40) {
sendbyte(u | 0x80);
goto tail0;
}
if (u < 2000) {
sendbyte((u >> 7) | 0x80);
goto tail1;
}
if (u < 100000) {
sendbyte((u >> 14) | 0x80);
goto tail2;
}
if (u < 8000000) {
sendbyte((u >> 21) | 0x80);
goto tail3;
}
sendbyte((u >> 28) | 0x80);
sendbyte((u >> 21) & 0x7f);
tail3:
sendbyte((u >> 14) & 0x7f);
tail2:
sendbyte((u >> 7) & 0x7f);
tail1:
sendbyte( u & 0x7f);
tail0:
sendack();
}
#define SCRATCHSIZE 0x40
unsigned char scratch0[SCRATCHSIZE];
unsigned char scratch1[SCRATCHSIZE];
#define STACKSIZE 10
unsigned int stack[STACKSIZE];
void push(void)
{
stack[9] = stack[8];
stack[8] = stack[7];
stack[7] = stack[6];
stack[6] = stack[5];
stack[5] = stack[4];
stack[4] = stack[3];
stack[3] = stack[2];
stack[2] = stack[1];
stack[1] = stack[0];
// Some libs don't have memmove, and memcpy doesn't work with overlapping ranges
// memmove(&stack[1], &stack[0], (STACKSIZE-1) * sizeof(stack[0]));
}
void copy_in(unsigned char *adr, int len)
{
while (len--) {
*adr++ = getbyte();
}
}
void copy_out(unsigned char *adr, int len)
{
while (len--) {
sendbyte(*adr++);
}
}
int perform(unsigned char b)
{
int (*func)() = (void *)stack[0];
if (b & 0x20) {
stack[0] = func(stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7]);
if (b & 1)
send(stack[0]);
else
sendbyte(0xc0);
} else {
switch (b)
{
case 0: break;
case 1: stack[0] = *(int *)stack[0]; send(stack[0]); break;
case 2: stack[0] = *(unsigned short *)stack[0]; send(stack[0]); break;
case 3: stack[0] = *(unsigned char *)stack[0]; send(stack[0]); break;
case 4: *(int *)stack[0] = stack[1]; break;
case 5: *(short *)stack[0] = (short)stack[1]; break;
case 6: *(char *)stack[0] = (char )stack[1]; break;
case 7: send(stack[0]); break;
case 8: return (1);
case 9: push(); stack[0] = (unsigned int)scratch0; break;
case 10: push(); stack[0] = (unsigned int)scratch1; break;
case 11: copy_in((void *)stack[1], stack[0]); break;
case 12: copy_out((void *)stack[1], stack[0]); break;
case 13: sendack(); break;
}
}
return (0);
}
int handle_byte(unsigned char b)
{
if ((b & 0x80) == 0) {
stack[0] = (stack[0] << 7) | b;
return 0;
}
if ((b & 0x40) == 0) {
push();
stack[0] = b & 0x3f;
return 0;
}
return perform(b & 0x3f);
}
void tether_loop()
{
sendack(); // This ACK informs the host that we are ready
while (handle_byte(getbyte()) == 0)
;
}