Added the unary + operator, and cleaned up the display of values from the expression parser

This commit is contained in:
Jeff 2017-03-21 12:47:43 -07:00 committed by Jeff Parsons
commit 127ddb88b4
7 changed files with 1006 additions and 817 deletions

View file

@ -5,14 +5,15 @@ date: 2017-02-28 22:00:00
permalink: /blog/2017/02/28/
---
Since I've more or less achieved all my goals for the PCjs [PDP-11 Emulator](/devices/pdp11/machine/), I've decided
to turn my attention to an even older historically important DEC computer: the [PDP-10](https://en.wikipedia.org/wiki/PDP-10#Cancellation_and_influence).
Since I've more or less achieved all my goals for the PCjs [PDP-11 Emulator](/devices/pdp11/machine/),
I've decided to turn my attention to an even older historically important DEC computer:
the [PDP-10](https://en.wikipedia.org/wiki/PDP-10#Cancellation_and_influence).
Having never used a real DEC PDP-10, I've had a steep learning curve creating a [PDP-10 Emulator](/modules/pdp10/).
So far, the only [PDP-10 Hardware Documentation](/pubs/dec/pdp10/) I've read (or rather, skimmed) is the
[PDP-10 System Reference Manual (1968)](http://archive.pcjs.org/pubs/dec/pdp10/ka10/DEC-10-HGAA-D_PDP-10_System_Reference_Manual_May1968.pdf).
To make it easier to create and run some simple [PDP-10 Opcode Tests](/apps/pdp10/tests/), most of my initial development
To make it easier to create and run some simple [PDP-10 Opcode Tests](/apps/pdp10/tests/opcodes/), most of my initial development
efforts have centered on the built-in PDP-10 Debugger. In addition to the usual disassembler capabilities, I've also included
an assembler, so that I can easily enter instruction sequences like this:
@ -34,7 +35,7 @@ and immediately see the corresponding opcodes:
It's not a full-featured assembler; it's little more than a brute-force reverse look-up through the disassembler's
opcode-to-mnemonic tables. But it gets the job done.
Next, I've written an initial set of [PDP-10 Opcode Tests](/apps/pdp10/tests/) and compared the results to those produced
Next, I've written an initial set of [PDP-10 Opcode Tests](/apps/pdp10/tests/opcodes/) and compared the results to those produced
by [SIMH](https://github.com/simh/simh), and so far, so good. More than anything else, that's just a testament to DEC's
excellent documentation.

View file

@ -0,0 +1,184 @@
---
layout: post
title: The MACRO-10 Assembler, 50 Years Later
date: 2017-02-28 22:00:00
permalink: /blog/2017/03/21/
machines:
- id: testka10
type: pdp10
config: /devices/pdp10/machine/ka10/test/debugger/machine.xml
debugger: true
commands: a 30724 /apps/pdp10/diags/klad/dakaa/MYDAKAA.MAC
---
A few weeks ago, I finished my first cut of the *core* PDP-10 instructions in PDPjs. Most of my remaining work
falls into these categories:
- UUOs (Unimplemented User Operations)
- Floating-point instructions
- Input-Output instructions (eg, BLKI, BLKO, DATAI, DATAO)
- Miscellaneous instructions (eg, variations of PUSH, POP, JUMP)
Floating-point is the biggest chunk of work, which I'm going to save for last, with the hope that most PDP-10 software
didn't use floating-point. However, if all PDP-10 systems included floating-point hardware (which I haven't been able to
confirm yet), I may have no choice. Floating-point emulation in JavaScript isn't hard -- PCjs already includes an
[8087 Coprocessor Emulator](/modules/pcx86/lib/x86fpu.js) -- but getting all the details right is time-consuming.
The PDP-10 has a lot of instructions, and I quickly had far more instructions than I was willing or able to write tests for.
Besides, any tests I wrote would be based on the same potentially-flawed understandings that I had gleaned from DEC's
[PDP-10 Hardware Documentation](/pubs/dec/pdp10/). It was obvious that I needed to find a set of DEC PDP-10 diagnostics, much
like the [DEC PDP-11 Paper Tape Diagnostics](/apps/pdp11/tapes/diags/) I had used to flush most of the early bugs out of my
PDP-11 emulator.
Unfortunately, this presented something of a chicken-and-egg problem, because the PDP-10 emulator is still too immature to
support any simulated devices, even something as simple as paper tape. However, since I did find a series of PDP-10 Model KA10
diagnostics in [source code form](http://pdp-10.trailing-edge.com/klad_sources/), all I needed was some way to assemble those
files into binaries that I could load directly in a PDPjs test machine.
One solution would have been to run a TOPS-10 simulation using the SIMH emulator, assemble the desired tests inside that virtual
machine, extract the binaries, and then load them into a PDPjs machine. But that would have meant familiarizing myself with
both SIMH and TOPS-10, and the process of editing, assembling, and transferring binaries to PDPjs would have been cumbersome at
best.
### Introducing the MACRO-10 "Mini-Assembler"
I decided that the shortest turn-around from *assembly* phase to *run* phase (aka *crash-and-burn* phase) would be to
assemble the files in PDPjs itself. So the PDPjs MACRO-10 "Mini-Assembler" was born. Any machine that includes the PDPjs
Debugger (like the machine below) now includes MACRO-10 support as well.
{% include machine.html id="testka10" %}
The above machine uses the Debugger's assemble ('a') command to assemble DEC's "DAKAA" diagnostic
[KA10 Basic Instruction Diagnostic](/apps/pdp10/diags/klad/dakaa/) and load the resulting code at address 30724:
a 30724 /apps/pdp10/diags/klad/dakaa/MYDAKAA.MAC
The Debugger invokes the MACRO-10 mini-assembler whenever the argument following the target address appears to be a URL.
As previously described in the section on [PDPjs PDP-10 Opcode Tests](/apps/pdp10/tests/opcodes/), the Debugger already
allowed you to assemble instructions directly into memory (eg, `a 100 hrli 1,111111`). This "immediate mode" feature is provided
exclusively by the Debugger; MACRO-10 mini-assembler features are not available in that mode.
However, one feature that *is* available in both the Debugger and the mini-assembler is full MACRO-10-style expression evaluation,
since they share the same expression parser. Here are some examples, using the Debugger's 'print' command:
>> print 27
23. 0o000027
Both the Debugger and MACRO-10 start out by assuming a default base (radix) of 8, so the value "27" is evaluated as an octal
number, and for convenience, the "print" command displays the result in both decimal and octal. You can change the default base
in the Debugger with the `s base` command (eg, `s base 10`), where the new base is always interpreted as a decimal number.
This command differs from MACRO-10, which uses the `RADIX` pseudo-op to change the default base.
Obviously, it would be nice if both the Debugger and the MACRO-10 mini-assembler used matching commands, but the PCjs debuggers
were written before MACRO-10 support was a consideration, so for now, that's life.
All of the MACRO-10-style base prefixes are supported (^D for decimal, ^B for binary, an ^O for octal):
>> print ^D27
0o000000000033 27.
>> print ^B1001
0o000000000011 9.
>> print ^O1001
0o000000001001 513.
Expressions using angle brackets, another MACRO-10 convention, is also supported:
>> print ^D<45-22>
0o000000000027 23.
Note that the base modifier (^D) applies to the entire expression. You can also add MACRO-10 suffixes to integers:
K: "kilo-", thousands
M: "mega-", millions
G: "giga-", billions
so:
>> print 5K
0o000000005000 2560.
>> print ^D5K
0o000000011610 5000.
Binary shifting using a B suffix is also supported:
>> print 1B0
0o400000000000 -34359738368.
>> print 1B17
0o000001000000 262144.
>> print 1B35
0o000000000001 1.
>> print -1B35
0o777777777777 -1.
>> print -1B53
0o000000777777 262143.
>> print -1B70
0o000000000001 1.
And as the MACRO-10 ASSEMBLER PROGRAMMER'S REFERENCE MANUAL (June 1972), p. 1-17, points out, all the following expressions are
equivalent:
>> print 10B32
0o000000000100 64. '@'
>> print ^O10B32
0o000000000100 64. '@'
>> print 10B<42-10>
0o000000000100 64. '@'
>> print 10B<^D<42-10>>
0o000000000100 64. '@'
>> print 10B<^D42-^D10>
0o000000000100 64. '@'
"Left arrow" shifting is also supported, although the character that the original MACRO-10 manuals referred to as a "left arrow"
is actually an underscore; later manuals refer to this as "underscore shifting":
>> print 1_^D18
0o000001000000 262144.
>> print -1_^D-18
0o000000777777 262143.
Character-based constants are also supported, in the both 7-bit ASCII the 6-bit SIXBIT formats. Use double-quotes
for 7-bit right-justified character constants and single-quotes for 6-bit right-justified character constants:
>> print +'SIXBIT'
0o635170425164 -13255955852.
>> print +"HELLO"
0o221054623117 19473311311.
NOTE: When using the PCjs debugger, the constant must be preceded by an arithmetic operator, otherwise it interprets the
quoted argument as a string. This is an idiosyncrasy of the "print" command, not the expression parser.
All MACRO-10 logical and arithmetic operators are supported, including:
- Unary arithmetic operators (+ and -)
- Unary complement operator (^-, aliased to ~)
- Unary base overrides (^D, ^O, and ^B)
- Binary shifting (B) and underscore shifting (_) operators
- Logical binary operations (! for OR, ^! for XOR, and & for AND)
- Multiplication and division (* and /)
- Addition and subtraction (+ and -)
- Angle brackets for expression grouping (< and >)
Much more work is needed to make the MACRO-10 "Mini-Assembler" a general-purpose assembler, but it's already proved itself
useful in assembling the following tests:
- [KA10 Basic Instruction Diagnostic #1](/apps/pdp10/diags/klad/dakaa/)
- [KA10 Basic Instruction Diagnostic #4](/apps/pdp10/diags/klad/dakad/)
- [Assorted MACRO-10 Mini-Assembler Tests](/apps/pdp10/tests/macro10/)
*[@jeffpar](http://twitter.com/jeffpar)*
*Mar 21, 2017*

View file

@ -1,11 +1,11 @@
---
layout: page
title: DEC Paper Tape Diagnostics
title: DEC PDP-11 Paper Tape Diagnostics
permalink: /apps/pdp11/tapes/diags/
---
Paper Tape Diagnostics
----------------------
PDP-11 Paper Tape Diagnostics
-----------------------------
Thanks to the [iamvirtual.ca](http://iamvirtual.ca/collection/systems/mediadoc/mediadoc.html#papertape)
website, we have been able to archive the PDP-11 diagnostics shown below. The tapes have been added to the

File diff suppressed because it is too large Load diff

View file

@ -52,7 +52,7 @@ y:121,z:122,"{":123,"|":124,"}":125,"~":126,po:127},ma={};ma[173]=n["-"];ma[186]
p[192]=n["~"];p[219]=n["{"];p[220]=n["|"];p[221]=n["}"];p[222]=n['"'];p[173]=n._;p[61]=n["+"];p[59]=n[":"];
function na(a,b){var c;if(a){b||(b=10);var d,e,f=0<a.indexOf(",");f&&(a=a.replace(/,/g,""));d=e=a.charAt(0);"#"==e?(b=8,e=""):"$"==e&&(b=16,e="");d!=e?a=a.substr(1):(d=e=a.substr(0,2),"0b"==e&&f||"^B"==e?(b=2,e=""):"0o"==e||"^O"==e?(b=8,e=""):"^D"==e?(b=10,e=""):"0x"==e&&(b=16,e=""),d!=e&&(a=a.substr(2)));d=e=a.slice(-1);"Y"==e||"y"==e?(b=2,e=""):"."==e?(b=10,e=""):"H"==e||"h"==e?(b=16,e=""):"K"==e?e="000":"M"==e?e="000000":"G"==e&&(e="000000000");d!=e&&(a=a.slice(0,-1)+e);var g;d=0;if(e=a.match(/(-?[0-9]+)B([0-9]*)/))a=
e[1],d=35-((e[2]||35)&255);e=a;((f=b)&&10!=f?16==f?e.match(/^-?[0-9a-f]+$/i):8==f?e.match(/^-?[0-7]+$/):2==f&&e.match(/^-?[01]+$/):e.match(/^-?[0-9]+$/))&&!isNaN(g=parseInt(a,b))&&(d&&(0>g&&(g+=Math.pow(2,36)),g=0<d?g*Math.pow(2,d):Math.trunc(g/Math.pow(2,-d))),c=g)}return c}
function oa(a,b,c){b?9<b&&(b=9):(b=Math.abs(a),b=65535>=b?4:4294967295>=b?8:9);c=c?"0x":"";var d;d=void 0===d?-1:d;var e="";isNaN(a)?a=null:null!=a&&(0>a&&-1<a&&(a=-1),0>a&&(a+=Math.pow(16,b)),a>=Math.pow(16,b)&&(b=Math.ceil(Math.log(a)/Math.log(16))));for(var f=d;0<b--;){f||(e=","+e,f=d);if(null==a)e="?"+e;else{var g=a%16,g=g+(0<=g&&9>=g?48:55),e=String.fromCharCode(g)+e;a=Math.trunc(a/16)}f--}return(void 0===c?"":c)+e}
function oa(a,b,c){b?9<b&&(b=9):(b=Math.abs(a),b=65535>=b?4:4294967295>=b?8:9);c=c?"0x":"";var d;d=void 0===d?0:d;var e="";isNaN(a)?a=null:null!=a&&(0>a&&-1<a&&(a=-1),0>a&&(a+=Math.pow(16,b)),a>=Math.pow(16,b)&&(b=Math.ceil(Math.log(a)/Math.log(16))));for(var f=d||-1;0<b--;){f||(e=","+e,f=d);if(null==a)e="?"+e;else{var g=a%16,g=g+(0<=g&&9>=g?48:55),e=String.fromCharCode(g)+e;a=Math.trunc(a/16)}f--}return(void 0===c?"":c)+e}
function pa(a,b){var c=a,d=a.lastIndexOf("/");0<=d&&(c=a.substr(d+1));d=c.indexOf("&");0<d&&(c=c.substr(0,d));b&&(d=c.lastIndexOf("."),0<d&&(c=c.substring(0,d)));return c}function qa(a){var b="",c=a.lastIndexOf(".");0<=c&&(b=a.substr(c+1).toLowerCase());return b}function ra(a,b){return-1!==a.indexOf(b,a.length-b.length)}function sa(a){return a.replace(/[&<>"']/g,function(a){return ta[a]})}function ua(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}
var ta={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"},va={0:"NUL",1:"SOH",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",7:"BEL",8:"BS",9:"TAB",10:"LF",11:"VT",12:"FF",13:"CR",14:"SO",15:"SI",16:"DLE",17:"XON",18:"DC2",19:"XOFF",20:"DC4",21:"NAK",22:"SYN",23:"ETB",24:"CAN",25:"EM",26:"SUB",27:"ESC",28:"FS",29:"GS",30:"RS",31:"US"};
function wa(){function a(a){return(10>a?"0":"")+a}var b=new Date;return b.getFullYear()+"-"+a(b.getMonth()+1)+"-"+a(b.getDate())+" "+a(b.getHours())+":"+a(b.getMinutes())+":"+a(b.getSeconds())}function xa(a,b){var c=0,d=1,e;for(e in a){if(d>=arguments.length)break;var f=a[e],g=arguments[d++],c=c&~f.qg|g<<f.shift&f.qg}return c}function ya(a,b){return(b&a.qg)>>a.shift}var za=[31,28,31,30,31,30,31,31,30,31,30,31],Aa=Date.now||function(){return+new Date};

View file

@ -673,6 +673,9 @@ class Debugger extends Component
continue;
}
if (!(nUnary & (0xC0000000|0))) {
if (sOp == '+') {
continue;
}
if (sOp == '-') {
nUnary = (nUnary << 2) | 1;
continue;
@ -1020,9 +1023,9 @@ class Debugger extends Component
if (value !== undefined) {
fDefined = true;
if (this.nBase == 8) {
sValue = value + ". " + Str.toOct(value, 0, true);
sValue = this.toStrBase(value, this.nBits, 8, 1) + " " + value + '.';
} else {
sValue = Str.toHex(value, 0, true) + " " + value + ". " + Str.toOct(value, 0, true) + " " + Str.toBinBytes(value, 4, true);
sValue = this.toStrBase(value, this.nBits, 16, 1) + " " + this.toStrBase(value, this.nBits, 8, 1) + " " + this.toStrBase(value, this.nBits, 2, this.nBits <= 32? 8 : 6) + " " + value + '.';
}
if (value >= 0x20 && value < 0x7F) {
sValue += " '" + String.fromCharCode(value) + "'";
@ -1113,24 +1116,26 @@ class Debugger extends Component
}
/**
* toStrBase(n, nBits)
* toStrBase(n, nBits, nBase, nGrouping)
*
* Use this instead of Str's toOct()/toDec()/toHex() to convert numbers to the Debugger's default base.
*
* @this {Debugger}
* @param {number|null|undefined} n
* @param {number} [nBits] (-1 to strip leading zeros, 0 to allow a variable number of digits)
* @param {number} [nBase]
* @param {number} [nGrouping] (if nBase is 2, this is a grouping; otherwise, it's a prefix condition)
* @return {string}
*/
toStrBase(n, nBits = 0)
toStrBase(n, nBits = 0, nBase = 0, nGrouping = 0)
{
var s;
switch(this.nBase) {
switch(nBase || this.nBase) {
case 2:
s = Str.toBin(n, nBits > 0? nBits : 0);
s = Str.toBin(n, nBits > 0? nBits : 0, nGrouping);
break;
case 8:
s = Str.toOct(n, nBits > 0? ((nBits + 2)/3)|0 : 0);
s = Str.toOct(n, nBits > 0? ((nBits + 2)/3)|0 : 0, !!nGrouping);
break;
case 10:
/*
@ -1140,7 +1145,7 @@ class Debugger extends Component
break;
case 16:
default:
s = Str.toHex(n, nBits > 0? ((nBits + 3) >> 2) : 0);
s = Str.toHex(n, nBits > 0? ((nBits + 3) >> 2) : 0, !!nGrouping);
break;
}
return (nBits < 0? Str.stripLeadingZeros(s) : s);

View file

@ -179,7 +179,7 @@ class Str {
}
/**
* toBase(n, radix, cch, sPrefix, grouping)
* toBase(n, radix, cch, sPrefix, nGrouping)
*
* Displays the given number as an unsigned integer using the specified radix and number of digits.
*
@ -187,10 +187,10 @@ class Str {
* @param {number} radix (ie, the base)
* @param {number} cch (the desired number of digits)
* @param {string} [sPrefix] (default is none)
* @param {number} [grouping]
* @param {number} [nGrouping]
* @return {string}
*/
static toBase(n, radix, cch, sPrefix = "", grouping = -1)
static toBase(n, radix, cch, sPrefix = "", nGrouping = 0)
{
/*
* An initial "falsey" check for null takes care of both null and undefined;
@ -222,11 +222,11 @@ class Str {
cch = Math.ceil(Math.log(n) / Math.log(radix));
}
}
var g = grouping;
var g = nGrouping || -1;
while (cch-- > 0) {
if (!g) {
s = ',' + s;
g = grouping;
g = nGrouping;
}
if (n == null) {
s = '?' + s;
@ -242,16 +242,16 @@ class Str {
}
/**
* toBin(n, cch, grouping)
* toBin(n, cch, nGrouping)
*
* Converts an integer to binary, with the specified number of digits (up to a maximum of 36).
*
* @param {number|null|undefined} n (supports integers up to 36 bits now)
* @param {number} [cch] is the desired number of binary digits (0 or undefined for default of either 8, 18, or 36)
* @param {number} [grouping]
* @param {number} [nGrouping]
* @return {string} the binary representation of n
*/
static toBin(n, cch, grouping)
static toBin(n, cch, nGrouping)
{
if (!cch) {
// cch = Math.ceil(Math.log(Math.abs(n) + 1) / Math.LN2) || 1;
@ -264,7 +264,7 @@ class Str {
cch = 36;
}
} else if (cch > 36) cch = 36;
return Str.toBase(n, 2, cch, "", grouping);
return Str.toBase(n, 2, cch, "", nGrouping);
}
/**