Tips

How to automatically mount an NFS drive (UNIX network) on OS X

This article is a variation/enhancement to a post I found at http://sourceforge.net/apps/phpbb/freenas/viewtopic.php?f=49&t=9289 for a FreeNAS server. Of course purely Mac folks would likely want to stick with the AFP protocol. In terms of generalities the NFS server does not necessarily have to be running FreeNAS. It just has to work. FreeNAS is basically a “Swiss Army Knife” of external storage. I find it does a lot of things very easily. It doesn’t work on all machines (I’ve had a 60% success rate) but when it works, it works well.

The question of why anyone would want to run storage of any sort connected to a network drive over their LAN is simple. When people upgrade to new machines they frequently have the old machine sitting around doing nothing. In the case of some Intel Macs (or PCs) they can just throw in a HUGE amount of storage for the price of adding a little amount of storage to a current Mac. In my case I have access to free/cheap old PCs and parts. This allows me to use these machines to experiment and learn for free. Since we were looking at a problem regarding NFS at the lab this is exactly what I did.

First off the person doing this needs access to a working NFS machine. As I suggested before, FreeNAS isn’t necessary but is easy to install and configure. This step is one I will leave to the reader.

Second, go into a terminal and edit the /etc/auto_master file. The reader will have to use the sudo command to do so since it is a system file. If the reader is paranoid like me they should back up the file first so they can recover if there are any mistakes. The author of the article I used as reference wanted to install things under the /home directory. This is fine. Personally I wanted to see if I could put things in a different location. In this case I wanted them in /nfs. Rather than modify the existing line I added the following line…

/nfs                    auto_nfs        -nobrowse

The first part says where to mount the drive. The second is another file with further instructions. I have no idea what the third part does, but it was made clear it was to be there.

Third was to create the auto_nfs file. In this case I copied the auto_home file to auto_nfs. At that point I’ll skip ahead an hour and a half to the point where I replaced the last line in the file with the following line… ;-)

server 192.168.X.Y:/mnt/main           # Use directory service

First there has to be the name of a directory. In this case the drive will be mounted under /nfs/server. I gather it is set up this way because there is the possibility someone may want to mount multiple machines this way. Then is the location of the NFS device. The 192.168 part refers to a non-internet LAN. The reader should replace “X” and “Y” with the detailed location of the NFS server. The /mnt/main is the location on the server where the area to mount is located. The final part is a coment left over from the previous file and sort of kind of makes sense.

Apart from Step 5 everything is the same as in the reference article. Essentially all the reader has to do is type “sudo automount -vc”, test things, and they are done.

The down side is it appears to be mounted as read only. I’ll post an update when I figure out how to make it read and write. It may just be a permissions issue but it may be something else.

As a final thing I thought it would be a good idea to have quicker access to the device. I made a symbolic link to the mounted directory in my home area. This can be done in a number of ways. I just went into the terminal and typed “ln -s /nfs nfs”. So now I don’t have to go hunting through the whole computer to find the directory.

Considering I can access things with less trouble through afp, I’m not sure I’ll keep this available on the Mac.

Mac Terminal tip: Adding a directory to the $PATH environment variable

The Mac has a unique way of setting up its $PATH variable. These are the locations the BSD portion of OS X looks at to find programs to run. In this case the list of paths can be found in the file /etc/paths. To edit it the administrator needs to sudo their favourite editor since the file itself is owned by root. For example “sudo pico /etc/paths”.

This is useful for the programmer who wants to have their programs accessible anywhere in the terminal. In my case I added “~/bin” to the end of the file. This allows any user to create a program or script in their own “bin” directory.

As someone who has done this in Linux I can say it is a whole lot easier to do this in OS X.

Programmers Tools: Four FREE text editors for OS X

Apple and the BSD folks have included two of the four editors as part of the base install packages. The first goes by two names and is accessed via the terminal (Applications –> Utilities –> Terminal). They are either pico or nano (note the lowercase). These are almost as basic as one can get and still refer to them as full screen editors. The other editor included by Apple is Textedit. This is entirely accessed by the GUI. It has more features than pico but is slightly larger.

