R60 Z-System Corner -- Issue 53 Implementing a Keyboard Buffer Using the NZCOM Virtual BIOS Jay Sage First Some Personal Comments Until I was 27 years old and met my wife, I had¨ essentially never been out of the United States, and the¨ same is true of my parents to this day. My whole family had¨ the common American myopia that pictures the whole world as¨ thinking and acting pretty much the same way we do. My wife, on the other hand, came from a very different¨ background. She was born in one country and spent several¨ years living in a second before she came to the United¨ States. Her father's history was similar. When my wife was¨ born, he was living in his fourth country, and today he¨ lives in his sixth! It was not until our honeymoon that I made the dramatic¨ step of leaving North America and setting foot on another¨ continent. My life and outlook were irrevocably altered. A¨ few years later when my employer offered me the opportunity¨ to go to Japan and spend a year working at the Toshiba¨ Central Research Laboratory, I jumped at the opportunity. It¨ was a splendid year, and I loved Japan. The very different¨ culture, however, helped me see my own culture in a way that¨ was probably not possible without that experience. I came¨ back with an enormously greater appreciation for what my own¨ country offers, an appreciation that continues unabated. My wife, keenly aware of the importance of a world¨ perspective, wanted to get our children started early. She¨ had the idea that, rather than simply visiting with her¨ parents in Switzerland, we should take the opportunity to¨ expose our children deeply to European culture by enrolling¨ them in the public school there. (The school calendar in¨ Europe is different, and our children can get in about six¨ weeks of school after their school here ends.) Since we preferred that the children learn a language¨ more widely spoken than a Swiss dialect of German, we¨ decided to rent a vacation house in the mountains of the¨ nearby Black Forest in Germany. This is a wonderful place to¨ spend a vacation, and the children learn the language that¨ my wife's family has used since coming to the United States.¨ Last summer was the sixth year, and I am always thrilled to¨ hear the children talking comfortably with their German¨ friends. Last summer, rather than sleep late and have us¨ drive her to school, my 11-year-old daughter preferred to¨ get up early enough to catch the school bus at 6:30 am (!)¨ so that she could spend the time with her friends on the¨ bus. Computer User Groups in Germany Now we come to the first of the connections between this¨ story and my TCJ column for this issue. In the first few¨ years I was in Europe, I tried very hard to promote Z-System¨ there, but it was a very frustrating experience. I knew that¨ MS-DOS computers had not yet made the same inroads that they¨ had here and that even businesses were still using CP/M¨ computers. However, in Germany and Switzerland, the culture¨ apparently did not lend itself to the formation of user¨ groups as in the United States (and England and Holland). I¨ was able to find a few individuals interested in 8-bit¨ computing, but no user groups. Those individuals confirmed¨ my impression; they, too, felt all alone. One year I travelled to Munich to visit a fellow owner of¨ a BigBoard I computer. I learned of him when he wrote a¨ letter to MicroCornucopia magazine. While in Munich, I¨ wandered into computer stores, each time asking if they knew¨ of any CP/M clubs in Germany. The answer was always no. I¨ did find one salesman, Zvonimir Racic, who was interested¨ enough to start one, but it did not last even to the next¨ summer. I have had no further contact with him. Since I was having no luck searching for 8-bit¨ enthusiasts in person, I decided to try another approach.¨ With great effort (and help from my wife and a German¨ colleague at work here), I composed a letter-to-the-editor¨ in German to one of the major German hobbyist magazines.¨ They never even acknowledged it. At that point I gave up¨ trying. ZedFest Germany! Given those earlier experiences, you can imagine how¨ excited I was this last summer to be at the first European¨ Z-System Festival. In the small town of Brackenheim,¨ Germany, near Stuttgart, a small but growing group of 8-bit¨ activists got together to make plans, to share viewpoints,¨ and, most importantly, to meet each other face to face. It all started when Uwe Herczeg, at whose computer store¨ in Brackenheim the meeting was held, decided to put a small¨ ad in "Computer Flohmarkt" (Computer Fleamarket), a¨ newsprint magazine filled almost entirely with classified¨ ads. His ad sought contact with any other CP/M computerists¨ who might still be left in Germany. Amazingly, he received¨ more than one hundred responses! Among them were the people¨ at the ZedFest, who form the core of the CP/M activist¨ community in Germany today. The man I was most eager to meet was Helmut Jungkunz. I¨ no longer remember exactly how we first came into contact¨ (maybe he will tell some stories some day here in TCJ), but¨ we had been in very active communication for over a year.¨ Helmut is the 'nuclear reactor' that powers the 8-bit¨ community in Germany. He introduced Z-System to Germany, is¨ sysop of Z-Node #51 in Munich, is the only Z-System dealer¨ in Europe, and heads the Schneider/Amstrad CPC User Group.¨ If only I had run into Helmut years before when I was in¨ Munich! Also at ZedFest Germany was Tilmann Reh, whose articles¨ on the Z280-based CPU280 computer you have seen in this and¨ the previous issue of TCJ. I had communicated with him¨ several times over the Internet, and I was eager to meet¨ him. He travelled to the meeting by motorcycle and was quite¨ a sight in his sleek, black leather riding outfit and space­ age helmet. Tilmann brought along a CPU280 card to show me. It was¨ hard to believe that such a powerful computer could fit on¨ such a tiny card! Of greater interest to others at the¨ meeting (they were almost all using the CPU280 already!) was¨ the prototype he brought of the IDE disk controller for use¨ with the CPU280. Uwe Herczeg was working on the software to¨ integrate it into the system. Others present were as follows: Fritz Chwolka, head of¨ Club 80 in Aachen, on the western border of Germany near¨ Belgium and Holland; Herbert Oppmann, member of the MTX User¨ Group, which now supports ZCPR33 for the MTX computers;¨ Andreas Kisslinger, also from Munich, well at home with¨ several operating systems, and an expert Z80 programmer; and¨ Guenther Schock, a hardware expert whose projects, such as¨ an LCD terminal and a CP/M-Plus laptop computer, we may soon¨ read about in TCJ. Finally, I was especially flattered by two hobbyists who¨ came from very far away to meet me. Juergen Peters, a¨ mainframe hardware engineer by profession, drove down from¨ Hamburg in the far north of Germany. Franz Moessl's trip¨ seemed the most impressive, even though the actual distance¨ was considerably less than from Hamburg. He came all the way¨ from Italy, where he lives in 8-bit isolation in the Tirol,¨ a part of northern Italy adjacent to Austria. For two days, we all had a wonderful time talking¨ computers and socializing (and, of course, drinking beer!).¨ During one of the discussions I was told about an important¨ issue that constitutes the second connection between¨ computers and my personal comments earlier. Programming for Compatibility -- Again! In my previous column I talked about making Z-System¨ programs compatible -- or at least tolerant -- of vanilla¨ CP/M. Ideally, Z-System programs would work to the extent¨ possible in whatever environment they found themselves; at¨ the least they would terminate in a graceful manner. It is¨ too early to tell how responsive the community will be to my¨ message, but the initial indications are very positive.¨ Howard Goldstein, after proofreading the draft of my column,¨ immediately fixed up LPUT and LBREXT. As soon as Rob¨ Friefeld received and read my column, he started to look at¨ his programs. There is a second issue in programming for compatibility,¨ and that is programming for compatibility in culture and¨ language. For years, we in the United States have been¨ writing our programs with only our own keyboards, our own¨ screens, and our own language in mind. We did not do this¨ out of disrespect or deliberate disregard; we did it because¨ nothing made us think of a more cosmopolitan picture. In our¨ experience, only Americans were using Z-System. That has now¨ changed, and we need to change. I won't be able to cover all the implications of this¨ here. For one thing, I do not yet know myself what all the¨ implications are. We will need to communicate with and learn¨ from others in the world who are using and developing 8-bit¨ computers. There are two issues that I will mention here. The most¨ important one is the use of special characters. In the¨ United States, we are quite accustomed to using various¨ special characters, often for pseudo-graphics. For example,¨ we may use the verticule (vertical bar) character (ASCII 7C)¨ as a separator. Most European languages (and probably¨ non_European languages, too) have characters with accents,¨ and these are represented by the ASCII codes for special¨ characters. In Germany, for example, the following¨ associations are apparently used: [ upper case A with umlaut \ upper case O with umlaut ] upper case U with umlaut { lower case a with umlaut | lower case o with umlaut } lower case u with umlaut When we include those characters in our programs, the screen¨ displays in Europe (and Canada, for that matter) are often¨ not pleasant. The second issue concerns language. For example,¨ programmers in the United States generally assume that¨ yes/no questions will be answered with 'Y' or 'N'. In¨ Germany, however, it would be nicer if 'J' (for 'ja') and¨ 'N' (for 'nein') could be used. In France it would be 'O'¨ (for 'oui') and 'N' (for 'non'). We also, of course, display¨ all screen information, including prompts, in English. Is there anything we can do to be more accommodating in¨ these matters? I'm not sure about the whole solution, but I¨ have some ideas. For yes/no questions, we could allow for¨ all common language possibilities: 'Y', 'J', 'O', and 'S'¨ for the affirmative, for example (do we need anything other¨ than 'N' for the negative?). Another possibility is that we¨ include a language configuration screen with the ZCNFG CFG¨ file provided with programs. Simple language changes, such¨ as the letters used for yes and no and the characters used¨ for special functions, could be changed using this screen.¨ ZCNFG might be able to handle more significant text items¨ for programs with little text output. When a program provides a lot of text output to the¨ screen, there may be too many changes to handle with ZCNFG,¨ and we might have to go back to the old overlay method of¨ configuration. Our programs could come with source code for¨ language overlays. Authors with skills in other languages¨ might even take a stab at providing some of these overlays¨ themselves. Otherwise, native speakers in various countries¨ could provide the overlays. To make it easier to use such overlays, one would want to¨ collect all the messages that a program uses in one area¨ rather than scattering them throughout the program as we¨ generally do today. Rather than using SYSLIB routines like¨ PRINT that display characters provided in-line in the code,¨ we should use routines like PSTR that display characters¨ pointed to by a register pair. Such source code is not as¨ convenient to read, but it would make patching much easier¨ (and it makes debugging easier, too). Another small detail¨ would be to provide some extra spacing between message¨ strings in case a message in another language is longer.¨ Except in very rare instances, the slightly longer code that¨ results from this approach will not be an undue burden. New Z-Nodes Before turning to the main technical subject, I would¨ like to announce four new Z-Nodes that joined in the past¨ two months. Dave Chapman in Victoria, British Columbia¨ (604-380-0007), and Terry Bendell in Collingwood, Ontario¨ (705-444-9234) are our two new nodes in Canada. Ewen¨ McNeill has established the first Z-Node in New Zealand. His¨ node is running, I believe, on a mainframe computer. Those¨ who call in at 64-4-389-5478 in Wellington will learn about¨ other ways to connect to the system. Finally, we also have a¨ new node in the United States. The Kaypro Club of St. Louis¨ has turned its system, run by sysop Bob Rosenfeld, into a Z­ Node. It can be reached at 314-821-0638 on the MOSLO/24¨ outdial of PC-Pursuit. Give these new nodes a call and¨ welcome them to our ranks. An NZCOM Virtual BIOS Keyboard Buffer Z-System's extended batch processor, ZEX, is a very¨ powerful tool with many fascinating and effective uses. Like¨ SUBMIT or the MS-DOS batch facility, it can carry out a¨ sequence of commands. However, if this is all one needs,¨ then alias scripts should almost always be used instead,¨ since they impose no additional system overhead. ZEX, because of all the powerful features it provides,¨ takes up quite a lot of memory. I just ran the utility¨ TPA.COM on my system. Invoked from the command line, it¨ reported a TPA of 51.5K; running it under ZEX gave a figure¨ of 47.5K. Thus ZEX cost 4K of memory: 2K for its own code¨ and 2K because its RSX (resident system extension) locked in¨ the 2K command processor. The situation where ZEX has been indispensible is when¨ one wants a script not only to invoke commands but also to¨ provide 'interactive' input to a program. If programs can¨ accept input on the command line, then an alias script can¨ handle the situation, but many programs take their input¨ only interactively. Once ZEX is loaded, it remains in memory until all¨ commands in its script have been totally completed. There¨ have been numerous occasions when I have wanted to feed just¨ a short string of characters to a program before I proceed¨ manually. In such a case, I really hated to suffer the¨ memory penalty of ZEX to accomplish this. In some cases,¨ such as with my database manager, the program would not be¨ able to run with ZEX in place. The solution I present here provides yet another example¨ of the power of the NZCOM virtual BIOS. I simply added a¨ little code to my normal virtual BIOS to implement a¨ keyboard buffer, and then I wrote a utility to fill that¨ buffer. The BIOS I called KEYBIOS; the utility I called¨ KEYIN. A typical command line (probably in an alias) would¨ look like: KEYIN string for program;PROGRAM KEYIN fills the keyboard buffer. Then, when PROGRAM runs and¨ requests user input, the KEYBIOS sees characters in the¨ buffer and returns them to the program. How this works will¨ be clearer after you see the code for KEYBIOS. KEYBIOS The new code contained in the virtual BIOS to support the¨ keyboard buffer is shown in Listing 1. Here is what the code¨ has to accomplish. When a program calls the BIOS to get a¨ character, the virtual BIOS must first look in the keyboard¨ buffer. If it finds a character there, then it returns that¨ character and sets its pointer to the next character. If the¨ keyboard buffer is empty, then the code simply passes the¨ job on to the real BIOS, which will return a character¨ actually typed at the keyboard. One complication is that the BIOS also supports a¨ function (called console status) that asks if there is a¨ character ready without actually fetching it. We have to¨ fake out that call, too. We follow a similar strategy. We¨ first look in the keyboard buffer. If there is a character¨ there, then we report back that a character is ready. If¨ there is no character in the buffer, then we pass the job on¨ to the console status routine in the real BIOS. It is¨ amazingly simple! How do we do all this? Well, I think the code in Listing¨ 1, with all its comments, is fairly clear. There are just a¨ couple of things I would like to elaborate on. The code includes two items that are not actually needed¨ by KEYBIOS for its functioning. First, the code includes a¨ signature string, 'KEYIN', at an established location. This¨ allows a utility, such as the KEYIN.COM program that we will¨ discuss in more detail shortly, to determine that the¨ appropriate VBIOS is present. Following the signature string¨ is a byte containing the length of the buffer. KEYIN.COM¨ needs this to know how much space is available in the¨ buffer. Without this information, it might overfill the¨ buffer and clobber code. The implementation of the buffer itself is much like the¨ multiple command line in ZCPR3. At the beginning of the¨ buffer there is a word that contains the address of the next¨ character available from the buffer. A null character (ASCII¨ value zero) is used to indicate the end of the buffer. The KEYIN Utility Listing 2 shows a very rudimentary version of the KEYIN¨ utility that is used to add characters to the keyboard¨ buffer in KEYBIOS. Again, the listing with its comments is¨ largely self-explanatory, and I will elaborate on only a few¨ issues. First, in view of my earlier discussion, I am embarrassed¨ that this code does not have the facilities for language¨ invariance that I recommended. I was tempted to put them in¨ for the listing, but I decided that there would be too much¨ risk of introducing an error. Before releasing the full¨ version of the utility, I certainly will follow my own¨ advice. The code does try to be quite rigorous. Once the program¨ has displayed it signon message, it checks to see if any¨ data has been passed on the command line. If there is none,¨ a syntax message is displayed and the program terminates. In¨ the final version, the code should check for the standard Z­ System help request "//" in the command tail. Next, the code looks for the signature string at the¨ proper offset in the BIOS. If it does not find it, then an¨ appropriate message is displayed and execution terminates.¨ Otherwise, various information from the buffer header is¨ fetched and stored for later use. There may be characters already in the buffer, and KEYIN¨ is designed to retain them and to append any new input. In¨ the final version, one might want an option switch to flush¨ any characters that remain in the buffer. To make the work¨ easier, a temporary buffer in KEYIN is used to form the new¨ contents for the keyboard buffer. Therefore, we start out by¨ copying anything in the key buffer to the working buffer. Next we append characters from the command tail. In the¨ final version of KEYIN, the code should include all the¨ special string interpretation techniques used in ECHO.COM so¨ that control characters and other special characters that¨ cannot be entered directly in the command tail (such as¨ semicolons) can be included. Since the structure of KEYBIOS is such that the buffer¨ can never be longer than 255 characters, we monitor the¨ number of characters in the working buffer and abort if the¨ count exceeds that value. Once the working buffer is¨ completely filled, then we check the actual character¨ account against the actual size of the keyboard buffer. If¨ all the characters will fit, we move them into the buffer¨ and set the pointer to the beginning of the buffer. KEYIN¨ can then return control to the command processor. If the characters will not all fit in the buffer, then we¨ have to do something else. In the simple version in Listing¨ 2, the program simply gives an error message and aborts.¨ This leaves the key buffer unchanged. In a fully developed¨ version of KEYIN, the error handler should be called so that¨ the user can decide how to deal with the problem. If no¨ error handler is available, then we have a more difficult¨ problem. One course of action would be to clear the key¨ buffer and flush the entire command line buffer (and¨ terminate ZEX if it is running). Another possibility might¨ be to clear the key buffer but allow the subsequent commands¨ in the command line buffer (or ZEX) to run. The user would¨ then have to do manually what KEYIN was trying to care of¨ for the user. Very Important Caveats As it turns out, dealing with characters at the BIOS¨ level, as we do with KEYBIOS, involves some troublesome¨ issues. When I first got KEYBIOS running, I was surprised¨ that I always lost the first character that I put into the¨ buffer. That missing character then appeared the next time¨ the command processor prompt appeared. Many of you have¨ probably seen a mysterious character like this, and you may¨ have recognized it as something you typed earlier that was¨ ignored. It is beyond the scope of this article to deal with this¨ issue fully. Basically, it derives from a fundamental flaw¨ in the design of CP/M. As we have seen, CP/M has a function¨ to get a character from the BIOS and to ask the BIOS if a¨ character is ready. But there is no way to ask what the¨ character is without actually fetching it. Why is that a problem? Well, the disk operating system¨ (BDOS or equivalent) often provides special processing when¨ the characters control-C, control-S, or control-P are¨ pressed. Unfortunately, as we just noted, it has no way of¨ finding out if one of those characters has been pressed¨ without reading the next character. If the character turns¨ out to be one of the three special ones, all is well; it¨ processes the character. But what if it is some other character. The BDOS would¨ really like to say, "so sorry, not for me" and put the¨ character back for the application program to read it. But¨ it can't do that. So it does the next best thing. It puts it¨ into a special buffer in the BDOS, and the BDOS plays the¨ same kind of trick that we do in KEYBIOS. When a program¨ asks for a character, BDOS first checks its special one­ character buffer. When programs perform all their character I/O using the¨ BDOS or all of their I/O using the BIOS, then things will¨ work reasonably well. However, if calls to the BDOS and BIOS¨ are mixed, then characters can get lost in the BDOS buffer¨ only to appear later when not wanted. Every time I tried using KEYIN, I found that the command¨ processor swallowed a character when it took control after¨ KEYIN was finished. The pragmatic solution was to include a¨ backspace character as the first one in the keyboard buffer,¨ since the command processor would ignore it. There are some other issues that KEYBIOS does not¨ address. For example, some programs begin by flushing¨ characters from the BIOS. Here is how they do it. They call¨ CONST, and if there is a character they read it. This is¨ repeated until CONST reports that no characters remain. Such¨ a program will completely defeat KEYIN/KEYBIOS. Joe Wright¨ has addressed these issues very carefully and cleverly in¨ his IOPs (such as NuKey). KEYBIOS, however, is meant to be a¨ very simple, minimal-memory solution, and it seems to do the¨ job in my applications.