TweetFollow Us on Twitter

Serial I/O
Volume Number:5
Issue Number:7
Column Tag:Assembly Lab

Related Info: Serial Drivers

Cerial (Serial) I/O

By Frank Henriquez, Los Angeles, CA

Programming The Serial Ports

The Macintosh serial ports are a neglected programming subject. Few (if any) books on programming cover them, and this lack of information is compounded by an absence of source code to study.

The simplest way of programming the serial ports is to use a high level language and fool the compiler into thinking that the ports are disk files. Then you can use the language’s generic file I/O routines for serial I/O. While this solution provides a simple way of dealing with the serial ports, it may not provide the speed or flexibility that may be required in certain applications.

I thought that a better approach would be to write a set of low level routines that can be linked with compiler generated code, or with assembly language. How difficult could this be? All I’d need was the hardware manual for the Mac’s serial port chip, a copy of Inside Macintosh, a steady source of caffeine and a few hours of coding...right?

I quickly discovered that the task of writing drivers for the Mac is serious business. Life is too short for the hassles involved in trying to write a driver for the the complex (and bizarre) serial port chip, a Zilog 8530 SCC. Besides, it’s already been done; Kirk Austin’s excellent articles “A Midi Library for Pascal” and “Midi Demo Uses Midi Library” in the July, 1987 and December, 1987 issues of MacTutor show how to write drivers for this chip, if you’re interested in working close to the bare metal of the machine.

How I Learned To Stop Complaining And Love The Device Manager or “Gee, I didn’t realize it would be this easy”

Midi has certain timing and handshaking constraints that demand specialized serial drivers. I needed something a lot simpler; a set of routines that could send and receive data, at baud rates that would probably never exceed 19200 kilobaud. There had to be a way of doing this that didn’t involve writing a complex driver. As often happens when programming the Macintosh, a solution became obvious only after re-reading several chapters of Inside Macintosh (or to put it another way: “when all else fails, read the manual”). While the Serial Drivers chapter would seem like the obvious place to look for information on using the serial ports, the Device Manager chapter is far more important; the Macintosh treats the serial ports as devices, so accessing the drivers requires using the Device Manager I/O routines.

The Device Manager can be divided into Pascal based file I/O routines, and low level traps. The high level Pascal routines (they can also be called from C. But you already knew that) have an extremely simple calling sequence, and insulate the programmer from the inner workings of the Device Manager. However, since these routines are not in the ROMs, the glue routines have to be linked with any assembly language program that uses them.

The low level Device Manager traps require more work to use, but they provide greater control over the I/O. I could have used the high level routines in my program, but since I had to use a low level Device Manager trap to configure the serial port, I decided to write everything using the other low level traps; the code is smaller and probably a bit faster.

The serial ports have been assigned four driver reference numbers, which are used by the Device Manager to identify the device it is working with:

Modem port input ( port A ) -6

Modem port output ( port A ) -7

Printer port input ( port B ) -8

Printer port output ( port B ) -9

I’ve used these reference numbers directly in the code, instead of using the reference numbers returned by the _Open trap (the numbers should be the same). It simplifies the coding, at the risk of making the routines non-functional if and when Apple decides to change the reference numbers.

The Device Manager traps use a block of memory (pointed at by register A0) called a parameter block. It holds the pointers, commands and flags that may be required by the Device Manager trap being called. Depending on the call, it may also contain information returned from a device. A generic parameter block can be up to 80 bytes long, but when calling the serial ports most of the parameter block entries can be ignored. This still means that my routines have to set up and maintain a parameter block each time they are called. At first, even this small overhead in execution time and memory requirements made me uncomfortable; however this is an acceptable compromise considering the amount of code required to write a low level serial driver.

In retrospect, using the Device Manager to control the serial ports turned out to be an easy task, although I spent more time than I’m willing to admit learning how to write these serial I/O routines. In the process, I discovered that Apple provides serial port drivers in a resource called SERD (built into the ROMs of the more recent Macs). At the time I wrote my routines, SERD was not in ROM and its use was (and still is) poorly documented. It was also more code than I had expected, so I went ahead with my routines.

One of my first attempts at using the Device Manager was a simple terminal program written in Turbo Pascal. It worked, but I decided to re-write it in assembly because (I know many of you will cringe) I enjoy writing in assembly language! I realize that it’s an acquired taste and that most of you would prefer to keep your contacts with assembly language to a minimum, so these routines have been written to make them accessible from Pascal. The routines will also work with C, but you may have to modify them to match the C parameter passing protocol. I’ve also written a small terminal program that shows the routines in use. The bulk of the program consists of code to use TextEdit, manipulate the clipboard and code to do the usual housekeeping routines. You’ll almost need a magnifying glass to find the calls to the serial routines!

Serial I/O Routines

The serial I/O routines make extensive use of the stack, for parameter passing and for holding the parameter block required by the Device Manager routines they’ll be calling.

Parameter blocks are common structures, and can be found in most Macintosh I/O routines. In assembly language, we can define a parameter block as a buffer in the global variable space, or we can use the approach used by many high level language compilers and define the parameter block as a temporary structure on the stack. On entry, each routine sets aside a small section of the stack, known as a stack frame, and points to it with register A6. This private section of memory is then set aside as a parameter block.

Making the parameter block a temporary structure on the stack saves us from having to declare it as part of the global variable space; the local stack frame and all its contents are discarded when the routine terminates. Using this technique increases the complexity of the routines, but their portability and ease of use far outweighs the minor increase in execution time and code size.

Before we use a serial port, its driver must be open and the port must be configured. OpenSerial opens the specified serial port driver (either the modem or the printer port) and kills any pending I/O operations to it. The routine uses the driver’s input reference number (-6 for the modem port and -8 for the printer port) to open both the input and output sections, and expects to find the appropriate number on the stack. Figure 1 shows the local stack for the OpenSerial routine.

OpenSerial starts by setting up the stack frame and allocating the space for the parameter block. It then makes register A0 point to the parameter block, as expected by the Device Manager. OpenSerial uses the Device Manager _Open trap which at most has only three entries in it’s parameter block. Since none of these serial I/O routines are making asynchronous calls to the Device Manager, OpenSerial can ignore the ioCompletion pointer. It can also ignore the ioPermssn byte, leaving only ioNamePtr, which is filled after OpenSerial checks which port is to be opened. Since both the input and output sides of a port must be opened, OpenSerial calls the Device Manager _Open trap twice. Once this is done, the routine saves the result code (returned in the parameter block at ioResult) on the return stack and calls the Device Manager one more time, to kill any pending I/O operations to the port. Since the parameter block remains unchanged, OpenSerial can call the _KillIO trap directly. Before returning to the calling program, OpenSerial discards the local stack (and the parameter block) and clears the port number from the return stack, leaving only the result word.

The code segment below shows the calling sequence for OpenSerial (PortA refers to the modem port driver and was set to -6 by an equate statement somewhere else in the program).

