Im Magazin „The Computer Journal" wurde in der Ausgabe Nummer 14 vom Oktober 1984 der folgende Artikel abgedruckt.
Der erste Artikel einer Serie über die Programmierung des Hayes Micromodem II für die 6502 (Apple II+)
|
|
Controlling the Hayes Micromodem II from Assembly Language
In this article and the one that follows, we will be examining the Hayes Micromodem ® II (or He), learning how to access and control it from assembly language, and in the process, writing a complete terminal program.
Our program will have a large capture buffer (35K!), and will enable us to download text and programs from bulletin board systems (BBS) and other computers, and upload text and programs directly from floppy disk.
This will allow uploading of programs of any length, as long as they will fit on a floppy!
You will need an Apple® II, II+ , or IIe, one disk drive, DOS 3.3, and a Hayes MicromodemII or IIe.
An assembler of some kind would be very helpful.
Source code in this article will be presented in the S-C Assembler format.
For those without assemblers, I will give you a complete hex dump of the program, so you can just type it in from the monitor if you choose.
This will allow you to use it as is, although modifying it would be difficult.
The program will be presented in sections, for the sake of clarity.
As you examine
Listing 1, you will notice some lines missing.
These will be filled in later, in part two of this article.
This time, we'll examine the dialing routine, the main program loop (incoming data and keyboard output), the command handler, and toggling the capture buffer.
I'm assuming a reasonable knowledge of assembly language in this article.
If you find that it is over your head, I suggest the following references:
- Assembly Lines: The Book by Roger Wagner, pub. by Softalk Books. †
- Assembly Cookbook by Don Lancaster, pub by Howard Sams.
- Apple Assembly Lines, published monthly by S-C Software Corp.
I also write a monthly beginners' column for Scarlett, the Big Red Apple Club newsletter, so that might help you out.
Dialing the Phone
Before we can do anything, we must dial the phone, right?
At the beginning of
Listing 1, in line 1940, you'll see a
JSR DIALUP
.
After program initialization (which we'll cover next time) this is the first routine.
The routine itself is in lines 5710-6390.
First, we clear the screen with a
JSR HOME
.
This is the monitor routine commonly used, at $FC68.
Then we tab down to line 5, using another monitor routine,
VTAB
.
Then we
JSR
to our own
PRINT
routine (we'll examine it in detail next time) which grabs the ASCII in line 5760 and puts it on the screen.
Then we
JSR
to our
INPUT
routine (also, next time) and get the phone number from the user.
The number is stored in memory, and
IPTR
is left pointing to the address.
Now we come to the heart of the dialup routine.
In order to pass commands to DOS, it is necessary to output a CTRL-D followed by the command at the beginning of a line.
From assembly, this is easily accomplished by using the COUT
routine built into the Apple monitor.
Our print routine uses COUT
, so that makes things easy.
In lines 6250-6280 we use our print routine to print
(the
CR
means carriage return, $8D), which tells DOS to turn on slot 2 for output.
DOS takes care of the details.
Then we print "CTRL-Q", which is now output to the modem, and tells it to dial the upcoming number.
These are simply the same commands you would issue from the keyboard if you were doing it manually.
In my Hayes manual, this information is on page 12.
Then in lines 6300-6340 we get the phone number from where we stored it in memory, and output it to the modem, which obligingly picks up the phone and dials the number.
Easy, huh?
Then we print a
CR
"CTRL-D PR#0"
CR
to turn the modem off for output (this does not hang up the phone!).
The program returns now to line 1950, where we JSR CDETECT
to see if a carrier is present.
If not, we start over.
If we have a carrier, we continue to line 1960, where we enable the modem for input with
.
Let's look at the carrier detect routine, in lines 7310-7370.
Here we use one of the modem registers, CR1.
Bit 3 is the "No carrier detect" flag, in other words, if bit 3 is set, no carrier is present.
So, if we load the accumulator with CR1, and then AND
it with #$04
, the result will be zero unless bit 3 is set, in which case the BNE
in line 7350 will branch over the RTS
in 7360, and JMP
to the start of the program.
What are modem registers, you ask?
Read on.
Modem Registers
Controlling the MMII is comparatively easy.
There are two registers we need to be concerned with, called CR1 and CR2.
CR1 is the status register, and gives us information on the incoming data, as well as various error conditions.
For our purposes, it is only necessary to understand that if bit one of CR1 is on, then a character is ready in the input port, which is at CR1 + 1.
CR1 is also used to detect the carrier, as we noticed above.
CR2 is the modem control port, and controls whether the modem is on line or off line, and can be used to set the mode, turn on the transmitter, etc.
We use CR2 to hang up the phone, in lines 3580-3610, by simply storing a zero there.
Fortunately for us, the Micromodem firmware takes care of the rest.
The actual memory locations for these registers are slot-dependent.
I will present the program to you configured for slot 2, and I'll give you a configuration program which will allow you to modify it for any slot you choose (and also to select upper case only, or upper and lower case... see "
A Note on Lower Case" below).
For now, let's concentrate on CR1.
Incoming Data
In line 2040, the main loop begins with a JSR
to a routine which will display the address of the top of the capture buffer as it fills with data.
We'll look at that routine next time.
Next, we check CR1 to see if a character has been received.
The ROR
in line 2070 puts bit one in the carry, thus if the carry is clear, we know there has been no character received, and we branch to the keyboard input routine.
If the carry is set, a character has been received, and we branch to the character input routine.
The program goes around and around in this loop, checking CR1 for input, checking the keyboard for output, and handling each character accordingly.
The character input routine (CHARIN
) is in lines 2590-2890.
First, we collect the character from the input port which is located at CR1 + 1. For the sake of the Apple, we then set the high bit.
Next we check for linefeeds, and discard any we find.
Otherwise all our lines would be double-spaced.
In line 2640 we check the capture flag.
This flag will be set by the "toggle capture" part of the program, and indicates whether or not we want to capture the incoming data in the Apple's memory.
If the flag is zero, we want to capture it, and the program branches accordingly to the STORE
routine in lines 2730-2890.
Otherwise, we simply display the character on the screen in lines 2660-2720.
Notice in lines 2660-2700 we are checking for lower case characters, which will be greater than $E0
(remember the high bit is set).
If lower case is found, it is converted to upper case by subtracting $20
.
Then the Apple COUT
routine is called to output the character to the screen.
Next is the STORE
routine, which simply stores the character in the memory location pointed to by PTR
and PTR + 1
in the Apple zero page.
Next, we go through the lower case checking routine again, and display the character on the screen as before.
Then, in lines 2820-2850 we increment PTR
in preparation for the next incoming character.
Thus, all characters are simply stored sequentially in memory.
In our final program, the buffer will extend from $800
to $9000
, 35K of space in a 48K Apple, and 10K is for DOS!
See why I like assembly?
If you have a 64K Apple, you could get snazzy and use the extra RAM to create an even bigger buffer, up to 47K or so.
Or if you have a 128K Apple...
I leave it up to you.
Finally in lines 2860-2890, we check to see if the buffer is full, and if so, branch to a routine which prints a warning and stops the incoming data.
Otherwise we simply return to the main loop. The buffer-full routine is in lines 2930-3020, and works as follows:
First we put a $3F
at INVFLG
.
This results in inverse characters being printed to the screen.
Then we print "BUFFER FULL" in inverse (clever!) and set INVFLG
to $FF
, which is back to normal.
INVFLG
actually is a mask value, which is ANDed with the character before it is displayed on the screen.
Thus the $FF
results in no change, while $3F
strips off the two highest bits
(Can you guess what would happen if we used $7F
to strip off just the high bit?
Right, flashing characters!).
Next, we execute the subroutine XOFF
, which sends a CTRL-S out through the modem.
Most hosts recognize this character as a signal to stop sending data.
Thus, everything stops with "BUFFER FULL" displayed on the screen, and the program waits for you to do something about it.
In the complete program, you'll be able to either save the buffer to disk, or just zero it out.
A Note About Lower Case
As you no doubt noticed in the previous code, all lower case letters were converted to upper case for display on the Apple screen.
This is to make Big Buff compatible with regular AppleII's and II + 's.
If you have a IIe, or if you have a lower case chip installed, these routines are unnecessary.
In my efforts to make this program useful to the most people, I decided the best way to handle it was to write the program so that it always converts lower case to upper case.
Then, a simple configuration program can be written which will modify the program to use lower case on those machines which can handle it, and incidentally, modify it for a MicroModem in any slot.
I'll give you the configuration program next time.
Sending From the Keyboard
The keyboard input routine is in lines 2140-2580.
For upper case only, we simply skip over all the conversion stuff.
However, for Apple
II's or
II + 's with the popular shift key modification, we can do some conversion.
This modification simply connects a wire from the shift key to one pin of the game port.
This pin can be read from assembly language at location
$C063
.
If the value is less than
$80
, the shift key is being pressed.
With this information and a little knowledge of ASCII, it is quite easy to write an upper-lower case routine.
This routine is presented in
Listing 2.
The configuration program will poke this into the proper locations for those users who have lower case capabilities.
Notice that in lines 2160-2180 of
Listing 1 we have left space for this purpose.
Table 1 is the Apple ASCII Character set.
It differs from regular ASCII in that all the high bits are set.
Thus a capital A, which would normally be $41
, becomes $C1
.
Thisarrangement is due to the fact that the Apple displays characters in three ways: normal, inverse, and flashing.
Regular ASCII appears as flashing characters on the Apple screen (Remember, we discovered this in the "Buffer Full" routine, above).
In Big Buff, I have chosen to set all the high bits on incoming characters, and then deal with them thereafter as "high" ASCII.
Hex $80 $90 $A0 $B0 $C0 $D0 $E0 $F0
-------------------------------------------------------------------
$0- nul dle 0 @ P p
$1- soh dc1 ! 1 A Q a q
$2- stx dc2 " 2 B R b r
$3- etx dc3 # 3 C S c s
$4- eot dc4 $ 4 D T d t
$5- enq nak % 5 E U e u
$6- ack syn & 6 F V f v
$7- bel stb ' 7 G W g w
$8- bs can ( 8 H X h x
$9- ht em ) 9 I Y i y
$A- lf sub * : J Z j z
$B- vt esc + ; K [ k {
$C- ff fs , < L \ l |
$D- cr gs - = M ] m }
$E- so rs . > N ^ n ~
$F- si us / ? O _ o rub
Table 1: The ASCII Character Set
|
Looking at
Listing 2 now, in lines 1130-1150, we check for a keypress.
If there is one, we go ahead and get the character, otherwise we just jump back to the main loop.
In lines 1160-1190, we first clear the keyboard strobe, check to see if the shift key is pressed, and then branch accordingly.
Lines 1200-1250 check for the three special cases on the Apple keyboard:
shift-M, shift-N, and shift-P, which normally output ], ^ , and @ respectively.
Since we want normal capital letters, these three must be converted.
The rest of the capitals come out normally.
In lines 1270-1330, the lower case conversion takes place.
Since we only want to convert letters, we first check to make sure the character is between A and Z.
If it is, we add $20
to it, which makes it lower case.
In lines 1340-1390, the three special cases we mentioned earlier are converted.
Finally, in lines 1400-1450, we check for a back-arrow.
If we have one, we call a monitor routine at
$FC10
, which obligingly backspaces for us, and we then output a blank to clear the screen of the unwanted character.
Big Buff uses combinations of the
ESC
and one other key for all its commands.
This keeps all commands on line.
In lines 1460-1490 we check for an
ESC
key, and if it has been pressed, we branch to the command handler routine.
Otherwise, we store the character at location
$778
, called
CHAR
.
Included in the MMII firmware is a routine called OUTA
.
Like the registers, its location is slot-dependent, and we'll go into that along with the configuration program later.
OUTA
takes whatever character is in $778
and outputs it through the modem.
All we have to do, then, is put our character at $778
, and JSR OUTA
.
We will also use OUTA
in the uploading routine.
Finally in line 1500, we return to the main loop.
The Command Handler
Next, we come to the command handler, in lines 3210-3400.
As we noted before, control branches here if
ESC
is pressed.
First, the
XOFF
is sent to stop incoming data.
Then we check the next key pressed. Big Buff has seven commands, as follows:
ESC Z | | Zeroes the buffer |
ESC X | | Hangs up the phone |
ESC S | | Saves the buffer to disk |
ESC B | | Toggles the buffer on or off |
ESC U | | Uploads files from disk |
ESC C | | Catalogs the disk |
ESC R | | Reviews the buffer |
Thus, we simply check for each of these keys, and branch accordingly.
If the key pressed is none of these, the XON
is sent to start incoming data again, and we return to the main loop.
By the way, if you don't like the letters I chose, just plug in letters you like better.
I will go into how to use each command next time, when I describe how to use Big Buff.
The Capture Buffer
In the initialization part of the program, PTR
is set up to point to $800
, the beginning of the capture buffer.
Then, as we saw above, as incoming data is stored in the buffer, PTR
is incremented so that it always points to the next available memory location.
Then, all we need is LDY #0 : STA (PTR),Y
to store the incoming character.
In lines 3480-3540, the zero buffer routine simply resets PTR
to $800
, effectively "forgetting" all the data.
ESC-B takes us to line 3660, which toggles the capture buffer on and off.
First, we check CAPFLAG
.
If it does not contain $FF
, capture is on, so we go turn it off in lines 3760- 3810 by simply storing $FF
there.
This value is checked by the incoming data routine, as we saw before, and if $00
is found there, the character is stored.
In lines 3690-3750, the opposite condition is true, so we turn on the capture by putting a $00
there.
Notice in lines 3860-4030, two short routines to first save the current cursor position, then move to the top of the screen, where we print "OFF" or "ON"
depending on what's happening, and then to restore the cusor to its original location.
This is so the user can easily tell the status of the capture buffer.
You may have noticed also that each routine ends with JSR XON : JMP MLOOP
. The X-ON character is CTRL-Q, which tells the host to begin sending data again.
Remember, when ESC was pressed, we sent the X-OFF, so before returning the the main loop, we have to send X-ON.
Next Time
Next time, we'll examine the disk access routines and develop a way to save the buffer to disk, and to upload files directly from disk.
We'll also add the initialization, a way to review the buffer, input and print routines, and wrap the whole thing up.
I'll give you a complete listing, both source and hex, and describe how to use the program.
I will also be happy to supply you with the program and complete source code on disk for $10.00, to save you some typing.
Send it to me at 1543 N.E. 123 St., N. Miami, FL 33161
Listing 1
Initialization goes here...
Other stuff goes here..
|
Other stuff goes here..
|
Other stuff goes here..
|
Listing 2
|
Eingescanned von
Werner Cirsovius
Dezember 2015
© The Computer Journal