Tag Archives: programming

Led Firewall (netled)

FW led box without labels

Above is a picture of a Box with leds which lightup when certain network packets are seen on the network.

It is connected to the parallel port of a PC (using port 0x3bc)

Makefile:

CC=gcc
CCOPT=-O2 -I/usr/include/pcap
LIBS=-lpcap

all:	netled

netled:	netled.c
	$(CC) $(CCOPT) -o netled netled.c $(LIBS)

netled.c code ( older version, i will upload a newer if found)

#include <stdio.h>
#include <pcap.h>
#include <netinet/in.h>
#include <sys/io.h>
#include <sys/time.h>
#include <signal.h>
#include "ether.h"
#include "ethertype.h"
#include "ip.h"
#include "tcp.h"

#define LP_PORT 0x3bc
#define CAPLEN 64
#define DELAY 30000

char *program_name;
static pcap_t *pd;
const u_char *snapend;
u_char leds = 0;
int mode = 0;
long packets;

void do_leds() {
    mode ^= 1;

    if(!leds && mode) return;

    if(mode) {
	outb(leds, LP_PORT);
	leds = 0;
    }
    else {
	outb(0, LP_PORT);
    }
}

int do_tcp(register const u_char *bp) {
    register const struct tcphdr *tp;
    u_int16_t sport, dport;

    tp = (struct tcphdr *)bp;
    sport = ntohs(tp->th_sport);
    dport = ntohs(tp->th_dport);

    if (sport == 22 || dport == 22) {
	leds |= 8;
    }

    return;
}

int do_ip(register const u_char *bp, register u_int length) {
    register const struct ip *ip;
    register u_int hlen, len, len0, off;
    register const u_char *cp;

    ip = (const struct ip *)bp;

    if ((u_char *)(ip + 1) > snapend ||
	length < sizeof (struct ip)) {
	return;
    }

    hlen = IP_HL(ip) * 4;
    if (hlen < sizeof (struct ip)) {
	fprintf(stderr, "bad-hlen %d\n", hlen);
	return;
    }

    len = ntohs(ip->ip_len);
    if (length < len)
	(void)printf("truncated-ip - %d bytes missing!",
	    len - length);
    len -= hlen;
    len0 = len;


    off = ntohs(ip->ip_off);
    if ((off & 0x1fff) == 0) {
	cp = (const u_char *)ip + hlen;

	switch(ip->ip_p) {

	    case IPPROTO_TCP:
		// fprintf(stderr, "TCP!\n");
		leds |= 128;
		do_tcp(cp);
		break;

	    case IPPROTO_UDP:
		// fprintf(stderr, "UDP!\n");
		leds |= 64;
		break;

	    case IPPROTO_ICMP:
		// fprintf(stderr, "ICMP!\n");
		leds |= 32;
		break;

	    default:
		fprintf(stderr, "HUH? [ip_proto: %i]\n", ip->ip_p);
		break;
	}
    }
}

void handler(u_char *user, const struct pcap_pkthdr *h, const u_char *p) {
    u_int caplen = h->caplen;
    u_int length = h->len;
    u_short ether_type;
    register const struct ether_header *ep;
    u_short extracted_ethertype;

    if (caplen < ETHER_HDRLEN) {
	printf("c: [%d] e: [%d]\n", caplen, ETHER_HDRLEN);
	return;
    }

    ep = (struct ether_header *)p;
    ether_type = ntohs(ep->ether_type);

    snapend = p + caplen;
    p += ETHER_HDRLEN;
    length -= ETHER_HDRLEN;
    if (ether_type > ETHERMTU) {


	switch (ether_type) {

	    case ETHERTYPE_ARP:
	    case ETHERTYPE_REVARP:
		// leds |= 8;
                // fprintf(stderr, "(R)ARP\n");
		break;

	    case ETHERTYPE_IP:
		// fprintf(stderr, "IP!\n");
		do_ip(p, length);
		break;

	    default:
		fprintf(stderr, "HUH? [et: %i]\n", ether_type);

	}
    }
}

