PDA

View Full Version : Trying to do serial I/O via CP/M BIOS calls



smp
February 16th, 2013, 01:03 PM
Hello all,

I have been at this for a little while now, and I am stumped.

I want to create a program on my Osborne 1 that will allow me to transfer text files into the Osborne from my PC using Hyper Terminal.

I figured that this would be a fairly simple way to get back into assembly language programming, and also get familiar with using the CP/M BIOS calls that are available. My Osborne 1 is running CP/M version 2.2.

I figured that the first step would be to get the Osborne to receive characters typed on the PC keyboard and echo them back to the PC display. I will go on to tackle creating a file on the Osborne and saving the text into it later.

So, I created this simple program:



;
; PCCOMM
; This CP/M program will communicate with a HOST via a serial port,
; take input from the HOST keyboard, and
; echo it back to the HOST display.
;

;
;Define ASCII characters used
;
LF EQU 10
CR EQU 13
;
ORG 100H
;
START:
LXI D, SIGNON ;Get sign-on message
MVI C,9 ;Print sign-on message
CALL 5
;
;Set up private stack
;
LXI H,0 ;HL=0
DAD SP ;HL=STACK FROM CP/M
SHLD STACK ;SAVE IT
LXI SP,STACK ;SP=MY STACK
;
;Set CP/M IOBYTE for I/O via serial I/O port
;
LXI H,3 ;Save current CP/M IOBYTE
MOV A,M
STA IOBYTE
MVI A,01010100B ;Bit pattern for IOBYTE
STA 3 ;Set IOBYTE
;
JMP PC$COMM
;
SIGNON:
DB CR,LF,LF
DB 'Establishing connection with',CR,LF
DB 'HOST using CP/M BIOS calls'
DB CR,LF,LF,'$'
;
;Perform serial I/O with HOST
;
PC$COMM:
MVI E,CR ;Output CR to PUNCH (send to HOST)
MVI C,4
CALL 5
MVI E,LF ;Output LF to PUNCH (send to HOST)
MVI C,4
CALL 5
;
LOOP:
MVI C,3 ;Input char from READER (read from HOST)
CALL 5
ANI 7FH ;Strip parity bit
PUSH PSW ;Save A and FLAGS
MOV E,A ;Move char to E for echo
MVI C,4 ;Output char to PUNCH (send to HOST)
CALL 5
POP PSW ;Restore A and FLAGS
CPI 40H ;Check for terminate char (40H = @)
JZ DONE ;Yes, terminate
JMP LOOP ;Else loop
;
;Done - Restore original CP/M conditions
;
DONE:
CALL EXIT
;
DB CR,LF,LF,'COMMUNICATIONS COMPLETE',CR,LF,LF,'$'
;
;EXIT PRINTING MESSAGE FOLLOWING 'CALL EXIT'
;
EXIT: POP D ;Get exit message
MVI C,9 ;Print exit message
CALL BDOS
;
;Restore CP/M IOBYTE
;
LXI H,IOBYTE ;Restore CP/M IOBYTE
MOV A,M
STA 3
;
;Restore CP/M stack
;
LHLD STACK ;Get original stack
SPHL ;Restore it
RET ;<------------ EXIT TO CP/M
;
;
;Defined storage space
;
DS 40 ;Private stack area
STACK DS 2 ;Saved CP/M STACK POINTER
IOBYTE DS 1 ;Saved CP/M IOBYTE
;
;
END
;


Let me explain the bit pattern that I am using for IOBYTE. Normally, the IOBYTE in the Osborne is 01000000. That is, the LST: device is set to be the serial output port, and the PUN: and RDR: and CON: devices are all set to be the Osborne keyboard and video display. When I do STAT DEV: I get back CON: is TTY:, RDR: is TTY:, PUN: is TTY:, and LST is CRT:. So, as you can see in my program, I am changing the IOBYTE to be 01010100, in order to point both the PUN: and RDR: devices to the serial I/O port.

All of this is actually working. My problem is that CP/M thinks that it is constantly receiving characters from the PC, when I am not typing anything on the keyboard. When I run the program, the program on the Osborne successfully signs on and transfers the Osborne input and output over to the serial I/O port, and I get a (usually short) string of garbage on the Hyper Terminal until an @ shows up which is properly recognized by the program as the terminate signal, and the program terminates and returns the Osborne to its proper operating condition.

I have tested the program by setting only the PUN: to go out to the PC but still take its input from the Osborne keyboard. That works just fine. I have also tried only setting the RDR: to come in from the PC and this ends up working in the same fashion as having both PUN: and RDR: pointed to the PC - the Osborne screen shows the string of garbage before the @ shows up and the program terminates.

