I'm assisting a project to restore a CDC 6500 built in 1967 to operating condition. I was specifically hired to wrote diagnostic code,
and my later work is in assembly language, but the first simple stuff was machine code, in octal rather than hex. In original usage,
an operator would toggle this code into an array of 12 rows of 12 switches, The museum engineers built an interface using a
Xylinx FPGA board to enable the invention of a larger virtual version of that panel, giving us 4k rows of 12 bits. So then the engineer
wrote a utility to take a machine program in an ASCII file appropriately formatted and download that program to the virtual
"deadstart" panel. Here's an example:
0001-0100 START LJM
0002-0040 0040
0040-7700 FCN 0,
0041-1234 1234
0042-1477 LCN 77
0043-3463 STD COUNT
0044-3763 LOOP SOD COUNT
0045-6500 IJM 0,
0046-0051 REPORT
0047-0574 NJN LOOP
0050-0300 UJN *-* CHANNEL DID NOT GO INACTIVE, HALT
0051-1177 REPORT LMN 77 COMPLEMENT A
0052-3457 STD 57 STORE A IN STM INSTRUCTION BELOW
0053-3461 STD 61 STORE A IN LJM INSTURCTION BELOW
0054-2000 LDC 0,
0055-0300 'UJN *-*'
0056-5400 STM
0057-7777 (A)
0060-0100 LJM
0061-7777 (A)
0062-0000
0063-0000
FFFF-FFFF Halts at 0050 if inactive not received. Else halts at addr=loop count
I don't think the physicality of toggling a program into the switches makes it truly any different than hacking up
a special device to get bits into the computer memory. So this makes machine language, whether written as 1s
and 0s on a sheet of paper, or as octal or hex digit groupings, as the lowest form of programming language.
Having it on punch cards is also no important difference. I was there, I did it.
The assembly mnemonics you see above are all just comments I included to make the engineer happy. The
only processing done is to use the first 4-digit value as the address at which to store, and the second 4-digit
value is what is stored at that address.
I have seen an intermediate level between machine code and assembly language, here is an example for the
never-built CDC 8600:
** CBH - C$ONVERT /B$INARY TO /H$EXADECIMAL.
*
* CBH $CONVERTS A BINARY NUMBER TO AN /ASCII $CODED
* $HEXADECIMAL REPRESENTATION.
*
* E$NTRY /B = $NUMBER TO BE CONVERTED.
CBH D=B
B=(CBHA-P) =1H
C=0
CBH1 E=D $EXTRACT 4 BITS
D=D(-4)
E=E.(4)
F=E-10
E=E+#10
P=CBH2,F(0 $IF /'DIGIT'
E=E+7
CBH2 E=E(C)
C=C+8
B=B+E
P=CBH1,D/0 $LOOP TO ZERO DIGIT
R=A
CBHA 1H
END
Next higher is assembly language. To contrast the above sample, here is some assembly language for the same architecture:
** HEX - P$ROCESS /H$EXADECIMAL /C$ONVERSION. FTNDDP 75
* E$NTRY /X1 = $DATUM ADDRESS. FTNDDP 76
* X2 = $NUMBER OF DATA ITEMS. FTNDDP 77
* X3 = $CHARACTER ADDRESS. FTNDDP 78
* X4 = $FIELD WIDTH. FTNDDP 79
* E$XIT /X1 = $ADVANCED. FTNDDP 80
* X2 = 0 $IF NO ILLEGAL CHARACTERS IN FIELD. FTNDDP 81
* X3 = $ADVANCED. FTNDDP 82
* S$AVES /XA - XE. FTNDDP 83
FTNDDP 84
FTNDDP 85
HEX IX8 X4-17 $CHECK FIELD WIDTH FTNDDP 86
+ IX7 X3+X4 $SET END OF FIELD FTNDDP 87
NG X8,*+1 $IF WIDTH < 16 FTNDDP 88
IX3 X3+X6 $SKIP LEADING CHARACTERS FTNDDP 89
RX6 X3 $FIRST CHARACTER FTNDDP 90
SX5 0 $CLEAR ASSEMBLY FTNDDP 91
HEX1 IX8 X6-1R0 $CHECK CHARACTER FTNDDP 92
IX9 X6-1R9-1 FTNDDP 93
BX9 X9-X8 FTNDDP 94
SX3 X3+1 $ADVANCE CHARACTER ADDRESS FTNDDP 95
NG X9,HEX2 $IF /'DIGIT' FTNDDP 96
IX8 X6-1RA FTNDDP 97
IX9 X6-1RF-1 FTNDDP 98
BX9 X9-X8 FTNDDP 99
SX8 X8+10 FTNDDP 100
PL X9,HEX3 $IF NOT /'A' - 'F' FTNDDP 101
HEX2 RX6 X3 $NEXT CHARACTER FTNDDP 102
LX5 4 $ADVANCE ASSEMBLY FTNDDP 103
BX5 X5+X8 FTNDDP 104
LT X3,X7,HEX1 $LOOP TO END OF FIELD FTNDDP 105
SX2 X2-1 $DECREMENT ITEM COUNT FTNDDP 106
WX5 X1 $STORE DATUM FTNDDP 107
SX1 X1+1 $ADVANCE DATA ADDRESS FTNDDP 108
NZ X2,HEX1 $LOOP FOR ALL ITEMS FTNDDP 109
JP X0 $RETURN FTNDDP 110
FTNDDP 111
HEX3 IX8 X6-1R FTNDDP 112
ZR X8,HEX2 $IF CHARACTER IS /' ' FTNDDP 113
JP ERR1 FTNDDP 114
Then the various tiers of high-level languages.