The other two editors can be thought of as program editors. They offer features such as syntax highlighting and bracket matching. Again they are larger because of the greater number of features. The first is Gedit (http://www.gedit.org). To be fair, I’ve only used it twice. It is a port from the Linux world where I prefer to use Kate (which IS available for the Mac if a whole bunch of other software is installed). The other editor is called Text Wrangler and is available from Bare Bones Software (http://www.barebones.com). It is essentially a scaled down version of their commercial product BBEdit.

My preference for non-program documents is Textedit. For programs my preference is Text Wrangler.

Three Ideas: Handy Tools for Business BASIC

Text by Dave. Lingwood
Programs by Brian Matthews (Action-Research NW)

BASIC COMPARISONS
Most Apple /// Business BASIC users also work with Applesoft, either from earlier ][ days, or through emulation mode. Business BASIC (hereafter “BB”) has all the professional features you need, but it lacks the flexibility provided by the openness of the ][. This article and attached programs recount some of the pitfalls we encountered and useful tricks devised in transferring a large statistical analysis package from Applesoft to BB. The tricks add back some of the flexibility of Applesoft.

1: ARRAY HANDLING AND REQUEST.INV
If you haven’t already discovered it, REQUEST.INV, provided with BB, contains very useful routines for rapid reading and saving of numeric data arrays. They are FILREAD and FILWRITE. FILREAD/FILWRITE do a read or save directly into/from a vector. The data are saved as a binary file on the disk. The PERFORM command tells the invokable the name of the array or vector, the file name, and the number of bytes to transfer. Let’s assume you have an array DIMensioned as DD%(N) which contains data you want to save save to disk, then re-fill with other data on the disk. Here are the appropriate BB statements to do it:
10 INVOKE”request.inv”:REM at beginning of program
20 DIM RW$(1): RW$(0)= “Read”: RW$(1)= “Writ”
100 filename$= “dataout”: RW= 1: REM write data
110 GOSUB 1000
200 filename$= “datain” RW= 0: REM read data
210 GOSUB 1000
1000 REM Subroutine for array read/write. RW=0 to read, =1 to write
1010 PRINT RW$(RW); “ing array DD%”
1020 nbytes%= (N+1) * 2:REM 2 since integer array
1030 datafile%= filenumb: REM # of data file
1040 DD$= “DD%”: REM name of array to transfer
1050 OPEN#datafile%,filename$
1060 IF RW THEN PERFORM FILWRITE(%datafile%,@DD$,%nbytes%)
1070 IF NOT RW THEN PERFORM FILREAD(%datafile%,@DD$,%nbytes%,@count%): IF nbytes%<>count% THEN PRINT “Err: Read file <> array length!”
1080 CLOSE#datafile%
1090 RETURN

The RW variable controls reading or writing. DD$ is the important value passed to FILREAD or FILWRITE which tells it the name of the array to find in memory, and from the beginning (0th cell) of which to begin reading or writing “nbytes%” bytes. The “count%” variable is returned after a read, containing the number of bytes actually read. The IF test after the read checks that this figure agrees with the number of bytes in the array (though you might want to read fewer bytes into an array, you should never read more!).

You could even put the names of various arrays into a string array, then assign DD$=ARRAY$(k), for example, or pass the array name into the subroutine as a parameter. There is a lot of flexibility here, and more importantly, a lot of SPEED. Data transfer is much faster than with INPUT/PRINT.

2: DIMENSIONS, STRINGS AND THE 256K ///
A problem we bumped into almost immediately with the stat package was the limit on array size, and the rule about string arrays in BB that affects the 256K machine.

Simply put:
a. No numeric array can be longer than 64K
b. Strings must be defined in the first 64K block of program memory.

The first, while a pain, you can live with by careful program design (especially with FILREAD/FILWRITE available). The second causes real speed problems if you have many string arrays. Being an interpreter, BB has to scan through the list of all arrays to find the one you want. Normally you’d define your fast numeric arrays first; but if your numeric array is a full 64K in size, that would force any string arrays subsequently defined to cross into the second 64K block, generating an error that is not mentioned anywhere handy:

Error code 21!
Variable/memory error
Again, it means you’ve accessed a string that crosses the block boundary on a 256K machine. It brings the program to a screeching halt! Once you know this, the cure is to dimension all the strings first. Uhg. Speed tumbles as the program wades through all the string arrays to find the numerics. The solution we came up with, though a bother, was to dimension just one string array, then store all the various command lists, etc., in it, using an offset variable to find the “base” of each list.

For example, if we had 10 commands, 6 options, and 20 error codes the master string array would look like this:

Cmdlist=0 strings cmdlist+1—cmdlist+10 are commands
Optlist=10 “ optlist+1—optlist+6 “ options
Errlist=16 “ errlist+1—errlist+20 “ error messages

What a classic pain. But, it works.

3: BLOAD+BSAVE+PEEK+POKE = PROGRAM OVERLAY
A heck of an equation. Why resurrect these commands we thought the superior design of the /// made unnecessary? The answer lies in the heart of our program design, and is related to disk space and speed.

The AIDA statistical package consists of a chunk of common code that is always used to process commands, read and save data, etc., plus specific code that is used depending on which command the user gives. The traditional way to handle this is to have a main “menu” program chain various separate programs depending on which command is given. The trouble with this is that the common code has to exist in each chained program. That means longer read time and disk space wasted through duplication of that code.

The ][ version already had solved this by inventing program overlay (see “Call-A.P.P.L.E.,” Nov., 1980). The various command-specific program segments were saved as binary files, then BLOADed quickly right into the middle of the running program, which never knew what had hit it. When we converted to the ///, the improved CHAIN was faster than would have been the case on the ][, but there just wasn’t enough disk space for the program!

Back to the drawingboard. The way overlay worked on the ][ was to find the RAM address of one line of the program: a simple subroutine peeked the Applesoft zero page locations containing the current line pointer — in effect the one-line subroutine found its own address in memory.

Then the code was BLOADed, the only trick being that each “module” so loaded must be shorter than the original “back end” of the program, so as not to overwrite the data.

Brian began to dig around in memory, and added a lot to what we had previously known about reserved locations in BB. Once he found the statement pointer he was able to create the needed “overlay” statements by peeking the counter, then BLOADing the module to that point — after creating PEEK, POKE, BLOAD, and BSAVE, of course!

First, let’s describe those four new commands, which are useful in their own right. Later we’ll combine them into the form needed for program overlay.

Peeking and Poking
Peek and Poke are defined in the AIDA.TOOLS invokable. Both routines are set up to peek or poke one- or two-byte values, and to use the “extend byte” for the RAM banks. The syntax is:

x = exfn%.peek(%address,%xtend,%length)
PERFORM poke(%address,%xtend,@VALUE%,%length)
where: address = address of low byte (0-65535)
xtend = extend byte (0-3)
length = 0 for onebyte, 1 for 2-byte
VALUE% = value to poke

For example:
VALUE%= exfn%.peek(%61,%0,%1)
VALUE%= VALUE% + 512: PERFORM poke(%61,%0,@VALUE%,%1)

The peek will set variable “VALUE%” equal to the two-byte data found at locations 61 (low byte) and 62 (high byte) decimal in the “true” (xtend=0) zero page. The poke will poke the two-byte value contained in “VALUE%” into 61 and 62. This would add 512 bytes onto the BB end of program pointer (clobbering variables, by the way). Careful: poking in particular assumes that you know WHAT you’re poking, and why. The list of zero-page and other BB locations found elsewhere on this disk provides valuable guidance.

Bload and Bsave
The syntax for these commands, set up as invokables, looks like this:
PERFORM bsave(@filename$,%address,%xtend,@savelength%)
PERFORM bload(@filename$,%address,%xtend,@foundlength%)

Before BSAVE the number of bytes to be written must be stored in the “savelentth%” variable. After a BLOAD this same variable will contain the number of bytes actually read from the disk.

All four of these commands are contained in the AIDA.TOOLS invokable described at the end of this article.

Overlay
This technique lets you break long programs into shorter pieces, where you don’t want to have to re-read (as with chain) extensive common code. The program must be laid out like this:

Common code (low line numbers)
Transition subroutine (finds its own RAM address)
Overlay “module” (high line numbers)

When the program is first loaded and run, initialization or startup code is contained in the overlay area. This initialization code MUST be longer than any later module will be, even if you must pad it out with long REM statements. Later this initialization code will be overwritten with the binary modules successively BLOADed.

There are a few other rules for writing the initialization code, based on the obvious fact that those lines of the program aren’t going to be there later. They are: a) no function statements may be defined there, no ONERR or ONEOF statements may be defined or have their destination there,  b) no common or module code may refer to any line number here after the first BLOAD, and c) any string assignments must force the string contents out of the program and into normal string storage. The approach below will do the latter:

