So, I'm working on a project where I'm aiming to get the first winner of the IOCCC (obfuscated c code contest) dissected down to the last detail... The program is mullender.c which you can find by following that link.

Anyway, I've gotten it running under pdp-11/70 using simh as well as vax780. The thing is it relies on the 'write' syscall (and maybe one other). Just for fun, I'd like to try getting this running on a pure pdp-11/vax machine (i.e, no OS providing syscall support). What should I be looking into doing here? It looks like MACRO-11 (pdf warning) is the assembly language for the pdp. You can also enter octal directly into the memory in simh but that's a little arduous.

I'm totally out of my depth here so basically I'm asking for a starting point - where do I need to begin looking to make this work? There are a million manuals but I've seen nothing concrete (i.e, example programs). From what I've researched I need to poll the status on the dz11 device (pdf warning) and if it's ready just write to it, then repeat. Seems easy enough in theory but of course the question of how to get it working is another matter =).

  • It may be usefull if you link information instead of talking about not doing so. Also, without an OS, you need to know the hardware for any kind of output. Or write your own minimalist OS for that hardware, isnt it? So this question might be rather broad.
    – Raffzahn
    Commented Jul 6, 2020 at 0:03
  • 1
    I have added links to anything that might be ambiguous. I have stated that I am aiming to write a function that will output to the dz11 terminal which I know is a device that attached to the pdp-11 (the hardware you're talking about). I don't need to write a minimalist OS, I need to know how to write a function in MACRO-11 or pure Octal in the PDP-11 address space (because it uses memory mapped IO) which will display that output to the dz11 terminal. The main suggestion seems to be handling a trap in the processor but I don't know how to do that hence why I'm on this website Commented Jul 6, 2020 at 0:20

2 Answers 2


So bare metal, no OS?

On both systems, console terminal I/O is considerably easier than any other terminal interface.

On many VAXen, there's a couple of processor registers that are status and data registers for console I/O. Much easier than dealing with DZ-11 or similar.

Here's the important part of a VAX 'putchar' routine from a standalone libc I wrote in the late 80s. Polling, no interrupts.

do _MFPR(PR$_TXCS, &tmp);
while (!(tmp&0x80));

_MTPR(ch, PR$_TXDB);

Basically: read status until the transmitter is ready, then load the character into the transmitter data buffer.

_MTPR, _MFPR are builtins for the 'move to/from processor register' instructions; you can look up the register numbers in any VAX handbook. Transliterating that to MACRO-32 should be obvious enough.

The same sort of thing is true on the average PDP-11, except that the console DL-11 (or similar) has status and data registers in the I/O page. The control/status register is at 17777564, the data buffer at 17777566.

Assuming the MMU is disabled, or in the usual state where kernel page 7 maps to the I/O page.

10$:  TSTB @#177564
      BEQ 10$
      MOVB CH,@#177566

A DZ-11 is a little harder, especially on the VAX where I imagine you'll have to set up the UBA.

In either case, turning on paging complicates life further. And, if you've truly got no OS, you'll have to initialize the processor, cache, MMU, maybe size memory, … I'd start with the easy case

  • It looks like the DL-11 is installed with the simulator I'm using (simh), they call it the TTO device though. I'll look into poking around with that, thank you! Commented Jul 6, 2020 at 4:17
  • I think (not certain) all PDP-11s have a console terminal interface at that address, whether it's an actual DL-11 or merely something in the microcode to respond appropriately. The DL-11 itself is a replacement for the earlier KL-11, as far as I recall.
    – dave
    Commented Jul 6, 2020 at 11:00
  • Too many 7s in yr pdp11 register addresses
    – pm100
    Commented Jul 8, 2020 at 14:17
  • 1
    17777564 = 22-bit physical, 777564 = 18-bit Unibus, 177564 = kernel virtual.
    – dave
    Commented Sep 7, 2020 at 23:58

If you want to print a string (terminated with a NUL (000)) suggest the following:

string: .asciz "the quick brown ..."

      mov #1000,sp     ; init stack

      mov #string,R0   ; pass string as argument
      jsr pc,Print    ; to Print function

Print:mov r1,-(sp)        ; save R1
      br  20$             ; jump to NUL test

10$:  TSTB @#177564
      BEQ 10$             ; spin until busy bit clears
      MOVB R1,@#177566    ; send a char
20$:  movb (R0)+, R1      ; get a character, keep sending
      bne  10$            ; if it is not NUL
      mov (sp)+,r1        ; restore saved register
      rts pc              ; return

There is no point in doing this with interrupts enabled, because you would just go into a spin loop waiting for the interrupt anyway. The spin loop on not busy is just as good.

  • 1
    Your subroutine call has a typo - you want JSR PC,PRINT (or JSR PC,@#PRINT is possible). Immediate mode would have you jumping into the middle of the JSR instruction itself.
    – dave
    Commented Feb 25, 2021 at 3:13
  • You're right, I coded in Macro-11 from 197x through 1993, but not much since. I should have seen if the Pro-380 or the PDP11-83 in the basement would fire up and have tested the code.
    – jfwfmt
    Commented Feb 25, 2021 at 23:50
  • I think my last serious PDP-11 programming was around 1983.
    – dave
    Commented Feb 25, 2021 at 23:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .