Skip to content

Programming the AVR for the 1802 Olduino

I had a tough time doing this the other day because I had switched laptops and forgotten the process. I went through it clean today to document it for myself.

I’m starting with a fresh ATMEGA328 chip. I don’t need a bootloader but i need the fuses set and the easiest way is to get the arduino IDE to burn a bootloader. I put the chip in an arduino and hook it up to my Pololu PGM03A(which identifies as an stk500v2 type programmer). Note the awkward cabling. The key thing is getting the red stripe on the cable lined up with the +V on the boards.

The programmer is showing up as COM8

In the ide tools menu set the chip type, speed to 20mhz, and burn the bootloader.

Load the sketch (OlduinoII_ISPV7 available here) and upload using the programmer.

Move the chip from the arduino to the olduino board and test it with any of the example – like “blink”

UPDATE: Going back through old posts I found this one where I had a minor epiphany about the cable connection. I sort of remembered this the other day but i was afraid to try it. I’ll dig the stuff out again and verify it.

16-03-07 better

Crosslib -CrossShoot/CrossChase

I’ve been working for a while with Fabrizio Caruso and Marcel van Tongren on 1802 support for Fabrizio’s cross platform game framework Crosslib. Fabrizio has the games working on literally hundreds of vintage computers. It’s written in C so for the 1802 it uses the LCC1802 compiler. Marcel is a whizbang 1802 programmer and he has been doing the low level coding while i wrestle the compiler. He also maintains the Emma 02 compiler which i’ve been using to emulate cosmac game systems like the Telmac who’s screen is shown above.

The games are enormous, generating 20-30K bytes of code so they’re a good optimization target for the compiler.

Pixie Dust

One of the defining elements of the original 1802 systems was “Pixie Graphics”. This used a CDP1861 chip to stream bits out of the 1802’s memory onto a TV screen. Resolution was a maximum of 64X128 monochrome pixels but more typically 64X64 or 64X32. Here I’m using a library developed by a fellow on the Cosmac Elf mailing list with the screen in 64X64.

In the Beginning Was the Command Line – So Where is the D*mned Thing Now?

I’m a longtime windows command line user – I was used to starting it from the file explorer by right clicking on a directory but with Windows 10 the option disappeared from the context menu. I knew you could drag a directory icon onto a command line window to change to that directory but I found a couple of better solutions

Carbot/370 Redux – Now With Added Computers

This is largely a cheat – all of the navigation code is running in the arduino. The Raspi is just telling it when to start and stop based on commands sent from my windows machine over ssh and relaying telemetry from the sensors. There’s not a whiff of 370 code in the mix and hardly any pi. The vision is to have the arduino doing the close in work while the 370/raspi sets broad parameters like speed and object avoidance patterns and maybe looks for stall situations.

The current code is trying to keep within a range of distances to the right hand wall and makes a hard left when the clearance in front is too small.

The robot car platform was a christmas gift. It had an ultrasonic sensor that i’m using for the wall distance and i’ve connected the sharp infrared sensor as the forward distance measurement. There are four DC motors on the wheels controlled by a custom motor shield on an arduino clone. It also has a line following sensor, a bluetooth add-on, and an infrared remote control. Either the bluetooth or infrared remotes could do what the Pi is doing but I need the pi in the loop for the telemetry and future considerations. The kit is the Elegoo Smart Robot Car Kit V3.0 and it has a ton of function built in.

#!/usr/bin/python
import sys, time
#import difflib
import pigpio
import termios, fcntl, sys, os

def get_char_keyboard_nonblock():
  fd = sys.stdin.fileno()

  oldterm = termios.tcgetattr(fd)
  newattr = termios.tcgetattr(fd)
  newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
  termios.tcsetattr(fd, termios.TCSANOW, newattr)

  oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
  fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

  c = None

  try:
    c = sys.stdin.read(1)
  except IOError: pass

  termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
  fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

  return c

RX=25 #input 4 of the explorer phat
GO=6  #output 1 of the explorer phat
print "trying bb_serial"
#from https://www.rs-online.com/designspark/raspberry-pi-2nd-uart-a-k-a-bit-banging-a-k-a-software-serial
try:
        pi = pigpio.pi()
        pi.set_mode(RX, pigpio.INPUT)
        pi.bb_serial_read_open(RX, 9600, 8)
        pi.set_mode(6, pigpio.OUTPUT) # GPIO 6 is output 1 of explorer phat
        print "Car Controller:"
        pi.write(6,0)
        ch=get_char_keyboard_nonblock() #does not wait for input - returns a char or none
        while ch!='q':
                if ch=='f': #forward
                    pi.write(6,0)
                    #print hex(ord(ch)),1,
                elif ch=='h': #halt
                    pi.write(6,1)
                    #print hex(ord(ch)),0,
                (count, data) = pi.bb_serial_read(RX)
                if count:
                        sys.stdout.write(data)
                        #for character in data:
                        #  print chr(character), #hex(character)
                ch=get_char_keyboard_nonblock()
        pi.write(6,1)
finally:
        pi.write(6,1)
        print "done"
        pi.bb_serial_read_close(RX)
        pi.stop()
//www.elegoo.com
#include 
#include   //servo library
Servo myservo;      // create servo object to control servo

#define ENB 5   // Left  wheel speed
#define IN1 7   // Left  wheel forward
#define IN2 8   // Left  wheel reverse
#define IN3 9   // Right wheel reverse
#define IN4 11  // Right wheel forward
#define ENA 6   // Right wheel speed
#define carSpeed 220  // initial speed of car >=0 to oldwdist) {//but going the right way
        analogWrite(pwmleft,basespeed);analogWrite(pwmright,basespeed); //proceed
        Serial<<" wf-1\n";
    }
    else{
        analogWrite(pwmleft,lowspeed);analogWrite(pwmright,basespeed); //bear left
        Serial<<" wvlft\n";
    }
}
void toofar(){//if we are further than minwdist from the wall on our right side
    if (wdist<oldwdist) {//but going the right way
        analogWrite(pwmleft,basespeed);analogWrite(pwmright,basespeed); //proceed
        Serial<<" wf-2\n";
    }
    else{
        analogWrite(pwmleft,basespeed);analogWrite(pwmright,lowspeed); //bear right
        Serial<<" wvrt\n";
    }
}

