home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-02-29 | 101.9 KB | 2,335 lines |
-
-
-
-
-
-
- Issue #10 March, 1992
- ========================================================================
- The Pascal Newsletter (C) Copyright 1992 by Alex Boisvert
- ALL RIGHTS RESERVED
-
- [][][][][] [] [] []
- [] [] [][] [] []
- [] [] [] [] [] []
- [][][][][] [] [] [] []
- [] [] [] [] []
- [] [] [] [] []
- [] [] [][] []
- [] [] [] [][][][][]
-
- T H E I N T E R N A T I O N A L P A S C A L N E W S L E T T E R
-
- ========================================================================
-
- Table of Contents
-
- (*) Introduction ................................................. 2
- by Alex Boisvert - Editor, Writer
- (*) BigFiles - A Large File Viewer................................ 3
- by Mich Davis - Contributing Writer
- (*) BigArrays - You've Never Seen Arrays This BIG!................ 10
- by Mich Davis - Contributing Writer
- (*) High-Quality Sound with Turbo Pascal.......................... 13
- by Alex Boisvert - Editor, Writer
- (*) Overlays - How to fit your programs in memory................. 15
- by Alex Boisvert - Editor, Writer
- (*) Turbo Vision Without Getting GUI.............................. 19
- by Richard Morris - Editor Over-the-Pond
- (*) Answers to Frequently Asked Questions ........................ 26
- by Trevor Carlsen - Contributing Writer
- (*) Running 80286 programmes on 8088/8086 Computers............... 37
- by David J. N. Begley - Contributing Writer
- (*) Conclusion ................................................... 41
- by Alex Boisvert - Editor, Writer
-
-
- The Pascal Newsletter is published by Alex Boisvert as a medium for all
- Pascal programmers to share ideas, comments, techniques or experiences
- concerning any subject related to the Pascal programming language.
-
- DataMAX BBS is the home of PNL. It can be reached in Sherbrooke,
- Quebec, Canada at (819) 563-6327 or FidoNet @ 1:167/405.0 between 23:00
- and 8:00 Eastern Standard Time.
-
- Articles and source code submitted by others are the property of the
- authors and are used with permission. They may not be treated
- separately from this newsletter without the author's permission and thus
- maintain all distribution rules of the newsletter as a whole. It may be
- freely distributed in un-modified form, but no charge whatsoever may be
- incurred on the recipient. All code is provided 'as-is' with no
- guarantees whatsoever.
-
- ========================================================================
- PNL #10 Page 2 March, 1992
-
-
-
- =================================================================
- Introduction - Editor's Note
- =================================================================
-
- After more than a year of silence, the Myth becomes a reality.
- The Pascal NewsLetter has revived.
-
- Many people will ask themselves why this is issue #10 and why wasn't PNL
- published during the last year.
-
- The facts. Pete Davis, founder and ex-editor of PNL, had personal
- problems and was forced to sell his computer. He was unable to continue
- his work.
-
- I must thank him for the outstanding work he has done with PNL. He
- worked very hard to keep the issues out and each of them was a success.
-
- For the sake of the Pascal community, I am taking up the baton. I will
- be publishing the Pascal NewsLetter starting with issue #10 and up. I
- expect to be publishing one issue bimontly, if I have sufficient
- material (articles) to fill it. Since I am starting from scratch,
- everything has to be done a second time. I must find new columnists,
- contributing writers and people to distribute the newsletter all around
- the world. Richard Morris will be distributing it in Australia but, I
- have yet to find a distributor in Europe and, possibly, in Asia.
-
- As you may have noticed, I have changed a bit the format of the
- newsletter. The table of contents now shows on the first page. This
- will be handy if you are keeping the newletters on print.
-
- I hope you all enjoy the "New PNL" and I wish that more people get
- interested in it.
-
- Alex Boisvert
- PNL's New Chief Editor.
- PNL #10 Page 3 March, 1992
-
-
-
- =================================================================
- BigFiles - a large file viewer
- =================================================================
-
- G'Day! Well, it sure is good to see the Pascal NewsLetter alive and well
- again. In this article and the followup in the next edition, we'll be
- working on a very useful tool - a technique for accessing large text
- files.
-
- On the way we'll be exploring a LOT of really neat techniques all rolled
- into the one program. Unless you're a closet guru, I'm SURE you'll find
- something you haven't come across yet. We'll be dabbling in:
-
- o Take-only-what-you-need dynamic memory allocation.
- o Vertical Scrolling.
- o Turbo Pascal's Built-in Assembler.
- o Using DOS to allocate memory.
- o Treating TEXT files as TYPED files.
- o Buffering Text files to improve access speed.
- o A technique for quick access to any part of a text file.
- o Objects.
- o Very large arrays (bigger than 64k in size, or more than 64k
- elements).
- o A standalone program for viewing text files, a la Buerg's LIST.
- o i286-capable programs.
- o Horizontal scrolling.
- o Multitasking.
- o Turbo Vision.
- o Optimization using Turbo Profiler.
- o Optimization using the built-in assembler.
- o DesqView-aware programs.
-
- This is all done with Turbo Pascal 6.0. I apologise to those who don't
- have 6.0, but that's progress.
-
- Part 1:
-
- [Note: I have a 286, so I compile my programs with $G+ on. If you don't,
- remove the $G+ from compiler options, and the "Test186" from the
- uses-block of each program it appears in.]
-
- What we aim to have by the end of this article is a tool which will allow
- us to view arbitrarily large text files. Each of the included programs
- builds on the one before it, starting with MDP1.PAS.
-
-
- The way we'll first explore is to read the lines of the file into an
- array of strings. MDP1.PAS is about as simple as you can get! It has
- two sections - the first section opens the file, and the second prints it
- forwards and backwards. Note that this series of programs get the name
- of the file to show from the command line, ie MDP1 c:\autoexec.bat. If
- you forget to give it a filename, the program will try to read from the
- keyboard. You can exit by pressing ^Z and Enter. Note that IDE users
- can set the command-line parameters by selecting [R]un, p[a]rameters
- inside the IDE, and [R]un, [A]rguments inside Turbo Debugger.
-
- Note that this program is extremely limited:
- PNL #10 Page 4 March, 1992
-
-
-
- o It is wasteful of resources to declare an array larger than we'll
- need, but for many files, 100 lines won't be enough.
-
- o The maximum number of lines we can possibly extend this approach to,
- is about 250 lines.
-
- For these reasons, MDP1.PAS is a bit of an evolutionary dead-end.
-
- MDP2.PAS is a little better. This program works by reading the file
- line-by- line, and storing it IN ONLY AS MUCH MEMORY AS IT NEEDS to store
- it. Thus, files with (on average) shorter lines will be able to have
- more lines loaded before the computer runs out of memory.
-
- This program introduces the idea of a table to point to the start of the
- lines in the file. Once we have this table, it's a simple matter to look
- up the line, get the address of that line in memory and print it out.
-
- Note the table is made of records, with a 4-byte pointer field for the
- start of the line, and a word for the size of the line. We need to keep
- the size so that later we can feed the size to FreeMem - TP doesn't keep
- track of how big the memory chunk it allocates you with GetMem is, so you
- have to remind it at FreeMem time.
-
- MDP2.PAS is limited by two things - It stores the lines from the file on
- the heap, so can only handle files of about half a megabyte or less.
- Secondly, the table of line pointers is limited to about 10000 entries,
- since the address table can't be larger than 64k (ie, a little larger
- than 6*10000).
-
- Now let's turn our attention to something different - scrolling. Take a
- look at MDP3.PAS.
-
- Perhaps the best way to see what it does is to run it. When you do,
- you'll see a line of numbers, which you can move along by using the + and
- - keys.
-
- This program has two parts. The first draws the initial screen. Note
- the call to min (minimum) in the For - this roughly translates to "draw
- until you hit the end of the screen, or until you run out of numbers".
-
- The second part of the program is the standard repeat-until/readkey/case
- input loop. WinTop by-the-way is the line number which is currently
- displayed at the top of the screen.
-
- Note that the - and + sections scroll the screen by using Crt's DelLine
- and InsLine. An unfortunate consequence of this is that the status line
- at the bottom of the screen flickers quite badly when scrolling takes
- place. To stop this, you'd either have to put the status line at the top
- of the screen, or declare a Crt.Window. I've done neither since I didn't
- find it all that annoying. The followup article in the next PNL WILL
- address this problem.
-
- It's a good idea to step through this program using F7 to get an idea of
- just how it performs the scrolling. Note that after the initial redraw,
- only the lines that are just appearing are rewritten - this is faster,
- and reduces the inevitable screen flash (not CGA snow) that occurs when
- you update a screen line-by-line.
- PNL #10 Page 5 March, 1992
-
-
-
- Now let's have a look at MDP4.PAS. This is a hybrid - it combines the
- file reading section from MDP1.PAS with the scroller from MDP3.PAS, so it
- shouldn't contain too many surprises. Note that in the scrolling
- section, the writeln's which outputted numbers have been replaced with a
- reference to the data at that line position (eg, line [WinTop]). This is
- then passed to procedure PrLn, which acts like a writeln, except that it
- chops off lines that are longer than 79 characters. If you don't do
- this, a long line will overflow onto the next, spoiling the display.
-
- Until now, the programs we've looked at have been fairly elementary.
- Well, you've been warned! Programs after this point get quite a bit
- harder to understand, for many reasons:
-
- o They're more sophisticated.
- o They use techniques not seen in everyday Turbo Pascal programs.
- o I wrote them, and you're having my less-than-perfect programming style
- inflicted on you. Sorry! :-D
-
- Let's look at three interesting areas before we proceed to MDP5.PAS. The
- first is a device which dramatically speeds up TEXT file access -
- SetTextBuf.
-
- SetTextBuf is part of Turbo Pascal's Run-Time Library [RTL]. It lets you
- tell Turbo Pascal to use a given area of memory as a cache or buffer when
- dealing with a text file. Let's say we had the following fragment of
- code:
-
- var TBuff:array [0..8191] of byte;
- f:text;
- line:string;
-
- begin
- assign (f,'whatever.doc');
- SetTextBuf (f,TBuff);
- reset (f);
- readln (f,line); readln (f,line);
- close (f);
- end;
-
- Without the SetTextBuf, the two readlns would probably have cause two
- disk accesses. Instead, the RTL will now try to fill TBuff with as much
- data from f as it can. Then, requests to access the file come out of
- TBuff, instead of an access to the disk.
-
- If you don't already, try it in your next program which deals with lots
- of text files - you'll be pleasantly surprised at the speed increase!
-
- The second concept overcomes an annoying limitation with Turbo Pascal -
- when you're reading TEXT files, you have no way of using the FilePos
- function to find out where you are, or the Seek procedure to reposition
- yourself. To get around this, let me introduce to you ("How do you do?")
- a unit written by a fellow Australian - TextUtl2. This unit lets you
- perform FilePos, FileSize and Seek on text files, and is a real life
- saver!
-
- Third, the idea of going to DOS for memory, rather than Pascal's
- GetMem/FreeMem. Why? Because Turbo Pascal is limited to handing you
- blocks of 64k at a time, whereas DOS will happily give you a couple of
- PNL #10 Page 6 March, 1992
-
-
-
- hundred k's of consecutive memory, which we'll be using for our mega
- arrays.
-
- Take a look at MDP5.PAS. The first thing to note is that now we don't
- read in the actual data from the file - all we do is store the file
- OFFSET of each line. Later when we want to go to a certain line, we'll
- look up its offset, Seek to that position, and happily read away. This
- lets us view up to 16000 lines, which is the limit of Vern Buerg's LIST
- program.
-
- Observe how the text buffer is set up: TBuffSize is set to how many K we
- want the buffer to be (I've picked 20k). Then TBuffType is declared to
- be an array of that size in K's. Note that the buffer differs from the
- code fragment we saw before, because it'll come off the heap, instead of
- being a static variable.
-
- Note the call to TextFilePos on line 47. This shows just how easy it is
- to use the TEXTUTL2 unit. Before each line is read, we store the offset
- for that line for future reference.
-
- If you're wide awake, you'll see that line 48 we use a readln without
- telling it where to put the data it reads. This is because we're not
- interested in what's in the line, just where they start. The reading of
- the data will be done later.
-
- So, the program reads in the file, and asks you for the starting line
- number. When you've typed a valid number in, line 64 looks up the offset
- of that line, and moves the file pointer to that position. The lines
- following that read the file to cover the screen, either until the bottom
- of the screen is hit, or the end of the file occurs.
-
- Note line 71 - this is a shady trick! If you use Crt.KeyPressed, then
- even when it detects a key it leaves it in the keyboard buffer. So, the
- program pauses at line 71 until you press a key. When you do, it loops
- back to the readln in line 61, which gets its first key free - the one
- you pressed.
-
- This trick lets you prompt for information in a way which isn't going to
- spoil your nice screen layout until the user is ready to type something.
-
- Note that you only have to perform ONE TextSeek for a whole screen. You
- COULD perform a TextSeek for EVERY line on the screen, but why bother?
- Reading a line leaves the file pointer (and cursor!) sitting fair and
- square on the next line, all ready for you to use.
-
- Note in line 75 we Dispose of TBuff AFTER the file has been closed.
-
- Now we're going to sidetrack for a moment. Just for an experiment, we're
- going to ask DOS for the memory for TBuff, instead of TP, which uses the
- heap.
-
- This program will be called MDP5A.PAS ("A" because it's an experiment).
- But wait! Do I hear you say there's no MDP5A.PAS included? You're
- right. Instead, I've put the DIFFERENCES between MDP5.PAS and MDP5A.PAS
- into a file called MDP5A.EDL. This file is really a script for EDLIN
- (and you thought you'd never use EDLIN again eh?!). To get MDP5A.PAS,
- type this at the command line:
- PNL #10 Page 7 March, 1992
-
-
-
- C:\PNL010> edlin MDP5A.PAS < MDP5A.EDL
-
- ... and this will create MDP5A.PAS.
-
- In fact, I've built a Turbo Vision revision control system which uses
- purely DOS commands to do the nitty gritty work - it compares two files
- with FC, and generates an Edlin script for the differences. I'm telling
- you this because it'll be in a future edition of the Pascal Newsletter,
- so keep reading! (And keep writing, else there won't BE a future
- edition!)
-
- Note that MDP5A.PAS uses a unit called DosMem. I've written this as an
- easy-to-use interface to the DOS memory functions. There are three
- modules:
-
- function Alloc(paras:word): word;
- procedure Free(p:word);
- function Largest: word;
-
- Note that each of these works with "paragraphs", which on PCs means
- "16-bytes". Alloc is like TP's GetMem - it'll give you "paras"
- paragraphs of memory, returning with the segment of the block. If the
- request can't be met, it will return a segment of zero. Free is like
- TP's FreeMem - it'll hand the block at "p" back to DOS when you've
- finished with it. Note that you don't have to tell DOS how large the
- block was - now if only FreeMem would do that! Largest is like TP's
- MaxAvail - it'll tell you the largest size block you can ask for. The
- result is in paragraphs.
-
- Note that the DosMem unit is written using the Built-in assembler.
- Sorry, pre-6.0 people. Needle me enough and I'll write you a .OBJ or
- some inline code which does it.
-
- Ok, back to business. Line 39 of MDP5A.PAS performs the same function as
- line 39 in MDP5.PAS, except that it asks DosMem.Alloc for the memory.
- Because DosMem uses paragraphs, we have to convert the buffer size in K's
- (20) to paragraphs. We do this by multiplying by 64. (20k * 64 = 1280
- paras = 1280 * 16 = 20480 bytes = 20k).
-
- Note that TBuff is a pointer type, with two halves - the segment, and the
- offset. The segment comes from the result of Alloc, and the offset will
- always be 0. The two are joined into a standard TP pointer by the Ptr
- function.
-
- The other lines that have changed (57 and 75) pass the segment of TBuff
- back to DOS - note that you do this AFTER the file is closed.
-
- The reason we change a perfectly good program (MDP5.PAS) into one that
- has to rely on DOS (!) is to give you a feel for working with DOS memory,
- which is what we'll be doing in the next program.
-
- MDP6.PAS is the first of our programs to use the BigArray unit, which is
- covered in another PNL article. Basically, the BigArray unit has an
- object type called BigDOSArray, which makes use of the >64k ability of
- DOS's memory management to implement a VERY large array.
-
- We'll be using one mega-array, and our data type is the longint, which
- takes 4 bytes. So, line 44 tells the array object all about it. Line 45
- PNL #10 Page 8 March, 1992
-
-
-
- asks the object to say how many elements it could possibly hold, of the
- type you've told it. In this case, we'll be using an element for every
- line, so line 46 tells us how many lines our program is capable of
- accessing.
-
- Line 47 asks the object to set up the array as LARGE as it can possibly
- be made. Note that this will consume ALL DOS memory. If you were using
- more than one of these objects, you'd probably not pass it the maximum.
-
- Line 55 is where things start to get interesting. One of BigDOSArray's
- methods is called Elem. If you pass it the element number, it will
- return with a pointer to where that element is stored in memory. So Elem
- gets passed the line number, and LinePtr then points to where we put the
- offset. Line 56 shows how the array gets its value.
-
- Because LinePtr holds the element's address, we could just as easily read
- from LinePtr^ as written to it, and that's what happens in lines 73 and
- 74. We find the offset of the line we want, we seek to the position, and
- we read the file, just like we did in MDP5.PAS.
-
- One last point - note the calls to the Done method of LineBank in lines
- 66 and 86. This hands back the memory the array consumed back to DOS.
-
- We now have the shell of a fairly powerful program. On my machine, it
- says it's happy to handle over 130,000 lines (60,000 in the TP IDE). The
- fidonet nodelist (052) is 16302 lines long, and in a few weeks, Vern
- Buerg's LIST program isn't going to be able to handle that. At least
- you've now got something that can!
-
- If you've understood everything so far, then relax - from now on, things
- are all down hill. MDP7.PAS is just MDP6.PAS with the scrolling stuff we
- worked out in MDP4.PAS. It covers nothing new, just merges the two
- techniques we developed before.
-
- MDP8.PAS is as far as we're going to take things this issue. Still, it's
- quite a program! It's been split up into separate procedures (as it
- should have right from the beginning!), and a number of features have
- been added. First, it shows you a percentage as you load the file. This
- is useful when you're loading the FidoNet nodelist, or an online version
- of the Bible [one of the best reasons to download it - it gives you a
- great exerciser for your text-processing programs! ;-D]
-
- Also, it now responds to extended keys, such as the arrows, and the PgUp/
- PgDn/Home/End keys. ESC has replaced 'q' for exitting, and you can press
- # to jump to a certain line. This is accomplished by having TWO CASEs -
- one handles extended keys, and the other, normal files.
-
- I'm the first to admit that it's not the fastest thing around. That's
- why in the next issue of PNL we'll discuss optimising it. We'll use the
- profiler to investigate where the program is spending the most time
- (everywhere, ie, equally slow!). We'll rewrite a large chunk of it in
- assembler, and we'll use multitasking to remove the annoying delay at the
- start of the program.
-
- Also, we'll discuss how we'd port the program to TurboVision, and how to
- have it run RIGHT under DesqView. Finally, we'll see what we can do
- about viewing lines that are longer than the screen by horizontal
- scrolling.
- PNL #10 Page 9 March, 1992
-
-
-
-
- Until next issue,
-
- Make merry with your Pascal!
-
- +-------------------------------------------+
- | Mitch Davis |
- | 10/1072 Whitehorse Rd., |
- | Box Hill, Victoria, Australia. 3128. |
- | |
- | Ph. +61-3-890-2062. Fidonet: 3:634/384.6 |
- +-------------------------------------------+
- PNL #10 Page 10 March, 1992
-
-
-
- =================================================================
- BigArray - you've never seen arrays this BIG!
- =================================================================
-
- *** Error 22: Structure too large.
-
- *** Error 49: Data segment too large.
-
- *** Error 96: Too many variables.
-
- *** Runtime error 201 at XXXX:YYYY. {Range check}
-
- *** Runtime error 203 at XXXX:YYYY. {Heap full}
-
- How many times have you been frustrated by these little beauties? How
- many times has it been because you had an array that just wouldn't stay
- down? There are two ways to fix this - you could put your thinking on a
- diet and work out some other way that doesn't consume quite so much
- memory (recommended!) or you could do it the lazy way, and slot in a unit
- called the (drum roll!) *** BigArray ***.
-
- BigArray will give you arrays which can be as big as will fit in
- conventional memory, ie, they aren't limited to 64k. The arrays can be
- of any type, and are of a single dimension. (A version being written now
- lets you have arrays that spill over to disk or EMS, as well as multi
- dimensionals.)
-
- The large arrays are implemented as objects. You use the .SetElemSize
- method to tell it how big your elements are, and you can subsequently
- find out via .GetMaxSize how many elements the largest array can be.
- Then you call the Init method with the number of elements you'd like, and
- voila, your array is done! When you're finished with the array, call the
- .Done method.
-
- How are elements accessed? You pass the element subscript to the .Elem
- function, and it returns a pointer to that element. The idea is that you
- then assign that pointer to a pointer variable of the type you're
- storing. Then by deferencing the pointer (following it by a caret [^])
- you can then access any fields, bits, WHATEVER that you would with a
- plain-jane variable.
-
- Let's look at a sample of how you'd use them:
-
- program BigArrayTest;
-
- {Program to accompany article in issue #10 of the Pascal NewsLetter. }
- {Author: Mitch Davis, (3:634/384.6) +61-3-890-2062. }
-
- {$G+,M 16384,0,0} {Make SURE you tell TP not to steal all the DOS memory }
- {for a heap! If you're not using dynamic variables, }
- {set both numbers to 0. }
-
- {This program provides a nonsense demonstration of how you use the tools }
- {in the BigArray unit. }
-
- uses Test186, BigArray; {I run my programs on a i286. If you have an XT,}
- {Remove the "G+," from above and the "Test186" }
- {from the uses section. }
- PNL #10 Page 11 March, 1992
-
-
-
-
- type PigeonType = record
- value:real;
- changed:boolean;
- end;
-
- var PigeonHole:BigDosArray;
- PigeonPtr:^PigeonType;
- PHnum, MaxSize:longint;
- GlobalChanged:boolean;
-
- begin
- writeln ('Welcome to the pigeon-hole.');
- with PigeonHole do begin {This sets up the big array.}
- SetElemSize (sizeof (PigeonType));
- {Tells it how big each element will be}
- MaxSize := GetMaxSize;
- Init (MaxSize); {Make it as big as possible.
- This is not compulsory. }
- end;
- writeln ('There are ',MaxSize,' pigeon-holes, numbered from 1 to '
- ,MaxSize,'.');
- write ('Please wait while I clear them... ');
- for PHnum := 1 to MaxSize do begin
- PigeonPtr := PigeonHole.Elem (PHnum); {Get the address of the element}
- PigeonPtr^.Changed := false; {Reference the changed field within that}
- end; {element. }
- writeln ('Done.');
- GlobalChanged := false;
- {This will save us search time later if no changes}
- repeat
- write ('Which pigeon hole? (1-',MaxSize,', 0 to quit): ');
- readln (PHnum);
- if (PHnum > 0) and (PHnum <= MaxSize) then begin
- PigeonPtr := PigeonHole.Elem (PHnum);
- with PigeonPtr^ do begin
- case Changed of
- true :writeln ('That pigeon-hole has the value of ',Value:3:2);
- false:writeln ('That is a new pigeon hole.');
- end;
- write ('Change it to? ');
- readln (Value);
- Changed := true; {means this ph will be shown in the end summary}
- end;
- writeln ('Pigeon-hole changed.');
- GlobalChanged := true;
- end;
- until PHnum = 0;
- writeln ('-------------------------------------------------');
- case GlobalChanged of
- false:writeln ('You didn''t change any pigeon holes.');
- true :begin
- writeln ('The pigeon holes you changed were:');
- write ('Wait..',#13);
- for PHnum := 1 to MaxSize do begin {scan thru the ph's}
- PigeonPtr := PigeonHole.Elem (PHnum);
- with PigeonPtr^ do
- if changed then writeln (PHnum,': ',value:3:5);
- PNL #10 Page 12 March, 1992
-
-
-
- end;
- end;
- end;
- writeln ('Thanks for using the pigeon-holes!');
- PigeonHole.Done;
- end.
-
- If you understand this (in conjunction with the "interface" section of
- the BigArray unit), then you should have no problem working out how to
- use it.
-
- A few usage notes:
-
- o The Big Arrays are 1-based, that is if you call
- BigDOSArray.Init(10), then the element subscripts run from 1 up to
- 10.
-
- o There is NO checking to ensure that the element you pass to
- BigDOSArray.Elem is within the defined range. If it isn't, then
- you'll get garbage results for the pointer result. Beware!
-
- o The code for BigDOSArray hasn't been extensively tested. Caveat
- Emptor!
-
- o BigDOSArray needs optimising BADLY! I plan to write it in
- optimised assembler throughout; in the meantime, you'll have to
- sacrifice speed for convenience.
-
- o I am aware that Turbo Power markets code which implements large
- arrays (and probably MUCH better than I've managed here!). I
- haven't looked at their code. Trevor Carlsen's OverSize unit also
- performs a similar function. I looked at his code, and couldn't
- understand it. That's not meant as a slur on Trevor (3:690/644),
- who writes code so good it should be framed. I just thought I'd
- take a different approach. Upshot: If any of the other large-array
- products fit your bill better, use them!
-
- o The BigDOSArray unit currently compiles in i286 mode. If you have
- an XT or similar, chop out the G+ and Test186 bits, in the same way
- as the comments in the demo program mention.
-
- Well that about wraps it up. I'd be more than happy to answer any
- queries you have about the unit. Next issue, time and space permitting,
- I'm planning on having a followup article which goes into optimisation,
- multi-dimensional arrays, etc. Till then? Make merry with your Pascal!
-
- +-------------------------------------------+
- | Mitch Davis |
- | 10/1072 Whitehorse Rd., |
- | Box Hill, Victoria, Australia. 3128. |
- | |
- | Ph. +61-3-890-2062. Fidonet: 3:634/384.6 |
- +-------------------------------------------+
- PNL #10 Page 13 March, 1992
-
-
-
- =================================================================
- High Quality Sound in Turbo Pascal... Not Really.
- =================================================================
-
- Many Turbo Pascal programmers find that the SOUND procedure does not
- produce anything interesting as far as music goes. This is not due to
- Turbo Pascal's limitation to use the speaker but due to the speaker
- itself. The original PC speaker is almost useless... Unless you happen
- to have the public-domain utility called RESPLAY, written by Mark J. Cox
- and (of course) Turbo Pascal.
-
- --- From RESPLAY v1.0 documentation: ---
-
- "RESPLAY is a memory resident program designed to help high-level
- language programmers make the most of the PCs useless speaker. By use of
- some simple procedures, you can playback digital samples from within your
- programs and sample yourself. Playback can either be to the PC speaker
- (no too bad) or to some external hardware [...] "
-
- Possible uses of RESPLAY:
-
- o Arcade Games ("He's Dead Jim!" or realistic shooting noises)
- o Sound Analysis (Fast Fourier Transforms/ analysing spoken words)
- o Spelling Games for kids.
- o Sampling your friends and making them sound silly (by changing
- their speed, or by FFTs and altering their pitch!)
- o The next 'Jive Bunny' single
-
- ------------------------------------------
-
- Resplay is fairly easy to use from Turbo Pascal because it uses interrupt
- calls. Simply fill the register set and call interrupt 2F. If you have
- any knowledge of assembler, RESPLAY will work for you!
-
- The original archive file of RESPLAY contains a C source code that I have
- "ported" to Turbo Pascal. I say "ported" because the two programs work
- differently with memory (loading & storing digital samples) but they both
- do the same thing: Play a digital sample file.
-
- I have done much of the work for you. I have created a Pascal unit which
- can read and play any sample. I have used TP's INTR procedure to call
- the interrupt instead of direct BASM instructions for compatibility
- purposes. Turbo Pascal v5.5 users will be able to use this code without
- changing one line of code.
-
- The unit is name DIGISND.PAS and the program is PLAYDIGI.PAS.
-
- I must admit that the program is not fully optimized. It reads the
- sample file by allocating 64k memory segments and plays these segments
- one after the other... I have played samples up to 350k with it. If the
- sample is bigger than 64k, you will notice some delay (under 10 ms.)
- between each 64k "chunck". It is possible to tell RESPLAY to play the
- whole sample if the sample is stored continuously in memory. I have
- decided to play the game safely and play the samples by 64k segments.
-
- The code is object-oriented so that anyone wishing to modify it may do so
- by creating a descendant object type. Someone might want to add a method
- to play only part of the sample read (eg. from time x to time y), play
- PNL #10 Page 14 March, 1992
-
-
-
- the sample on a COVOX card instead of the PC speaker, etc...
-
- Please refer to RESPLAY1.ZIP before modifying the unit.
-
- [Editor's Note: RESPLAY1.ZIP has been included in the distribution
- archive of PNL#10. It contains the original documentation and a digital
- sample.]
-
- By the way, I have tested RESPLAY with my program on a COVOX card and the
- resulting "music" is better than with a SoundBlaster card. Also, try
- getting Macintosh (Yes, Macintosh!) digital samples, they work just fine
- with RESPLAY.
-
- Hope you have fun with your PC speaker now!
-
- Alex Boisvert
- DataMAX Communications Enr.
- FidoNet @ 1:167/405
- PNL #10 Page 15 March, 1992
-
-
-
- =================================================================
- Overlays - How to fit your programs in memory
- =================================================================
-
- Overlays in Turbo Pascal help you, the programmer, to write programs that
- otherwise would not fit into the 640k limitation of DOS. They can also,
- in special cases, reduce your code's memory requierement in order to get
- more memory for dynamic allocations.
-
- To accomplish this, overlays are "parts" of your programs that are stored
- on disk and which are controlled by the program, meaning that they are
- loaded in memory only when they are needed. The principle is to allocate
- the same (limited) memory space to several sections of program... at
- different time obviously.
-
- The major drawback of using overlays is the time involved in loading the
- useful sections at run time, or "on-the-fly". You will therefore use
- this method only when available memory is less than your program's size.
- Your programs will then become independent of RAM memory available, to
- which the computer has access to.
-
- The developpers' of Turbo Pascal have kept to a minimum the rules to obey
- when using the overlays. Management of overlays is handled by the
- routines of the OVERLAY unit, which you will have to add to your USES
- statement at the beginning of your main program.
-
- The smallest portion of code which can be declared as "overlayed" is a
- unit. With Turbo Pascal, it is not possible to overlay specific
- routines. Each unit is loaded in the heap memory zone, which is the
- reserved memory between the stack and the memory of dynamically allocated
- variables.
-
- When using overlays, you must declare all your program's routines as FAR.
- The easiest way to do this is to add the following compiler directive
-
- {$F+},
-
- just after your program's header. Any unit compied with the directive
-
- {$O+},
-
- can then be compiled as an overlayed unit, but they do not have to. This
- directive will tell the compiler to produce the needed overlay control
- code.
-
- The following compiler directive
-
- {$O UNIT_NAME}
-
- will indicate if a unit specified in the USES statement will, in fact, be
- overlayed. It is therefore possible to use this unit without
- "overlaying" it simply by using the USES statement and ommiting the {$O
- UNIT_NAME} directive. This unit would then be treated "normally", as any
- other unit declared by the USES statement.
-
- This represents an alternative. You can overlay the unit or not...
- depending on the program using it.
- PNL #10 Page 16 March, 1992
-
-
-
- When you use the overlay technique, you have to specify the OVERLAY unit
- in the USES statement and, initialize overlay management by calling the
-
- OVRINIT('filename.OVR'),
-
- routine. It is best if this is done right at the beginning of the
- program. Here is an example:
-
- PROGRAM myprogram;
- {$F+} {forces FAR calls}
- USES
- OVERLAY, CRT, {TP 6 units}
- myunit1, myunit2, myunit3; {your own units}
-
- {$O myunit1}
- {$O myunit2} {Overlay these two units ... }
- {Note that "myunit3" is overlayed.}
-
- {...}
- {declaration goes here}
- {...}
-
- BEGIN {main program}
- OVRINIT('MYPROG.OVR');
- {...}
- {other commands here}
- {...}
- END.
-
- In this case, the compiler creates two files: MYPROG.EXE, the executable
- file, and MYPROG.OVR, the overlay file.
-
- Now, the following example will demonstrate the basics of overlay
- management. The program will integrate two units, OVER1 and OVER2, and
- call two routines that they contain: HELLO1 and HELLO2. These two
- procedures will simply display a message.
-
- Here are the two overlays:
-
- Unit OVER1; {OVER1.PAS}
- {$F+,O+}
-
- Interface
- procedure HELLO1;
-
- Implementation
- uses
- crt; {TP unit}
-
- procedure HELLO1;
- begin
- writeln('Hello from unit OVER1.');
- delay(1000);
- end;
-
- end. {end of unit OVER1}
- PNL #10 Page 17 March, 1992
-
-
-
- Unit OVER2; {OVER2.PAS}
- {$F+,O+}
-
- Interface
- procedure HELLO2;
-
- Implementation
- uses
- crt; {TP unit}
-
- procedure HELLO2;
- begin
- writeln('Hello from unit OVER2.');
- delay(1000);
- end;
-
- end. {end of unit OVER2}
-
- Now, compile the sources of the two overlays. Once this is done, your
- disk will contain two source files OVER1.PAS and OVER2.PAS and also their
- corresponding compiled TPU unit OVER1.TPU and OVER2.TPU. Do not forget
- to select the option "Compile/Destination" to Disk, otherwise, the two
- TPU files will not be created.
-
- The main program looks like this:
-
- program OverlayDemo; {OVRDEMO.PAS}
- {$F+}
-
- uses
- overlay, crt, {TP units}
- over1, over2; {overlays}
-
- {$O over1}
- {$O over2}
-
- var
- i: integer; {loop counter}
-
- begin {main program}
- ovrinit('ovrdemo.ovr'); {initialization}
- for i:= 1 to 10 do
- begin
- clrscr;
- Hello1; {call routine in unit OVER1}
- Hello2; {call routine in unit OVER2}
- end;
- write('Program is finished. Please press [ENTER].');
- readln;
- end. {program}
-
- The two units merged together to form the overlay OVRDEMO.OVR when
- compiling the program. Compiling will also (obviously) create the
- executable OVRDEMO.EXE.
-
- The overlay file must always reside in the same directory as the
- executable file but it is also possible to merge the overlay file to the
- .EXE file with the command
- PNL #10 Page 18 March, 1992
-
-
-
-
- COPY /B ovrdemo.EXE + ovrdemo.ovr
-
- which, in fact, appends the overlay at the end of the .EXE file. In this
- case, the command OvrInit('ovrdemo.EXE') will initialize the overlays
- integrated in the .EXE file.
-
- As you have seen by now, the overlays are not difficult to implement.
- Just a few modifications around the USES statements and the addition of
- the compiler directives {$O ...} and {$F+} can lead to a program which is
- less RAM memory dependent. You may wish to consult your Turbo Pascal
- manuals for further informations on using EMS memory with overlays,
- clearing the overlay buffer (increasing dynamically-"allocatable" memory)
- and modifying its size.
-
- I have used overlays largely with the Object Professional Library from
- Turbo Power because object-oriented programs tend to create larger
- programs because the compiler cannot easily "strip" unused virtual
- objects out of the .EXE file.
-
- For obviously reasons, another drawback of using overlays is that your
- program cannot be compressed by a program like PKLITE or LZEXE, which
- dynamically uncompress your program in memory when executed. The overlay
- file has to stay in its original uncompressed form on disk because this
- is how your .EXE file expects to read it.
-
- Alex Boisvert
- DataMAX Communications Enr.
- FidoNet @ 167/405
- PNL #10 Page 19 March, 1992
-
-
-
- =================================================================
- Turbo Vision without getting GUI.
- Copyright (c)1991 Richard A. Morris
- =================================================================
-
-
- INTRODUCTION
-
- Around November of 1990, as most of us know, Borland released Turbo
- Pascal 6.0, and with their add-in toolbox Turbo Vision unveiled a new
- Programming Methodology for Turbo Pascal called Event Driven Object
- Oriented Programming.
-
- Well, did the Fewmets hit the Windmill or What !?!
-
- Erstwhile conservative commercial programmers ranted that TV was every
- thing from a Goblin to a toy toolbox destined to be unsupported by
- Borland, they complained that it consisted merely of cute saccharine
- windows, of little or no practical use in professional projects. The
- silence from a lot of Third-Party Code Shops, who had always provided
- outstanding and timely Developer support packages, was deafening as they
- waited to see how the programming community would react (Blaise being the
- only exception). In a classic Catch-22, Programmers have been warily
- watching Borland to see if they will support Turbo Vision, Meanwhile
- Borland, and third-party Code shops, quietly had their eyes on the Turbo
- Pascal community to see if TV was to become popular enough to support,
- potentially making the bitching of disgruntled users a self-fulfilling
- prophesy.
-
- It's now a year on, slowly but surely Programming journals, and Reference
- Books are starting to constructively cover the complexities of TV (Most
- notably Messrs. Rubenking and Duntemann, thank you sirs). OK so why do
- so many good Pascal programmers have so much dirt to throw at TV, surely
- they must know what they're talking about? Well TV is a not merely
- another toolbox full of useful functions, but rather an application
- framework that impertinently confronts the programmer to completely
- rethink his/her programming design, from scratch. It's a big ask for a
- lot of us who have several years of training invested in our trade. Most
- of us looked at the Demos, and thought 'Well I'm certainly not going to
- put my name on such a "Mickey-Mouse" program. Wonder if I can make it
- look a bit more professional?', had a look at the code and found it too
- difficult to personalize the interface, and so gave up any attempt at
- using it, and concentrated on our own custom interfaces that we'd
- invested so much skull sweat upon.
-
- Two programmers I have much respect for, convinced me by their comments
- to take a second look at TV. Is it worth worrying about then, well in a
- my native vernacular, 'Bloody Oath'! I must add that it took me several
- months of painful re-education to bend my Object Oriented programing
- abilities around TV. Turbo Vision is certainly no beginners toolbox as
- it would appear from Borland press releases and manuals, this may have
- something to do with the sluggish move of programmers to using it.
-
- Firstly any prospective programmer must firmly understand the usage of
- Pointers and heap memory, Next Object Oriented principles must be almost
- second nature, Finally the Event Driven paradigm should be well
- understood. Them's three steep learning curves! The next problem as
- outlined by Jeff Duntemann in Dr Dobbs Journal Nov, 1991 in his
- PNL #10 Page 20 March, 1992
-
-
-
- "Structured Programming" column (Read it!), is that there no "Front Door"
- to start off your Research, that you must digest Turbo Vision in a whole
- gestalt.
-
- I plan to do some articles on my experience of coming to grips with Turbo
- Vision in the next few issues of PNL, however today I'll start with a few
- nice little goodies that come with TV, which can be used independently of
- the Application Framework. This allows us to at least deal with some of
- the TV topics without haveing to see the "Big Picture" at once. Let's
- discuss TV's Objects Unit, which contains among other goodies
- Collections, and Stream. You should have a fair grasp of Object oriented
- programming, and naturally a good concept of dynamic memory and heap
- usage.
-
-
- INTERESTING OBJECTS
-
- Lurking in the set of Turbo Vision Units that comes free with every copy
- of Turbo Pascal 6.0 is the Unit "Objects.TPU". It's well worth looking
- into this Unit to see the usefull routines and objects that the Turbo
- Vision User interface uses as it's glue to bind itself together. I'll
- leave Collections and Streams for last.
-
- Open the file Objects.Int (In your DOC subdirectory of a standard turbo
- pascal setup), and open your Turbo Vision Guide to page 190. Refer often
- to the Global reference from page 327 as we talk of Types, procedures,
- and functions, and the Object Reference from page 205 as we talk of
- Objects.
-
-
- TYPE CONVERSION TYPES
-
- One of the main advantages and disadvantages of Pascal has always been
- the strong typing. This means that if you have a variable of a certain
- type you can't usually assign it's contents to a variable of another
- type. The advantage is that it saves the unwary programmer writing a
- large variable over a smaller variable and corrupting the memory of all
- variables following. The disadvantage is that it constrains the
- programmer from passing data between dissimilar types.
-
- Turbo pascal changed all this by allowing you to typecast between any two
- types of the same size, for example a Pointer and a longint are both 4
- bytes long so we can do the following;
-
- Var
- Ptr : Pointer;
- Long: Longint;
- begin
- Long := Longint(Ptr);
- end;
-
- And the Long variable will contain a copy of the bytes of information in
- the Pointer variable, this may not sound particularly usefull. But
- consider the WordRec, it allows you to extract the high and low Bytes
- from a word, Visualise this example for Colours.
-
- Var
- MyColour : Word;
- PNL #10 Page 21 March, 1992
-
-
-
- MyBackground : Byte;
- Const
- Black = 0; White = 7;
- begin
- WordRec(MyColour).Hi := Black;
- WordRec(MyColour).Lo := White;
- MyBackground := WordRec(MyColour).Hi;
- end;
-
- Then we have the Pointer array types which can be type cast over a
- pointer to access the pointers contents as an array of Bytes, or Words.
- Remember you can typecast types of the same size, and all pointers are 4
- bytes in size regardless of the size of the block they point to. So in
- the following you can access the ith byte of a dynamic variable.
-
- Var
- BlockPtr : Pointer;
- Index : Word;
- begin
- GetMem(BlockPtr,2000);
- For Index := 1 to 1000 do
- pWordArray(BlockPtr)^[Index] := Index;
- end;
-
- This allows you to allocate a block of 1000 words (2 bytes each in size)
- numbered from 1 to 1000, each initialised with their index number.
-
-
- STRING POINTERS
-
- The PString type, the NewStr Function and the DisposeStr procedure are
- usefull items, they are used throughout TurboVision and they allow you
- to allocate and dispose of dynamic Strings easilly. You simply pass the
- newstring function a String variable and it will return a Pointer to a
- copy of that string stored in Dynamic Memory. You pass that pointer to
- DisposeStr and it will return the Dynamic Memory used back to available
- heap memory. Two nice tricks are that NewStr('') returns a nil pointer
- wasting no Space, and dispose doesn't abort with an error if you dispose
- a nil pString.
-
- Sure you could implement this stuff yourself, and most of us have.
- However it is all done in Objects, and it links in well with usefull
- routines like the String Collection.
-
- COLLECTIONS
-
- Often as programmers we want to be able to store a collection of items,
- we can use an array, the limitation being that the size must be
- determined at compile time, and can't change, and can't be greater than
- 64k. The other option is to create a linked list structure, this is
- middling difficult to create, but almost impossible to debug if you make
- mistakes, and accessing an item requires traversal through preceding
- items, hardly optimised.
-
- Turbo vision introduces an Object called (AHA!) a Collection. It is in
- fact a object encapsulate mutated resizing index array that allows
- indexed access like an array, with the resizability of a linked list, and
- because it indexes pointers to your objects you can store any dynamic
- PNL #10 Page 22 March, 1992
-
-
-
- variable or Object.
-
- It's use is relatively simple, you simply create a descendant of
- TCollection and overwrite it's freeItem method, to tell the collection
- mechanisms how to get rid of an Item's memory, so that for example when
- you dispose of the whole collection it knows how to dispose of the memory
- for each item in the collection.
-
- Initialise the Collection with two words, and Insert pointers to your
- items already stored on the heap. The two variables are used internally
- by the collection when building the index table, the first variable is
- your best guess as to the starting number of items you will store and the
- second is the amount you want the table to grow by each time you add more
- variables than the current index can hold. I generally use 10 and 10, if
- I don't know the nature of the collection. Have a look at StrList for a
- simple example of how to store a collection of strings.
-
- If you look at my main program loop it initialises a dynamic variable
- pointing to a my new collection type, it then uses the newStr function to
- store a string constant in the heap, and uses the collection method
- insert(p : Pointer) to insert the pointer to that string into the
- collection. It's a shorhand method that could be represented in longhand
- as follows;
-
- StrVar := 'Four';
- StrPtr := NewStr(StrVar);
- MyStringList^.Insert(StrPtr);
-
- Then my main loop calls a new method I created for this object called
- Print_Sentence, which then calls a method in Tcollection called
- ForEach(Action : Pointer), this calls a far local (Important can not be
- global) procedure pointer (PrintWord) for each Item in the collection.
- Finally I dispose of my special collection using the TCollection.Done
- destructor, which calls FreeAll, which in turn Deletes the index item and
- calls freeItem for Each Item. Which is why we overwrote FreeItem, to
- show our collection how to dispose of an item.
-
- I Cheated a bit, there is actually a StringCollection Object provided in
- Objects, that already knows all about pStrings, and sorts them into the
- bargain, but I thought as an example it would be good to show a little of
- the mechanism of creating a specialised collection type.
-
- But there is much more to collections, the standard freeItem method is
- not just abstract, it actually typecasts an object of type TObject over
- the pointer to your Item, and calls IT's virtual destructor. Why, well
- all turbo vision objects are in fact descended from a TObject, and if you
- had inserted a TV object (Or any of your own objects descended ultimately
- from a TObject), a vanilla TCollection would know all about how to
- dispose of such an item, and we would not need to descend from
- TCollection and override TCollection.FreeItem.
-
- Unfortunately a pstring is not an object and thus can never be descended
- from TObject, however a TCollection IS descended from TObject, and thus
- you could have a Collection of Collections, or (and this is the good bit)
- a collection of various objects, as long as each was descended from a
- TObject.
-
- I show an example of a Collection of Collections in the included file
- PNL #10 Page 23 March, 1992
-
-
-
- ReadIni.Pas, which is a simple unit to read a Windows type INI File. It
- reads a Text file (See example Test.INI) and creates a collection of
- TagCollections, each being a collection of parameters, ie:
-
- TAGCollection = Object(tCollection)
- |
- +- PARAMCollection = Object(tCollection)
- TAG : PString
- |
- +- PARAMItem = Object(TObject)
- Param : pString
- Vars : pString;
-
- As the paramItem is a descendant of TObject, no special hadling is
- required to add it to a PARAMcollection, which has it's own TAG string,
- then all PARAMCOllections (One for each GROUP TAG) are stored in a
- TAGCollection.
-
- There is sparse documentation in the Source of READINI, To fully explain
- the code here would bulk up this article, so I'll leave it as a
- demonstration of what you can do with collections, and it could be a
- usefull unit as it stands without you modifying anything.
-
- STRINGLISTS
-
- The String List is another usefull object in the OBJECTS unit. It is a
- way of collecting a bunch of strings indexed by words. The way it works
- is this, you create a program to make a StringList using a tStrListMaker
- Object, which once Constructed with an INIT, simply requires you to use
- tStrListMaker.Put(Index,TextString) which adds TextString to the Object
- indexed by INDEX. You then save it to a file (More about this in Streams
- and Resources). Now you create another program that reads a tStringList
- from this File, then you simply ask the object for the text string that
- corresponds to a particular index number.
-
- The procedure is simple, I have included the sample files Make_Err,
- Err_Desc, and Test to describe the procedure. You'll want to read on
- about streams and resources as they are used in these files. However
- I'll describe a few uses for String Lists. In the example I have given
- Make_Err simply puts a Text String detailing each Runtime error, and
- stores the description indexed with the run time error code. Desc_Err
- simply finds out the exitcode and if non-Zero it prints out a
- description. So what you say, well let's say for example that you have
- to create a version of your program for a swedish audience. You simply
- create a new ERRORS.STM file to put with your program, and you don't need
- to change and recompile your code.
-
- The REAL advantage is that now that the Europeans are finally getting
- their act together with a common economic bloc, there will be a lot of
- demand for programs that are multi lingual. If you use a stringList for
- ALL text strings in your code, you'll be able to have users select upon
- startup the language they wish to converse with the program in, and
- simply activate THAT string List.
-
- As a favour to me could those of you who are fluent in Non-English
- languages, send in to the Newsletter a modified Make_Err for your
- language, we'll compile them all together and distribute a multi lingual
- Runtime error descriptor.
- PNL #10 Page 24 March, 1992
-
-
-
-
- STREAMS AND RESOURCES
-
- About the most sophisticated file handling in Turbo pascal up until
- TP5.0, was the TEXT file type. With the TP5.0 demo files Borland
- introduced a sophisticated Object called a Stream that allows you to
- easilly perform polymorphic Object File Input/Output. Turbo power
- extended this with their own stream objects in Object Professional, and
- finally we have it integrated in Turbo Vision. There is a great
- discussion on Streams in Chapter 8 of the TV Guide, I shan't try to
- repeat this here, however I'll give a general run down on the use of
- Streams, and their application in the real world. I do plan to do a more
- detailed tretise on Streams in the future (given time) based on a unit I
- use called NetStrm, which extends the buffered stream to work on
- Networks.
-
- How does it work, well most turbo vision objects (And any Objects you
- want to use with Streams) have two extra methods, a constructor usually
- called Load, and a procedure called Store, which each take as a parameter
- a stream variable, and do pretty much what they say they do. The
- trickiness is that when you prepare to use objects in streams, you
- register the Objects you will use.
-
- Registering an Object entails sending information to the Objects unit
- information so that it knows where to find your objects Load and Store
- methods, a unique word called an ObjType, and the VMT link (Which within
- OOPS Pascal uniquely defines an Object Type) which allows the Objects
- unit to know the size of the dynamic object it must create during a load
- construction.
-
- When an object is stored to a file, it's ObjType number is first saved to
- the Stream, then the Store method is called to put it's information onto
- the file. Ok So what is the VMTLink for, well when you tell a stream to
- load an object, it reads the OBJType number, then searches it's list of
- registered objects and uses the VMTlink that it finds to do all the OOP
- trickiness to construct the Object, then uses the pointer to the Load
- constructor registered with the object, to fill the object with
- information from the file.
-
- So What? Well what this means is that your stream is asked to get an
- object, it will return to you a pointer to an object, after constructing
- it for you, WITHOUT KNOWING WHAT THE OBJECT IS. Ha, So what again. Well
- it means that you can store any registered descendant of TObject with a
- store and complimentary Load mechanism, and load it back again without
- having to know at compile time the information you are sending to the
- screen.
-
- Resources are simply streams with an index comprising of key strings, in
- the case of Make-Err and Desc_err I store the stringlist to a resource
- under the keystring "ERRORDESC".
-
- As an example of the use of Streams, Say you have 5 or 6 different record
- types that have to be input and output to a file. In the 'Olden days'
- you would use the case command to create a variant record, and each
- record you stored would be the size of the LARGEST record case. This way
- you simply declare a different Object for each record type, and you use
- only as much space as each record needs. Furthermore if you have put a
- method called EditRec into an abstract Object, and descended all your
- PNL #10 Page 25 March, 1992
-
-
-
- record objects from it, and in each case overriden Editrec to do the
- individual record editing and manipulation you could simply do the
- following;
-
- Incognito := TStream.Get;
- Incognito^.EditRec;
-
- It's a lot easier if your object is a Turbo Vision View with record
- information, and editing dialogs, but then you'd have to use the Turbo
- Vision User Interface. Ah well, we'll all be using it sooner or later.
-
- Refference sources
-
- Turbo Vision Guide
- Borland Intl
-
- "Structured Programming"
- Dr. DOBBS journal
- November 1991
-
- About the author
-
- Richard Morris is the CEO of KHIRON Software, a Queensland,
- Australia based Computer consultancy, that has been providing
- contract programmers, and computer support to small to medium
- business' sin 1985.
-
- Richard can be contacted via the following
- Fidonet: 3:640/372.5
- IntlNet: 58:1100/378
- Voice: (07) 812-3218
- Post: C/- KHIRON Software
- P.O. Box 544,
- INDOOROOPILLY Qld 4068.
- PNL #10 Page 26 March, 1992
-
-
-
- =================================================================
- FREQUENTLY ASKED QUESTIONS IN THE PASCAL ECHO
- =================================================================
-
- Q1. How do I pass an error level code when my program finishes?
-
- A1. The halt procedure takes an optional parameter of type word. Thus -
-
- halt(1);
-
- terminates the program with an errorlevel of 1. If halt is used
- without a parameter it is the same as -
-
- halt(0);
-
- Note: When a program is terminated using the halt procedure any
- exit procedure that has previously been set up is executed.
-
-
- Q2. How do I empty the keyboard buffer?
-
- A2. There are several ways that this can be achieved. However the
- safest is -
-
- while Keypressed do ch := ReadKey;
-
- This requires that a variable ch of type char is declared and the
- crt unit be used. To do it without using a variable -
-
- while Keypressed do while ReadKey = #0 do;
-
- or if using TP6 with extended syntax enabled -
-
- while KeyPressed do ReadKey;
-
- If you do not wish to incur the substantial overhead involved with
- the use of the CRT unit and there is no requirement for the program
- to run under a multi-tasker -
-
- var
- head : byte absolute $40:$1c;
- tail : byte absolute $40:$1e;
-
- tail := head;
-
-
- Q3. When I redirect the screen output of my programs to a file the file
- is empty and the output still appears on the screen. What am I
- doing wrong?
-
-
- A3. You are probably using the CRT unit and its default method of
- writing to stdout is by direct screen writes. In order to enable
- output to be redirected all writes must be done by DOS. Setting the
- variable DirectVideo to false has no effect on redirection as all it
- does is use the BIOS for screen writes - not DOS.
-
-
- PNL #10 Page 27 March, 1992
-
-
-
- To enable redirection you must not use the CRT unit
-
- OR
-
- assign(output,'');
- rewrite(output);
-
- This will make all output go through DOS and thus can be redirected
- if desired. To restore the default situation -
-
- AssignCRT(output); rewrite(output);
-
-
- Q4. How do I make a string that is lower or mixed case all uppercase?
-
- A4. There are several ways to convert lower case characters to upper
- case. Here are some of them.
-
- As a procedure (excluding asm code this is the fastest way)
-
-
- procedure StrUpper(var st: string);
- var x : byte;
- begin
- for x := 1 to length(st) do
- st[x] := UpCase(st[x]);
- end;
-
- As a function (slower but sometimes more convenient) -
-
- function StrUpper(st: string): string;
- var x : byte;
- begin
- StrUpper[0] := st[0];
- for x := 1 to length(st) do
- StrUpper[x] := UpCase(st[x]);
- end;
-
- Both the above are suitable for the English language . However
- from version 4.0 onwards, DOS has had the facility to do this in a
- way that is country (language) specific. I am indebted to Norbert
- Igl for the basic routine. I have modified his code slightly. For
- the anti-goto purists this is a good example of a goto that is
- convenient, efficient, self-documenting and structured. The dos
- calls would make this method the slowest of all.
-
-
- function StrUpper(s: string): string;
-
- { Country specific string-to-uppercase conversion. }
- { Requires DOS unit }
- label
- fail;
- var
- regs : registers;
- x : byte;
- begin
-
- PNL #10 Page 28 March, 1992
-
-
-
- if lo(DosVersion) >= 4 then begin
- with regs do begin
- ax := $6521;
- ds := seg(s);
- dx := ofs(s[1]);
- cx := length(s);
- msdos(regs);
- if odd(flags) then { the attempted conversion failed so }
- goto fail;
- end; { with }
- end { if DOS >= 4.0 } else
- fail:
- for x := 1 to length(s) do
- s[x] := UpCase(s[x]);
- StrUpper := s;
- end; { StrUpper }
-
-
- Q5. When I include ANSI codes in a string and write that string to
- the screen the actual codes appear on the screen, rather than the
- results they are supposed to achieve.
-
- A5. In order for ANSI codes to be interpreted, screen writes must be
- directed through DOS and there must have been a suitable driver
- loaded via the config.sys file at boot time. All output can be
- directed through DOS and the driver by -
-
- Not using the crt unit
-
- OR -
-
- assign(output,'');
- rewrite(output);
-
- in which case ALL screen writes are "ANSI code sensitive"
-
- OR -
-
- You can set up write procedures that will be "ANSI code sensitive".
- (You will need an initialisation procedure to set this up.)
-
- var
- ansi : text;
-
- procedure AssignANSI(var ansifile : text);
- begin
- assign(ansifile,'CON');
- rewrite(ansifile);
- end; { AssignANSI }
-
- procedure WriteANSI(var st: string);
- begin
- write(ansi,st)
- end; { WriteANSI }
-
-
-
- PNL #10 Page 29 March, 1992
-
-
-
- procedure WriteLnANSI(var st: string);
- begin
- writeANSI(st);
- writeln(ansi);
- end; { WriteANSI }
-
- ObviousLy, if the ANSI.SYS driver (or an equivalent) is not
- installed none of the above can work.
-
- Setting the variable DirectVideo in the CRT unit to false will not
- achieve the desired result as this merely turns off direct screen
- writes and uses the BIOS for all screen output.
-
- Q6. When I try to shell to DOS nothing happens. What am I doing wrong?
-
-
- A6. In order to be able to execute any child process there must be
- sufficient memory available for it to load and execute. Unless
- you advise differently at compile time, a Turbo Pascal program
- grabs all available memory for itself when it is loaded. To
- reserve memory for a child process use the compiler memory
- directive -
-
- {$M 16384,0,0)
-
- the default is -
-
- {$M 16384,0,655360}
-
- The first figure - StackMin - is the amount of memory to be
- allocated for the stack:
-
- Minimum is: 1024
- Default is: 16384
- Maximum is: 65520
-
- The next figure - HeapMin -is the minumum amount of memory to be
- allocated for the heap. If there is less memory available than
- this figure the program will not load.
-
- Minimum is: 0
- Default is: 0
-
- Maximum is: 655360 In practice it will be the amount of free
- memory less the space required for the stack,
- less the code space of the program. You
- should set this to 0 unless your program uses
- the heap. In that case, set it to the lowest
- possible figure to prevent heap allocation
- errors. In most cases it is best to leave it
- at zero and do error checking within the
- program for sufficient memory at allocation
- time.
-
- The last figure is the crucial on as regards child processes. It
- should always be low enough to leave memory left over for a child
- process and high enough not to cause problems for the program when
- allocating heap memory.
- PNL #10 Page 30 March, 1992
-
-
-
-
-
- Minimum is: HeapMin
- Default is: 655360
- Maximum is: 655360 If less than the requested amount is
- available no error is reorted. Instead all
- available memory is allocated for heap use.
-
-
- Q7. How do I shell to DOS?
-
- A7. SwapVectors;
- exec(GetEnv('COMSPEC','');
- SwapVectors;
-
- Read previous section on memory allocation.
-
- I find that it is a good idea to write my own Exec function which
- will do everything that is needed for me. I have it return an
- integer value that is the DosError code.
-
- function Exec(p1,p2: string);
- begin
- SwapVectors;
- Dos.Exec(p1,p2);
- SwapVectors;
- Exec := DosError;
- end;
-
- This enables me to have a statement such as -
-
- ReportError(Exec(GetEnv('COMPSEC'),''));
-
- Now you can have an empty ReportError procedure or you can make it
- report the error - whatever is suitable for you application.
-
-
- Q8. When I execute a child process redirection does not work. Why?
-
-
- A8. Redirection of a child process's output only works if it is run
- under another copy of the command processor. So -
-
- exec('YourProg.exe',' > nul'); will not work but
-
- exec(GetEnv('COMSPEC'),'/c YourProg > nul'); will work.
-
-
- Q9. How do I read an errorlevel from a child process?
-
- A9. After executing a child process the errorlevel returned can be
- read by calling the DosExitCode function which returns a word.
- The low byte is the errorlevel. A full description is in the
- manual.
-
- If the command interpreter is the child process and it in turn
- executes a child process then the errorlevel of the second child
- process cannot be read without resorting to some trickery.
- PNL #10 Page 31 March, 1992
-
-
-
-
- Q10. When I read a text file that has lines exceeding 255 characters
- I lose all those characters from the 256th one on each time there
- is a line that exceeds that length. How can I prevent this?
-
- A10. Turbo Pascal's readln procedure reads a line up to the 255th
- character then skips to the next line. To get around this you
- should declare a buffer at least as large as the longest possible
- line and then use the read procedure. The best size for the
- buffer is a multiple of 2048 bytes.
-
- const
- BufferSize = 2048;
- LineLength = 78;
- type
- textbuffer = array[1..BufferSize] of char;
- var
- st : string;
- f : text;
- buffer : textbuffer;
-
- function ReadTxtLn(var tf: text; var s: string; max: byte): integer;
- { Reads a string of a maximum length from a text file }
- var
- len : byte absolute s;
- begin
- len := 0;
- {$I-}
- while (len < max) and not eoln(tf) do begin
- inc(len);
- read(tf);
- end;
- if eoln(tf) then
- readln(tf);
- ReadTxtLn := IOResult;
- {$I+}
- end; { ReadTxtLn }
-
- begin
- assign(f,filename);
- reset(f);
- SetTextBuf(f,buffer);
- while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) do
- writeln(st);
- close(f);
- end.
-
-
- Q11. How do I convert nul terminated asciiz strings to Turbo Pascal
- strings?
-
- A11. Here is a function that will do that -
-
- function Asc2Str(var s; max: byte): string;
- { Converts an ASCIIZ string to a Turbo Pascal string }
- { with a maximum length of max. }
- var starray : array[1..255] of char absolute s;
- len : integer;
- PNL #10 Page 32 March, 1992
-
-
-
- begin
- len := pos(#0,starray)-1; { Get the length }
- if (len > max) or (len < 0) then { length exceeds maximum }
- len := max; { so set to maximum }
- Asc2Str := starray;
- Asc2Str[0] := chr(len); { Set length }
- end; { Asc2Str }
-
-
- Q12. How can I tell if a particular bit of a variable is set or not?
- How can I set it? How can I turn it off? How can I make a large
- bit map and then determine if a particular bit - say bit 10000 is
- on/of?
-
- A12. This question, or a variation of it, is one of the most commonly
- asked questions in the echo and there are several ways of doing what
- is wanted. None are necessarily right or wrong. The way I will
- describe is designed to take up as little code/data space as
- possible. I do not attempt to explain the theory behind these
- functions as this can be obtained from any good book.
-
- The use of sets can be the best bit manipulation method if you have
- control over the data being used. Here is an example of a byte
- variable for a BBS program which sets various user access level flags.
-
- Bit 0 = Registered User
- 1 = Twit
- 2 = Normal
- 3 = Extra
- 4 = Privileged
- 5 = Visiting Sysop
- 6 = Assistant Sysop
- 7 = Sysop
-
- type
- status_type = (Registered,
- Twit,
- Normal,
- Extra,
- Privileged,
- VisitingSysop,
- AssistantSysop,
- Sysop);
- status_level = set of status_type;
-
- var
- access_flags : status_level;
-
- Let us assume you have someone who logs on and you wish to determine
- his user access level. After reading access_flags from the user data
- file -
-
- if Sysop in access_flags then ....
-
- To set the sysop flag -
-
- access_flags := access_flags + [Sysop];
- PNL #10 Page 33 March, 1992
-
-
-
- To reset the sysop flag -
-
- access_flags := access_flags - [Sysop];
-
- However on many occasions using a set may not be a suitable method.
- You may simply need to know if bit 5 is set or not. Here is the
- method that I consider the best -
-
- function BitIsSet(var V, bit: byte): boolean;
- begin
- BitIsSet := odd(V shr bit);
- end;
-
- To set a bit -
-
- procedure SetBit(var V: byte; bit: byte);
- begin
- V := V or (1 shl bit);
- end;
-
- To reset a bit -
-
- procedure ResetBit(var V: byte; bit: byte);
- begin
- V := V and not(1 shl bit);
- end;
-
- To toggle (flip) a bit -
-
- procedure ToggleBit(var V: byte; bit: byte);
- begin
- V := V xor (1 shl bit);
- end;
-
- Now a bit map can be made up from an array of bytes. If stored on
- the heap you can test any bit up to number 524159 (zero based).
- Here's how.
-
- type
- map = array[0..maxsize] of byte;
- { set maxsize to number of bits div 8 -1 needed in the bit map }
-
- function BitSetInBitMap(var x; numb : longint): boolean;
- { Tests the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7));
- end;
-
- procedure SetBitInBitMap(var x; numb: word);
- { Sets the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7))
- end;
-
- procedure ResetBitInBitMap(var x; numb : longint);
- { Resets the numb bit in the bitmap array }
- PNL #10 Page 34 March, 1992
-
-
-
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7));
- end;
-
- procedure ToggleBitInBitMap(var x; numb : longint);
- { Toggles (flips) the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7));
- end;
-
-
- Q13. How can I find a particular string in any file - text or binary?
-
- A13. The Boyer-Moore string search algorithm is considered to be the
- fastest method available. However in a rare worst-case scenario it
- can be slightly slower than a linear brute-force method. The
- following demonstration program will show how it works and could
- easily be modified to allow for command line paramters etc.
-
-
- program BMSearchDemo;
-
- type
- bigarray = array[0..32767] of byte;
- baptr = ^bigarray;
- BMTable = array[0..255] of byte;
-
- const
- KeyStr : string = 'Put whatever you want found here';
- fname : string = 'f:\Filename.txt';
-
- var
- Btable : BMtable;
- buffer : baptr;
- f : file;
- result,
- position : word;
- offset : longint;
- finished,
- Strfound : boolean;
-
- procedure MakeBMTable(var t : BMtable; var s);
- { Makes a Boyer-Moore search table. s = the search string}
- { t = the table }
- var
- st : BMtable absolute s;
- slen: byte absolute s;
- x : byte;
- begin
- FillChar(t,sizeof(t),slen);
- for x := slen downto 1 do
- if (t[st[x]] = slen) then
- t[st[x]] := slen - x
- end;
-
- function BMSearch(var buff,st; size : word): word;
- PNL #10 Page 35 March, 1992
-
-
-
- { Not quite a standard Boyer-Moore algorithm search routine }
- { To use: pass buff as a dereferenced pointer to the buffer}
- { st is the string being searched for }
- { size is the size of the buffer }
- { If st is not found, returns $ffff }
- var
- buffer : bigarray absolute buff;
- s : array[0..255] of byte absolute st;
- len : byte absolute st;
- s1 : string absolute st;
- s2 : string;
- count,
- x : word;
- found : boolean;
- begin
- s2[0] := chr(len);
- { sets the length to that of the search string }
- found := false;
- count := pred(len);
- while (not found) and (count < (size - len)) do begin
- if (buffer[count] = s[len]) then
- { there is a partial match } begin
- if buffer[count-pred(len)] = s[1] then
- { less partial! } begin
- move(buffer[count-pred(len)],s2[1],len);
- found := s1 = s2;
- { if = it is a complete match }
- BMSearch := count - pred(len);
- { will stick unless not found }
- end;
- inc(count); { bump by one char - match is irrelevant }
- end
- else
- inc(count,Btable[buffer[count]]);
- { no match so increment maximum }
- end;
- if not found then
- BMSearch := $ffff;
- end; { BMSearch }
-
-
- begin
- new(buffer);
- assign(f,fname);
- reset(f,1);
- offset := 0;
- MakeBMTable(Btable,KeyStr);
- repeat
- BlockRead(f,buffer^,sizeof(buffer^),result);
- position := BMSearch(buffer^,KeyStr,result);
- finished := (result < sizeof(buffer^)) or (position <> $ffff);
- if position = $ffff then
- inc(offset,result);
- Strfound := position <> $ffff;
- until finished;
- close(f);
- if Strfound then
- writeln('Found at offset ',offset)
- PNL #10 Page 36 March, 1992
-
-
-
- else
- writeln('Not found');
- end.
-
- Q14. How can I put a apostrophe in a string?
-
- A14. Just put in extra apostrophes. If you want st to be equal to
- the string - The word 'quoted' is in quotes do this -
-
- st := 'The word ''quoted'' is in quotes';
-
- if you want the following to be written to screen -
- 'This is a quoted string'
- do this -
-
- writeln('''This is a quoted string''');
-
-
- Q15. What are the best books to purchase to help me learn Turbo Pascal?
-
- A15. There are many good books for learners. Here are a few -
-
- Complete Turbo Pascal - Third Edition - Jeff Duntemann
- Mastering Turbo Pascal 6 - Tom Swann
- Turbo Pascal - The Complete Reference - O'Brien.
-
- For advanced users there are also many good books. Here are a
- few
- that I have found useful - (Those marked with an asterisk are
- not
- purely for Turbo Pascal)
-
- Turbo Pascal 6 - Techniques and Utilities - Rubenking
- Turbo Pascal Internals - Tischer
- * PC System Programming for Developers - Tischer
- * Undocumented DOS - Schulman
-
- Any learner would be well advised to obtain a well known library
- such
- as Technojock's Turbo Toolkit (TTT) which is shareware and study
- the
- source code.
-
- Trevor Carlsen
- (TeeCee)
- PNL #10 Page 37 March, 1992
-
-
-
- =================================================================
- Running 80286 Turbo Pascal Programmes on 8088/8086 Computers
- =================================================================
-
- [ Editor's note: Intentionally, the author uses English spelling instead
- of American spelling ]
-
- Introduction
-
- When Borland released Turbo Pascal Version 6.0 in 1990, they added many
- impressive features to the compiler which reaffirmed Turbo Pascal as the
- de facto standard Pascal compiler for PCs. One such feature is the
- ability to generate iAPX286 instructions for added speed on AT or higher
- machines; unfortunately, they forgot (neglected?) to add a check to the
- RTL (runtime library) for whether or not an iAPX286 was actually present.
- Any programme compiled in the {$G+} state will undoubtedly "hang" when
- executed on an 8088 or 8086-based computer.
-
- In what seems an afterthought, Borland added a small demonstration
- programme called "TEST286" in the \DEMOS subdirectory of your Turbo
- Pascal subtree, and wrote a small mention in the "HELPME!.DOC" file.
- Following Borland's own advice, however:
-
- "If you want to put code like this in a program with
- {$G+} enabled, put the test and halt code in the
- initialization section of the first unit in the main
- program's USES clause."
-
- What follows is a description of a unit (provided) which does exactly
- that. Place the unit's label immediately after the "uses" keyword before
- any other unit labels, and forget about it; at runtime when the
- initialisation code is executed and no 80286, 80386 or 80486 is detected,
- a message is displayed informing the user of the reason why the programme
- immediately thereafter exits (returning an errorlevel of one).
-
-
- Directives Overview
-
- This section merely explains the logic behind each of the compiler
- directives used for the benefit of novices, and can be ignored by more
- competant programmers.
-
- As presented, the unit is compiled in the {$G-} state to ensure proper
- execution on computers based on the 8088 or 8086 processor. Further, it
- is compiled with far calls and overlay ability enabled (viz., {$F+} and
- {$O+}) so upon completion in large applications, the code can be swapped
- out (although this shouldn't be too necessary as the entire unit is less
- than one kilobyte).
-
- The routine exhibited no problems on the test machines (an 80486, an
- 80386 AST and an NEC V20-based XT), so has all debugging facilities
- disabled ({$D-,I-,L-,R-,S-}). This should not prove a problem in any
- programmes in which you have debugging enabled, unless you wish to trace
- through the code.
-
- In addition to not including debugging information, the "usual" code
- optimisation switches are enabled ({$B-,V-}). Since word-aligned data
- has no effect on the 8088, yet increases execution speed on all 80x86
- PNL #10 Page 38 March, 1992
-
-
-
- processors, it is enabled ({$A+}). There is no use of the "extended
- syntax" option (again, a Version 6.0 enhancement), so it is disabled with
- {$X-}.
-
- The addition of {$E-} and {$N-} is merely to "complete" the switch
- directives, and indicate that there is no need for any additional numeric
- or emulated floating point processing.
-
-
- Unit Description
-
- As there are no functions or procedures to call (variables to read and
- alter, constants to use, et al.), the "interface" section of the unit
- remains void (empty).
-
- Both the errorlevel returned, and the message displayed are defined as
- constants so that they can be easily altered by an external setup or
- configuration utility. The errorlevel can be any value from zero (0) to
- two hundred and fifty-five (255), and the error message can be upto
- eighty (80) characters in length. For users of Object Professional's
- OpClone unit, the string constant "i286 config data 1.00" is provided.
-
- The one internal variable declared, "Is286Able", is used to pass the
- result of the detection routine to the remainder of the code; this was
- necessary as no separate function is used (to keep execution speed to the
- bare minimum), and standard functions and procedures (such as WriteLn()
- and Halt()) are not callable from within the built-in inline assembler
- (one short-coming of BASM).
-
- The core of the original function provided in TEST286 is:
-
- asm
- pushf { Push flags register onto the stack }
- pop bx { Pop a word from the stack, store in BX }
- and bx, 00FFFh { Logical AND operands, result in BX }
- push bx { Push word contents of BX on the stack }
- popf { Pop from stack into flags register }
- pushf { Push flags register onto the stack }
- pop bx { Pop a word from the stack, store in BX }
- and bx, 0F000h { Logical AND operands, result in BX }
- cmp bx, 0F000h { Compare operands, update status flags }
- mov ax, 00h { Store word 0x0000 (zero) in AX }
- jz @@1 { If the zero flag is set, jump to "@@1" }
- mov ax, 01h { Store word 0x0001 (one) in AX }
- @@1: { Internal label "@@1", end of routine }
- end;
-
- The essential logic here is that for processors earlier than the 80286
- (viz., 8088, 8086, 80188, 80186, V20 and V30) bits 12 to 15 of the CPU
- flags register cannot be cleared. This routine merely tries to clear
- those bits (without disturbing the others), and if it is unable to do so,
- assumes an iAPX286 or higher (i386, i486) is not present.
-
- For the supplied unit, the core is:
-
- asm
- xor ah, ah { Logical XOR operands, result in AH }
- pushf { Push flags register onto the stack }
- PNL #10 Page 39 March, 1992
-
-
-
- pop bx { Pop a word from the stack, store in BX }
- and bx, 00FFFh { Logical AND operands, result in BX }
- push bx { Push word contents of BX on the stack }
- popf { Pop from stack into flags register }
- pushf { Push flags register onto the stack }
- pop bx { Pop a word from the stack, store in BX }
- and bx, 0F000h { Logical AND operands, result in BX }
- cmp bx, 0F000h { Compare operands, update status flags }
- je @@1 { If the zero flag is set, jump to "@@1" }
- inc ah { Increment the contents of AH by one }
- @@1: { Internal label "@@1", store result of }
- mov [Is286Able], ah { detection in "Is286Able" variable }
- end;
-
- Logically, the two code segments are identical (ignoring the last "mov
- [Is286Able], ah" instruction above). If we compare their logic structure
- in English:
-
- (Original Code)
- 1. Clear flag bits 12-15.
- 2. Compare - did they clear?
- 3. Set the result to zero (false).
- 4. If the comparison in #3 resulted in the zero flag
- being set, ie., the bits did NOT clear, jump to
- the end of the routine.
- 5. The bits DID clear, so set the result to one (true).
-
- (New Code)
- 1. Set the result to zero (false).
- 2. Clear flag bits 12-15.
- 3. Compare - did they clear?
- 4. If the comparison in #3 resulted in the zero flag
- being set, ie., the bits did NOT clear, jump to
- the end of the routine.
- 5. The bits DID clear, so increment the result to one (true).
-
- Up to this point, the comparison has been discussed as resulting in the
- zero flag being set or cleared; to understand why, you must remember that
- comparisons are conducted by subtraction, so that if two items equal each
- other numerically, their difference is zero (hence the the zero flag is
- set). To indicate more accurately the logic of the routine, the "jz"
- (jump if zero set) instruction was replaced with the "je" (jump if equal
- - ie., zero set) instruction. Both are functionally identical.
-
- Saving Clock Cycles
-
- The differences remain now with two instructions: "xor ah, ah" and "inc
- ah". Since only eight bits are needed for the result, AH is used rather
- than AX in its entirety. The zeroing of the register with XOR instead of
- MOV ("xor ah, ah" instead of "mov ah, 0") saves one clock cycle on 8086
- processors (none on 80286, 80386 or 80486 processors) and works since
- performing a logical exclusive OR (as opposed to a logical inclusive OR)
- on a number with itself always results in zero.
-
- Moving the placement of the "set-result-to-false" instruction to the
- start not only makes more sense, but is necessary as the XOR instruction
- modifies the zero flag (which then could not be used in the ensuing jump
- instruction).
- PNL #10 Page 40 March, 1992
-
-
-
-
- Rather than loading AH with one ("mov ah, 1" indicating "true"), the
- increment instruction is used ("inc ah") as, with XOR, it saves an extra
- valuable clock cycle on 8086 processors. Admittedly, if the host CPU is
- an 8086 the increment instruction is never reached, but it does not hurt
- to optimise at the instruction level.
-
-
- Remaining Code
-
- The last five lines of the routine proper are in Pascal (as explained
- above, BASM doesn't allow calling standard procedures and functions).
- These merely check the status of the "Is286Able" variable, and if the
- assembly code set it to "false", print a message before exiting.
-
-
- Test Programme
-
- A driver programme to test the unit is simply:
-
- program Testi286;
- {$G+}
-
- uses
- i286;
-
- {-If a '286 is absent, the init code of the unit will exit}
- begin
- WriteLn('Obviously an 80286 or higher is in this machine.')
- end. { Testi286 }
-
-
- Conclusion
-
- While many will claim the saving of two (well, one) clock cycles is no
- more than academic, this was not the main aim of the unit. The "i286"
- unit provides a very easy to use, "plug 'n' play" method of detecting and
- exiting which requires no further effort on the part of the programmer.
-
- David J. N. Begley
- 58:2100/142@intlnet, 3:712/211.3@fidonet
- Department of Computing, Faculty of Science and Technology
- University of Western Sydney, Nepean
- PNL #10 Page 41 March, 1992
-
-
-
- =================================================================
- Conclusion
- =================================================================
-
- That's it for now.
-
- In the next issue, I will publish the first part of what I call the
- Beginner's Toolbox. It is a set of units to help beginners to program
- easily. It will include input/output routines, a help system, a menuing
- system, etc...
-
- I thank everybody who participated in the rebirth of PNL. Richard
- Morris, the editor over-the-pond has collected most of the articles
- here. Without him, there would have been only 2 articles here by March
- 1st, 1992.
-
- Again, I beg you all to send in articles. Everybody has his/her own
- techniques to program, comments to share with others. You can send in
- book reviews or software reviews,
-
- The newsletter depends on your contribution...
-
- Anybody interested in publishing an article can request the Article
- Specifications with the magic name ARTSPEC on FidoNet 1:167/405 - It
- will also be available soon from Richard Morris, FidoNet 3:640/372.5.
-
- If you would like to receive back issues of PNL directly from me, send a
- diskette and $2.00 for shipping. Don't forget to include your address.
-
- Send your order to:
- Alex Boisvert
- 86 Bryant St.
- Sherbrooke, Quebec
- Canada J1J 3E4
-
- If you are a SysOp that will regularly carry PNL and would like to have
- your bulletin board listed as such, here, send me a message either by
- postal mail or at one of the electronic addresses given on the title
- page, with your bulletin board's name, phone number, and your name.
-
- Distribution List
-
- The following is the phone numbers to bulletin boards known to carry
- PNL. If you would like your bulletin board's name and number added to
- or deleted from this list, please send me a message at one of my many
- addresses. I can not guarantee whether a listed board will have any
- particular issue, however.
-
- Thieve's World ......................... Phone: (713) 463-8053
- Hippocampus ............................ Phone: (203) 484-4621
- Turbo City BBS ......................... Phone: (209) 599-7435
- The Final Frontier BBS.................. Phone: (518) 761-0869
-