;1

clr.w   -(sp)  ; room for result
move.w  #PortA,-(sp) ; modem port
jsrOpenSerial
tst.w   (sp); leave result on stack
bneAbort

Since OpenSerial is usually followed by the configuration routine, I leave the result code on the stack (it will be zero if the port opened successfully) and use it to mark the space for the the next routine’s result code. Config resets the serial port and sets it to the new baud rate, data bits, stop bits and parity desired. Config doesn’t modify some of the other important serial port parameters (mainly handshaking) since the default parameters suited my needs. Like OpenSerial, Config expects to see the port’s input driver reference number on the stack. It also expects to see a configuration word, which represents the baud rate, data bits, stop bits and parity being set. Figure 2 shows the local stack for Config and the code section below shows its calling sequence.

;2

move.w  #PortA,-(sp) ; modem port
move.w  #Config1200,-(sp)
jsrConfig
tst.w   (sp); leave result on stack
bneAbort

Config uses the Device Manager _Control trap, which requires a few more parameters than OpenSerial. Two parameters, ioCompletion and ioVRefNum (serial ports don’t have volume names) can be ignored, but it must set the ioRefNum to that of the port being configured. Config uses the csCode parameter to tell the Device Manager to reset the port. The new configuration value is passed in csParam.

For some applications, opening and configuring the serial port is all you need to do to initialize it for use. Unfortunately, the default size of input driver’s buffer (set by the Device Manager) is a tiny 64 bytes, and this small buffer can become a major problem.

We all know that TextEdit is no speed demon. If you’re writing a communications program and use TextEdit to display the incoming text, characters may be received by the port faster than TextEdit can display them. The 64 byte serial input buffer will quickly overflow and you will lose some characters. Luckily, this input buffer can be resized using SetBuf. This routine, like OpenSerial and Config, requires a reference number for the port’s input driver. It also requires a long word that represents the buffer’s new size.

SetBuf, like all the other routines, starts by setting aside some room for the parameter block. It will then request a nonrelocatable block from the Memory Manager to serve as the new input buffer. If SetBuf succeeds in getting the new block, it tells the input driver to use this buffer through the Device Manager _Control trap. Figure 3 shows the local stack for SetBuf.

;3

move.w  #PortA,-(sp) ; modem port
move.l  #512,-(sp) ; 512 byte buffer
jsrSetBuf
move.w  (sp)+,d0 ; take result off stack
bneAbort

In this example, the input buffer is made 512 bytes long. Since SetBuf is usually the last step in initializing a serial port, the result word that was floating on the stack from the previous two routines (OpenSerial and Config) finally gets popped off. As we’ve come to expect, if SetBuf has a problem allocating the memory for the buffer, this word will be non-zero.

It’s important to set the input buffer back to the default value before terminating your program (the programmer’s motto should be “always leave things the way you found them”). This can be done with SetBuf by passing a value of 0 as the size of the buffer. The Device Manager will then set the input buffer back to its default value.

Once the serial port has been opened and initialized and the size of the serial input buffer has been increased, your program is ready to send and receive data through the port.

The input routine GetSerial takes two parameters, the port number and a pointer to the buffer that will hold the incoming data (note that this isn’t the same as the input driver buffer). GetSerial first checks to see if there are any characters waiting to be read by calling SerGetBuf, which is a special version of the Device Manager _Status trap. This Device Manager call returns a long word containing the number of characters in the driver’s input buffer. If the input buffer is empty, GetSerial will leave this zero count on the stack and end. If there are characters available (and if you use TextEdit, there will be quite a few backed up), GetSerial reads them into the new buffer using the Device Manager _Read trap. GetSerial then leaves the character count on the stack and ends. Figure 4 shows the local stack for GetSerial. Here’s an example from the terminal program:

;4

clr.l -(sp) ; room for char count
move.w  #PortA,-(sp) ; use the modem port
peaIOBuf(a5); put data here
bsrGetSerial
move.l  (sp)+,CharCnt(a5)  ; save count

PutSerial simply shoves data out the port, letting the Device Manager worry about errors (like overflowing the output buffer). Since we are calling PutSerial synchronously, this is not a serious shortcoming and PutSerial will work well, without problems.

PutSerial takes three parameters, the port number, a pointer to the buffer holding the data to be sent and the number of characters to be sent. Even though PutSerial ignores errors, it returns a “no errors” result value to the calling program; this is to keep it consistent with the other routines and to make room for a real error value if a future version of the routine does check for errors. The calling sequence is pretty simple:

;5

clr.w   #0,-(sp) ; room for dummy 
move.w  #PortA,-(sp) ; modem port
peaIOBuf(a5); send data in buffer
peaCharCnt(a5) ; this many
bsrPutSerial
move.w  (sp)+,d0 ; pop dummy 

These serial I/O routines are lacking in a few features (mainly better and more complete error checking) but since the interface to the calling program (be it a high level language or assembly) is consistent, these routines can be used as the basis for more complete routines, or used as-is, without any major changes to the calling program.

Using the Serial I/O Routines in the Real World: An Example

The easiest way to learn how to use the serial I/O routines is with an example. Term is an extremely simple terminal program; it sends characters typed at the keyboard out the modem port and uses TextEdit to display the incoming characters. Besides the standard File and Edit menus, Term has a Command menu with an Erase Screen item and four (300, 1200, 2400 and 9600) baud settings. It also uses the clipboard, so you can cut and paste text to and from its window. While the text that is pasted to the terminal window is displayed, it is not sent out the port (as my old calculus book would say “the solution to this problem is left to the reader”). The main window in Term is moveable, but not resizable. This is a minor annoyance, which can be easily fixed.

Term will run on almost any Macintosh (it doesn’t run on Macs with the old 64K ROMs) and is structured to serve as the skeleton of a more complex communications program. To maximize speed and reduce the need for global variable storage, most assembly language programmers store frequently used handles and pointers in unused CPU registers. Since I don’t know what you plan to do with Term, I’ve tried to minimize the use of registers for handle storage. Most of the menu and window handles are kept as global variables.

Macintosh applications often share the same basic structure and even some of the initialization and event parsing routines. Term is no exception. It is a quilt of routines that have been borrowed from other applications. Term begins by initializing the modem port and going through the usual ritual of setting up things and initializing managers. It finishes the initialization by copying the contents of the clipboard to the TextEdit scrap. Term ends when a non-zero value is passed to the event loop, either when the user selects Quit from the menu or clicks in the program window’s close box. Term then cleans up, resets the input buffer back to its default size and returns to the Finder.