void inthezone(){ //if we are between minwdist and maxwdist
    analogWrite(pwmleft,basespeed);analogWrite(pwmright,basespeed); //proceed
    Serial<<" wf-3\n";
}

void hardleft(){
  analogWrite(ENA, carSpeed);
  analogWrite(ENB, carSpeed);
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
  Serial.println(" HL ");
}

//Ultrasonic distance measurement Sub function
int getDistance() { //returns distance in cm
    digitalWrite(Trig, LOW);
    delayMicroseconds(2);
    digitalWrite(Trig, HIGH);
    delayMicroseconds(10);
    digitalWrite(Trig, LOW);
    return (int)pulseIn(Echo, HIGH) / 58;
}
void cruise(){
  if (0!=wdist) oldwdist=wdist; //track the wall distance after the first time
  wdist=getDistance(); //get the wall distance in cm
  Serial<<"cw "<<wdist<<" ";
   if (wdistmaxwdist){
      toofar();
  }else{
      inthezone();
  }
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, HIGH);
  delay(60);
}
int sharpy(){
  int aval=analogRead(0);
  if (aval<85) aval=85;
  int dist=(6787/(aval-3))-4;
   return dist;
}
void loop() {
    if (digitalRead(goPin)){
        fdist=sharpy();
        Serial<<millis()<<" f="<<fdist<minfdist){
           cruise();
       }else{
           hardleft(); //begin left turn
           Serial<<" L\n";
           while(fdist<=minfdist){ //until we're clearing the wall
              delay(1);
              fdist=sharpy();
           }
           Serial<<"K f="<<fdist<<"\n";
           stop();
         }
    }else{
      stop();
    }
 }

https://pinout.xyz/pinout/explorer_phat

https://www.raspberrypi.org/forums/viewtopic.php?t=106410

This Little Pi-guy Went To Florida But Its IP Connection Stayed Home


I brought a couple of Pi’s with me to play with over the winter months including the Olduino/370 dressed in its mini-370 case. Both were used regularly on my home wifi. I updated the wpa_supplicant.conf to reflect the apartment’s wifi but then got stuck. I’m used to having to fumble around to find the IP address on new networks but this setup seems resistant to my fumbling. I don’t have a password for the router so i can’t look at its tables and all of the scanners i tried come up dry showing only my own PC’s address. I finally resorted to lighting up a hotspot access point on my windows box using the same ssid and password as my home router(yes, clever, i know) and the Pi which normally uses a static IP at home popped up although with a dhcp-assigned address.

So now i have one Pi up on my faux-home win10 hosted network and another one that won’t connect to either my faux-home or the apartment network. Possibly:
-finger problems overall
-the apartment router won’t serve the pi an ip address but windows will
-something stops the PI’s from using the wpa_supplicant.conf that i put in the boot directory of the SD card. **Seems very likely – see below**

As an extra bit of fun i don’t find ping works on the apartment network. I can’t ping one windows laptop from another, ping my phone etc.

UPDATE:

  • The basic problem is that the router has “Access Point Isolation” activated.  This means that one IP on the local network can’t communicate with the others.  Hence I can’t ping anything other than the router from windows and none of the IP scanners work.
  • I had the bright idea to activate a hotspot on my laptop with the same ssid and password as my home router.  The Pi was assigned an IP but I couldn’t get through to it because it thought it’s IP was 192.168.0.203 and windows was routing addresses in the 192.168.137.xx range.
  • I finally found a very nice piece of software called “Linux File System For Windows” that happily let me access the linux partition on the SD card from windows and take out the static IP setting.
  • I saw a post somewhere about a setup where a guy had et up his Pi to first try for an IP from his laptop and failing that go for a static IP(or something like that) that would be a good plan.  Also something where the Pi advertises its IP address.  I had various ways of doing that but the static address was so convenient at home that i dropped them all.

Reference:
here‘s the site i got the static ip info from.

interface wlan0

static ip_address=192.168.0.203/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1 8.8.8.8

 

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="therowes24"
    psk="********"
    scan_ssid=1
}

Yippee – In Which I Improve My Workflow With “cat”

yikes

I’ve been doing a lot of compile/link/test sequences with wiringMVS.  The workflow consists of:

  • editing the C program along with its JCL and submitting it to MVS from textpad
  • switching over to putty and watching the MVS console to guess whether the compile worked based on: how long it runs, number of lines produced etc.
  • if i think it failed switching to winscp and reloading the printer output screen
  • switching to textpad again to bumble through multiple jobs output looking for error messages
  • rinse and repeat

I had the idea that if i could compile/assemble/link on windows it would be more interactive.  GCCMVS runs well enough and i can get the C errors out of the way but copying the assembler output into a job stream and submitting that is a bit painful and then i’m guessing again because sometimes i’ll get assembler errors and missing functions can show up in the link step.  I’ve been pursuing the Z390 assembler as an alternative but i’m not there yet.

In the meantime i had a bit of a brainstorm.  I set up a Hercules printer as a pipe to “cat”.  An MVS job sends output to the printer, hercules pipes it to “cat”, and cat sends it back to its stdout which Hercules displays on the console.  Bingo – instant interactive output!

I haven’t done it yet but it should be easy to direct the assembler and lked diagnostic steps to the same “printer”. I could even allocate it to JES2 and establish a “print to console” class!

I also added a couple of crude notification job steps at the end: YIKESC runs only if the compiler step fails and sends a message to the console, and YIPPEE runs only if all steps succeed and again sends a message. I sould clean them up with a stored PROC but the overall result is pretty gratifying.

//GPIOMULE JOB CLASS=A,MSGCLASS=A,REGION=4096K,
// MSGLEVEL=(1,1),
// USER=HERC01,PASSWORD=CUL8TR
//S1 EXEC GCCCLF,COS1='-S',
// PARM.ASM='DECK,NOLIST,TEST,TERM,NOXREF,NOMLOGIC,NORLD',
// PARM.LKED='TERM,TEST'
//COMP.INCLUDE DD DSN=HERC01.WIRINGMV.INCLUDE,DISP=SHR
//SYSIN DD *
pigs=undef(); //deliberate error
  ...
  ...
  rest of program
