Skip to content

Excellent Prototype Shield Bargain

I picked this up at random in a local shop. What a wonderful thing. Stacking Headers, reset button, extra leds and a spare button with a wired up footprint for a bluesmirf bluetooth module. All for under ten bucks!
15-06-20 protoshield

Worth noting that Active is one of only two ottawa shops where you can buy individual electronic components. Active has now several racks of arduino-style components at quite decent prices. Gervais, the other local option has a few arduino type things behind the counter but they’re good for ic’s and passives and they’re close to me.

What I should have bought

 
The spark fun basic ftdi breakout matches the pin out for the pro mini exactly. 

Well This Seems Awkward – FTDI Cables and the Arduino-ish Pro Mini

So the arduino pro-mini came today (ordered May 11: 5 weeks). That made me think I’d better try hooking it up with the 3.3v cable that came last week which turns out to be a bit of a mess.
15-06-17 Cables
The first thing I noticed is that the new 3.3V cable on the right has wildly different pinouts than my old standard 5v cable that i’ve been using for years. Yes, it does go vcc-gnd instead of gnd-cts-vcc!

15-06-17 prominipdf
Next I see that the Pro Mini has a different pinout again with gnd-gnd-vcc at one end instead of gnd-cts-vcc (maybe that’s ok though CTS is just being held low).

Anyway, it looks like some kind of adapter will be needed. I might be able to get away with something scungy but it would be better by far to do it right.

Further, these cables seem to have 3.3V I/O but the VCC line is 5V which means I can’t just hook it up anyway. The pro-mini has an onboard regulator so maybe i can feed 5V to its RAW input. Anyway, some kind of adapter will be needed.

Thanks to pighixxx on the arduinio.cc forum for the pinout
http://forum.arduino.cc/index.php?topic=152386.0

Dirty PCB’s and Ali Express

I got interested in building a quadrupedal robot after reading a post on hack-a-day.
15-06-09 tote
The author makes the pcbs available from DirtyPCBs and the bill of materials links to Ali Express. I think i’ve ordered from Ali but not dirty pcbs so i’m treating this as a learning experience.

The first part came in the mail today:
A 3.3 volt ftdi serial cable, ordered May 13, received june 9. Approx 3 weeks.

The PCBs were ordered May 13 and shipped may 18.

UPDATE:
The Infrared Receiver modules arrived. Ordered May 11, rxd June 16, Approx 5 weeks.

What was wrong with Carbot?

I’m generally having a summer of no-code but something was bugging me about Carbot. On its last outing, I tried a new control regime(call it S2) which kept it in a while loop for left turn until it had cleared the wall. This was pretty hopeless and i tried going back to the S1 regime which turned for a fixed time then went back to the main loop. This was still crap and I put it aside not knowing what was going on.

Today I checked the code from my last successful iteration and found that what I was calling S1 wasn’t much like it. Specifically, the successful code had a 1ms turn increment while the “S1″ had a 60ms turn which i know never worked well. I’ll charge up the batteries and give it a go sometime.

Carbot is in the weeds


I had changed the carbot control scheme to make it quicker and more precice. HAH! The S2 version ran head on into the wall at high speed and consistently made 180 degree turns instead of 90. I detuned the speed a bit but it stuck to its 180 degree turns and had no interest in following the wall.

I reverted to the previous(s1) control scheme but found the same 180 degree turns and wall disinterest. I’m documenting two runs with video here but i’m going on a no-robot walking holiday and the heck with it!

