• Please review our updated Terms and Rules here

CP/M-80 binaries on V20 with PDOS/86-V20

So when the first instruction is 31 xx xx, it's a what? (e.g. DRI ASM, GENHEX, etc.)--or 01 xx xx (DDT/ZSID). C3 is very common, particularly when initialization code is at the high addresses and will be overlaid (e.g. EBAS). A relative jump at the beginning is only useful when the jump distance is short.
A program can use the flags register (slightly different behavior between 8080 and Z80) to determine what the host CPU is, but few do. Many just use 8080 code to be safe.

01 xx xx = 'LXI B,xxxx' (or 'LD BC,xxxx' in Z80 syntax). On x86 it would be 'ADD ew,rw' and not be a valid starting instruction since the register contents are undefined.

If the first byte is 31h (or 2Ah), you look at the second byte to see if it is clearing a register using XOR/SUB:

11:000:000 (C0h) XOR AX,AX / SUB AL,AL
11:001:001 (C9h) XOR CX,CX / SUB CL,CL
11:010:010 (D2h) XOR DX,DX / SUB DL,DL
11:011:011 (DBh) XOR BX,BX / SUB BL,BL
11:100:100 (E4h) XOR SP,SP / SUB AH,AH
11:101:101 (EDh) XOR BP,BP / SUB CH,CH
11:110:110 (F6h) XOR SI,SI / SUB DH,DH
11:111:111 (FFh) XOR DI,DI / SUB BH,BH

If it's not one of these, it should clearly be 8080 code ('LXI SP,xxxx' or 'LHLD xxxx'). That's 8 possibilities out of 256 for a mis-identification.

Yes, it is not entirely perfect in theory, but good enough in practice, especially compared to OP's plan of interpreting all .COM files as 8080 code.

edit:

Since we're discussing "interesting" starting bytes of CP/M programs, SLR Systems' Z80ASM.COM starts with EB 18 5F (and a religious message). On x86, that will jump forward to another jump, which leads to printing a message saying "CP/M emulator required" and exiting. On the Z80 the first byte is a "garbage" instruction (EX DE,HL) followed by a short jump, to another regular jump, to the actual entry point. There is no consideration for what happens on an 8080 at all.

A more elegant way I've thought of doing CPU detection at the start of a .COM file would be 81 C3 xx xx.

x80: ADD C ; JMP xxxx
x86: ADD BX,xxxx
 
Last edited:
Or you can do what I did--rename all of the x80 .COM files to .CPM and use the .COM version for an emulator loader. Gives a great amount of flexibility--and is bulletproof. After all,

"What's in a name? That which we call a rose
By any other name would smell as sweet;"

And then there's the matter of the added 8085 instructions...
 
Last edited:
Or you can do what I did--rename all of the x80 .COM files to .CPM and use the .COM version for an emulator loader. Gives a great amount of flexibility--and is bulletproof.

The renaming part of this technique was also used by Micro Solutions UniDOS PC products. In addition, they used an environment variable, CPMDIR, which would act like a PATH variable for all your CP/M programs.

@Chuck(G): Since I can't believe you are unaware of UniDOS (you know everything and are older than, ahem, silicon), what did you think of their approach compared to your 22NICE?
 
I've got a legit copy of it here--came with my Matchpoint PC. It did the job for most. Initially, I was after using the V20 8086 mode, but then once the PC AT made an appearance, I left the code in but went for full emulation. Even then, there were copro Z80 ISA cards available, so I thought that I had to do a bit more--simply being able to run CP/M ordinary code wasn't going to get there.

So, I put some extra features in where a user could include his own x86 handlers for I/O devices. It was useful when replacing x80 PLCs on industrial gear. Here's the skeleton of a 22NICE IOmap file:
Code:
	page	,132
	title	IOM_Skeleton - I/O Map Skeleton

	.model	small,c
	.code
	assume	cs:@Code,ds:@Code,es:@Code,ss:nothing

; --------------------------------------------------------------------
;	Note that this program is assumed to be loaded at 0000 in the
;	current segment.
;
;	This overlay can be compiled with -
;
;		ML /Fe ioskel.iom ioskel.asm /link /T
;
;	An IOMAP overlay must begin with the following header:
; ---------------------------------------------------------------------


