PDP-11 CoProcessor Technical Reference ====================================== J.G.Harston, 70 Camm Street, Walkley, Sheffield S6 3TR http://mdfs.net/Docs/Books/PDP11CoPro/Technical Date: 10-Jun-2022 The PDP-11 CoProcessor system is a PDP-11 system that communicates with the Acorn Tube protocol with a host computer system via some form of communication channel, such as a hardware Tube or a serial link. Host Interface ============== The EMT opcode is used to communicate with the host. On return from an EMT call, if V is set, then R0 points to an error block, otherwise V is clear. All registers are preserved, unless otherwise stated. Flags are corrupted, unless a stated value is returned. EMT 0 - QUIT Calls the current Exit handler. All registers are passed to the exit handler, but only R0 is valid. R0 should contain 0 for a normal exit and should hold a non-zero exit code for an abnormal exit. If no exit handler has been installed, then this returns to the Supervisor prompt. EMT 1 - CLI Passes the command line pointed to by R0 to the host. If the command results in a file being run from disk that can be recognised as a PDP-11 file, it is loaded directly into the PDP-11 memory and entered. If the executed file or a selected ROM has a valid PDP-11 ROM header, then it is made the current program and is entered. The executed program can return a return value in R0, with R0=0 being 'ok'. EMT 2 - BYTE Does OSBYTE R0,R1,R2; returning results in R0, R1, R2 and Carry. On return, b0-b7 of R2 is copied to b8-b16 of R1 so that R1 can be used as a 16-bit value. The memory request OSBYTEs &82, &83 and &84 return values appropriate to the PDP-11 environment. &82 (High order address) returns &0000, &83 returns the lowest usable address, &84 returns the address after the last usable address. EMT 3 - WORD Does OSWORD R0 with R1 pointing to the parameter block. OSWORD 0 (read line) returns values in R2 and Carry. EMT 4 - WRCH Outputs character in R0. EMT 5 - NEWL Outputs LF,CR and returns R0=13. EMT 6 - RDCH Read character to R0 and Carry. EMT 7 - FILE Calls OSFILE with R0=function, R1=>control block. EMT 8 - ARGS Calls OSARGS with R0=function, R1=channel, R2=>control block. EMT 9 - BGET Reads a byte from the channel in R1, returns character in R0 and Carry. EMT 10 - BPUT Writes byte in R0 to channel in R1. EMT 11 - GBPB Calls OSGBPB with R0=function, R1=control block. EMT 12 - FIND Calls OSFIND with R0=0 and R1=channel or R0<>0 and R1=>filename. EMT 13 - SYST Various system control functions. Returns R0=0 and R1,R2 corrupted if implemented. Returns all preserved if not implemented. R0=0 - Loads and enters BBC BASIC with R1=>parameters or 0, does not return if implemented, returns R0=0 if not implemented. R0=1 - Set up new program environment. This must be called by code that wants to become the current program instead of being transient code. The current program is re-entered at Soft Break. R0>1 - Reserved. R0<0 - Emulator control functions. EMT 14 - CTRL Reads and writes handlers and EMT dispatch table entries. R0>=0 - reads or writes EMT dispatch address: R0=EMT number R1=address of EMT routine, or zero to read On exit: R0 preserved R1 previous EMT dispatch address R0<0 - reads or writes environment handler: R0=Environment handler number R1=address of environment hander or zero to read R2=address of environment data block or zero to read On exit: R0 preserved R1=previous environment handler address R2=previous environment data address Environment handler numbers are: R0 R1 R2 &FFFF Exit handler version &FFFE Escape handler Escape flag (one byte) &FFFD Error handler Error buffer (256 bytes) &FFFC Event handler unused &FFFB Unknown IRQ handler (used during data transfer) &FFFA (used during EMT) EMT dispatch table (512 bytes) Internal handlers: &FFF9 LPTR ADDRHI &FFF8 MEMBOT MEMTOP &FFFA ADDR TRANS &FFFB PROG MISC+ESCFLG The Exit handler is entered with R0=return value. The Escape handler is entered with R0=new escape state in b6, must preserve all registers other than R0 and return with RTS PC. The Error handler is entered with R0=>error block. Note that this may not be the address of the error buffer, the error buffer is used for dynamically generated error messages. The Event handler is entered with R0,R1,R2 holding the event parameters, must preserve all registers, and return with RTS PC. The Unknown IRQ handler must preserve all registers, and return with RTI. EMT 15 - ERROR Error block follows EMT opcode in the following manner: EMT 15 EQUB errornumber EQUS "error message" EQUB 0 ALIGN EMT 15 enters the current error handler with R0 pointing to the error block after the EMT 15. Program Environment =================== Code can be executed by selecting a ROM, running a file from disk, or entering it with *GO. When code is entered it is checked to see if it has a code header with ROM type 7 at the code entry point. If there is a Tube transfer address, it is followed by an offset from the Tube transfer address to the code entry address. The lowest possible address to load code without a Unix header is &100. Executed code is also checked for a Unix file header. If a valid Unix header is found, then the code is moved and/or remapped so that the code from byte 16 onwards is located at the bottom of memory at MEMBOT onwards, and any uninitialised data segment is zeroed immediately after the initialised data. A stack frame is built at the top of memory and pointed to with SP, and the code is entered at location zero. On systems without a Memory Mapping unit, MEMBOT will usually be &0100; on systems with an MMU, MEMBOT will be &0000. Code is entered with the registers holding the following values: R0: 0=raw code (code without a header), 1=code has ROM header R1=>command line parameters R2,R3,R4=0 R5=&0BBC to indicate it is running in a BBC environment and the BBC EMT calls are available R6=stack (and any stack frame) and the top of memory CC=entered at RESET, CS=entered from OSCLI/OSBYTE On systems with an MMU, code is entered in User mode, without an MMU it is entered in Kernel mode. You must be cautious about using the string pointed to by R1 as if the command originated on the host processor, the command line will only exist on the host processor. OSARGS 0,1 returns the address in the host processor of the command line of a program run from disk. Executing code has a small stack of at least 16 bytes (8 subroutine calls) for transient code. If code wants to become the current program, it must call EMT 13,1 to claim the program environment. It will then be re-entered on Soft Break, all memory is available is from MEMBOT read with OSBYTE &83 up to MEMTOP read with OSBYTE &84, and the stack can be moved to MEMTOP. If the stack is not changed, the code can return with either RTS or OSQUIT. If the stack is changed the code must return with OSQUIT with EMT 0. Executing code is allowed to trash all registers, they are restored to the caller on OSQUIT or RTS. If the code has a Unix header the stack does not contain a return address, the code must exit with a call to exit() with TRAP 1 or OSQUIT with EMT 0. When code returns it can return a return value in R0, with R0=0 being 'ok'. If you set up any environment handlers, they must be restored on exit with EMT 14. If you set up an exit handler, it must restore the environment handlers, and then call the restored exit handler with EMT 0. Code header ----------- Executed code can have a header to identify it as PDP-11 code: .CodeStart CodeStart+0 EQUB 0,0,0 ; or BRA CodeEntry ; If CodeStart+6 has bit 5 clear ; EQUB 0 CodeStart+3 EQUB 0,0,0 ; or EQUB &4C ; 6502 JMP for ROM service entry ; EQUW Service CodeStart+6 EQUB &47 ; Code+PDP11 ; or EQUB &67 ; Code+Tube+PDP11 ; or EQUB &E7 ; Service+Code+Tube+PDP11 CodeStart+7 EQUB Copyright-CodeStart ; Offset to (C) CodeStart+8 EQUB 0 ; Version CodeStart+9 EQUS "PDP11 BASIC" ; ROM title EQUB 0 EQUS "0.01 (10 Feb 2008)" ; Version string .Copyright EQUB 0 EQUS "(C)J.G.Harston" ; Copyright message EQUB 0 ; If CodeStart+6 has bit 5 set, EQUD CodeStart ; 32-bit Tube transfer address and EQUD CodeEntry-CodeStart ; offset to execution address ; ALIGN .CodeEntry The Tube transfer address is only used by the host computer if the code is in a sideways ROM/RAM or is loaded from a host system that does not have load/exec addresses, such DOS/Windows. The transfer address and execution offset are the equivalent of the load/exec address of a file run from disk. Executed code is also checked for a Unix file header. ; > UnixHeader EQUW &0107 ; Magic number &o000407, also branch to CodeStart EQUW CodeEnd-CodeStart ; size of text (code) EQUW DataEnd-CodeEnd ; size of initialised data EQUW ZeroEnd-DataEnd ; size of uninitialised data EQUW &0000 ; size of symbol data EQUW CodeEntry-CodeStart ; entry point EQUW &0000 ; not used EQUW &0001 ; no relocation info ; .CodeStart ; Program code goes here .CodeEntry ; Program code goes here .CodeEnd ; Initialised data goes here .DataEnd ; Uninitialised data goes here, does not need to exist ; in the file, will be zeroed by the program loader .ZeroEnd If there is no header of either kind, the code is entered at it's start address - the execution address for a loaded file, or the first byte for code copied from a ROM or code entered with *GO. Escape State ------------ A program can read the current Escape state in three ways: - Read the Escape flag address, and poll that address to check bit 7. MOV #&FFFE,R0 ; Read/Write Escape handler MOV #0,R1 ; Read Escape handler address MOV #0,R2 ; Read Escape flag address EMT 14 TSTB (R2) ; Check Escape flag BMI EscapeState - Set an Escape flag address within the program's workspace, and poll that address to check bit 7. This is usually the simplest method. ; At program statup MOV #&FFFE,R0 ; Read/Write Escape handler MOV #0,R1 ; Read Escape handler address ADR MyEscFlag,R2 ; Set Escape flag address EMT 14 MOV R2,OldEscFlag ; Save old address ; To check Escape flag TSTB MyEscFlag ; Check Escape flag BMI EscapeState ; On program exit MOV #&FFFE,R0 ; Read/Write Escape handler MOV #0,R1 ; Read Escape handler address MOV OldEscFlag,R2 ; Restore old Escape flag address EMT 14 - Set an Escape handler to process it yourself, such as by setting your own flag. ; At program statup MOV #&FFFE,R0 ; Read/Write Escape handler ADR MyEscHandler,R1 ; Set Escape handler address ADR MyEscFlag,R2 ; Set Escape flag address EMT 14 MOV R1,OldEscHandler ; Save old address MOV R2,OldEscFlag ; Save old address ; My Escape handler .MyEscHandler ADD R0,R0 MOV R0,MyEscFlag RTS PC ; To check Escape flag TSTB MyEscFlag ; Check Escape flag BMI EscapeState ; On program exit MOV #&FFFE,R0 ; Read/Write Escape handler MOV oldEscHandler,R1 ; Read Escape handler address MOV OldEscFlag,R2 ; Restore old Escape flag address EMT 14 If you find that Escape is set, it must be acknowledged with OSBYTE &7E. Errors ------ Errors are generated with the EMT 15 opcode: EMT 15 EQUB errornumber EQUS "error message" EQUB 0 Errors enable all interupts, and then jump to the error handler with R0 holding the address of the error block after the EMT 15 opcode. The error handler must set up a stack before making any subroutine calls. Memory Map ========== Typical values: +--------------+--------------+ 0000 | | Kernal space | | | Hardware | | | vectors, etc | CoPro Unix MEMBOT +--------------+--------------+ 0100 0000 | | | | | ^^^User Stack^^^ | CoPro Unix MEMTOP +-----------------------------+ F500 E000 | Client MOS workspace | | and handlers | EMTTABLE +-----------------------------+ | EMT dispatch table | START +-----------------------------+ | Tube client code | +-----------------------------+ FFFF User Memory vs Kernel Memory ============================ Currently, the PDP11 Tube Client only supports a single unified memory map. All code runs in Kernel memory space with the hardware vectors visible at &0000-&00FF, and OSBYTE &82 returns R1<>0. Provisionally, with a split User/Kernel system: * if code is running in User memory, OSBYTE &82 will return R1=&0000. * OSBYTE &83 will return &0000 if the code is running in User memory, and <>&0000 if code is running in Kernel memory, with b15-b14 being a copy of the User/Kernel bits in b15-b14 in the PSW. * Calls from User code will only be able to load/save/read/write file data in User space. * b23-b22 of file start addresses will indicate which memory space to accesses as per b15-b14 of the PSW, ie: &0xxxxxxx : User memory &4xxxxxxx : reserved &8xxxxxxx : reserved &Cxxxxxxx : Kernel memory along with the usual &FFxxxxxx : I/O memory.