//olduino carbot simple version Jan 24, 2015
//15-04-26 using <> version of cpu1802spd4port7.h
//15-04-29 cruising speed increased, left turn now 5 ms
#include <olduino.h>
#include <nstdlib.h> //supports c d s x l cx
#include <cpu1802spd4port7.h>
#include "softpwm.h"
#include "carbot.h"
#include "pingBN.h"
unsigned int wdist,oldwdist, fprox; // distance to wall on right current & previous, proximity of barrier in front
#define maxfprox 160 //max front barrier proximity
#define maxwdist 30 //min right barrier dist
#define minwdist 25 //min right barrier dist
#define basespeed 192	//was 192
#define lowspeed 32	//was 32
unsigned int sharpy(){  //reads the Sharp GP2D12 IR rangefinder via the MCP3002 ADC, returns raw ADC value
	asm("	dec	sp\n" //make a work area
		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0x68\n"	//send 68 to read channel 0
		"	sex sp\n	inp 6\n" //pick up top bits
		"	ani 3\n	phi 15\n"	//put bits 8&9 of result into return register

		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0xAA\n"	//send pattern to read low bits
		"	sex sp\n	inp 6\n	plo 15\n" //pick up low bits to return register
		"	inc	sp\n"	//restore stack pointer
		"	cretn\n");	//actual return
	return 0;	//dummy return for compiler
}
void killdrive(){//stops both motors
	digitalWrite(pwmb,LOW);//kill power right
	digitalWrite(pwma,LOW);//kill power left
}
void hardleft(){//hard turn with left wheel running backwards
	printf("L>\n");
	digitalWrite(pwmb,HIGH);//full power right
	digitalWrite(pwma,HIGH);//full power left
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,LOW); digitalWrite(ain2,HIGH); //reverse left wheel
	delay(1); //nominal delay only
}

void tooclose(){ //if we are closer than minwdist to the wall on our right side
	printf("TC");
    if (wdist>oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-1 ");
    }
    else{
		analogWrite(pwma,lowspeed);analogWrite(pwmb,basespeed); //bear left
		printf(" wvlft ");
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
	printf("TF");
    if (wdist<oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-2 ");
    }
    else{
		analogWrite(pwma,basespeed);analogWrite(pwmb,lowspeed); //bear right
		printf(" wvrt ");
    }
}

void inthezone(){ //if we are between minwdist and maxwdist
	printf("IZ");
	analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
	printf(" wf-3 ");
}

void cruiseAlongWall(){
	if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
	wdist=pingQ()/2; //get the wall distance in cm
	printf("cw %d\n",wdist);
	if (wdist<minwdist){
		tooclose();
	}else if (wdist>maxwdist){
		toofar();
	}else{
		inthezone();
	}
	//the routines above will have set the motor power levels, now we activate them
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,HIGH); digitalWrite(ain2,LOW);
	pwmcycleN(15);//forward a bit
}

void main(){
	unsigned int ttl=300; //time to live limit,
	printf("Simpler Panopticon Carbot(L=1,B=255,S=64,min/max=25/30) O-carbotS1.c\n");
	PIN4=0x80;out(4,0x80);
	while(ttl!=00){
		printf("@%d:",ttl);
		digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
		printf("<%d ",fprox);
		if (fprox<maxfprox){ // more than 1 ft from barrier
			while((fprox<maxfprox)&&ttl!=0){
				printf("F>\n");
				cruiseAlongWall();//set a course to track the wall
				digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
				ttl--;
				printf("[%d]",fprox);
			}
			killdrive();
		}else{
			while((fprox>=maxfprox) && ttl!=0){
				hardleft();
				digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
				ttl--;
				printf("(%d)",fprox);
			}
			killdrive();
		}
	}
	PIN4=0;out(4,0); //kill it all
}
#include <olduino.c>
#include <nstdlib.c>
#include "softpwm.c"
#include "pingBN.c"
//olduino carbot simple version Jan 24, 2015
//
#include <olduino.h>
#include <nstdlib.h> //supports c d s x l cx
#include "cpu1802spd4port7.h"
#include "softpwm.h"
#include "carbot.h"
#include "pingBN.h"
unsigned int wdist,oldwdist, fprox; // distance to wall on right current & previous, proximity of barrier in front
#define maxfprox 160 //max front barrier proximity
#define minwdist 30 //min right barrier dist
#define basespeed 192
#define lowspeed 32
unsigned int sharpy(){  //reads the Sharp GP2D12 IR rangefinder via the MCP3002 ADC, returns raw ADC value
	asm("	dec	sp\n" //make a work area
		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0x68\n"	//send 68 to read channel 0
		"	sex sp\n	inp 6\n" //pick up top bits
		"	ani 3\n	phi 15\n"	//put bits 8&9 of result into return register

		"	sex 3\n"	//set x to pc
		"	out 6\n	db 0xAA\n"	//send pattern to read low bits
		"	sex sp\n	inp 6\n	plo 15\n" //pick up low bits to return register
		"	inc	sp\n"	//restore stack pointer
		"	cretn\n");	//actual return
	return 0;	//dummy return for compiler
}
void killdrive(){//stops both motors
	digitalWrite(pwmb,LOW);//kill power right
	digitalWrite(pwma,LOW);//kill power left
}
void hardleft(){//60 ms turn with left wheel running backwards
	printf("L>\n");
	digitalWrite(pwmb,HIGH);//full power right
	digitalWrite(pwma,HIGH);//full power left
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,LOW); digitalWrite(ain2,HIGH); //reverse left wheel
	delay(60);
}