int main(int argc, char *argv[]) {
    char *device;
    char ebuf[PCAP_ERRBUF_SIZE];
    register char *cp;
    u_char *pcap_userdata;
    void *sig_old;
    struct itimerval timer_old, timer_new;

    if ((cp = (char *)strrchr(argv[0], '/')) != NULL)
	program_name = cp + 1;
    else
	program_name = argv[0];

    if(ioperm(LP_PORT,3,1))
	error("IOPEEEEERM!\n");

    sig_old = signal(SIGALRM, do_leds);
    if (sig_old == SIG_ERR)
	error("SIGNAAAAAAAAAAAAAL!\n");
    timer_new.it_value.tv_usec = DELAY;
    timer_new.it_value.tv_sec = 0;
    timer_new.it_interval.tv_usec = DELAY;
    timer_new.it_interval.tv_sec = 0;
    if(setitimer(ITIMER_REAL, &timer_new, &timer_old))
	error("SETITIMEEEEEER!\n");

    device = pcap_lookupdev(ebuf);
    if (device == NULL)
	error("%s", ebuf);

    pd = pcap_open_live(device, CAPLEN, 1, 1000, ebuf);
    if (pd == NULL)
	error("%s", ebuf);

    if (pcap_loop(pd, -1, handler, pcap_userdata) < 0) {
	(void)fprintf(stderr, "%s: pcap_loop: %s\n",
	    program_name, pcap_geterr(pd));
	exit(1);
    }

    return 0;
}

Made a webinterface for my DIY webcam

Using a steppermotor controller with two motors. A video capturing device (videoblaster) and a mini B/W camera.

  • Up/down/left/right and diagonal
    • Red double speed green single speed
  • Reset view
  • 2 Presets with save and recall
Setup with parallel cable

Written software in html and some CGI scripts.
Perl and C.

#include <asm/io.h>

# C Code for moving left

int main(int agrc,char agrv[])
{
  int i,wachten;
  int richting1[8]={0x27,0x2d,0x1c,0x0d,0x03,0x09,0x38,0x29};
  int richting2[8]={0x29,0x38,0x09,0x03,0x0d,0x1c,0x2d,0x27};
  ioperm(0x378,3,1);
  ioperm(0x37a,3,1);
  wachten=100;


for (i=0; i<=7; i=i+1)
        {
        outb(richting2[i], 0x378);
        outb(1, 0x37a);
        usleep(wachten);
        outb(0, 0x37a);
        usleep(wachten);
        outb(1, 0x37a);
        usleep(wachten);
        }

 return(0);
}
#!/usr/bin/perl
# Perl CGI script 

# Uses 204 no content trick to stay on same page
use LWP::Simple;
my $img = get ('http://10.1.0.1/cgi-bin/left.cgi');
print "Status: 204 No content\n\n";

Streaming video was done using progressive JPG push.
Later i used the capturing command in the loop below.

#!/bin/sh

# push jpg, and update after 1sec
# output mime header

echo Content-type: multipart/x-mixed-replace;boundary=--WebcamRules\n
echo
echo --WebcamRules

# create stream

while true; do
   echo Content-type: image/jpeg
   echo
   cat /var/lib/httpd/htdocs/webcam.jpg
   echo
   echo --WebcamRules

   sleep 1
done

Steppermotor card was using a parallel port.

Aluminum machined part by Joost

VGA image on dos without borders.

This year i’ve been really into assembly on Intel x86 machines.
With EDK we made some demo’s and generally trying to find the limits of the machines we had.

Funny story, edk made a program which changed the palette every scanline (if memory serves me right), while running the program and looking at the screen the colors faded to grayscale. (Something with run-away tables) We look at eachother and said: We must have been using up all colors, we need to refill the graphics card.

I previously made a copperbar alike thingy on a hercules system. (Have not seen them before on a pc back then )
But with below program, i could display pictures which removed the borders.

Below a nsfw video example (pass protected), but a simplified example in pictures below that.

Restricted Content
To view this protected content, enter the password below:

Test image i’ve used on a 320×200 screen resolution

    name split_screen
.286

parm_vert equ 0

data segment
intmsk      db ?
oldvidtab   db 18h dup (?)      ;actuele video-parameters
newvidtab   label byte
            ;HORIZONTAAL
            db 0
            db 12
            db 12
            db 0
            ;VERTIKAAL
            db 0
            db 5
            db 5
            db 0;-40
            ;
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            ;       REGISTER 13H (OFFSET)
            db 0
            ;
            db 18h dup (0)
data ends

stack segment stack
    dw 128 dup (?)
stack ends

code    segment
        assume cs:code,ds:data