Since Macintosh programs spend most of their time in the event loop, this is the best place for Term to check the modem port and display any new characters that may have been received. You may notice that the EventRecord is declared and used as a global variable. It used to be a common practice to declare the EventRecord as a constant; this saved a few keystrokes when typing in the program. The practice did not cause problems when the program was running because the 68000 doesn’t make any distinctions between the memory used for variable storage and that used for constants and code. With the 68020, 68030 and future CPUs, memory management becomes part of the computer hardware. With an MMU, an operating system could set aside memory to be used as storage for a program’s code and constants... and declare this memory to be read only. Imagine the surprise of a program trying to write to a write-protected EventRecord!

Term Routines

Instead of a detailed description of every routine in Term, I’ll focus on the routines that make it different.

PutScreen filters the characters in the input buffer, IOBuf, and transfers the displayable characters to Outbuf, the output buffer. The variable CharCnt is used to tell TextEdit how many characters are in the output buffer.

A communications program must be able to display characters on the screen quickly, especially at the higher baud rates, when data may be flooding in through the serial port. On the Macintosh, the quickest way to get a character on the screen is to use QuickDraw’s DrawChar function, and this is what I used in the early versions of Term. Unfortunately, DrawChar does little else besides drawing a character on the screen. All text manipulations (simple things like backspace and carriage return) must be handled with a stupendous amount of code. I knew that TextEdit could handle some of these simple functions, and TextEdit had the extra bonus of making it easy to cut and paste the text to the clipboard and back. Unfortunately, TextEdit is extremely slow. My initial attempts to use TextEdit were a miserable failure; I was losing most of the incoming data. The solution, of course, was to increase the size of the serial input buffer.

While TextEdit is an improvement over DrawChar, it will only recognize a limited number of control characters, so PutScreen must deal with any control characters it finds in IOBuf. In this version of PutScreen, only the carriage return (CR), Bell, Tab and backspace (BS) characters are recognized; all other control characters are discarded. By replacing the simple compare and branch code with a lookup table, PutScreen could be made to emulate a real terminal, like the VT-52 or VT-100.

PutScreen lets TextEdit handle carriage returns. To backspace, PutScreen decrements the output buffer pointer by one (to erase the previous character) and decrements the character count by two (to account for the deleted character and the backspace character). If the character count is zero, then no characters have to be displayed, and PutScreen passes control to the NextEvent routine. If CharCnt is greater than zero, there are still characters to be processed, and PutScreen goes on to the next character in IOBuf. If the CharCnt is less than zero, we are no longer dealing with “fresh” characters and must delete a character that has already been displayed. To do this, PutScreen passes the BS character directly to the _TEKey trap, which deletes the last character displayed. It then passes control to the NextEvent loop.

If the control character is a Bell (control G) PutScreen will generate a short beep, decrement the character count, and continue with the filtering.

Tabs are hard coded for five spaces. PutScreen places five spaces in the output buffer and increments the character count by four (we don’t want the tab character itself to be displayed) before continuing with the filtering.

Displayable characters are transferred from the IOBuf to the OutBuf, and the filtering continues until all the characters have been dealt with. PutScreen then uses _TEInsert to pass the OutBuf to TextEdit, which displays it. PutScreen also calls _TESelView, to scroll the window, if needed.

KeyDown takes characters typed at the keyboard and saves them in IOBuf. If the command key is pressed, it converts the character to a control character, and inserts it into the IOBuf. KeyDown then sends the character out the modem port.

InBaud is used to change the baud rate. This routine unchecks the previous menu item, checks the new one and sets the new baud rate, using the Config routine.

Possible Enhancements

Like most programs, Term has room for improvements. The easiest additions would be to make the window resizable and make the program MultiFinder friendly.

Term will operate at 19,200 baud; I was just too lazy to add it as a menu option. A menu option to change the default port might be useful, but dangerous. Since the printer port is used with networks and printers, the routine to switch ports must check to see if AppleTalk is active or if the printer port is being used by another program.

A slightly more difficult task would be to have Term send text pasted in it’s window, from the Clipboard, out the serial port. Another TextEdit related problem is a cosmetic one; I implemented a screen erase function in Term by clearing out the all the data in the TextEdit record. It works, but it makes the screen flash like an IBM PC screen...somewhat disconcerting.

A final improvement would be to add a file transferring capability, using the MacBinary extension to the XMODEM protocol. This wouldn’t be as difficult as it sounds; both the MacBinary and XMODEM protocols are well described. It would be a good idea to increase the input serial buffer to at least 1280 bytes, room enough to fit a 1K XMODEM block. This would speed up the data rates by allowing the program to work on one block of data while another block is being received in the background.

Borrowed Routines

A few of the routines and techniques that I used in Term were borrowed from other programs.

The code to deal with the clipboard is a slightly modified version of the code in the Clipboard chapter in Dan Weston’s “The Complete Book of Macintosh Assembly Language Programming, Volume II.” Some of the ideas on how to handle TextEdit came from Dan’s “The Complete Book of Macintosh Assembly Language Programming, Volume I.” It should be pretty obvious that these are two very useful (if somewhat dated) books on Macintosh assembly language programming.

Victor Barger’s Rose Curve Generator program is an excellent example of good code layout and presentation. I’ve tried to make Term as easy to read.

References

Dan Weston, “The Complete Book of Macintosh Assembly Language Programming” Volumes I and II.

Victor Barger, “Rose Curve Generator,” MacTutor, January 1987

Apple Computer, “Inside Macintosh” volumes I - V (mainly volumes I and II)

Apple Computer, Technical Note 130, ioCompletion

Kirk Austin, “A MIDI Library for Pascal,” MacTutor, July 1987

Kirk Austin, “MIDI Demo uses MIDI Library,” MacTutor, December 1987

Listing: term.job

asmterm.asm exec edit
link  term.link  execedit
rmaker  term.r termedit
Listing: term.link
 
/Output term.code

/Type ‘TEMP’ ‘????’

term
SerI/O

;All done
/End    
Listing:  SerI/O.asm

; File: SerI/O.asm
;------------------------------------------------------------------
; SerI/O.asm consists of following stand-alone serial I/O routines:
;
; FunctionOpenSerial(Port: integer): OSErr
; FunctionConfig(Port, SerVal: integer): OSErr
; FunctionGetSerial(Port: integer; buffer: pointer): CharCnt
; FunctionPutSerial(Port: integer; CharBuf: pointer; count: longint): 
OSErr
; FunctionSetBuf(Port: integer; BufSiz: long): OSErr
; 
; Port refers to port reference number, which is -6 for modem 
; input port (port A) or -8 for printer input port  (port B)
;
; For description of how to write modular assembly language
; routines see Weston’s “Complete Book of Macintosh Assembly
; Language Programming Volume I”, Appendix D
;
;  1/14/88Working version of the routines
;
; written by Frank Henriquez
;------------------------------------------------------------------
; Register usage:
; d0: general purpose. 
; d1, d2, d3, d4, d5, d6, d7 : not used.
;
; a0: general purpose, used for buffers and parameter blocks.
; a1: general purpose.
; a2, a3, a4: not used.
; a6: used as the local stack frame pointer
; a5, a7 : system use. 
;------------------------------------------------------------------