READ A$: TITLE$= A$ + “”
B$= “This is a string” + “”

The string concatenation forces BB to move the string out of the program area. Any other string function, such as MID$, could also be used. In our program, the common code runs through line number 997. Line 998 is the transition subroutine, and code just above that point does the actual overlay. Each proto-module is created as a BB program. You may choose to write it following the common code to permit testing.

When ready to save the module as a binary module, SAVE the program first, then EXEC the following text file:

DEL 1, 998: REM Remember, modules can’t have these low line numbers
800 INPUT “MODULE NAME TO SAVE? “;A$: FILE$= “MODULE.”+A$
810 GOSUB 998: last%=exfn%.peek(%61,%0,%1): len%=last% - addr%
820 PERFORM bsave(@FILE$,%addr%,%xtend%,@len%)
830 PRINT FILE$;” contains “;len%;” bytes.”: END
998 addr%=exfn%.peek(%79,%0,%1): xtend%=exfn%.peek(%5712,%0,%0): addr%=addr%+exfn%.peek(%addr+1,%xtend,0): RETURN
RUN

Line 998 calculates the address of the line following it. The peek in line 810 reads the BB end of program pointer. Thus, each module will begin at the line following 998, and the length written is (as calculated in variable “len%”) the number of bytes between this point and the end of the program. In BB it does not matter (as it emphatically does in Applesoft) that the common code be in memory, and always of the same length, when the binary files are written or read. This is because BB uses relative next line pointers. That is, each line of BB code begins with a two-byte pointer containing the number of bytes to add to the line pointer in order to find the next line. In Applesoft this pointer contains the absolute address of the next line. The happy result of this design improvement in BB is that you may modify the common code to your heart’s content after the modules have been saved.