cols label byte
set_scrparms:
    mov dx,3d4h
    mov al,11h
    out dx,al
    inc dx
    in al,dx
    and al,7fh  ;clear bit 7: enable writes to vga reg 0..7
    out dx,al
    dec dx
    mov al,13h      ;offset register
    out dx,al
    inc dx
    in al,dx        ;get actual value
    add al,4
    out dx,al
    ret
;
; RE-INIT DISPLAY ROUTINE
;
get_oldvidparms:
    mov dx,3d4h
    mov cx,18h  ;18h registers
    mov di,offset oldvidtab
    mov bl,0    ;begin met register 0
govp1:
    mov al,bl   ;register index
    out dx,al
    inc dx      ;3d5
    in al,dx    ;get actual register content
    dec dx
    mov [di],al
    inc di
    inc bl      ;volgend register
    loop govp1

    mov si,offset oldvidtab
    mov di,offset newvidtab
    mov cx,18h
donewparms:
    mov al,[si]
    add al,[di]
    mov [di],al
    inc si
    inc di
    loop donewparms
    ret
set_newvidparms:
    mov dx,3d4h
    mov cx,18h  ;18h registers
    mov si,offset newvidtab
    mov bl,0    ;begin met register 0
snvp1:
    mov al,bl   ;register index
    out dx,al
    inc dx      ;3d5
    mov al,[si]
    inc si
    out dx,al   ;set register value
    dec dx
    inc bl      ;volgend register
    loop snvp1
    ret

;
; MAIN ENTRY POINT
;
init:
    mov ax,data
    mov ds,ax
    in al,21h
    mov intmsk,al
    cli
    mov al,11111101b
    out 21h,al
    sti
    mov ax,13h
    int 10h
;extend video-memory to 256kb or more
    mov dx,3ceh
    mov al,06h
    out dx,al
    inc dx
    in al,dx
    and al,0f3h
    out dx,al
    ;
    mov ax,0a000h
    mov es,ax
    call set_scrparms
    call get_oldvidparms
    call set_newvidparms
    mov dx,3cch
    in al,dx
    and al,03fh
    mov ah,parm_vert
    ror ah,2
    or al,ah
    mov dx,3c2h
    out dx,al
;
    ;
    push ds
    mov ax,5000h
    mov ds,ax
;
; pallet
;

setpal:
    mov dx,3c8h
    xor al,al
    out dx,al
    inc dx
    mov cx,256*3
    mov si,100h
    cld
    rep outsb
;disp picture routine
    mov ax,6000h
    mov ds,ax
    mov si,0
    mov di,0
    mov ax,0
    mov cx,352*8
    cld
    rep stosw
    mov si,di
    mov cx,8
    cld
    rep stosw
    mov bp,170
    mov si,di
 hiero:
    mov cx,320
    cld
    rep movsb
    mov ax,0
    mov cx,16
    cld
    rep stosw
    mov si,di
    dec bp
    jnz hiero


    xor si,si
    xor di,di
    mov ax,0b000h
    mov es,ax
    mov ax,07000h
    mov ds,ax
    mov cx,3000
    cld
    mov ax,0
    rep stosw
    mov ax,0a000h
    mov es,ax

    mov ax,6000h
    mov ds,ax
    mov di,0
    mov si,di
rhiero:
    push ax
    mov ah,8
    int 21h
    pop ax
    std
    mov cx,-1
    rep movsb


    xor si,si
    xor di,di
    mov ax,0b000h
    mov es,ax
    mov ax,07000h
    mov ds,ax
    mov cx,3000
    cld
    rep movsw
    mov ax,0a000h
    mov es,ax
    pop ds
    ;
mloop:
    mov dx,3dah
wtv1:
    in al,dx
    test al,8
    jnz wtv1
wtv2:
    in al,dx
    test al,8
    jz  wtv2
    mov ah,1
    int 16h
    jz mloop
    xor ah,ah
    int 16h
exit:
    mov ax,3
    int 10h
    cli
    mov al,intmsk
    out 21h,al
    sti
    mov ax,4c00h
    int 21h

code ends
end init

Sakura demo

I think i started programming in assembly on PC around 1992. I learned a lot from my friend Edk. Who was a assembly wizard just like Sepp. Reverse engineering routines, writing emulators etc.

We made several demo’s like the one below. It must have been around 1994.

Dos emulator running our demo from 1994

Just after this one, we started a demo which could run from a 5.25″ boot disk. No dos operating system.
When starting your pc, booting from a floppy you would get a starfield, with some text (from a bootsector) ,after that it would load the next sectors, wich contained the rest of the demo.
Due to directly programming soundcard and graphics card, this was hard to pull off on different kinds of hardware.