Include Traps.D
Include SysEqu.D

 XDEF OpenSerial
 XDEF Config
 XDEF GetSerial
 XDEF PutSerial
 XDEF SetBuf 

; equates for each stack frame

pBlock  equ -ioQelSize

OpenPar equ  2   ; OpenSerial - # of parameter words on stack
OpenRes equ 10   ; OpenSerial - offset to result word
OpenPortequ  8   ; OpenSerial - offset to Port Reference #

ConfPar equ  4   ; Config - # of parameter words on stack
ConfRes equ 12   ; Config - offset to result word
ConfPortequ 10   ; Config - offset to port Reference #
ConfVal equ  8   ; Config - offset to port config value

GetPar  equ  6   ; you’ve probably figured the pattern
GetCnt  equ 14   ; offset to character count
GetPort equ 12   ; out by now...
GetBuf  equ  8   ; offset to character buffer pointer

PutPar  equ 10
PutPort equ 16
PutBuf  equ 12   ; offset to character buffer pointer
PutCnt  equ  8   ; offset to character count pointer
PutRes  equ 18   ; dummy result 

SetPar  equ  6
SetRes  equ 14   ; offset to SetBuf result word
SetPort equ 12
SetSiz  equ  8   ; offset to new buffer size

; misc. equates

SerResetequ  8   ; from Inside Macintosh
SerSetBuf equ   9; from Inside Macintosh
ioNamePtr equ  18; missing from MDS

;-------------- OpenSerial ------------------
; Opens the modem port for reads and writes.
;
; FunctionOpenSerial(Port: integer): OSErr

OpenSerial
 link a6,#pBlock ; local space for parameter block
 lea  pBlock(a6),a0; point to parameter block
 lea  ‘.AIn’,a1  ; assume port A input
 cmpi.w #-6,OpenPort(a6)  ; open port B if not -6 (port A)
 beq.s  @1
 lea  ‘.Bin’,a1  ; open port B input
@1 bsr.sOpenit
 lea  ‘.AOut’,a1 ; assume port A output
 cmpi.w #-6,OpenPort(a6)
 beq.s  @2
 lea  ‘.Bout’,a1 ; open port B output
@2 bsr.sOpenit

OExit move.wd0,OpenRes(a6); procedure returns a 0 if no errors
 _KillIO; kill any pending calls
 unlk a6; discard local stack area
 move.l (sp)+,a0 ; get return address
 addq.l #OpenPar,sp; clean up stack
 jmp  (a0); end of InitSerial

Openit  
 move.l a1,ioNamePtr(a0)  ; tell the Device Manager which
 _Open  ; port to open
 rts

;---------- Configure ports --------------
; Sets the Port to the baud rate, data length,
; parity, stops, etc. held in SerVal.
;
; FunctionConfig(Port, SerVal : integer) : OSErr

Config
 link a6,#pBlock ; local space for parameter block
 lea  pBlock(a6),a0; point to parameter block
 move.w ConfPort(a6),d0   ; get Input Reference number
 bsr.s  doConf   ; configure the input side
 tst.w  d0; check for errors and
 bne.s  CExit    ; exit with the error flag in d0
 move.w ConfPort(a6),d0   ; get Input Ref number
 subq.w #1,d0    ; and make it the output ref num
 bsr.s  doConf   ; configure the output side

CExit move.wd0,ConfRes(a6); save result
 unlk a6
 move.l (sp)+,a0
 addq.l #ConfPar,sp; and clean up the stack
 jmp  (a0); end of Configure

doConf  move.w d0,ioRefNum(a0); port #
 move.w #SerReset,csCode(a0); reset port to
 move.w ConfVal(a6),csParam(a0)  ; these new settings
 _Control
 rts

;---------------- SetBuf ------------------
; increases the size of the Port input buffer
;
; Function SetBuf(Port:Integer; BufSiz:long):OSErr

SetBuf  linka6,#pBlock    ; local space for parameter block
 move.l SetSiz(a6),d0
 beq.s  @1; if size = 0, reset input buffer
 _NewPtr; get a nonrelocatable block
 tst.l  d0; abort if couldn’t get it
 bne  Exit
 move.l a0,a1    ; save block ptr in a1
 move.l SetSiz(a6),d0; reload the buffer size
@1 lea  pBlock(a6),a0; point to parameter block
 move.w SetPort(a6),ioRefNum(a0)
 move.w #SerSetBuf,csCode(a0)
 move.l a1,csParam(a0)    ; pointer to new buffer
 move.w d0,csParam+4(a0)
 _Control
Exit  move.wd0,SetRes(a6)
 unlk a6; standard exit routine
 move.l (sp)+,a0
 addq.l #SetPar,sp
 jmp  (a0); end of SetBuf

;-------------- GetSerial ----------------
; GetSerial checks the Port to see if any
; characters have been received, and proceeds
; to read them into the input buffer.
; If none have been received, GetSerial returns
; a 0 in the character count variable.
;
; FunctionGetSerial(Port:integer; buffer:pointer) : CharCnt: long

GetSerial
 link a6,#pBlock
 lea  pBlock(a6),a0
 move.w GetPort(a6),ioRefNum(a0)
 move.w #2,csCode(a0); call SerGetBuf
 _Status
 move.l csParam(a0),d0    ; get # of characters received
 move.l d0,GetCnt(a6)
 beq.s  @2; if none, then leave, if there are
 ; characters available, read them.
@1 move.w GetPort(a6),ioRefNum(a0)
 move.l GetBuf(a6),ioBuffer(a0)  ; the input buffer
 move.l d0,ioReqCount(a0) ; read the characters
 _Read
@2 unlk a6; standard exit routine
 move.l (sp)+,a0
 addq.l #GetPar,sp ; leave char count on stack
 jmp  (a0); end of GetSerial

;-------------- PutSerial ----------------
; PutSerial sends the characters in the buffer
; out the Port. This routine is pretty
; primitive - it ignores any pending writes
; and errors, but...it works. It returns a dummy result.
;
; functionPutSerial(Port:integer; CharBuf:pointer; count: pointer): OSErr

PutSerial
 link a6,#pBlock ; local space for parameter block
 lea  pBlock(a6),a0; point to parameter block
 move.w PutPort(a6),d0    ; get the port ref #
 subq.w #1,d0    ; make it the port output ref num
 move.w d0,ioRefNum(a0)   ; modem output port
 move.l PutBuf(a6),ioBuffer(a0)  ; point to buffer
 move.l PutCnt(a6),a1; get pointer to char count
 move.l (a1),ioReqCount(a0) ; and put char count here
 _Write
 move.w #0,PutRes(a6); save a 0 as a dummy result
 unlk a6; standard exit routine
 move.l (sp)+,a0
 adda.l #PutPar,sp
 jmp  (a0); end of PutSerial
Listing:  term.asm

