Disassembler now has a compatibility mode for classic native macro

65 views
Skip to first unread message

Bill E

unread,
Sep 28, 2025, 10:19:26 AM (12 days ago) Sep 28
to [PiDP-1]
In compatibility mode, instead of MACRO1 output, it outputs assembler that the native PDP-1 MACRO assembler can handle. The output lacks some of the nice info you get from regular macro mode, like labels, but it assembles. Note that I've tested this on a few tapes, assembling them on the pidp-1 and running them, but not extensively tested. It's not a mode I would use myself, much easier to use the cross-assembler.
Bill
 

Oscar Vermeulen

unread,
Sep 28, 2025, 7:21:49 PM (12 days ago) Sep 28
to [PiDP-1]
That is a major comfort factor!

Oscar Vermeulen

unread,
Sep 30, 2025, 8:57:28 PM (10 days ago) Sep 30
to [PiDP-1]
Bug report (or user ignorance report, you never know) 
- trying to disassemble a version of Expensive Typewriter that I wanted to investigate (it will not work well on either simh or the pdp1 simulator, I wonder why), I get this error:

disassemble_tape -m et_sa100.bin
Unterminated RIM block at tape position 432

...but inspecting the tape, there's nothing odd on line 432 of the tape, it is in the middle of the RIM block (the blue part of the tape).

What is unusual, and maybe the issue: there's no empty lines on the paper tape between RIM (blue) and BIN (red) data.
Also, the BIN loader only loads a small 7-word program, that then gets executed (to clear memory I bet) and then, that program calls the BIN loader again to load the rest of the program, overwriting the previous one. Tricky tape!

I've attached the offending paper tape image if you want to inspect.

Screenshot From 2025-10-01 02-49-21.png
Explanation: in the blue RIM block, the marker R signals the DIO instruction, the a markers the address, and the next three lines the 18-bit word to be loaded in.
The BIN block starts with a DIO for the start address and a DIO for the end address, then the consecutive words, and the 'c c c' identifies the checksum at the end of the BIN block. There's a JMP to run the code, and on the tape, then comes another BIN block.

It might not be worth your while to deal with such oddities, but just to report they exist.


I just pushed the update of tape_visualiser shown above to the github. 

Leading to some questions! Because the next step for the tape_visualizer tool is to use  your disassembler to disassemble each loaded line on the tape, and show it at the bottom of each line. 
My goal would be to use the disassembler unmodified - maintaining one disassembler version is work enough as it is, best to avoid creating variants of it.

I'm still thinking what the best way is, as tape_visualizer is a Python program rather than C. 

I can compile disassembler.c as a shared library, and then call formatInstr() from python & intercept its output with some tricks - not too elegant though.
Do you have any suggestions? The core point being, the Python program submits one 18-bit octal word and receives the disassembly of it straight away.

Feel free to ignore the request though, a fool can ask more than any wise man wants to answer. Or something like that.

Kind regards,

Oscar.

et_sa100.bin

Bill E

unread,
Oct 2, 2025, 11:21:21 AM (8 days ago) Oct 2
to [PiDP-1]
That is one strange tape. The problem is that once the disassembler sees the end of a BIN block followed by a JMP outside the block, that means that in the -1 world execution passes to that address, which is the start addr from the prior BIN. Once seen, it looks for a new RIM block. But, what's on the tape is ... BIN JMP BIN..., the start of a new BIN block instead of a RIM block. I'm not certain what to do. I can change my assumption that the next thing after a JMP is another BIN instead of a RIM. Is that generally correct, though? The JMP is what macro1 puts out for the 'start xx' directive in code. That means the rest of the stuff on the tape was put there by doing some other process. If macro had been run again, there should be a RIM block unless it was told to not generate one. This gets complicated. Also, there is no reliable way to really tell if the start of a block is a RIM or a BIN without at least 3 words of lookahead. I might need to do that, but it will take a bit of thought. So, to sum it up, the disassembler is doing what I expect it to do, I just didn't expect a tape like this one. I'll update when I figure out what I want to do.

Oscar Vermeulen

unread,
Oct 2, 2025, 12:12:04 PM (8 days ago) Oct 2
to [PiDP-1]
I think maybe the best thing is to do nothing - this is not a standard output from MACRO. And maybe the disassemble_tape is guaranteed for MACRO-generated tapes, not for oddball tapes.

>> But, what's on the tape is ... BIN JMP BIN...,

I think what is happening is that the first BIN block is loaded and executed to just clear memory. Then ET itself is loaded and has a neat empty workspace. 

I'm now looking at Kalah.bin, that is an even odder tape. It uses its own non-standard BIN loader. Also something to ignore if disassemble_tape wants to be clean and aimed at disassembling normal MACRO-generated tapes.

Kind regards,

Oscar.

Bill E

unread,
Oct 3, 2025, 8:04:49 AM (7 days ago) Oct 3
to [PiDP-1]
Oscar, I did make a change, I improved the error handling. Now, macro mode will stop processing as soon as it sees a 'start' (JMP outside a block), not just exit. In detail mode, processing will continue, dumping the data as usual. So, disassembling et will produce little of use in macro mode, but will disassemble the entire contents in detail mode.
Bill

Oscar Vermeulen

unread,
Oct 3, 2025, 9:30:56 AM (7 days ago) Oct 3
to [PiDP-1]
Bill,

Excellent! 

If you have any thoughts on how I could call your disassembler for individual words (in my tape_visualizer) please let me know. I'll add disassembly to the tape_visualizer in the coming days and I am eager to have one standard disassembler for the PDP-1, instead of different ones for different purposes :-)

One idea was to compile your disassemble_tape as a shared library (requires no code changes). And call formatInstr() from Python, which is easy. But formatInstr() prints to stdout, which makes it a little bit ugly to integrate into a Python program.
But only if you feel this is useful, I will find a good solution one way or the other :-)

Kind regards,

Oscar.

Bill E

unread,
Oct 3, 2025, 9:46:01 AM (7 days ago) Oct 3
to [PiDP-1]
formatInstr() is definitely what you want to use. I could easily modify it to take a FILE * arg if that's easier, and could even move it into a separate c file so you only need to drag that in.
It also uses some global vars from the main code. Yes, not terribly fond of that, but it's easier than passing a bunch of stuff or having a struct.
I could extern those in the separate c file, can you map to them in Py?
I haven't used Py in ages, not my favorite lang. (sorry all you Py lovers)

So, easiest, pass a FILE * to formatInstr()
Harder, move it to another C file, but not that hard.

Thoughts? Wouldn't take me much effort.
Bill
PS - I thought about doing the FILE * change a while back when I was thinking about being able to specify an output filename on the cmd line, but decided that was boring to do. :)

Oscar Vermeulen

unread,
Oct 6, 2025, 10:09:11 AM (4 days ago) Oct 6
to [PiDP-1]
Bill,

Apologies, I was offline for a few days.

Python can call a C function, so that is no problem. I'll compile the C program as a shared library, needs no code changes. 
The challenge is that formatInstr() prints directly to stdout. Python needs to do clumsy things to intercept that. So my ideal would be if formatInstr() prints to a string, which Python can then work with.

But only if this is no trouble for you and you feel like it!

Kind regards,

Oscar.

Reply all
Reply to author
Forward
0 new messages