Demo gfx

Example of assembly code for a effect.

    NAME plasma

.model small
.386
.data
colshades   db +001h, 001h,+001h
            db -001h,-001h,-000h
            db +000h,-000h,-001h
            db -000h,-000h,+000h
rgb_cols    db 256*3 dup (?)
cosptr dw 0
sinptr dw 30

.code
demo        proc near

show proc near
    xor di,di
    mov bp,200
show1:
    mov cx,320
    mov si,0
    mov dx,0
show0:
;    push ds
;    mov ax,7000h
;    mov ds,ax
;    lodsb
;    pop ds
    call getsincos
    add cosptr,1
    stosb
    loop show0
;    add dx,1
    add sinptr,1
    dec bp
    jnz show1
    ret
show endp

effect proc near
;   add cosptr,1
;    add sinptr,0
    ret
effect endp

getsincos proc near
    push di
    push ds
    mov si,cosptr
    mov di,sinptr
    mov ax,7000h
    mov ds,ax
    lodsb           ;get cos value
    cmp si,320      ;einde costab?
    jb cosok
    xor si,si
    lodsb
cosok:
    mov ah,al
    xchg si,di
    lodsb           ;get cos value
    cmp si,320      ;einde costab?
    jb sinok
    xor si,si
    lodsb
sinok:
    xchg si,di
    pop ds
    mov cosptr,si
    mov sinptr,di

    mov dx,0
    mov dl,al
    add dl,ah
    adc dh,0
    shr dx,1
    mov al,dl
;    xor al,ah
;    add al,ah
    pop di
    ret
getsincos endp

setcols proc near
    push    es
    push    ds
    pop     es
    mov     di,offset rgb_cols
    mov     si,offset colshades
    mov     dl,0    ;start with black
    mov     bh,0
    mov     bl,0
    mov     bp,4
set_rgball:
    mov     cx,64-1
set_rgb:
    mov     al,dl
    stosb
    mov     al,bh
    stosb
    mov     al,bl
    stosb
    mov     al,[si]
    add     dl,al
    mov     al,[si+1]
    add     bh,al
    mov     al,[si+2]
    add     bl,al
    loop    set_rgb
    add     si,3
    dec     bp
    jnz     set_rgball
    pop     es
    ret
setcols endp

setrgb proc near
    mov dx,3c8h
    xor al,al       ;start with colour 00h
    out dx,al
    inc dx
    mov si,offset rgb_cols
    mov cx,256*3
    rep outsb       ;set 256 RGB values
    ret
setrgb endp

wvtr proc near
    mov dx,3dah
wtv:
    in al,dx
    test al,8
    jz wtv
    ret
wvtr endp

start:
    cld
    mov ax,@data
    mov ds,ax
    mov ax,0a000h
    mov es,ax
    mov ax,13h
    int 10h         ;screen 320x200 256 colours
    call setcols
    call setrgb
    call show
    mov al,11111101b
    out 21h,al      ;disable int
mloop:
    call wvtr
;    call show
    call effect
    mov ah,1
    int 16h
    jz mloop
    xor ah,ah
    int 16h
exit:
    xor al,al
    out 21h,al      ;enable int
    mov ax,3
    int 10h         ;screen  80x25 text
    mov ax,4c00h
    int 21h         ;back to DOS
demo        endp

end start

Oscilloscope graphics using a amiga (bonus vectrex)

Somewhere in 1992 i got hold of a Oscilloscope, probably borrowed from someone. I don’t know what happend to it. I got the idea to generate drawings on the scope, because it had two inputs with you could switch to x and y inputs.

Example oscilloscope

My friend Sepp got into it also, we both wrote some software to do funky stuff with this. I found some software today (20220516), and having bought a old skool scope 2 years ago …

So i found source machine code, no executables. Now i needed to get a assembler running again.

Sidenote: I recently fixed a Amiga 500 and got a disk switch installed on the even cia.

Disk df0 df1 switch print at the center of the image


Booting some old seka disks and starting MasterSeka again in a looong time.

ESC - open editor
r (read file)
v (directory)
a + enter + enter (no options assemble)
g (go running the program)
FIrst part of machine code .. at the bottom part are arrays of coordinates to draw things