It is a help if the actual execution code in each module begins with the same line number. The common code can then execute that module by a simple GOSUB 1000, or whatever. If more than one command is contained in a module, then you’ll have to keep track of the beginning line numbers of each command, and use an ON k GOSUB ln1,ln2...,ln3 statement. Of course, you also have to know the name of the module in which each command was BSAVEd, and when a command is given by the user, BLOAD that module if it is not now present.

The module code must follow the rules given for the initialization code, above: no embedded strings that are used outside of the module, and no lines or functions called by other modules. The code to load in a module is similar to that above:

800 PRINT”LOADING MODULE “;A$: FILE$= “MODULE.”+A$
810 GOSUB 998
820 PERFORM bload(@FILE$,%addr,%xtend,@len%)
830 RETURN
998 addr%=exfn%.peek(%79,%0,%1): xtend%=exfn%.peek(%5712,%0,%0): addr%=addr%+exfn%.peek(%addr+1,%xtend,0): RETURN

When ready to load a module, GOSUB 800 with A$ equal to the last portion of the module file’s name. Then GOTO or GOSUB to the code in that module. In use, the modules load very quickly, and of course, there is much less space taken up by the program code. Try it.

THE AIDA.TOOLS INVOKABLE The assembler source code for AIDA.TOOLS is found in another file on this disk. The ready-to-use invokable is in its own file elsewhere on this disk. Note that the authors have provided AIDA.TOOLS for the individual use of our readers. Commercial rights are, however, reserved.

/// /// ///

Author Bibliograhies: Dave Lingwood sports the checkered career typical of many in microcomputing: a background in English and Journalism, PhD in Communication Research, and R & D work in technology transfer and marketing, before forming his own software/information service company. He is also Secretary of A.P.P.L.E..

Brian Matthews was one of the first Seattle /// devotees; and he is, as this disk goes to press (drive?), the proud holder of a new Computer Science B.S. from the U of Washington, which he is applying these days for the benefit of Motorola.

Program to Initialize a Diskette

100 CALL -93.6 :P RINT ” (insert your name, address and phone)”
200 PRINT “THIS DISK CONTAINS INTEGER BASIC PROGRAMS” (or game programs, etc.)
300 FOR 1=1 TO 1000:. NEXT I: CALL -936
400 PRINT “Dc CATALOG’:END
Note: In line 400 after the first quote, hit the Control key and the “D” key at the same time. This is how the disk system responds to commands within a Basic program, with Control D in a print statement.

Use of Color Mask Byte in HIRES