*/
//COMP.SYSPRINT DD DUMMY
//COMP.SYSTERM DD UNIT=30F
//LKED.SYSLMOD DD DSN=HERC01.LOAD(GPIO3),DISP=SHR
//YIPPEE EXEC PGM=IKJEFT01,PARM='SEND ''YIPPEE''',
// COND=((0,NE,S1.COMP),(0,NE,S1.ASM),(0,NE,S1.LKED))
//SYSPRINT DD DUMMY
//SYSOUT   DD DUMMY
//SYSTSPRT DD DUMMY
//SYSIN    DD DUMMY
//SYSTSIN  DD DUMMY
//YIKESC EXEC PGM=IKJEFT01,PARM='SEND ''YIKES THE COMPILER''',
// COND=((0,EQ,S1.COMP))
//SYSPRINT DD DUMMY
//SYSOUT   DD DUMMY
//SYSTSPRT DD DUMMY
//SYSIN    DD DUMMY
//SYSTSIN  DD DUMMY

MK48Z02 Non-Volatile RAM for the Boyd Calculator

My last effort with the Boyd used the onboard 64 byte ram memory with a 2732 eprom in an adapter.  I wasn’t hitting the ram limit but i thought using a non-volatile ram chip as primary memory would give me more flexibility.  I got an old signetics part from eBay to try out and today i got around to comparing the pinouts to the 2532 that’s original to the Boyd.

As usual the data and address lines are more-or-less ok.  The 4K 2532 has one extra address line A11 which would hit /CE on the nvram but that’s ok because it would just deselect the nvram for addresses above 2K.  The PD/PGM on the 2532 seems ominous but it really just acts as a low-going chip enable and connects to /G which is /output enable on the nvram.  again VPP sounds ominous but i bet it’s tied to +V in the Boyd – it connects to /write on the nvram.

So to use the nvram fully, I would need to connect /mwrite to pin 21 and /mread to pin 20.  I don’t think either of those would interfere with the normal 2532 chip.

I currently have a 2732 adapter in the boyd.  I should look hard at the 2732 pinout but it looks like i would have to remove A11 from pin 21 and supply /mwrite instead.  this is a bit simpler but the adapter is already high and putting the tall nvram on top of it would probably break the camel’s back.

18-12-11 TMS2732

Carbot/370 State of Play

As of today the Carbot hardware and software are basically functional, if rough.  The control program written in C runs on the MVS operating system on a simulated IBM 370 computer under the Hercules emulator running on a raspberry Pi Zero W.  The Pi is housed in a miniature mainframe case with an infrared rangefinder mounted on it and rides on a converted radio control car chassis.  The car’s motors are controlled by a pimoroni explorer pHAT mounted on the Pi.

I’m missing the side facing sensor needed for wall following and I’m using direct drive for the motors rather than using PWM to modulate them.  I do think it’s funny to see the mini-mainframe darting around getting stuck under the furniture.

I had been worried that running the motors from the same 5V supply as the Pi would be a problem but it seems not to be an issue so far.

 The Carbot software is pretty simple at the moment it’s meant to be run as a TSO command or a batch job and it does one action per run as controlled by parameters on the command line for example “carbot f” as a TSO command or // EXEC PGM=CARBOT,PARM=’F’ will each start the motors going forward. In the demo video “carbot g” starts a “Go” routine that runs forward and turns left when it sees an obstacle.

void go(int ttl){
    int maxprox=7500,tprox=7000;
    int fprox,now,start=millis();
    fprox=analogRead(0); //read front sensor
    printf("Go starts at %d\n",start);
    while (now-start< ttl){
    	if (fproxtprox) &&(now-start<ttl)){
    	    	drive(1,0,0,1); //hardleft
	    	now=millis();
	    	fprox=analogRead(0);
    	    }
    	    drive(0,0,0,0);
    	}
    	now=millis();
    	fprox=analogRead(0);
    }
    drive(0,0,0,0);
}

The "drive(r1,r2,l1,l2)" routine used above controls the right and left motors such that drive(1,0,1,0) starts both motors going forward while drive(1,0,0,1) runs lthe right motor full forward and the left motor full reverse so we get a hard left turn.

//CARBOT01 JOB CLASS=A,MSGCLASS=A,REGION=4096K,MSGLEVEL=(0,0),
// USER=HERC01,PASSWORD=CUL8TR
//S1 EXEC GCCCL,COS1='-S',
// PARM.ASM='DECK,NOLIST,TEST,NOXREF,NOMLOGIC,NORLD',
// PARM.LKED='TEST'
//COMP.INCLUDE DD DSN=HERC01.WIRINGMV.INCLUDE,DISP=SHR
//SYSIN DD *
#include 
#include 
#include "wiring.h"
int rfw=24,lfw=25; // forward right and left wheels
int rrv=28,lrv=29; // reverse right and left wheels
void init(){
  printf("init\n");
  pinMode(rfw,OUTPUT);pinMode(lfw,OUTPUT);
  pinMode(rrv,OUTPUT);pinMode(lrv,OUTPUT);
}
void drive(int rf,int rr,int lf,int lr){
    digitalWrite(rfw,rf);digitalWrite(rrv,rr);
    digitalWrite(lfw,lf);digitalWrite(lrv,lr);
}
void keepaway(int ttl){
    int minprox=5000, maxprox=8000;
    int fprox,now,start=millis();
    fprox=analogRead(0); //read front sensor
    printf("keepaway starts at %d\n",start);
    while (now-start< ttl){
    	//printf("keepaway at t=%d, prox is %d\n",now,fprox);
    	if (fproxmaxprox){
    	    drive(0,1,0,1);
    	} else {
    	    drive(0,0,0,0);
    	}
    	delay(100);
    	now=millis();
    	fprox=analogRead(0);
    }
    drive(0,0,0,0);
}
void go(int ttl){
    int maxprox=7500,tprox=7000;
    int fprox,now,start=millis();
    fprox=analogRead(0); //read front sensor
    printf("Go starts at %d\n",start);
    while (now-start< ttl){
    	if (fproxtprox) &&(now-start<ttl)){
    	    	drive(1,0,0,1); //hardleft
	    	now=millis();
	    	fprox=analogRead(0);
    	    }
    	    drive(0,0,0,0);
    	}
    	now=millis();
    	fprox=analogRead(0);
    }
    drive(0,0,0,0);
}