Under normal operation, I can use PIP to send a file over to the PC just fine. PIP LST: = B:PCCOMM.PRN works great.

I have tried this program with my PC but not connecting Hyper Terminal first. Since Hyper Terminal is not connected, my program acts as you would expect. It signs on and sits there waiting for something to come in. When I hit the button to connect the Hyper Terminal, in comes a stream of garbage, just like before. So I am pretty sure this is coming from the PC and is not a problem in the SIO inside the Osborne.

Finally, I have tried this with two different PCs, one with a real RS-232 SIO port, and another using a USB-to-RS-232 converter for communications. Both work the same as described here with my program. I have Hyper Terminal set up with N-8-1 and using X-ON / X-OFF for flow control. I have used both of the PCs as a terminal in this manner with other vintage computers.

So, I am wondering if there's something in the serial I/O arrangement is somehow always sending a signal that makes the Osborne think that its receiving characters, even when it's not?

Does anyone else out there have any experience with using the serial I/O port on an Osborne 1 under CP/M?

Thanks, in advance, for your attention to my long post.

smp

glitch
February 16th, 2013, 01:19 PM
You probably want to turn off all flow control. Some implementations of CP/M send a bunch of NULLs when using PUN/RDR, but that doesn't really explain your receive problem. The only condition in which a machine would be sending a constant stream of "characters" while not actively sending something would be the BREAK condition.

smp
February 17th, 2013, 10:32 AM
Thanks for the thought, glitch.

I've now tried PuTTY, another terminal program, with the same results. I have also gone back over to my IMSAI and tried both PutTTY and Hyper Terminal from my mainstay PC laptop, and both work fine with the IMSAI system monitor, which uses simple 3-wire serial communication.

My conclusion at this point is that there could be a problem on the receive side within the Osborne, or this could be something to do with the serial I/O implementation within CP/M. I am supposing that, IF CP/M serial I/O looks at the DSR or DTR (whichever) line, AND IF that line is being always held high to indicate the 'ready' condition, and I am not doing anything with it, THEN perhaps CP/M is getting the indication that there is always a character available, even when there is not. That would be a plausible explanation for the garbage characters that I am seeing.

Oh, well. Unless there's someone out there with a deeper understanding of how the BIOS calls are implemented in CP/M, I may be in for a lot more investigation than I had originally bargained for.

smp

MikeS
February 17th, 2013, 11:49 AM
Using a handshake line to indicate a character ready wouldn't make much sense if you think about it; they're usually just looped back anyway.