by Darrell Aldrich

This is a brief description of the use of the color mask byte (Location 81210) for high re solution graphics in Apple Integer Basic.

This mask specifies an 8 bit pattern of plottable X coordinates, with the pattern repeating itself every eight coordinates, going from left to right. The bits in this byte represent the colors violet and green, alternately. (See Figure 1).

Suppose we want to mask off every other green bar on the screen as in Figure 2a, where “X” indicates a point we want masked off. Set up an 8 bit byte as in Figure 2b, with all masking bits =0 and all non-masking bits=l. Convert this value to decimal & Poke in decimal location 812.

Routine to find page length

This handy little subroutine can be used in two manners; to fill a screen page with repetitive material or to determine the length of a screen page of print statements. Assume line 100 is a print statement with which you wish to fill the page.

100 PRINT” “:GOSUB 400
400 IF PEEK(37) < 18 THEN RETURN
410 POP:PRINT “FOR NEW PAGE HIT ANY KEY”:CALL 756:GOTO 000

Line 400 reads the cursor and finds whereitis on the page. If less than 18 lines, print more lines. Line 410 “Pops” the return address from the stack. Instead of Call 756 you could use INPUT A$ or Call -676, all of which are a means of halting the program to await further instructions.

Key Clicker Routine

If you have a newer Apple with the silent keyboard, Don Williams has written a short and sweet machine language routine to solve your problem.

0300:  48              PHA
0301 : A9 20        LDA #$20
0303: 8D 30 CO   STA $C030
0306: 20 A8 FC    JSR $FCA8
0309: 8D 30 CO   STA $C030
030C: 68              PLA
030D: 4C 1B FD   JMP $FDIB
0038: 00 03

Once you store the address ($0300) in location $38 and hit return, your keys will click merrily away until you hit reset. To restart the routine, reenter the address.

•A minor modification of this routine will give you a slow list (with tones) feature. Change the JMP and 030D to FOFD and store the address in $36. The value stored at $0302 may be increased for a slower list.

Loadable Program Follows:

————START OF LISTING———–

0300: 48 A9 20 8D 30 C0 20 A8

0307: FC 8D 30 C0 68 4C 1B FD

0038: 00 03

—————END OF LISTING

HIRES Capabilities and Limitations

By Darrell Aldrich

While written for Applesoft II, this article is also applicable to Integer Basic HIRES graphics mode. The High Resolution Graphics screen is composed of 280 vertical bars (X coordinates), by 160 units high (Y coordinates). The even-numbered bars are violet in color, while the odd-numbered bars are green. The color white is produced by plotting adjacent green and violet bars. (Green+Violet=White. )

By plotting only on even bars, (even X coordinates), violet plots can be made. However, since we are plotting only 50% of the points on the screen, horizontal resolution decreases to 140 points. Plotting green is exactly the same as plotting violet, except plotting is done on the odd-numbered bars (odd X coordinates ).

The HIRES routines produce the four available colors, (green, violet, white and black) by allowing us to mask off either the green or violet bars. Remember, when you set the HCOLOR variable (or location 81210) for green, that a point may not be plotted on a violet bar, and the inverse is true for plotting violet on a green bar.

Appending Applesoft

By Val J. Golding

Here are simple routines that will allow you to append programs in both version of Applesoft. While appending can be done under program control in App. II, it is really simpler to do it without. The routines are the same in both versions; only the pointers have been changed (to protect the innocent!). A word of caution: Apple soft programs store in memory just the opposite of Integer Basic, i. e., from the bottom up. Therefore, the first program appended should be the one with the lowest line numbers. You will get an error message in Ap. I, which should be disregarded. The secret is in changing the values of the pointers, 106 & 107 in App. I or 103 & 104 in App. II.  This hides the first program while the second is being loaded.  To do  this, these pointers must be set to equal the value of the program pointer, less 2.

In Ap. I the PP is 112 & 113; in Ap II it is 109 & 110. Here is the routine; just use the proper pointers for version I or II.

POKE 103, (PEEK(109)-Z)
POKE 104, PEEK(llO) I .
LOAD

POKE 103, l:POKE 104, 48 (for Ap. II)
POKE 106, I:POKE 107, 42 (for Ap. I)

Do not attempt to list, run, renumber, etc. until all of the routine has been completed or it will blow your Applesoft.