int main(int argc, char *argv[]){   
    int retval=42;
    int fprox=43;
    printf("Oh Hello - Carbot Here\n");
    init();
    switch(argv[1][0]){
    case 'G': case 'g': //"Go" for 3 seconds
        go(3000);    
        break;
    case 'K': case 'k': //play keepaway for 10 seconds
        keepaway(10000);    
        break;
    case '?': //read the front sensor
        fprox=analogRead(0);    
        printf("front sensor says %d\n",fprox);
        break;
    case 'T': case 't':
        drive(1,0,0,1);    
        break;
    case 'F': case 'f':
        drive(1,0,1,0);    
        break;
    case 'R': case 'r':
        drive(0,1,0,1);    
        break;
    case 'X': case 'x': //stop
        drive(0,0,0,0);
        break;
    default:
        printf("not doing %s(%x %x) \n",argv[1],argv[1][0],argv[1][1]);
    }
    //asm(" WTO 'OK Bye' ");
    return retval;
}
//LKED.SYSLMOD DD DSN=HERC01.LOAD(CARBOT),DISP=SHR
//* DISP=(,CATLG),
//* UNIT=SYSDA,SPACE=(TRK,(10,5,5)),
//* DCB=(RECFM=U,BLKSIZE=19069)
//*     EXEC PGM=*.S1.LKED.SYSLMOD,
//*     PARM='write 0 low'
//*SYSPRINT DD SYSOUT=*
//*SYSTERM DD SYSOUT=*
//*SYSIN DD DUMMY
//*     EXEC PGM=*.S1.LKED.SYSLMOD,
//*     PARM='read 0'
//*SYSPRINT DD SYSOUT=*
//*SYSTERM DD SYSOUT=*
//*SYSIN DD DUMMY

I had to update the SVC intercept hook code in Hercules and the wiring.h GCCMVS macros to support the millis() call.