But AFAIK Console input (INT 6) always returns a character, returning 0 if no character was actually received; if INT 5 does the same thing could you be echoing 0s (since you don't test for it) which look like garbage because of incompatible port settings?

Chuck(G)
February 17th, 2013, 12:36 PM
Are you certain that there's not a bug in the serial BIOS that always reports "data available"? Do you have source for the BIOS that you're using?

The assignment of the IOBYTE devices was far from universal--and many implementations were very buggy. Some systems didn't even bother to implement IOBYTE--it was optional, according to the System Modification Guide.

smp
February 17th, 2013, 12:50 PM
But AFAIK Console input (INT 6) always returns a character, returning 0 if no character was actually received; if INT 5 does the same thing could you be echoing 0s (since you don't test for it) which look like garbage because of incompatible port settings?

Hi Mike,

Thanks very much for your thought.

Since I can set the IOBYTE to have the RDR: input come from the Osborne keyboard, and have the PUN: output got to my PC running Hyper Terminal, and that all works correctly, I am assuming that I have the port settings correct. It's just a dirt simple N-8-1 at 1200 bps.

Also, I can print a file to the LST: device - the very same serial I/O port - and see it come up on my PC running Hyper Terminal just fine.

I have the output to the PUN: device nailed. I just do not understand why I am getting the phantom garbage characters coming in...

smp

smp
February 17th, 2013, 12:56 PM
Are you certain that there's not a bug in the serial BIOS that always reports "data available"? Do you have source for the BIOS that you're using?

The assignment of the IOBYTE devices was far from universal--and many implementations were very buggy. Some systems didn't even bother to implement IOBYTE--it was optional, according to the System Modification Guide.

Hi Chuck,

Thanks very much for your thoughts.

Yes, that's exactly what I am thinking, too. Nope, I do not have any source code for the BIOS. Sigh. IOBYTE is working properly, I believe. If I use STST to assign the devices, I can see the bit pattern change in the IOBYTE at location 3. I did some experiments to figure out what bit pattern meant what by using small assembly code programs and STAT: to figure out the 00 means the Osborne keyboard and display, and it is called TTY: by STAT:, and 01 means the serial I/O port on the 25 pin connector, and that is called CRT: by STAT:.

Yes, a bug is certainly very possible. And me with no source code to go poking around...

smp

smp
February 17th, 2013, 01:12 PM
EUREKA!!!

I have been wondering about how the Osborne 1 has both a modem port and a RS-232 serial I/O port, but only one 6850 ACIA chip...

So, I'm going over the Osborne Technical Manual, and on page 19 in Chapter 4 it says, "A close look at the circuitry in the Osborne 1 schematics will show that the modem and RS-232 interfaces are basically one and the same. In addition to the serial port, TTL level signals may be directly input into the 6850 ACIA using the modem port connection."

Yes, my Osborne 1 has a modem installed. When I disconnected the modem cable, bingo! no more garbage coming in when I run my program. Now, everything is running just as I thought it should.

Thanks very much to all who have offered their thoughts. It turns out to be a simple issue of having two devices attached simultaneously to the serial input.

Whew!

smp

MikeS
February 17th, 2013, 01:20 PM
...I have the output to the PUN: device nailed. I just do not understand why I am getting the phantom garbage characters coming in...LOL! Yeah, output is easy since you're in control; it's input, when the other end is in control, that can be tricky. I'd still try ignoring received NULs; can't hurt.

Could even be a flaky line receiver; does it use 1489s and can you easily swap in another one?

Is there a good solid common ground between the two machines?

RickNel
February 17th, 2013, 03:55 PM
Always a moment of joy when you crack something like this.

Out of curiosity - Osborne must have provided some way of handling the common inputs to the serial chip, other than physically unplugging the modem. Maybe there is a BIOS setup parameter somewhere other than the IOBYTE? Maybe some keystrokes to enable/disable modem? I'm not that familiar with Osbornes of that vintage.

Rick

smp
February 17th, 2013, 05:00 PM
Always a moment of joy when you crack something like this.

Out of curiosity - Osborne must have provided some way of handling the common inputs to the serial chip, other than physically unplugging the modem. Maybe there is a BIOS setup parameter somewhere other than the IOBYTE? Maybe some keystrokes to enable/disable modem? I'm not that familiar with Osbornes of that vintage.

Rick

Hi Rick,

Thanks.

It looks to me like Osborne did not expect to be using the modem and the serial I/O port at the same time - probably because CP/M (version 2.2) couldn't do two things at the same time. Since the serial I/O port was only supported for use connecting to a printer, it was essentially for output only and the code did not expect or look for anything coming in. The modem was bidirectional, but with nothing expected coming in from the serial I/O port it probably operated fine. However, I am working bidirectionally on the serial I/O port, and with the modem connected, I was able to see stuff on the input line from it as well as the SIO.

I think they didn't think things through all the way. And if they had, back in those days the solution probably would have been, "Just unplug the modem when you are utilizing the serial I/O port."

Anyway, I have not yet come across anything that indicates to me that there is something else to do besides unplug the modem.

Thanks!
smp

glitch
February 18th, 2013, 04:37 AM
It would most likely be relatively simple to add a relay in to connect/disconnect the modem from either a switch or a write to a hardware port. If you can dig up your Osborne's schematics you may even find a few bits of unused parallel I/O already in there.

smp
February 18th, 2013, 09:24 AM
Hello all,

I have successfully created my program to transfer a text file into my Osborne 1 using the CP/M IOBYTE and CP/M BIOS calls. I have attached the file PCGET.TXT to this post for anyone who may be interested in using it or modifying it for their own uses. On your CP/M machine, you should change the name of the file to PCGET.ASM.

A couple of comments:

The IOBYTE bit pattern that I use to set the RDR: and PUN: devices to go to the serial I/O port is most likely unique to the Osborne 1 running CP/M version 2.2. Also, you will note that I keep my system disk in drive A: write protected - that's why I stated the invocation as B:PCGET B:filename.ext.

Otherwise than that, I think this is all plain vanilla CP/M BIOS calls.

It is not an elegant program. I have transferred files with it on my Osborne 1 - at 1200 bps, there is an occasional error, mostly only a dropped character or two. However, if you do not have any other way to get a file into your computer and saved onto a disk, this could be a help for you. It is significantly better than typing in a several K assembly program, or other text file or source file that you may need.

Thanks again to all who offered their help.

smp