Some programs on the disk: (some are made by Sepp, who is a far better coder than i am)

  • Funny triangles
  • Lissajous figures
  • Moving square
  • House
  • House with door
  • Draw with mouse

Lissajous figures are simple sine and cosine functions to get:

So how does this work, well a amiga has stereo outputs. These are controlled by two DAC outputs on the 8364 (Paula) chip. (DAC – Digital Analog Convertor) ( Paula has 4 DMA controlled DACs !! )

Looking at the schematics of the audio part, we see a lot going on concering audio filters. The tests i’ve done today (2022) are on a amiga with unmodified audio filters. (Low on my prio list)
So frequencies are not direct what you get directly converted from digital values. Besides that, syncronisation between left and right channel, even using DMA can be an issue.
(DMA – Direct Memory Access, this means that it can be controlled without using the CPU)

Running the house draw code:

Note: Due to different hardware not a good working example .. yet

More examples .. hard to capture a still image

I tried a few years after we did this, to modify a generic monitor to display things using two inputs, not using scanlines. But to no avail. Only flipping the screen and colors using relais (more on this later)

Bonus part: Above did remind me of a Vectrex, a game console which utilises same display technic. So no raster lines and pixels, but line drawing by controlling the beam.

Movie from 2017 .. Vectrex was made in 1982-1983

Drawing lines using a laserbeam

While attending school, we had to come up with a computer related project.

I had access to a military grade laser (i think it was for aiming), so i went for a drawing-animations-with-a-laser project.

I started off by myself, but soon after my teacher was interested in the project. He knew someone at the University of Twente.
So he made  an appointment for me. I don’t know which teacher and guy at the Uni but it was really interesting.
Laser microscopes!

Image from https://physics.emory.edu/faculty/weeks/confocal/

I was using speakers with mirrors on it, and glued tiny mirrors on stepper motors. But these are far too slow.
The Uni guy gave me some tiny mirrors which can be controlled by putting power on the little coils. But even these lightweight mirrors from a video disc player are “slow”. At least for making sharp corner turns.

Kindda dusty


So i was given an electronic schematic also.
This was an amplifier with sensors, which would give a power boost when needed.

In 2022 I found some information about this on my fileserver.

Above a schematic about the feedback amplifier

Writing software and experimenting with coordinates to send to the laser mirrors.

A few years later this laser was used in my computer dungeon using a smoke machine from my friend Marco who used this machine for his mobile disco.
We closed all doors and windows, making it as dark as possible. Let the smoke machine run for a long long time, and played with this laser setup.
Next day when the room was aired, all computers had a fatty substance on it, so I spent the rest of the day cleaning everything.

Some time later the laser broke.

Pdp-11 and playing with printers

I got a free mainframe, when i was about 17.
It was a huge Pdp-11/34 which a had to get from Enschede to Holten using a trailer.

PDP-11/34
  • It was a 19inch rack (loads of metal)
    • 2x 8inch floppy drives
    • 2x 20MB harddisk drives (with cardridge) each 34KG!
  • multiple (3?) Decwriter III printers
  • loads of VT100 terminals
  • 2.5 meter of manuals
  • cables
  • disks (8 inch) and harddisk cartridges

I converted the power to a generic 230V connector. When booting the machine all the lights in the house dimmed.

I didn’t know anything about mainframes, but i got things working.
Sometimes i would play with it, but after a while it didn’t run anymore.

I’ve kept some of the parts of the machine.
Terminals we used for a long time to connect to a linux server.
( The VT100 where later switched for more modern Wyse terminals )

One of the two drives (34kg) (not my picture)

Some parts i’ve kept

VT100 serial terminal (gave this one to a colleage) Postit says: Could you install Windows on this for me??

Serial printer

Last Decwriter i’ve got, also gone now.

These printer we used for generic printing, and just for fun.
They made a lot of noise, and even they are serial printers they are fast!

So i resourced ms-dos into assembly and printed that, that was a sh*tload of paper.
We even made a racing game. (Can’t find the source, but i’ve recreated a lookalike in linux-bash)

Object of the game was to keep your car O character on the road.
The printer printed the lines, and you could use the keyboard to move your car, which also got printed.

Below the build-in-5-minutes bash lookalike. ( z left, x straight and c right)
Original had more intricate road, and probably the road was drawn using two lines, to speedup printing (Decwriter III could print at 180 characters per second bidirectional!)