; File: Term.asm
;------------------------------------------------------------------
; Term.asm is simple terminal program, shows how to use low
; level serial I/O routines.
; written by Frank Henriquez
;
; code that handles transfers between clipboard and Text Edit
; is based on the code in chapter 3 of Dan Weston’s “Complete
;  Book of Macintosh Assembly Language Programming Vol. II”

; Some coding ideas were borrowed from the program “Rose.Asm”
; by Victor Barger, appeared in Jan. 1987 issue of MacTutor.
;
;  7/15/87Initial Turbo Pascal version, using Quickdraw.
;   9/1/87assembly language translation.
; 10/10/87modified to use TextEdit.
; 10/18/87fully functional version.
; 10/20/87text handling improved, Baud rates added.
;  1/14/88modified to use external serial I/O routines.
;------------------------------------------------------------------
; Register usage:
; d0: general purpose, also holds the menu#.
; d1: general purpose, also menu item# and the Modify value.
; d2: general purpose.
; d3, d4, d5, d6, d7 : not used.

; a0: general purpose, used for buffers and parameter blocks.
; a1: general purpose.
; a2: holds TextEdit handle.
; a3: general purpose.
; a4, a6 : not used.
; a5, a7 : system use.
;------------------------------------------------------------------
; pre-compiled routines from Serial I/O.asm

 XDEF OpenSerial
 XDEF Config
 XDEF GetSerial
 XDEF PutSerial
 XDEF SetBuf

; Standard include files

Include Traps.D
Include Toolequ.D
Include Sysequ.D
Include Quickequ.D

;---------- Program constants ------------

AppleMenu equ  1 ; Apple
AboutItem equ  1 ; First item in Apple menu

FileMenuequ 2    ; File
QuitItem  equ  1 ; First item in File menu

EditMenuequ 3    ; Edit
UndoItemequ 1    ; Items in Edit menu
CutItem equ 3
CopyItemequ 4
PasteItem equ  5
ClearItem equ  6

CmdMenu equ 4    ; Command menu
EraseItem equ  1 ; Erase screen

B300    equ 3    ;  300 baud
B1200   equ 4    ; 1200 baud
B2400   equ 5    ; 2400 baud
B9600   equ 6

AboutDialog equ  1 ; About dialog
ButtonItemequ  1 ; First item in DITL

WindID  equ 1

PortA   equ -6
PortB   equ -8

baud300 equ 380
baud1200equ 94
baud2400equ 46
baud9600equ 10
stop10  equ 16384
noParityequ 8192
data8   equ 3072

Config300 equ  baud300  + data8 + stop10 + noParity
Config1200equ  baud1200 + data8 + stop10 + noParity
Config2400equ  baud2400 + data8 + stop10 + noParity
Config9600equ  baud9600 + data8 + stop10 + noParity

;---------- Program starts here ------------

Start
 bsr.s  InitSerial
 bsr  InitManagers
 bsr  SetupMenu
 bsr  SetupWindow
 bsr  SetupTextEdit
 bsr  CliptoTE

EventLoop
 _SystemTask; check for DA calls
 move.l a2,-(sp) ; get handle to text record
 _TEIdle; blink cursor etc.
 bsr  GetSerCh   ; get a character from the modem port
 bsr  PutScreen  ; and put it on the screen
 clr.w  -(sp)
 move.w #$0fff,-(sp) ; look for all events
 pea  EventRecord(a5)
 _GetNextEvent   ; get the next event (WaitNextEvent!?)
 move.w (sp)+,d0
 beq.s  EventLoop; loop until an event
 bsr  HandleEvent; take care of the event
 beq.s  EventLoop; time to quit if not zero
 move.l a2,-(sp)
 _TEDispose
 clr.w  -(sp)
 move.w #PortA,-(sp) ; modem port
 clr.l  -(sp)    ; reset default serial buffer
 jsr  SetBuf
 addq.l #2,sp
 _ExittoShell

;-------------- InitSerial ------------------
; Open serial port for reads and writes, set
; the baud rate to 1200 baud and increase the
; size of the serial input buffer to 512 bytes

InitSerial
 clr.w  -(sp)    ; room for routine results
 move.w #PortA,-(sp) ; open the modem port (Port A)
 jsr  OpenSerial
 tst.w  (sp); if Open failed, beep and quit.
 bne.s  Abort
 move.w #PortA,-(sp)
 move.w #Config1200,-(sp) ; set modem port to 1200 baud
 jsr  Config
 tst.w  (sp); if Config failed, beep and quit.
 bne.s  Abort
 move.w #PortA,-(sp) ; make the serial input buffer
 move.l #512,-(sp) ; 512 bytes big. Prevents data loss.
 jsr  SetBuf
 move.w (sp)+,d0 ; clean up the stack, and abort if
 bne.s  Abort    ; SetBuf failed.
 rts

;-------------- Abort --------------------

Abort
 move.w #30,-(sp)
 _Sysbeep
 _ExitToShell    ; jump back to Finder

;------------ InitManagers ----------------

InitManagers
 _MoreMasters    ; prevents heap fragmentation
 move.l #$ffffffff,d0
 _NewHandle ; compact heap
 pea  -4(a5); QuickDraw globals
 _InitGraf
 _InitFonts
 move.l #$0000ffff,d0; flush all events
 _FlushEvents
 _InitWindows
 _InitMenus
 clr.l  -(sp)    ; no restart procedure
 _InitDialogs
 _TEInit
 _InitCursor
 rts

;------------ SetupMenu ------------------

SetupMenu
;Apple menu:
 clr.l  -(sp)
 move.w #AppleMenu, -(sp)
 _GetRMenu; get Apple menu from the rsrc file
 move.l (sp),AppleMHdl(a5)  ; save handle to menu
 move.l (sp),-(sp)
 clr  -(sp)
 _InsertMenu
 move.l #’DRVR’,-(sp)
 _AddResMenu; add DA’s to Apple menu

;File menu:
 clr.l  -(sp)
 move.w #FileMenu,-(sp)
 _GetRMenu
 move.l (sp),FileMHdl(a5) ; save handle to menu
 clr  -(sp)
 _InsertMenu

;Edit menu:
 clr.l  -(sp)
 move.w #EditMenu,-(sp)
 _GetRMenu
 move.l (sp),EditMHdl(a5) ; save handle to menu
 clr  -(sp)
 _InsertMenu

;Command menu:
 clr.l  -(sp)
 move.w #CmdMenu,-(sp)
 _GetRMenu
 move.l (sp),CmdMHdl(a5)  ; save handle to menu
 clr  -(sp)
 _InsertMenu
 move.l CmdMHdl(a5),-(sp) ; put handle on stack
 move.w #B1200,d0
 move.w d0,BaudChk(a5)
 move.w d0,-(sp)
 move.w #-1,-(sp); next to the 1200 baud item
 _CheckItem
 _DrawMenuBar
 rts

;------------ SetupWindow ----------------