//INCLUDE JOB CLASS=A,MSGCLASS=A,REGION=4096K,
// USER=HERC01,PASSWORD=CUL8TR
//*ALLOC EXEC PGM=IEFBR14
//*SYSUT2   DD  DSNAME=HERC01.WIRINGMV.INCLUDE,DISP=(,CATLG),
//*             UNIT=SYSDA,SPACE=(TRK,(10,5,5)),
//*             DCB=(RECFM=VB,LRECL=255,BLKSIZE=6233)
//LOAD EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=A
//SYSIN DD DUMMY
//SYSUT2 DD DSNAME=HERC01.WIRINGMV.INCLUDE(WIRING),DISP=SHR
//SYSUT1 DD *
  //18-12-06 including millis() code 9
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
int adcSetup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,7 MCP3003 ADC SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval));
    if((retval<0)){
        printf("mcp3002Setup return value is %d\n",retval);
    }
    return retval;
}
int millis(){
    int retval='R';
    asm(" LA 2,0 \n"
        " LA 1,9\n" //code for analog read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        :); //no inputs to asm()
    return retval;
}
int analogRead(int pin){
    int retval='R';
    asm(" LR 2,%1 \n"
        " LA 1,8\n" //code for analog read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int setup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,6 SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15"
        : "=r" (retval));
    if((retval!=0) && (retval!=180923)){
        printf("setup return value is %d\n",retval);
    }
    return retval;
}
int digitalWrite(int pin, int value){
    asm(" LR 2,%0 \n"
        " LR 1,%1\n"
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" (value+2));//((value!=0)+2));
}
int digitalRead(int pin){
    int retval='R';
    asm(" LR 2,%0 \n"
        " LA 1,4\n" //code for read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int pinMode(int pin, int value){
    asm(" LR 2,%0 \n" //pin
        " LR 1,%1\n"  //value
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" ((value!=0)));
}
void delay(int value){
    asm(" LR 2,%0 \n" //ddelay time in MS
        " LA 1,5\n"   //code for delay
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (value));
}
//INCLUDE JOB CLASS=A,MSGCLASS=A,REGION=4096K,
// USER=HERC01,PASSWORD=CUL8TR
//*ALLOC EXEC PGM=IEFBR14
//*SYSUT2   DD  DSNAME=HERC01.WIRINGMV.INCLUDE,DISP=(,CATLG),
//*             UNIT=SYSDA,SPACE=(TRK,(10,5,5)),
//*             DCB=(RECFM=VB,LRECL=255,BLKSIZE=6233)
//LOAD EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=A
//SYSIN DD DUMMY
//SYSUT2 DD DSNAME=HERC01.WIRINGMV.INCLUDE(WIRING),DISP=SHR
//SYSUT1 DD *
  //18-12-06 including millis() code 9
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
int adcSetup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,7 MCP3003 ADC SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval));
    if((retval<0)){
        printf("mcp3002Setup return value is %d\n",retval);
    }
    return retval;
}
int millis(){
    int retval='R';
    asm(" LA 2,0 \n"
        " LA 1,9\n" //code for analog read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        :); //no inputs to asm()
    return retval;
}
int analogRead(int pin){
    int retval='R';
    asm(" LR 2,%1 \n"
        " LA 1,8\n" //code for analog read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int setup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,6 SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15"
        : "=r" (retval));
    if((retval!=0) && (retval!=180923)){
        printf("setup return value is %d\n",retval);
    }
    return retval;
}
int digitalWrite(int pin, int value){
    asm(" LR 2,%0 \n"
        " LR 1,%1\n"
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" (value+2));//((value!=0)+2));
}
int digitalRead(int pin){
    int retval='R';
    asm(" LR 2,%0 \n"
        " LA 1,4\n" //code for read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int pinMode(int pin, int value){
    asm(" LR 2,%0 \n" //pin
        " LR 1,%1\n"  //value
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" ((value!=0)));
}
void delay(int value){
    asm(" LR 2,%0 \n" //ddelay time in MS
        " LA 1,5\n"   //code for delay
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (value));
}

It Phits! (In which I Cram Everything In)

img_3008
Here’s Carbot/370 more or less slung together. All the wiring is there except the power and the only component missing is the sideways looking sensor. When complete the Carbot will be able navigate a simple arena like a wall-following racer. This is the same as previous incarnations of the Carbot except that the navigation and motor control will be done by MVS batch jobs.

Conceptually this is a variant of the IBM 370/158M (mobile edition). The self driving capability might have been used within a city but for longer moves I’m sure FEMA would have used trucks.

From the rear you can see the Pimoroni Explorer phat with the Raspberry Pi Zero W underneath it. The IR distance sensor on top is jumper wired to the phat analog inputs and the motor wires go to the phat’s motor control outputs. The OLED screen is connected to the I2C pins of the Pi although I had to solder to the stubs left exposed of the phat’s header which otherwise hides those pins.

Phinally! (In Which I Get the Pimoroni Explorer PHAT working)

3018-03
A while ago I bought a little circuit board that seemed perfect for the Olduino/370 Carbot.  It fits onto the Raspberry Pi Zero and provides motor drivers, analog-digital conversion, and buffered 5v i/o.  I put it aside because it started to seem like too much trouble to work with: it uses chips i’m not familiar with, all the examples and libraries are in python rather than C, and it sits over all the GPIO pins making it hard to connect other peripherals. (I’m a fan of python but i’m integrating with the C code base of Hercules and wiringPi so its’ just an added headache to convert).

Today, though, everything clicked into place.  The interfaces I need are:

  • The motor driver pins
  • The ADC converter

I got the motor driver pins figured out finally by running a python example and checking the state of the likely seeming pins.  I was quickly(well, not quickly) able to identify the left hand motor pins as 24&28, and the right hand pins as 25&29. i.e. if 24 is high and 28 low, the left motor runs forward full speed; reversing those causes the motor to run backwards; and pwm-cycling the high pin can modulate the speed.  These results are solid but they’ll have to be adjusted for final wiring.

#!/usr/bin/env python
import sys
import explorerhat as eh
eh.motor.one.invert()
def setmotor(m2,m1):
  if (m1>=0):
    eh.motor.one.forward(m1)
  else:
    eh.motor.one.backward(-1*m1)
  if (m2>=0):
    eh.motor.two.forward(m2)
  else:
    eh.motor.two.backward(-1*m2)
line=sys.stdin.readline()
while(line != ''):
  if line.startswith('0'):
    setmotor(0,0)
  elif line.startswith('1'):
    setmotor(0,100)
  elif line.startswith('2'):
    setmotor(100,0)
  elif line.startswith('3'):
    setmotor(100,100)
  elif line.startswith('-'):
    setmotor(0,-100)
  elif line.startswith('!'):
    setmotor(-100,0)
  line=sys.stdin.readline()
 #print("bye")

I’m reading the ADS1015 analog-digital-converter through an extension of wiringPi meant for the very similar ADS1115. The trickiest bit is that the 12 bits returned by the ADC get munged by the wiringPi code which expects 16 bits. I forget how i came to this but the answer was to readjust the result provided by wiringPi after the fact. Also, the ADS1015 is 4 channels but I’ve only found the channels labelled “1” and “4” available as analogRead(3) and (0) respectively.

//ads1015 test raspi adc from explorer phat
#include 
#include 
#include 
#define MY_BASE 2222
void main(){
  float v;
  ads1115Setup (MY_BASE, 0x48) ;
  int ch0 = analogRead (MY_BASE + 0) ;
  int ch3 = analogRead (MY_BASE + 3) ;
  printf("ch0 sez %d\n",ch0);
  printf("ch3 sez %d\n",ch3);
  if (ch3>0x7ff0) ch3-=0x10000;
  v=((float)(ch3>>4)/0x7ff)*5.0;
  if (ch0>0x7ff0) ch0-=0x10000;
  printf("channel 0 voltage is %f\n",((float)(ch0>>4)/0x7ff)*5.0);
  printf("channel 3 voltage is %f\n",((float)(ch3>>4)/0x7ff)*5.0);
}

Bundling this for execution meant simple changes to the wiringMVS.c code that gets copied into general2.c of the Hercules emulator.

//18-11-23 remove automatic inline setup calls
//         comment out most verbose msgs
//18-11-30 changed analogread setup and code to use the ads1115 extension rather than MCP3002
#define SPI_CHAN        0
#define MY_PIN_BASE     5555    // Anything >= 64
int wiringMVS(int function, int parameter){//implements wiringPi functions for MVS
	static int mcp3002_pinbase=0,wiringPiSetuped=0;
	int result=0;
	//printf("wiringMVS sez: function=%x,parameter=%x, setup@%X=%d\n",function,parameter,&wiringPiSetuped,wiringPiSetuped);
	switch (function){
		case 0:	//pinmode INPUT
			pinMode(parameter,INPUT);
			break;
		case 1: //pinmode output
			pinMode(parameter,OUTPUT);
			break;
		case 2: //digitalWrite(LOW)
			digitalWrite(parameter,LOW);
			break;
		case 3: //digitalWrite(HIGH)
			digitalWrite(parameter,HIGH);
			break;
		case 4: //digitalRead
			result=digitalRead(parameter);
			//printf("digitalRead(%d) sez %d\n",parameter,result);
			break;
		case 5: //
			delay(parameter);
			break;
		case 6: // setup()
			result=wiringPiSetup ();
			if (result!=0)
 				printf("wiringMVS Setup() sez %d\n",result);
			break;
		case 7: // mcp3002Setup()
			result=ads1115Setup (MY_PIN_BASE,0x48);
			if (result0x7ff0) result-=0x10000;
			break;
	}
	//printf("wiringMVS sez %d\n",result);
	return result;
}

Oh, also, I’m normally an SVN user on windows but i decided to try git for tracking changes to the olduino/370 components. For the ads1015c test module it was as simple as
git init
git add ads1015.c
git commit ads1015.c
This is not very sophisticated source code management but it saves me keeping multiple copies of my work for backing off.
 

In which Big Brother Gets His Eyes On

Here I’ve got the forward distance sensor mounted on top of the Rogue 370.

img_2982

When running the Carbot I will need a second sensor mounted on the car body.

img_2981

img_2980

The pi is actually inside there along with the mcp3002 adc chip and the motor driver although none of the motor wiring is in place.

img_2984img_2988

wiringMvs State of Play

“wiring” is the project that spawned the Arduino.  It is where constructions like digitalWrite(), digitalRead(), delay(), etc. came from.  wiringMVS is my attempt to port those sorts of functions to programs running under the MVS operating system from the 1970’s.  My instance of MVS is running on the Hercules emulator using the Tk4- Tur(n)key setup which i heartily recommend.  Because my emulator is running on a Raspberry Pi I am using the excellent wiringPi as the basis for my work.

So far I have implemented digitalRead, AnalogRead, digitalWrite, and delay functions.  I have not tried it yet but I believe analog(pwm)Write should be fairly easy.  These combined should be enough to let me program a car type robot driven by a MVS batch job. It will monitor it’s environment with a sharp infrared distance sensor. 

There are a number of parts to the work

  1. The “hook” in Hercules that invokes wiringPi functions
  2. A set of C functions that implement digitalRead() etc
  3. An MVS program called “gpio” that can exercise and test the functions.

---Near the top of the file
#include
#if !defined(_GENERAL2_C_)
#define _GENERAL2_C_
#include  //WJR
#include  //WJR
#include  //WJR
#endif
---Near line 1335
/*-------------------------------------------------------------------*/
/* 0A   SVC   - Supervisor Call                                 [RR] */
/*-------------------------------------------------------------------*/
DEF_INST(supervisor_call)
{
BYTE    i;                              /* Instruction byte 1        */
PSA    *psa;                            /* -> prefixed storage area  */
RADR    px;                             /* prefix                    */
int     rc;                             /* Return code               */

    RR_SVC(inst, regs, i);
//BEGINNING OF WIRINGPI CODE BY WJR 18-09-21
#if 1 
/* if we have an SVC 255 and R0 is set to a magic number(0x42), then
we invoke the wiringpi code for MVS */
if ((i == 255) && (regs->GR_L(0) == 0x00000042))
	{
	regs->GR_L(15)=wiringMVS(regs->GR_L(1),regs->GR_L(2));
    	PERFORM_SERIALIZATION (regs);
    	PERFORM_CHKPT_SYNC (regs);
    	RETURN_INTCHECK(regs);
	}
#endif
//END OF WIRINGPI CODE BY WJR 18-09-21

//18-11-23 remove automatic inline setup calls
//         comment out most verbose msgs
#define SPI_CHAN        0
#define MY_PIN_BASE     5555    // Anything >= 64
int wiringMVS(int function, int parameter){//implements wiringPi functions for MVS
	static int mcp3002_pinbase=0,wiringPiSetuped=0;
	int result=0;
	switch (function){
		case 0:	//pinmode INPUT
			pinMode(parameter,INPUT);
			break;
		case 1: //pinmode output
			pinMode(parameter,OUTPUT);
			break;
		case 2: //digitalWrite(LOW)
			digitalWrite(parameter,LOW);
			break;
		case 3: //digitalWrite(HIGH)
			digitalWrite(parameter,HIGH);
			break;
		case 4: //digitalRead
			result=digitalRead(parameter);
			//printf("digitalRead(%d) sez %d\n",parameter,result);
			break;
		case 5: //
			delay(parameter);
			break;
		case 6: // setup()
			wiringPiSetuped=1;
			result=wiringPiSetup ();
			if (result!=0)
 				printf("wiringMVS Setup() sez %d\n",result);
			break;
		case 7: // mcp3002Setup()
			mcp3002_pinbase=MY_PIN_BASE;
			result=mcp3002Setup (MY_PIN_BASE,SPI_CHAN);
			if (result<0)
 				printf("wiringMVS mcp3002Setup() sez %d\n",result);
			break;
		case 8: //analogRead
			result=analogRead(MY_PIN_BASE+parameter);
			//printf("analogRead sez %d\n",result);
			break;
	}
	//printf("wiringMVS sez %d\n",result);
	return result;
}

The C functions get placed in an MVS dataset: herc01.include(wiringMVS) and are copied into a program compiled with GCCMVS by #include “wiringmvs.h”.

//INCLUDE JOB CLASS=A,MSGCLASS=A,REGION=4096K,
// USER=HERC01,PASSWORD=CUL8TR
//*ALLOC EXEC PGM=IEFBR14
//*SYSUT2   DD  DSNAME=HERC01.WIRINGMV.INCLUDE,DISP=(,CATLG),
//*             UNIT=SYSDA,SPACE=(TRK,(10,5,5)),
//*             DCB=(RECFM=VB,LRECL=255,BLKSIZE=6233)
//LOAD EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=A
//SYSIN DD DUMMY
//SYSUT2 DD DSNAME=HERC01.WIRINGMV.INCLUDE(WIRING),DISP=SHR
//SYSUT1 DD *
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
int mcpSetup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,7 MCP3003 ADC SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval));
    if((retval<0)){
        printf("mcp3002Setup return value is %d\n",retval);
    }
    return retval;
}
int analogRead(int pin){
    int retval='R';
    asm(" LR 2,%1 \n"
        " LA 1,8\n" //code for analog read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int setup(){
    int retval='s';
    asm(" LA 0,X'42' FLAG FOR SVC 255 ");
    asm(" LA 1,6 SETUP ");
    asm(" LA 2,0 ");
    asm(" SVC 255 ISSUE SVC\n"
        " LR %0,15"
        : "=r" (retval));
    if((retval!=0) && (retval!=180923)){
        printf("setup return value is %d\n",retval);
    }
    return retval;
}
int digitalWrite(int pin, int value){
    asm(" LR 2,%0 \n"
        " LR 1,%1\n"
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" (value+2));//((value!=0)+2));
}
int digitalRead(int pin){
    int retval='R';
    asm(" LR 2,%0 \n"
        " LA 1,4\n" //code for read
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC\n"
        " LR %0,15\n"
        : "=r" (retval) //returned value
        : "r" (pin));
    return retval;
}
int pinMode(int pin, int value){
    asm(" LR 2,%0 \n" //pin
        " LR 1,%1\n"  //value
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (pin), "r" ((value!=0)));
}
void delay(int value){
    asm(" LR 2,%0 \n" //ddelay time in MS
        " LA 1,5\n"   //code for delay
        " LA 0,X'42' FLAG FOR SVC 255 \n"
        " SVC 255 ISSUE SVC"
        : /*no outputs*/
        : "r" (value));
}

The demo program GPIO is compiled into sys1.linklib to make it easily accessible. It can run as a batch job or TSO command. It responds to a set of commands to exercise wiringMVS

  • gpio read
  • gpio write high/low/0/1
  • gpio analogread
  • gpio delay

It is really only checking the first letter of the command so “m” will do instead of mcp3002setup.  The two setup calls have to be made before anything else will work. If nothing else, this is a good illustration of how to compile and run GCCMVScode.

//GPIOMULE JOB CLASS=A,MSGCLASS=A,REGION=4096K,
// USER=HERC01,PASSWORD=CUL8TR
//S1 EXEC GCCCL,COS1='-S',
// PARM.ASM='DECK,LIST,TEST,NOXREF,NOMLOGIC,NORLD',
// PARM.LKED='TEST'
//COMP.INCLUDE DD DSN=HERC01.WIRINGMV.INCLUDE,DISP=SHR
//SYSIN DD *
#include 
#include 
#include "wiring.h"

int main(int argc, char *argv[]){   
    int retval=42;
    int pin,hilo, delaytime;
    switch(argv[1][0]){
    case 'D': case 'd':
        delaytime=atoi(argv[2]);
        delay(delaytime);    
        break;
    case 'w': case 'W': //write
        pin=atoi(argv[2]);
        pinMode(pin,OUTPUT);
        switch(argv[3][0]){
        case 'h': case 'H': case '1':
            hilo=HIGH;
            break;
        case 'l': case 'L': case '0':
            hilo=LOW;
            break;
        default:
            return 12;
        }
        printf("setting %d to %d\n",pin,hilo);
        digitalWrite(pin,hilo);
        break;
    case 'R': case 'r':
        pin=atoi(argv[2]);
        retval=digitalRead(pin);    
        printf("pin %d reads %d\n",pin,retval);
        break;
    case 'A': case 'a':
        pin=atoi(argv[2]);
        retval=analogRead(pin);    
        printf("pin %d analogReads %d\n",pin,retval);
        break;
    case 'S': case 's':
        retval=setup();
        break;
    case 'M': case 'm':
        retval=mcpSetup();
        break;
    default:
        printf("not doing %s\n",argv[1]);
    }
    return retval;
}
//LKED.SYSLMOD DD DSN=SYS1.LINKLIB(GPIO),DISP=SHR
//*     EXEC PGM=*.S1.LKED.SYSLMOD,
//*     PARM='write 0 low'
//*SYSPRINT DD SYSOUT=*
//*SYSTERM DD SYSOUT=*
//*SYSIN DD DUMMY
//*     EXEC PGM=*.S1.LKED.SYSLMOD,
//*     PARM='read 0'
//*SYSPRINT DD SYSOUT=*
//*SYSTERM DD SYSOUT=*
//*SYSIN DD DUMMY

Initializing wiringMvs/wiringPi

Right now my code has a couple of explicit initialization paths in it (cases 6 and 7 below)

int wiringMVS(int function, int parameter){//implements wiringPi functions for MVS
	int result=0;

	switch (function){
		case 0:	//pinmode INPUT
			pinMode(parameter,INPUT);
			break;
		.
		.
		.
		case 6: // setup()
			result=wiringPiSetup ();
			if (result!=0)
 				printf("wiringMVS Setup() sez %d\n",result);
			break;
		case 7: // mcp3002Setup()
			result=mcp3002Setup (MY_PIN_BASE,SPI_CHAN);
			if (result<0)
 				printf("wiringMVS mcp3002Setup() sez %d\n",result);
			break;
		.
		.
		.
	}
	return result;
}

I would like to replace them with automatic initialization using static variables as below:

int wiringMVS(int function, int parameter){//implements wiringPi functions for MVS
	static int init=0;
	int result=0;
	if (!init){
		init=1;
		result=wiringPiSetup ();
		if (result!=0) printf("wiringMVS Setup() sez %d\n",result);
	}
	switch (function){
		case 0:	//pinmode INPUT
			pinMode(parameter,INPUT);
			break;
		.
		.
		.
		case 7: // mcp3002Setup()
			result=mcp3002Setup (MY_PIN_BASE,SPI_CHAN);
			if (result<0)
 				printf("wiringMVS mcp3002Setup() sez %d\n",result);
			break;
		.
		.
		.
	}
	return result;
}

UPDATE: I decided this wasn't a good idea and I kept the explicit initialization. I need to arrange for something to run when MVS starts to make the init cals.

It’s Alive! Olduino/370 Carbot Spins its Wheels!


I was amazed that the MVS part of this worked so easily. I had made some simple C wrappers for the wiringMVS pinMode, digitalWrite, and delay functions. Once I got the Pi hooked correctly to the motor driver board I was easily able to run a batch job to access the robot car wheel motors.

I need now a good solid mount for the Pi as well as getting the input functions working. I’m using the adapter board from the 1802 carbot because it’s handy but it’s striking how the Pi needs many of the same crutches as the 1802 did including software PWM, an outboard analog-digital conversion, and extra resistors to access the ultrasonic range finder.
img_2783
In the still image, the pi is in the lower left with eight leads going up to the adapter board. The motor power pack is below and behind it. The car body on the right came from an RC car with two independent rear motors, the four leads going left to the motor driver board are motor power, the two on the right are connected to the logic power pack which isn’t hooked to anything.

For each motor, the driver board takes three inputs, for the right side it’s PWMA, AIN1, and AIN2. AIN1 and AIN2 determine the direction the motor rotates and PWMA determines how hard it spins. In this case I’m just setting the PWM to full on but I can cycle it off and on to control speed.

The adapter board also has spots to attach an ultrasonic sideways sensor and an IR forward distance sensor with an ADC converter. It has a spot for a boost converter to generate 5V for the logic from two AA batteries. Probably the simplest approach is to make something similar but smaller and more suited to the Pi’s form factor. If it can fit into the 370 case, great.

//MOTOMULE JOB CLASS=A,MSGCLASS=A,REGION=4096K
//S1 EXEC GCCCLG,COS1='-S',PARM.ASM='DECK,LIST'
//SYSIN DD *
#include 
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
int setup(){
	int retval='s';
	asm(" LA 0,X'42' FLAG FOR SVC 255 ");
	asm(" LA 1,6 SETUP ");
	asm(" SVC 255 ISSUE SVC\n" 
	    " LR %0,15"
	    : "=r" (retval));
	printf("setup return value is %d\n",retval);
	return retval;
}
int digitalWrite(int pin, int value){
	asm(" LR 2,%0 \n"
	    " LR 1,%1\n"
	    " LA 0,X'42' FLAG FOR SVC 255 \n"
	    " SVC 255 ISSUE SVC" 
	    : /*no outputs*/
	    : "r" (pin), "r" ((value!=0)+2));
}
int pinMode(int pin, int value){
	asm(" LR 2,%0 \n" //pin
	    " LR 1,%1\n"  //value
	    " LA 0,X'42' FLAG FOR SVC 255 \n"
	    " SVC 255 ISSUE SVC" 
	    : /*no outputs*/
	    : "r" (pin), "r" ((value!=0)));
}
void delay(int value){
	asm(" LR 2,%0 \n" //ddelay time in MS
	    " LA 1,5\n"   //code for delay
	    " LA 0,X'42' FLAG FOR SVC 255 \n"
	    " SVC 255 ISSUE SVC" 
	    : /*no outputs*/
	    : "r" (value));
}
int main(int argc, char *argv[])
{	
	int retval=43;
	//int pwmb=0,bin1=2,bin2=3; //speed and direction control for left side motor
	//int pwma=1,ain1=4,ain2=3; //speed and direction control for right side motor
	asm(" WTO 'Hello Moto!' ");
	retval=setup();
	pinMode(0,OUTPUT);pinMode(2,OUTPUT);pinMode(3,OUTPUT); //Left side motor
	digitalWrite(0,LOW);
	pinMode(1,OUTPUT);pinMode(4,OUTPUT);pinMode(5,OUTPUT); //Right side motor
	digitalWrite(1,LOW);

	//drive left side forward for 1.5 sec
	digitalWrite(2,LOW);digitalWrite(3,HIGH);
	digitalWrite(1,HIGH);
	delay(1500);
	digitalWrite(1,LOW);

	asm(" WTO 'OK Bye' ");
   	return retval;
}

3D Printed Case For the Olduino/370

Like everything else in the world, 3D printing is a lot more trouble than you’d think. Anyway, here’s the rogue /158 complete with bogus IPL messages. The Pi is actually hidden behind the chassis although there’s certainly room inside it.

Just For the Record – the OLED

18-10-12 oled
This is based on code from: https://github.com/iliapenev/ssd1306_i2c compied with
gcc pipetext.c ssd1306_i2c.c -lwiringPi -o pipetext
and tested with
echo “Hello World!” | ./pipetext

to use it with Hercules I’ll attach it to a printer as I did with the led pipe.

I’ll use it to pipe output from a hercules printer to the display – i’m imagining something that will echo the stepname and completion code for the last job run.

/******
Pipe text to the ssd1306 i2c driver for  Raspberry Pi
******/
#include 
#include 

#include "ssd1306_i2c.h"

void main() {
        printf("printer pipe to ssd1306\n");

        ssd1306_begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS);

        ssd1306_display(); //Adafruit logo is visible

  while(!feof(stdin))
  {
        char line[200];
        char ss[200];
        fgets(line, 200, stdin);
        printf("%d: %2x %2x %2x\n",strlen(line),line[0],line[1],line[2]);
        ssd1306_clearDisplay();
        //char* text = "Hello World!";
        ssd1306_drawString(line);
        ssd1306_display();
  }
}

In Which WiringMVS Punches Through TK4- Hercules


Ok. I now have Hercules built to run the TK4- environment properly and I have ported my SVC 255 bodge to that version. This is a change to the emulation of the supervisor call instruction that causes it to execute my C++ code instead of running an MVS operating system routine if a program calls SVC 255 with 0x42 in register 0. My C++ code interprets registers 1 and 2 to call the appropriate wiringPi routine e.g. R1=0 R2=1 is equivalent to pinMode(1,INPUT); R1=5 R2=12345 is a delay of 12345 MS.

Below is the jobstream submitted in the demo video and the MVS console after the job ends. There’s nothing very exciting in these baby steps but I am closing in on submitting a batch job and having the rogue 370 driving a robot car.

Olduino/370 Goes Rogue

I’m planning to use the olduino/370 in a couple of projects where it will have to be mobile. It needs a case, and I think it would be funny to have a robot car running around with a miniature mainframe on top.

Accordingly, I am retcon-ing the IBM 370/158 mobile edition. This would’ve been a special purpose mainframe optimized for battery power and use in the field. It has the full 370 instructions set but only limited I/O and it’s meant to be set up in something like a FEMA field camp for keeping track of people and possessions.

The real /158 had a dedicated 3270-type terminal on a separate table and didn’t have much of a control panel. My reimagined version has the screen built into the front of the CPU and an IPL control panel in the bottom right. Only the power button is set up high to keep kids from fiddling with it. Once it’s IPL’d and running the control panel is disabled with a key switch. There’s a running status display on the integrated 3270 but all user interaction is through separate terminals connected through packet radio and the arpanet.

The original is 61 inches high 32 inches wide and quite deep. I am aiming for something like three inches high, 1.5 inches wide, and 1.5 deep mostly so it will be sort of proportional with a little oled display set into the front. The control panel is shown larger than life because I want at least some of the buttons to actually be functional and maybe show some lights behind it.

There would be bags of room inside for a Pi zero and batteries and probably a solderless breadboard for mounting things like the motor controller for the robot car. Not sure about mounting sensors but may be a blue coloured breadboard on top wouldn’t be crazy.

I did this with paper because I have access to a colour printer and it was easy, I am hoping I can find a 3-D printable raspberry pi case that I can adapt to my dimensions.

I found some good pictures and size information for the /148 on the Computer History Museum web site. Below there’s an image of a full /148 computer room and of a museum artifact that shows the outlines of it. I wouldn’t mind profiling the front the way the 148 panel is fitted.

MVS On the Pi 3B

img_2584
The Pi 3 B is faster than the Zero by a fair margin. 1.2MHz vs 700MHz. It’s also four cores vs the single core Zero although i wouldn’t expect it to make a difference running my single instance of Hercules/MVS. I suspect that there are details that make the single core performance better though. The Pi 3 B cranked out 100,000 Dhrystone passes in about 10 seconds. 10K/sec is 5-6 MIPS vs the Zero’s 2 MIPS.
It’s noticeably faster running the work as well and more responsive on TSO.