Looked more like this i think
#!/bin/bash
i=0
j=0
car=8
while true ; do
no=$(awk "BEGIN{print sin($i*atan2(0,-1)/180)*40+40}" | cut -f1 -d.)
way=$(awk "BEGIN{print sin($j*atan2(0,-1)/180)*10+13}" | cut -f1 -d.)
#echo $way
if [ $car -lt 0 ] ; then echo "boom" ; exit ; fi
if [ $car -gt $way ] ; then echo "boom" ; exit ; fi
carr=$car
rest=$((140-$no-$way))
i=$(($i + 1))
j=$(($j + 5))
while [ $no -gt 0 ] ; do
echo -n " "
no=$(($no - 1))
done
echo -n "#"
while [ $way -gt 0 ] ; do
echo -n " "
if [ $carr -eq 0 ] ; then
echo -n "O"
fi
way=$(($way - 1))
carr=$(($carr - 1))
done
echo -n "#"
while [ $rest -gt 0 ] ; do
echo -n " "
rest=$(($rest - 1))
done
echo ""
read -r -t 0.1 -n 1 -s key
if [ "$key" == "z" ] ; then
car=$((car - 1 ))
fi
if [ "$key" == "x" ] ; then
car=$((car - 1 ))
fi
if [ "$key" == "c" ] ; then
car=$((car + 1 ))
fi
done

BBC Acorn

While attending the LTS (lower vocational technical school), i could often be found in the computer lab.
I was the only student who had his own key.
We had a classroom with 16 computers, 2 drives at the master station and a printer.
Everything was connected using Econet. (These where the first networked computers i’ve worked with)
https://en.wikipedia.org/wiki/Econet

So every moment we didn’t have a class, i was there.
Even when i had to do final exams, i was late entering, and sometimes one of the first leaving.

Today (2022) i ran an emulator on my machine and typed in one of my old programs found in a notebook.
(The real system above pictured, i have to repair)
By the way, this is one of the computers from school, even with its original wooden monitor stand. The school contacted me (a few years after leaving this school) if i wanted to buy one of the machines.

My notebook containing programs

One of the shorter programs in basic

   20R=.8
   30Q=.05
   40MODE0
   50X=500
   60GCOL1,3
   70Y=500
   80MOVE650,650
   90DRAW670,650:DRAW670,670:DRAW650,670:DRAW650,650
  100A=GET-48
  110IFA=1THENX=X+Q
  120IFA=2THENX=X-Q
  130IFA=3THENY=Y+Q
  140IFA=4THENY=Y-Q
  150IFA=5THENR=.8
  160IFA=6THENR=0
  170X1=200*SIN(X)+500
  180X2=200*SIN(Y+X)+X1
  190X3=50*SIN(Y+X+R)+X2
  200X4=50*SIN(Y+X-R)+X2
  210Y1=200*COS(X)+500
  220Y2=200*COS(Y+X)+Y1
  230Y3=50*COS(X+Y+R)+Y2
  240Y4=50*COS(Y+X-R)+Y2
  250CLS
  260IFX4>650ANDX4<670ANDY4>650ANDY4<670THENPRINT"RAAK"
  270MOVE450,450
  280DRAW550,450
  290DRAW500,500
  300DRAW450,450
  310MOVE500,500
  320DRAWX1,Y1
  330DRAWX2,Y2
  340DRAWX3,Y3
  350MOVEX2,Y2
  360DRAWX4,Y4
  370GOTO80
  380MODE7
  390PRINTTAB(12,10)CHR$129CHR$141"FASH-SOFT"
  400PRINTTAB(12,11)CHR$130CHR$141"FASH-SOFT"
  410A=GET
  420RUN
Keys
1,2 - first arm (left/right)
3,4 - second arm (left/right)
5,6 - open/close grabber

This program got me in trouble because my teachers didn’t believe me. It wasn’t written by me according to them. Because my math grades were terrible!

Later versions had a nicer looking robotic arm. (More 3d, not a line but a arm with thickness)

Notes:

*CAT ; list disk files
LOAD"FSHDRAW" ; load

Print to file or clipboard
LIST07 ; page formatting
VDU2 ; start output redirection (screen + "printer")
LIST
VDU3 ; stop redirection

Installing the Emulator under linux

 git clone https://github.com/stardot/b-em.git
 sudo apt-get install autotools-dev automake
 sudo apt-get install liballegro5-dev
 cd b-em/
 ./autogen.sh 
 ./configure 
 make
 ./b-em