IOMAP_MAX_SIZE	equ	32767		; size of largest I/O map processor
IOMAP_SIGNATURE equ	'MI'		; IO Map signature

;	Layout of an IOMap comm block.

IOMAPB		struc
iom_ident	dw	?		; identifier of IOM
iom_init	dw	?		; offset of initialization routine
iom_deinit	dw	?		; offset of de-init routine
iom_input	dw	?		; offset of input routine
iom_output	dw	?		; offset of output routine
iom_interrupt	dd	?		; interrupt callback vector
iom_reserved	dw	8 dup (?)	; reserved, zero
IOMAPB		ends

;	Note that routines not used need not have "stubs"; an all-zero
;	offset will be ignored by 22Nice.

	IOMAPB	<IOMAP_SIGNATURE, offset Init, offset DeInit, offset Input,\
		 offset Output, 0>

;	The Init routine is called before any program code is actually
;	executed.

Init		proc	far
	ret
Init		endp

;	The DeInit routine is called after the program execution has
;	terminated.  Both normal and error termination go through here.

DeInit		proc	far
	ret
DeInit		endp

;	I/O processors have the I/O port in (bl) and the data byte in (al).
;	Any registers may be used, but the stack must be preserved.  Output
;	preserves (AL), and Input expects the I/O data to be returned in
;	(AL).

Input		proc	far
	ret
Input		endp

Output		proc	far
	ret
Output		endp

;	The interrupt callback vector is used by 80x86 interrupt servicers
;	that want to emulate a Z-80 or 8080 interrupt.	To enable the
;	interrupt handling, the iom_interrupt field in the IOMAPB block
;	must be assembled as non-zero to show that interrupt emulation
;	processing is required by this map.
;
;	After 22nice has completed loading the emulated program, a
;	doubleword pointer to a two-byte word will be inserted in the
;	iom_interrupt field.
;
;	When an interrupt is processed by the 80x86 handler, a Z-80 or
;	8080 interrupt may be emulated by storing the Z-80 or 8080
;	P-counter value into the word pointed to by iom_interrupt.
;	The emulation software will read this value, simulate an
;	interrupt and set the value pointed to by iom_interrupt to zero.
;	This means that you can interrupt to any location except 0000.

	end

Of course, the casual user didn't need this
 
Even in V20 emulated mode, we provided a few RST servicers for peek/poke/in/out to x86 memory and I/O ports. But users didn't want to go through their code to find the references and instructions, so the above was devised. The emulator turned out to be a big pile of assembly code--about 18K lines of it--lots of special-request stuff, such as a user-definable keyboard map. Even so, it's not a single emulator:
Code:
;       CPU_Mode says what we need to do -
;
;       00 - run using V20
;       01 - run using Z80
;       02 - run using 8080 emulator
;       03 - run using test processor
;       04 - run, emulate 8080, auto-switch to Z80 if need be
;       8x - run, use "x" as processor, and use I/O map
 
I've got a legit copy of it here--came with my Matchpoint PC. It did the job for most. Initially, I was after using the V20 8086 mode, but then once the PC AT made an appearance, I left the code in but went for full emulation.

I see, so the initial starting point was probably different, then. Your system also seems to have grown to cover most of the different ways the problem could be tackled save bundling your own hardware with it.

Even then, there were copro Z80 ISA cards available, so I thought that I had to do a bit more--simply being able to run CP/M ordinary code wasn't going to get there.

Yes, even Micro Solutions made a version of UniDOS that interfaced with a bundled Z80 Co-Processor card. So, clearly, this was a trend towards "better" hardware-based solutions. The drawback being that you couldn't run your CP/M software in a portable way. If you put the programs on the same disk as 22NICE, you could run them from practically any IBM. But the hardware-based emulators were fairly stationary, only being able to run on the computer with the ISA card. (You could install cards in more computers, but now you are running into hardware problems and the whole process is getting more expensive.)

So, I put some extra features in where a user could include his own x86 handlers for I/O devices. It was useful when replacing x80 PLCs on industrial gear.

I hadn't realized the industrial angle before. Looking back now, it seems obvious.

Thanks for the explanation. I hope some of this gave insight to the OP about what kinds of things to consider for running CP/M programs in PDOS.
 