SetupWindow
 clr.l  -(sp)
 move.w #WindID,-(sp)
 pea  WindowStorage(a5)
 move.l #-1,-(sp); make it the top window
 _GetNewWindow   ; load it from the rsrc file
 move.l (sp),WindowHdl(a5); save handle
 _SetPort ; make it the current port
 move.w #monaco,-(sp); Monaco
 _TextFont
 move.w #9,-(sp) ; in 9 pt.
 _TextSize
 rts

;----------  SetupTextEdit ----------------

SetupTextEdit
 clr.l  -(sp)    ; space for text handle
 pea  DestRect   ; DestRect Rectangle
 pea  ViewRect   ; ViewRect Rectangle
 _TENew
 move.l (sp)+,a0 ; save text handle
 move.l a0,a2    ; in a2
 move.l (a0),a0
 move.b #-1,teCROnly(a0)
 move.w #-1,-(sp)
 move.l a2,-(sp) ; enable TE auto scroll (128K ROM)
 _TEAutoView
 rts

;---------------- CliptoTE --------------
; CliptoTE copies text in the clipboard to the TE scrap

CliptoTE
 move.l #0,a3    ; check for TEXT in the desk scrap
 bsr.s  @1
 bmi.s  @2; leave if none

 move.l #0,d0    ; lets get the scrap, but first
 _NewHandle ; get a handle for it
 move.l a0,a3
 bsr.s  @1
 move.l TEScrpHandle,a0   ; replace old handle to TEScrap
 _DisposHandle
 move.l a3,TEScrpHandle   ; with ours
 move.l TEScrpHandle,a0
 _GetHandleSize  ; get length of our scrap
 move.w d0,TEScrpLength
@2 rts  ; leave  CliptoTE

@1 clr.l-(sp)    ; space for result
 move.l a3,-(sp)
 move.l #’TEXT’,-(sp); just check for TEXT
 pea  GSOffset(a5)
 _GetScrap
 move.l (sp)+,d0 ; return result
 rts

;-------------- GetSerCh ----------------

GetSerCh
 clr.l  -(sp)    ; room for character count
 move.w #PortA,-(sp) ; if modem port has data, save it
 pea  IOBuf(a5)  ; in this buffer.
  jsr GetSerial
 move.l (sp)+,CharCnt(a5) ; get # of characters received.
 bra  NextEvent

;-------------- PutScreen ----------------
; PutScreen will display the characters in
; the buffer, taking care of some control
; characters. It uses TextEdit for display.

PutScreen
 move.l CharCnt(a5),d1    ; skip if no characters in buffer
 beq  NextEvent
 subq.l #1,d1    ; adjust character count for loop
PutTE lea IOBuf(a5),a0    ; point to the character buffers
 lea  Outbuf(a5),a1
TELoop  move.b (a0)+,d0   ; get a char from the input buffer
 andi.w #$7f,d0  ; clear out 8th bit
 cmpi.w #$20,d0  ; if less than space, then it
 blt.s  CtrlCh   ; must be a Ctrl character
inBuf move.bd0,(a1)+ ; printable character
inLoop  dbrad1,TELoop
 pea  Outbuf(a5) ; print the sanitized output buffer
 move.l CharCnt(a5),-(sp)
 move.l a2,-(sp)
 _TEInsert
 move.l a2,-(sp)
 _TESelView ; scroll if needed
 bra  NextEvent  ; exit PutScreen

;---- handle control characters ----------
; Control characters are serviced here. A
; lookup table would be more efficient, in
; particular if we were to emulate a real
; terminal (like a VT-52 or VT-100).

CtrlCh  cmpi.w #$0d,d0    ; let Text Edit take care of CR
 beq.s  inBuf
 cmpi.w #8,d0    ; backspace
 beq.s  BS
 cmpi.w #7,d0    ; bell
 beq.s  Bell
 cmpi.w #9,d0    ; tab
 beq.s  Tab
 subq.l #1,CharCnt(a5)    ; ignore anything else
 bra.s  inLoop

BS subq.l #1,a1  ; move back outbuf pointer by 1
 subq.l #2,CharCnt(a5)    ; 1 for BS char, 1 for erased char
 beq  NextEvent  ; no chars to print - all done.
 bgt.s  inLoop   ; continue filtering

 move.w d0,-(sp) ; use TEKey to force backspace
 move.l a2,-(sp) ; if backed out of input buffer
 _TEKey
 bra  NextEvent

Bell  move.w#30,-(sp); make a beep
 _SysBeep
 subq.l #1,CharCnt(a5)    ; get rid of character
 bra.s  inLoop