void tooclose(){ //if we are closer than minwdist to the wall on our right side
    if (wdist>oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-1 ");
    }
    else{
		analogWrite(pwma,lowspeed);analogWrite(pwmb,basespeed); //bear left
		printf(" wvlft ");
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
    if (wdist<oldwdist) {//but going the right way
		analogWrite(pwma,basespeed);analogWrite(pwmb,basespeed); //proceed
		printf(" wf-2 ");
    }
    else{
		analogWrite(pwma,basespeed);analogWrite(pwmb,lowspeed); //bear right
		printf(" wvrt ");
    }
}
void cruiseAlongWall(){
	if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
	wdist=pingQ()/2; //get the wall distance in cm
	printf("cw %d\n",wdist);
	if (wdist<minwdist){
		tooclose();
	}else{
		toofar();
	}
	//the routines above will have set the motor power levels, now we activate them
	digitalWrite(bin1,HIGH); digitalWrite(bin2,LOW);
	digitalWrite(ain1,HIGH); digitalWrite(ain2,LOW);
	pwmcycleN(15);//forward a bit
}

void main(){
	unsigned int ttl=300; //time to live limit,
	printf("Simpler Panopticon Carbot O-carbotS1.c\n");
	PIN4=0x80;out(4,0x80);
	while(ttl!=00){
		printf("@ %d:",ttl);
		digitalWrite(7,LOW);fprox=sharpy(); digitalWrite(7,HIGH);
		printf("<%d ",fprox);
		if (fprox<maxfprox){ // more than 1 ft from barrier
			killdrive();//kill any turn that was going on
			printf("F>\n");
			cruiseAlongWall();//set a course to track the wall
		}else{
			hardleft();
		}
		ttl--; //reduce time to live
	}
	PIN4=0;out(4,0); //kill it all
}
#include <olduino.c>
#include <nstdlib.c>
#include "softpwm.c"
#include "pingBN.c"
15:02:49.211> @300:<60 F>
15:02:49.273> cw 12
15:02:49.460> TC wf-1 [56]F>
15:02:49.460> cw 12
15:02:49.702> TC wvlft [62]F>
15:02:49.764> cw 13
15:02:49.951> TC wf-1 [65]F>
15:02:50.011> cw 86
15:02:50.198> TF wvrt [65]F>
15:02:50.260> cw 28
15:02:50.510> IZ wf-3 [71]F>
15:02:50.510> cw 14
15:02:50.760> TC wvlft [76]F>
15:02:50.760> cw 15
15:02:51.009> TC wf-1 [85]F>
15:02:51.009> cw 15
15:02:51.259> TC wvlft [97]F>
15:02:51.259> cw 17
15:02:51.508> TC wf-1 [121]F>
15:02:51.508> cw 18
15:02:51.760> TC wf-1 [149]F>
15:02:51.760> cw 19
15:02:52.072> TC wf-1 [220]@289:<268 L>
15:02:52.072> (299)L>
15:02:52.135> (329)L>
15:02:52.197> (357)L>
15:02:52.197> (384)L>
15:02:52.260> (413)L>
15:02:52.322> (437)L>
15:02:52.322> (467)L>
15:02:52.384> (483)L>
15:02:52.447> (458)L>
15:02:52.509> (401)L>
15:02:52.509> (328)L>
15:02:52.634> (105)@277:<92 F>
15:02:52.634> cw 6
15:02:52.884> TC wvlft [28]F>
15:02:52.884> cw 27
15:02:53.133> IZ wf-3 [49]F>
15:02:53.133> cw 27
15:02:53.383> IZ wf-3 [51]F>
15:02:53.383> cw 27
15:02:53.632> IZ wf-3 [55]F>
15:02:53.632> cw 26
15:02:53.882> IZ wf-3 [62]F>
15:02:53.944> cw 25
15:02:54.132> IZ wf-3 [62]F>
15:02:54.132> cw 24
15:02:54.381> TC wvlft [65]F>
15:02:54.444> cw 24
15:02:54.631> TC wvlft [69]F>
15:02:54.693> cw 24
15:02:54.880> TC wvlft [78]F>
15:02:54.943> cw 26
15:02:55.130> IZ wf-3 [81]F>
15:02:55.192> cw 27
15:02:55.380> IZ wf-3 [89]F>
15:02:55.442> cw 30
15:02:55.629> IZ wf-3 [101]F>
15:02:55.692> cw 32
15:02:55.879> TF wvrt [127]F>
15:02:55.941> cw 32
15:02:56.191> TF wvrt [161]@263:<171 L>
15:02:56.253> (186)L>
15:02:56.316> (188)L>
15:02:56.378> (195)L>
15:02:56.378> (197)L>
15:02:56.440> (200)L>
15:02:56.440> (196)L>
15:02:56.503> (192)L>
15:02:56.565> (182)L>
15:02:56.565> (163)L>
15:02:56.693> (114)@253:<149 F>
15:02:56.693> cw 11
15:02:56.942> TC wvlft [151]F>
15:02:56.942> cw 20
15:02:57.192> TC wf-1 [153]F>
15:02:57.192> cw 20
15:02:57.504> TC wvlft [176]@250:<173 L>
15:02:57.566> (168)L>
15:02:57.566> (173)L>
15:02:57.629> (172)L>
15:02:57.691> (177)L>
15:02:57.691> (163)L>
15:02:57.816> (136)@244:<4 F>
15:02:57.816> cw 9
15:02:58.065> TC wvlft [55]F>
15:02:58.128> cw 88
15:02:58.315> TF wvrt [61]F>
15:02:58.315> cw 11
15:02:58.565> TC wvlft [61]F>
15:02:58.627> cw 85
15:02:58.814> TF wvrt [61]F>
15:02:58.877> cw 13
15:02:59.064> TC wvlft [61]F>
15:02:59.126> cw 86
15:02:59.313> TF wvrt [65]F>
15:02:59.376> cw 85
15:02:59.625> TF wf-2 [64]F>
15:02:59.625> cw 33
15:02:59.875> TF wf-2 [73]F>
15:02:59.875> cw 20
15:03:00.127> TC wvlft [82]F>
15:03:00.127> cw 132
15:03:00.377> TF wvrt [90]F>
15:03:00.377> cw 20
15:03:00.631> TC wvlft [93]F>
15:03:00.694> cw 22
15:03:00.881> TC wf-1 [100]F>
15:03:00.881> cw 32
15:03:01.130> TF wvrt [116]F>
15:03:01.130> cw 24
15:03:01.380> TC wvlft [145]F>
15:03:01.442> cw 25
15:03:01.691> IZ wf-3 [172]@229:<197 L>
15:03:01.754> (208)L>
15:03:01.754> (220)L>
15:03:01.816> (230)L>
15:03:01.879> (232)L>
15:03:01.879> (226)L>
15:03:01.941> (220)L>
15:03:02.003> (201)L>
15:03:02.003> (173)L>
15:03:02.128> (124)@220:<123 F>
15:03:02.128> cw 8
15:03:02.378> TC wvlft [104]F>
15:03:02.378> cw 25
15:03:02.627> IZ wf-3 [105]F>
15:03:02.627> cw 28
15:03:02.877> IZ wf-3 [118]F>
15:03:02.877> cw 24
15:03:03.127> TC wvlft [140]F>
15:03:03.127> cw 40
15:03:03.439> TF wvrt [172]@215:<185 L>
15:03:03.501> (196)L>
15:03:03.501> (203)L>
15:03:03.563> (204)L>
15:03:03.626> (207)L>
15:03:03.626> (209)L>
15:03:03.688> (188)L>
15:03:03.751> (161)L>
15:03:03.813> (145)@207:<81 F>
15:03:03.875> cw 11
15:03:04.063> TC wvlft [48]F>
15:03:04.125> cw 53
15:03:04.312> TF wvrt [37]F>
15:03:04.375> cw 14
15:03:04.562> TC wvlft [31]F>
15:03:04.624> cw 51
15:03:04.811> TF wvrt [28]F>
15:03:04.874> cw 18
15:03:05.061> TC wvlft [52]F>
15:03:05.123> cw 48
15:03:05.373> TF wvrt [48]F>
15:03:05.373> cw 22
15:03:05.610> TC wvlft [60]F>
15:03:05.610> cw 46
15:03:05.859> TF wvrt [52]F>
15:03:05.859> cw 44
15:03:06.109> TF wf-2 [69]F>
15:03:06.171> cw 43
15:03:06.358> TF wf-2 [77]F>
15:03:06.421> cw 41
15:03:06.608> TF wf-2 [86]F>
15:03:06.670> cw 39
15:03:06.858> TF wf-2 [98]F>
15:03:06.920> cw 37
15:03:07.107> TF wf-2 [117]F>
15:03:07.170> cw 37
15:03:07.482> TF wvrt [163]@193:<182 L>
15:03:07.482> (191)L>
15:03:07.544> (192)L>
15:03:07.544> (203)L>
15:03:07.606> (213)L>
15:03:07.671> (207)L>
15:03:07.671> (208)L>
15:03:07.736> (201)L>
15:03:07.799> (185)L>
15:03:07.861> (151)@184:<167 L>
15:03:07.923> (171)L>
15:03:07.986> (177)L>
15:03:07.986> (182)L>
15:03:08.048> (184)L>
15:03:08.048> (171)L>
15:03:08.173> (155)@178:<114 F>
15:03:08.235> cw 13
15:03:08.423> TC wvlft [59]F>
15:03:08.485> cw 23
15:03:08.672> TC wf-1 [36]F>
15:03:08.735> cw 22
15:03:08.922> TC wvlft [44]F>
15:03:08.984> cw 62
15:03:09.171> TF wvrt [28]F>
15:03:09.234> cw 79
15:03:09.483> TF wvrt [44]F>
15:03:09.483> cw 22
15:03:09.733> TC wvlft [60]F>
15:03:09.733> cw 56
15:03:09.985> TF wvrt [60]F>
15:03:09.985> cw 24
15:03:10.238> TC wvlft [65]F>
15:03:10.238> cw 53
15:03:10.488> TF wvrt [66]F>
15:03:10.488> cw 52
15:03:10.727> TF wf-2 [69]F>
15:03:10.789> cw 28
15:03:10.977> IZ wf-3 [74]F>
15:03:11.039> cw 30
15:03:11.226> IZ wf-3 [81]F>
15:03:11.289> cw 31
15:03:11.476> TF wvrt [92]F>
15:03:11.538> cw 32
15:03:11.725> TF wvrt [104]F>
15:03:11.788> cw 32
15:03:11.975> TF wvrt [116]F>
15:03:12.037> cw 31
15:03:12.225> TF wf-2 [130]F>
15:03:12.287> cw 30
15:03:12.537> IZ wf-3 [172]@161:<194 L>
15:03:12.599> (212)L>
15:03:12.661> (225)L>
15:03:12.661> (241)L>
15:03:12.724> (257)L>
15:03:12.786> (285)L>
15:03:12.786> (310)L>
15:03:12.849> (319)L>
15:03:12.849> (331)L>
15:03:12.911> (336)L>
15:03:12.973> (327)L>
15:03:12.973> (305)L>
15:03:13.036> (258)L>
15:03:13.098> (181)L>
15:03:13.223> (141)@147:<128 F>
15:03:13.285> cw 272
15:03:13.473> TF wvrt [105]F>
15:03:13.535> cw 20
15:03:13.722> TC wvlft [112]F>
15:03:13.722> cw 16
15:03:13.972> TC wvlft [119]F>
15:03:14.034> cw 14
15:03:14.221> TC wvlft [114]F>
15:03:14.284> cw 14
15:03:14.471> TC wvlft [77]F>
15:03:14.533> cw 13
15:03:14.733> TC wvlft [61]F>
15:03:14.733> cw 13
15:03:14.983> TC wvlft [73]F>
15:03:15.045> cw 13
15:03:15.232> TC wvlft [66]F>
15:03:15.232> cw 13
15:03:15.482> TC wvlft [61]F>
15:03:15.482> cw 14
15:03:15.732> TC wf-1 [61]F>
15:03:15.794> cw 14
15:03:15.981> TC wvlft [66]F>
15:03:15.981> cw 13
15:03:16.231> TC wvlft [85]F>
15:03:16.293> cw 13
15:03:16.480> TC wvlft [68]F>
15:03:16.543> cw 12
15:03:16.730> TC wvlft [4]F>
15:03:16.730> cw 40
15:03:16.980> TF wvrt [4]F>
15:03:17.042> cw 42
15:03:17.229> TF wvrt [4]F>
15:03:17.294> cw 44
15:03:17.481> TF wvrt [4]F>
15:03:17.544> cw 43
15:03:17.731> TF wf-2 [4]F>
15:03:17.793> cw 51
15:03:17.980> TF wvrt [4]F>
15:03:18.043> cw 51
15:03:18.230> TF wvrt [4]F>
15:03:18.292> cw 55
15:03:18.480> TF wvrt [4]F>
15:03:18.542> cw 53
15:03:18.729> TF wf-2 [4]F>
15:03:18.792> cw 67
15:03:19.041> TF wvrt [4]F>
15:03:19.041> cw 67
15:03:19.291> TF wvrt [4]F>
15:03:19.291> cw 66
15:03:19.540> TF wf-2 [5]F>
15:03:19.540> cw 66

Number Ninja – Fast and Flexible Binary to Ascii Conversion With Sharp Edges!

Double Dabble is way too gentle a tag for this bad boy. On my 4mhz processor this converts binary numbers to ascii strings at well over 1000 digits per second. It can handle numbers of arbitrary length up to buffer limits, and it punishes inattention with memory overruns – what more could you ask for!

Unlike the traditional itoa(int, char *) which takes an integer and a pointer to a buffer as parameters and returns a pointer to the ascii string, numberNinja takes three parameters: a POINTER to the first byte of the binary number which can be any length, a pointer to the TAIL of the buffer – not the 1st position, and the number of bytes in the binary number. The buffer, of course, has to be long enough for the result: e.g. 10 digits plus a terminating 0 for a long int.

The code is based on double dabble with some refinements: most notably the idea of storing the binary number in memory rather than a register and the use of an “add 120″ hack in the pre-correction loop. The general form of “double dabble” is to shift the bits from the top of the binary number into the bottom of the BCD number adjusting during each shift not to let the BCD digits get over 10. The previous versions used a pre-correction loop which are simplified dramatically with the 120 hack.

adding120

In the BCD digit sequence area, this code stores *one* BCD digit in
the low-order 4 bits of each byte. The high-order 4 bits are all
zeros. The rule for pre-correction is: if the BCD digit has the
value 4 or less, do *no* pre-correction at all! If the BCD digit has
the value 5 or more, add 3 for the pre-correction.

Note that if *no* pre-correction is done… the BCD digit has the
form “0xxx”; the high-order bit of the BCD digit will always be zero
if *no* precorrection is done. If a pre-correction *is* done, the
BCD digit will have the form “1xxx”. The BCD digit must have the
value 5 or greater to receive a pre-correction. 5 + 3 is 8, so the
smallest pre-corrected value for a BCD digit will be “1000” or 8.

RULE: BCD digits *not* needing correction will *always* have a “0”
high-order fourth bit; BCD digits that *are* corrected will *always*
have a “1” as the high-order fourth bit.

So if a pre-correction is done, the entire *byte* (containing the BCD
digit in the lower four bits)… will have the form “00001xxx”, where
“xxx” is some combination of three bits. The idea is to change the
pre-correction so the form of the byte will be “10000xxx”… or to
“move” that 1 bit at the high end of the BCD digit… to the high-
order bit in the byte. The “xxx” in the bottom will remain the
same. This is done *only* to BCD digits that are being pre-
corrected. The following chart shows how this works:

The above figure shows adding an additional 120 *after* the 3 pre-
correction has been done *and* the 5 has already been added back.
All these additions can be done at once by “adi 128″. Adding this
“block of ones” forces the 1 in the high-order bit of the BCD
digit… to become the high-order bit of the entire *byte*, so it
will shift into the carry (DF register) in the BCD digit shift loop.

Now the BCD digit shift loop can just do the left shift only and
*not* worry about the high-order 4 bits. BCD digits that were *not*
pre-corrected (4 or less in value) will have the form “00000xxx”
where “xxx” is 4 or less. The pre-corrected BCD digits will now have
the form “10000xxx”… and when this is shifted left, the high-order
1 goes into carry (DF register) and the high-order 4 bits will be
zero. So we are covered in both cases.

Number Ninja is, of course, coded in 1802 assembly but to be useful without being totally scary, it can be wrapped as for example:

char * numberninja(void * binary, char * buffertail, int sizeofbinarynumber);
char * itoa(int s, char *buffer){ //convert an integer to printable ascii in a buffer supplied by the caller
char * bptr;
if (s&lt;0){
s=-s;
bptr=numberninja(&amp;s,buffer+6,2)-1;
*bptr='-';
} else{
bptr=numberninja(&amp;s,buffer+6,2);
}
return bptr;
}
_numberninja:
;fierce and flexible - be careful with the inputs!
;binay-ascii conversion originally based the double-dabble algorithm
;handles variable length input and output
;thanks to Charles Richmond for the suggestion and code
;15-05-02 initial version
;POINTER to integer is passed in r12
;pointer to LAST BYTE of buffer is passed in r13
;number of bytes to convert is passed as the 3rd parameter
;a pointer to the 1st non-zero byte in the buffer is passed back in r15
;r8-11 are used as temps
;r13 is the working pointer to the BCD number
;r12 is the pointer to the input
;r15.0 is bit count in the current byte and the return value register
;r11.0 is the number of bytes to convert
;r10.0 is the count of digits as the bcd number is developed
;r9.0 is digit count for the current pass
ld2 r11,'O',sp,(2+4); pick up the number of bytes to convert

ldi 0	;source a 0
str r13	;terminate the buffer
dec r13	;back to units position
str r13	;initialize bcd number to 0
ldi 1	; initialize the bcd digit count at 1
plo r10 ;

$$nxtbyte:
ldi 8		;bits in byte
plo r15
$$nxtbit:
ldn r12		;pick up current byte
shl		;check top bit
bdf $$bitloop	;move on for a 1 bit
str r12		;put the byte back

dec r15		;decrement and test bits in byte
glo r15
bnz $$nxtbit

dec r11		;decrement and test bytes in number
glo 11
bz $$done	;the whole number was 0! no need for further processing

inc r12		;point to the next byte
br $$nxtbyte	;go back for it
$$bitloop:
glo r10	;digit count
plo r9
$$dcklp:
ldn r13 	;pick up a bcd digit
smi 5	;see if it's greater than 4
bnf $$dnoadd ;if not, bypass add
adi 128	;add the 5 black and 3 more and 120 per charles
str r13	;put it back
$$dnoadd:
inc r13
dec r9	;decrement digit count
glo r9
bnz $$dcklp ;and back for next digit

ldn r12 ;shift the current byte of the input number
shl
str r12

glo r10	;load the digit count again
plo r9
;r13 is now just past the units location and ready to walk back
$$dshlp:
dec r13	;walk back from 0's position
ldn r13	;get the digit back
shlc	;continue the shift
str r13	;put the digit back
dec r9	;decrement the digit count
glo r9
bnz $$dshlp ;back for more if needed
;we are now out of digit positions but if DF is 1 we need another digit
bnf $$nextbit	;no need to increase digits
inc r10	;increase BCD digit count
dec r13	;back up pointer to new digit position
ldi 1	;source a 1
str r13	;initialize the position

$$nextbit:
dec r15		;see if bits left in byte
glo r15
bnz $$bitloop

dec r11		;see if bytes left in number
glo r11
bz $$done
inc r12		;point to next byte of number
ldi 8		;prep for 8 more bits
plo r15
br $$bitloop

$$done: ;this is also used for for an early exit.  R13 and R10 must be correct
cpy2 r15,r13	;save the starting location of the digits to return
glo r10		;digit count again
plo r9
$$upnxt:
ldn r13		;get digit
ori 0x30	;make ascii
str r13		;put it back
inc r13		;next digit
dec r9		;counter
glo r9
bnz $$upnxt	;upgrade all spots
cretn		;return with pointer to 1st digit in r15

I did a little test of printing ip addresses which is what got me started on this. Using the number ninja, printf takes about 15ms to print an ip address as opposed to about 50 using itoa(). It will be a little bit of work to revise the compiler version of printf but that really needs an overhaul anyway: I did it a while ago and my understanding of c strings and pointers has improved a lot in the meantime.

Follow

Get every new post delivered to your Inbox.