As a side issue, I wanted to share files as well, rather than dividing CP/M files off into their own little patch of Paradise. The low-number DOS calls had problems with certain aspects of file handling, particularly on FAT32 volumes (size), so that part of the CP/M BDOS was re-cast into handle-based DOS calls. 22nice keeps track of open files and makes sure that everything gets cleaned up nicely when the x80 program exits. It also checks for DOS-illegal characters and translates them on the fly. CP/M allowed pretty much anything in file names; DOS doesn't work that way.

It's a real hoot running old CP/M code on a 64 bit Linux system, running a DOS emulator.
 
22nice [...] also checks for DOS-illegal characters and translates them on the fly. CP/M allowed pretty much anything in file names; DOS doesn't work that way.

That was one thing UniDOS didn't do. It was really interesting having a CP/M file with a "=" , for example, in its name on a DOS disk. That proved challenging to access. Later versions of DOS did allow access to it by just enclosing the whole filename in double quotes, but you usually had to run a CP/M program to rename the file to something both OS's could access prior to that.

It's a real hoot running old CP/M code on a 64 bit Linux system, running a DOS emulator.

And since you're one to use old software to get work done, not just play around, it must be nice not having to find a modern equivalent to the programs you started using 40+ years ago. To use programs that function in exactly the same way with all the known workarounds and no learning how to do the same things with different commands over and over again.
 
I've got a pretty good collection, so I'm not worried.

Regardless of platform, however, finding upgrade stuff can be hard. For example, I have an couple of old designs I did with SCHEMA SDT done on a 5160. Never have found any more recent tool that understands the format. D-sized schematics were printed on an Epson MX-100 using standard greenbar 14" tractor-feed stock and then taped together. I don't think I hung on to the executable binaries.
 
But since posting that, I'm now leaning towards a variant of the other possibility I mentioned - bringing MSDOS 2.0 (sort of) to the 8080.

Assuming I'm correctly understanding your meaning (especially given the prior mention of MSDOS 2.0 API, and directories) then that has already been done by MSX-DOS. Version 1 was essentially MSDOS 1 (including FAT disks) for Z80 (hence very similar to CP/M 2.x)

Then MSX-DOS version 2 added MSDOS 2.0 like capabilities, including sub-directories, and file handle based APIs.

See http://map.grauw.nl/resources/dos2_functioncalls.php, https://map.grauw.nl/resources/dos2_environment.php, and surrounding pages. Only a couple look awkward, being defined in terms of IX and/or IY.

I've no idea how many available programs make use of those APIs...
 
Then MSX-DOS version 2 added MSDOS 2.0 like capabilities, including sub-directories, and file handle based APIs.

Thanks for that.

See http://map.grauw.nl/resources/dos2_functioncalls.php, https://map.grauw.nl/resources/dos2_environment.php, and surrounding pages. Only a couple look awkward, being defined in terms of IX and/or IY.

After some research I found out (I believe) what the IX and IY references were. Those are Z80 extensions (extra registers) to the 8080.

That made me realize that there is no particular reason for me to be using the 8080 instead of the Z80 as I would be writing in C anyway. So I was looking closer at what I needed to do to run MSX-DOS.

Then sometime later I realized that since I want to implement the PDOS-generic API, it didn't matter which processor I used, e.g. 6502, and I also didn't need to be exactly the same as MSX DOS although I am roughly having a 1:1 relationship with those MSX DOS 2.0 file i/o calls, there is an unchanging C wrapper on top of them anyway.

And so I may as well choose the processor that I have the real hardware for - ie the NEC V20. So I've done a full circle and back to where I started. :)

I've no idea how many available programs make use of those APIs...

I don't need to run existing programs. I am bringing my own C code.

The main thing everything hinges on is my C library (PDPCLIB). That does seem to compile to code under 64k, so I really need to try it out and see what happens with SDCC or whatever.

I'm currently polishing off supporting running OS/2 binaries under PDOS/386 (or PDOS-generic), and once that is done I should probably look at some 8-bit C programming (I only ever did 8-bit assembler and BASIC on the Commodore 64 - I did do some Z80 C programming commercially though).
 
Back
Top