Tabmove.w #4,d2  ; 5 spaces for a tab
@1 move.b #$20,(a1)+ ; put them into the output buffer
 dbra d2,@1
 addq.l #4,CharCnt(a5)    ; adjust the character count
 bra.s  inLoop   ; ( # spaces, -1 for the tab char)

;------------ Handle Event  ------------
; This routine is the core of the program.
; The event number is used as an index into
; the EventTable. These entries cover all
; the events that could happen while the
; program is in the main loop.

HandleEvent
 move.w Modify(a5),d1
 move.w What(a5),d0; get event number
 add.w  d0,d0    ; *2 for table index
 move.w EventTable(d0),d0 ; point to routine
 jmp  EventTable(d0) ; and jump to it

EventTable

 dc.w NextEvent-EventTable;  0 (Null)
 dc.w MouseDown-EventTable;  1 mouse down
 dc.w NextEvent-EventTable;  2 mouse up      (Not used)
 dc.w KeyDown-EventTable  ;  3 key down
 dc.w NextEvent-EventTable;  4 key up              (Not used)
 dc.w KeyDown-EventTable  ;  5 auto-key
 dc.w Update-EventTable   ;  6 update
 dc.w NextEvent-EventTable;  7 disk inserted (Not used)
 dc.w Activate-EventTable ;  8 activate
 dc.w NextEvent-EventTable;  9 abort               (Not used)
 dc.w NextEvent-EventTable; 10 network             (Not used)
 dc.w NextEvent-EventTable; 11 I/O driver          (Not used)
 dc.w NextEvent-EventTable; 12 app1Evt       (Not used)
 dc.w NextEvent-EventTable; 13 app2Evt       (Not used)
 dc.w NextEvent-EventTable; 14 app3Evt       (Not used)
 dc.w NextEvent-EventTable; 15 app4Evt       (suspend/resume?)

;-------------- Mouse down ----------------

MouseDown
 clr.w  -(sp)    ; space for result
 move.l Point(a5),-(sp)   ; get mouse coordinates
 pea  WWindow(a5); Event Window
 _FindWindow
 move   (sp)+,d0 ; get region number
 add  d0,d0 ; *2 for index into table
 move.w WindowTable(d0),d0; point to routine offset
 jmp  WindowTable(d0); jump to routine

WindowTable
 dc.w NextEvent-WindowTable ; In Desk (Not used)
 dc.w InMenu-WindowTable  ; In Menu Bar
 dc.w SystemEvent-WindowTable ; In System Window
 dc.w Content-WindowTable ; In Content
 dc.w Drag-WindowTable    ; In Drag
 dc.w NextEvent-WindowTable ; In Grow (Not used)
 dc.w QuitRoutine-WindowTable ; In Go Away
 dc.w NextEvent-WindowTable ; In Zoom in box (Not used)
 dc.w NextEvent-WindowTable ; In Zoom Out box (Not used)

;---------------- in Menu ----------------
InMenu
 clr.l  -(sp)    ; make room on stack
 move.l Point(a5),-(sp)   ; mouse event
 _MenuSelect
 move.w (sp)+,d0 ; save menu
 move.w (sp)+,d1 ; and menu item

Choices ; called by command key too
 cmp.w  #AppleMenu,d0
 beq.s  InAppleMenu
 cmp.w  #FileMenu,d0
 beq.s  InFileMenu
 cmp.w  #EditMenu,d0
 beq.s  InEditMenu
 cmp.w  #CmdMenu,d0
 beq    InCmdMenu

ChoiceReturn
 bsr  UnHiliteMenu ; unhighlight the menu bar
 bra  NextEvent

;-------------- in InAppleMenu ----------------
; In the Apple menu.  If it wasn’t About, it must have been a
; desk accessory.  If so, open the desk accessory.

InAppleMenu
 cmp.w  #AboutItem,d1; is it About?
 beq.s  About    ; if so go do About...
 move.l AppleMHdl(a5),-(sp) ; look in Apple Menu
 move.w d1,-(sp)
 pea  DeskName(a5) ; get Item Name
 _GetItem
 clr  -(sp) ; space for opening result
 pea  DeskName(a5) ; open Desk Acc
 _OpenDeskAcc
 move.w (sp)+,d0 ; pop result

GoSetOurPort

 bsr  SetOurPort
 bra.s  ChoiceReturn

;-------------- About --------------------
; Set up the About dialog box, and wait for
; the proper click or keypress.  End by
; closing the dialog box and setting the
; port to us.

About
 clr.l  -(sp)    ; space for dialog pointer
 move #AboutDialog,-(sp)  ; dialog rsrc #
 pea  DStorage(a5)
 move.l #-1,-(sp); dialog goes on top
 _GetNewDialog
 move.l (sp),-(sp) ; copy handle for Close
 _SetPort ; make dialog box the port
 move.l a2,-(sp)
 _TEDeActivate

WaitOK
 clr.l  -(sp)    ; clear space for handle
 pea  ItemHit(a5)
 _ModalDialog    ; wait for a response
 move.w ItemHit(a5),d0    ; look to see what was hit
 cmp.w  #ButtonItem,d0    ; was it OK?
 bne.s  WaitOK
 _CloseDialog    ; handle already on stack
  bra.s GoSetOurPort

;---------------- in FileMenu ----------------

inFileMenu
 cmp.w  #QuitItem,d1 ; is it Quit?
 bne.s  ChoiceReturn ; no, go get next event
 bsr  UnHiliteMenu ; unhighlight the menu bar
 bra  RealQuit   ; go Quit


;---------------- in EditMenu ----------------
; The Edit Menu support routines transfer the
; TE scrap to the clipboard, and back. See the
; clipboard chapter in Dan Weston’s
; “Assembly Language Programming Volume II”

InEditMenu
 bsr  SystemEdit ; Desk accessory active?
 bne.s  ChoiceReturn ; yes, SystemEdit handled it
 cmp.w  #CutItem,d1
 beq.s  Cut
 cmp.w  #CopyItem,d1
 beq.s  Copy
 cmp.w  #PasteItem,d1
 beq.s  Paste
 cmp.w  #ClearItem,d1
 beq.s  ClearIt
 bra  ChoiceReturn

Cut
 move.l a2,-(sp)
 _TECut ; Cut and copy text
 bra.s  Convert

Copy
 move.l a2,-(sp)
 _TECopy; Copy text to scap
 bra.s  Convert

Paste
 bsr  CliptoTE   ; from the clipboard
 move.l a2,-(sp)
 _TEPaste ; Paste
 bra  ChoiceReturn

ClearIt
 move.l a2,-(sp)
 _TEDelete; Clear without copying

Convert
 bsr.s  TEtoClip ; copy to clipboard
 bra  ChoiceReturn

;-------------- TEtoClip --------------------
; TEtoClip copies the TE scrap to the Clipboard

TEtoClip
 move.l TEScrpHandle,a0
 _GetHandleSize  ; get length of our scrap
 move.l d0,a3    ; save length
 clr.l  -(sp)    ; space for result
 _ZeroScrap ; want to write over scrap
 move.l (sp)+,d0
 move.l TEScrpHandle,a0
 _Hlock ; lock pointer to our scrap
 clr.l  -(sp)    ; space for result
 move.l a3,-(sp)
 move.l #’TEXT’,-(sp); just check for TEXT
 move.l TEScrpHandle,a0
 move.l (a0),-(sp) ; save pointer to TE scrap
 _PutScrap
 move.l (sp)+,d0
 move.l TEScrpHandle,a0
 _HUnLock ; unlock handle
 rts    ; leave TEtoClip

;---------------- in CmdMenu ------------------

InCmdMenu
 cmp.w  #EraseItem,d1; baud rate change?
 bne.s  InBaud

; it wasn’t baud change command, fall through to erase screen

 move.l #0,-(sp) ; set Selection range to cover
 move.l #$ffff,-(sp) ; everything in this TE block.
 move.l a2,-(sp)
 _TESetSelect
 bra.s  ClearIt  ; and clear them out


;---------------- in BaudMenu ----------------

InBaud
 cmp.w  #B300,d1
 beq.s  @1
 cmp.w  #B1200,d1
 beq.s  @2
 cmp.w  #B2400,d1
 beq.s  @3
 cmp.w  #B9600,d1
 beq.s  @4
 bra  NextEvent

@1 move.w #Config300,SerCon(a5)
 bra.s  @5
@2 move.w #Config1200,SerCon(a5)
 bra.s  @5
@3 move.w #Config2400,SerCon(a5)
 bra.s  @5
@4 move.w #Config9600,SerCon(a5)
@5 move.l CmdMHdl(a5),-(sp)
 move.w BaudChk(a5),-(sp) ; uncheck previous item
 move.w d1,BaudChk(a5)    ; save new baud item
 move.w #0,-(sp)
 _CheckItem
 move.l CmdMHdl(a5),-(sp) ; put handle on stack
 move.w BaudChk(a5),-(sp) ; and place a check
 move.w #-1,-(sp); next to the current baud
 _CheckItem
 clr.w  -(sp)
 move.w #PortA,-(sp) ; set the new baud rate
 move.w SerCon(a5),-(sp)
 jsr  Config
 move.w (sp)+,d0 ; ignore the result
 bra  ChoiceReturn

;------------ UnhiliteMenu --------------

UnhiliteMenu
 clr.w  -(sp)    ; all menus
 _HiLiteMenu
 rts

;-------------- SystemEdit ----------------

SystemEdit
 clr  -(sp) ; space for result
 move.w d1,-(sp) ; get item in Edit menu
 subq #1,(sp)    ; SystemEdit is off by 1
 _SysEdit
 move.b (sp)+,d0
 rts

;-------------- SystemEvent --------------

SystemEvent
 pea  EventRecord(a5)
 move.l WWindow(a5),-(sp)
 _SystemClick    ; let the system do it
 bra  NextEvent

;-------------- in Content ----------------

Content
 clr.l  -(sp)    ; room for result
 _FrontWindow
 move.l (sp)+,d0 ; front window pointer
 cmp.l  WindowHdl(a5),d0  ; same as our pointer?
 beq.s  @1; ignore it
 move.l WWindow(a5),-(sp)
 _SelectWindow   ; do it, fall through to NextEvent
 bra  NextEvent

@1 pea  Point(a5)
 _GlobalToLocal
 move.l Point(a5),-(sp)
 btst #9,d1
 sne  d0
 move.b d0,-(sp)
 move.l a2,-(sp)
 _TEClick
 bra  NextEvent

;---------------- in Drag ----------------

Drag
 move.l WWindow(a5),-(sp)
 move.l Point(a5),-(sp)
 pea  WBounds
 _DragWindow; drag window and fall through
 bra  NextEvent

;-------------- Quit Routine ----------------

QuitRoutine
 clr.w  -(sp)    ; room for result
 move.l WindowHdl(a5),-(sp) ; get pointer to open window
 move.l Point(a5),-(sp)   ; mouse point
 _TrackGoAway
 move.w (sp)+,d0
 beq  NextEvent  ; didn’t really want to quit

RealQuit
 move.l WindowHdl(a5),-(sp) ; did want to quit
 _CloseWindow
 move.w #-1,d0   ; d0 is TRUE - ok to quit
 rts

;-------------- Key down ----------------

KeyDown
 move.w Message+2(a5),d0  ; get key code and character
 btst #8,d1 ; command key pressed?
 beq.s  SaveCh   ; yes, take care of it.
 andi.w #$001f,d0; convert to a control character
SaveCh  move.b d0,IOBuf(a5) ; save it in the buffer
 move.l #1,CharCnt(a5)    ; send the character typed
 clr.w  -(sp)    ; room for dummy result
 move.w #PortA,-(sp) ; in the input buffer
 pea  IOBuf(a5)
 pea  CharCnt(a5)
 jsr  PutSerial  ; and send it out the modem port
 move.w (sp)+,d0 ; get rid of dummy result
 bra.s  NextEvent

;---------------- Update ----------------

Update
 move.l WindowHdl(a5),-(sp)
 _BeginUpdate
 pea  ViewRect   ; erase visible window
 _EraseRect
 pea  ViewRect
 move.l a2,-(sp)
 _TEUpdate
 move.l WindowHdl(a5),-(sp)
 _EndUpdate
 bra.s  NextEvent

;---------------- Activate ----------------

Activate
 move.l WindowHdl(a5),d0
 cmp.l  Message(a5),d0    ; our window?
 bne.s  NextEvent
 btst #0,d1 ; activate?
 beq.s  Deactivate ; no

 move.l a2,-(sp) ; move Text Handle To Stack
 _TEActivate
 move.l EditMHdl(a5),-(sp); get handle to the menu
 move.w #UndoItem,-(sp)   ; disable 1st item (undo)
 _DisableItem

SetOurPort
 move.l WindowHdl(a5),-(sp)
 _SetPort
 bra.s  NextEvent

; Deactivate window, turn off TextEdit, enable undo for the desk accessories.

Deactivate
 move.l a2,-(sp) ; get Text Handle
 _TeDeActivate
 move.l EditMHdl(a5),-(sp); get handle to the menu
 move.w #UndoItem,-(sp)   ; enable 1st item (undo)
 _EnableItem
;bra  NextEvent  ; *CAUTION!* if more code
; is added after this routine, remember to uncomment the line above!
 
;-------------- Next Event --------------

NextEvent
 moveq  #0,d0    ; say that it’s not Quit
 rts    ; return to EventLoop

;------------ Window stuff --------------

ViewRectdc.w   5,4,290,500; Text Record’s View Rect
DestRectdc.w   5,4,290,500; Text Record’s Dest Rect
WBounds dc.w   5,5,335,510

;------------ Event variables ------------
; IMPORTANT! remember, the EventRecord is
; very much a variable and NOT a constant.
; Never declare the EventRecord variables
; with a dc command.

EventRecord ds.w 0
What:   ds.w   1 ; event number
Message:ds.l1
When:   ds.l1
Point:  ds.l1    ; mouse coordinates
Modify: ds.w   1 ; key and button state

;------ Application variables ------------

AppleMHdl ds.l 1 ; handle for apple menu
FileMHdlds.l1    ; handle for file menu 
EditMHdlds.l1    ; handle for edit menu 
CmdMHdl ds.l1    ; handle for command menu 

DStorageds.wDWindLen
DeskNameds.w16   ; DA name
ItemHit ds.w1
WWindow ds.l1
WindowStorage  ds.wWindowSize
WindowHdl ds.l 1 ; handle to the window
GSOffsetds.l1    ; Get Scrap Offset dummy

;Serial port variables
IOBuf   ds.b512  ; general I/O buffer
Outbuf  ds.b512  ; sanitized version of IOBuf
SerCon  ds.w1    ; current serial port config
BaudChk ds.w1    ; current checked item
CharCnt ds.l1    ; # of characters read

 end

Listing:  term.r

*
* This is the resource file for the program “Term”
* the \CA character generates a “hard” space.

* Give it a type APPL and creator FHMT.
Term
APPLFHMT

* Get the code portion.
INCLUDE term.code
TYPE MENU
  ,1
About Term...
(-

  ,2
File
Quit

  ,3
Edit
Undo
(-
Cut
Copy
Paste
Clear

  ,4
Command
Erase screen
(-
1200 Baud
2400 Baud
9600 Baud

Type DLOG
  ,1
  
100 100 200 350
Visible  NoGoAway
1
0
1

Type DITL
  ,1
2

Button
60 100 90 150
OK

StaticText
15 40 59 238
Simple Terminal Program\0D\CAFrank Henriquez 1/88

TYPE WIND
    ,1
Terminal
44 7 337 507
Visible GoAway
0
0

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »

Price Scanner via MacPrices.net

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
Top Secret *Apple* System Admin - Insight G...
Job Description Day to Day: * Configure and maintain the client's Apple Device Management (ADM) solution. The current solution is JAMF supporting 250-500 end points, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.