Technical Reference

MISCELLANEOUS NOTES
5.1 ROUNDING NUMBERS IN BASIC-86

The following is a short program to illustrate one technique for rounding numbers to any number of decimal places, (within the limits of the machine).

     10  DEFINT N
     20  DEFDBL X
     30  INPUT "ENTER NUMBER TO BE ROUNDED";X
     40  IF X<1 OR X>7 GOTO 110
     50  INPUT "ENTER NUMBER OF DECIMAL PLACES";N
     60  A$=STR$(X+.5*10^-N*SGN(X))
     70  N0=INSTR(A$,".")
     80  X1=VAL(LEFT$(A$,N+N0))
     90  PRINT "X=;X,"X1=";X1
     100 GOTO 30
     110 PRINT "END"
     120 END

Rounding to zero decimal places can of course be achieved by using the INT function.

Rounding numbers to be output to the screen, printer or file can be achieved by the use of PRINT USING.

The routine given above can easily be simplified if it is always required that numbers be rounded to a fixed number of decimal places (eg. 2).

5.2 Undocumented Commands and Functions of the BASIC86 Interpreter

5.2.1 DATE$

The DATE$ function returns a ten character string variable that has the following format:

     MM-DD-YYYY
where:
     MM = month (1 - 12)
     DD = day (1 - 31)
     YYYY = year (1980 - 2099)

     The delimiter is either - or /.

The date can be set through BASIC86, when doing so, the leading zeros are presumed for single months and days. The year can be entered as a 2 digit number.

Examples:

     PRINT DATE$
     DATE$="10-6-83"
     DATE$="10/06/83"
     DATE$="10-06-83"
     DATE$=VAR$
5.2.2 TIME$

The TIME$ function returns an eight character string variable, 2 of which are delimiters. It takes the following format:

     HH:MM:SS
where:
     HH = hour of day (0-23)
     MM = minutes (0-59)
     SS = seconds (0-59)

     delimiter is :

The Time can be altered within BASIC86, when doing so minutes and seconds are optional and so are leading zeros. The time is being constantly incremented by a hardware clock.

Examples:

     PRINT TIME$
     TIME$="15:10:40"
     TIME$="7:5"
     TIME$="8"
     NOW$="14:15:06"
     TIME$=NOW$
5.2.3 DATE

The DATE variable contains the numbers of days since the beginning of the year. (Does not work on compiler)

Example:

     PRINT DATE
5.2.4 TIME

The TIME variable contains the number of seconds since midnight (00:00:00). (Does not work on compiler)

Examples:

     PRINT TIME

     The following is a self-timing program:

     10 X=TIME
     20 FOR I=1 TO 10000
     30 NEXT I
     40 PRINT TIME-X
5.2.5 BLOAD
Format:

     BLOAD <filespec>[,<offset>]

Purpose:

Loads a memory image into memory.

<filespec> is a string expression returning a valid file specification as described in "Input and Output," except for the extension. If the device name is omitted, the current diskette drive is assumed. The only valid extensions are:

     (none)         (no extension)

     .B             for  Basic  programs in the  internal  format 
                    (created with the SAVE command).

     .P             for  protected Basic programs in the internal 
                    format (created with SAVE ,P command).

     .A             for  Basic programs in ASCII format  (created 
                    with SAVE ,A command).

     .M             for  memory image files (created  with  BSAVE 
                    command).

     .D             for  data files (created by OPEN followed  by 
                    output statements.

<offset> is a numeric expression in the range 0 to 65535. This is the address at which the loading is to start, specified as an offset into the segment declared by the last DEF SEG statement. If the <offset> is omitted, the <offset> specified in the last BSAVE is assumed.

WARNING: BLOAD does not perform address range checking. That is, it is possible to BLOAD anywhere in memory! You should be absolutely sure you are not overwriting the operating system, Basic, or your own program.

EXAMPLE

10 'LOAD AN ASSEMBLY PROGRAM INTO BASIC DS ASSUMING
20 'NO PROGRAM HAS BEEN LOADED.
30 DEF SEG    'SET THE DATA SEGMENT TO BASIC'S
40 BLOAD "MOVE",0   'LOAD THE CALLABLE PROGRAM.
5.2.6 BSAVE

Format

BSAVE <filespec>,<offset>,<length>

Purpose:

Allows you to save portions of the computer's memory on the specified device.

<filespec> is a string expression returning a valid file specification as described in BLOAD.

<offset> is a numeric expression in the range 0 to 65535. This is the address at which the saving is to start, specified as an offset into the segment declared by the last DEF SEG statement.

<length> is a valid numeric expression returning an unsigned integer in the range 1 to 65535. This is the length of the memory image to be saved.

Example:

10 'save the first 100 bytes of memory located
20 'at the start of Basic's Data Segment.
30 DEF SEG
40 BSAVE "PROGRAM.M",0,100
5.2.7 OPEN

In addition to the syntax described in the MS-BASIC manual, MS- BASIC supports the more powerful GW-BASIC syntax described in the Graphics Toolkit.

Format

     OPEN []  [FOR ] AS[#] 
     [LEN=]

Purpose:

The OPEN statement establishes addressability between a physical device and an I/O buffer in the data pool.

Remarks

<ev> is optionally part of the file name string and conforms to the description in section 3.12.

<filename> is a valid string literal or variable optionally containing a <dev>. If <dev> is omitted, disc A: is assumed. Disc file names follow the normal MS-DOS naming conventions.

<mode> determines the initial positioning within the file and the action to be taken if the file does not exist. The valid modes and actions taken are:

     INPUT     Position  to  the beginning of an  existing  file.  
               The  "File  Not Found" error is given if the  file 
               does not exist.

     OUTPUT    Position  to the beginning of the  file.   If  the 
               file does not exist, one is created.

     APPEND    Position to the end of the file.  If the file does 
               not exist, one is created.

If the FOR <mode> clause is omitted, the initial position is at the beginning of the file. If the file is not found, one is created. This is the Random I/O mode. That is, records can be read or written at will at any position within the file.

<file number> is an integer expression returning a number in the range 1 through 15. The number is used to associate an I/O buffer with a disc file or device. This association exists until a CLOSE <file number> or CLOSE statement is executed.

<lrecl> is an integer expression in the range 2 to 32768. This value sets the record length to be used for random files. If omitted, the record length defaults to 128-byte records.

When a disc file is OPENed FOR APPEND, the position is initially at the end of the file and the record number is set to the last record of the file (LOF(x)/128). PRINT, WRITE or PUT then extends the file. The program can position elsewhere in the file with a GET statement. If this is done, the mode is changed to random and the position moves to the record indicated.

Once the position is moved from the end of the file, additional records can be appended to the file by executing a GET #x,LOF(x)/<lrecl>.

5.3 An Example of Calling an Assembler routine from the MS-BASIC Compiler (For an example of calling an Assembler routine from the Interpreter, see Section 10.5)

The Assembler routine is coded and written to disc as a .ASM FILE using EDLIN or PMATE (fig 1). This is then assembled using MACRO-86 (fig 2).

The Basic program is coded and written to disc with the BASIC interpreter (ie. 'SAVED' with the ',A' option to produce an ASCII text file), (fig 3). This is then compiled using BASCOM (fig 4).

The object (.OBJ) modules produced by MACRO-86 and BASCOM are then linked using MS-LINK (fig 5). The Assembler object module must precede the Basic object module in the Link statement.

The resultant Run file (.EXE) may then be run simply by typing the name given to it during the link.

When writing the 'CALL' statement in BASIC the name used (lines 160, 220, and 280) must be the name that appears in the 'PUBLIC' statement in the assembler code and must also be the label used in the assembler code 'PROC' statement.

See also section 10.5 and chapter 11 for further examples.

     b:asmex.asm

NAME      EXAMPLE
CGROUP    GROUP     CODE
CODE      SEGMENT PUBLIC 'CODE'
ASSUME    CS:CGROUP,DS:CGROUP
PUBLIC    PFKDI
PFKDI     PROC      FAR
          PUSH      DS             ;save BASIC DS
          MOV       AX,CS          ;set-up DS for this module    
          MOV       DS,AX
          MOV       DX,OFFSET LINE
          MOV       AH,9
          INT       21H
          POP       DS             ;restore BASIC DS & return to
          RET                      ;caller
LINE      DB        1BH,78H,31H
          DB        1BH,59H,38H,20H
          DB        20H,20H,1BH,70H
          DB        ' F KEY 1 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 2 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 3 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 4 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 5 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 6 '
          DB        1BH,71H,20H,20H,1BH,70H
          DB        ' F KEY 7 '
          DB        1BH,71H,20H,20H,20H
          DB        1BH,48H
          DB        1BH,79H,31H
          DB        1BH,45H
          DB        '$'
PFKDI     ENDP
CODE      ENDS
          END
FIG. 1 SAMPLE ASSEMBLY MODULE
A>MACRO86 B:ASMEX;
The Microsoft MACRO Assembler
Version 1.00 (C)Copyright Microsoft 1981

Warning Severe
Errors  Errors
0       0
FIG 2. INVOCATION OF MACRO-86 TO PRODUCE .OBJ FILE
          b:msbex.bas

100  FOR X=1 TO 10
110  PRINT "THIS IS TEST LINE A 1234567890"
120  PRINT "THIS IS TEST LINE B 1234567890"
130  PRINT "THIS IS TEST LINE C 1234567890"
140  PRINT "THIS IS TEST LINE D 1234567890"
150  PRINT "THIS IS TEST LINE E 1234567890"
160  CALL PFKDI
170  FOR D=1 TO 250 : NEXT
180  PRINT "THIS IS TEST LINE F 0987654321"
190  PRINT "THIS IS TEST LINE G 0987654321"
200  PRINT "THIS IS TEST LINE H 0987654321"
210  PRINT "THIS IS TEST LINE I 0987654321"
220  CALL PFKDI
230  FOR D=1 TO 250 : NEXT
240  PRINT "THIS IS TEST LINE J ABCDEFGHIJ"
250  PRINT "THIS IS TEST LINE K ABCDEFGHIJ"
260  PRINT "THIS IS TEST LINE L ABCDEFGHIJ"
270  PRINT "THIS IS TEST LINE M ABCDEFGHIJ"
280  CALL PFKDI
290  FOR D=1 TO 250 : NEXT
300  NEXT
FIG. 3. SAMPLE BASIC MODULE
A>bascom b:msbex;

Microsoft BASIC Compiler
Version 5.32
(C)Copyright Microsoft Corp 1982

24241 Bytes Available
23463 Bytes Free

     0 Warning Error(s)
     0 Severe  Error(s)

   FIG. 4.  INVOCATION OF BASIC COMPILER TO PRODUCE .OBJ FILE

A>link b:asmex+b:msbex

  Microsoft Object Linker V1.08
(C) Copyright 1981 by Microsoft Inc.

Run file [A:ASMEX.EXE]: b:msbatst.exe
List file [NUL.MAP]: b:msbatst.map
Libraries [.LIB]:
FIG. 5. INVOCATION OF MS-LINK TO PRODUCE .EXE FILE

5.4 Program Size Limitations
The following limitations apply to program size as indicated:

     CBASIC                   62K

     MS-BASIC INTERPRETER     62K

     MS-BASIC COMPILER        64K code
                              64K data

     MS PASCAL                64K object code
                              64K default data segment
                              32767 lines of source code

     MS FORTRAN               64K object code
                              64K each named common block
                              64K all local variables
                              32767 lines of source code

In FORTRAN and Pascal you can compile any number of compilands separately and link them together later; the real limit on program size is thus determined by the capability of the linker (MS-LINK) which is approximately 386K.

In CBASIC and both MS-BASIC's, multiple programs may be linked together using the CHAIN command.

5.4.1 Memory Usage outside the default 64K segment in MSBASIC

The following routine is an example of the use of extra memory. The DEF SEG statement defines a segment address for POKEing to.

In this case, the address chosen is the start of the second memory board.

10  DEF SEG=&H2000
20  FOR I%=0 TO 20000
30  POKE I%,65
40  NEXT I%
50  FOR I%=0 TO 20000
60  J%=PEEK(I%)
70  A$=CHR$(J%)
80  PRINT "BYTE NUMBER ";I%;" - VALUE =";A$
90  NEXT I%
100 END
5.4.2 Memory Usage outside of the default 64K segment in MS-Pascal

A technique to access more than 64K of data space in MS-Pascal is to use the ADS facility of the language implementation.

Due to the storage mechanism of the language it is impossible to change segment under program control as in BASIC or to use named common blocks as in FORTRAN.

ADS gives a method of creating, manipulating and dereferencing actual machine addresses.

Examples of ADS are given in the Pascal Reference Manual, (Section 8, in V3.04 "Reference Types") and a more complete program listing using ADS and ADR is given on the following page.

NOTE. Great care must be taken with the use of this facility as you do deal with actual memory locations and, as no memory map is available for use at run-time the decision on where to store variables etc.... requires a great deal of thought.

TYPE B:MEM64S
{
     PROGRAM  TO TEST THE ALLOCATION OF STORAGE AREAS OUTSIDE  OF  THE 
     DEFAULT 64K SEGMENT OF MEMORY.

     DETAILS  OF  THE  FUNCTIONS USED ARE FOUND IN SECTION  6  OF  THE 
     SIRIUS MS-PASCAL MANUAL.
                                                                     }

PROGRAM MEM64(INPUT,OUTPUT);

VAR 
     INT_VAR : INTEGER;
     REAL_VAR : REAL;
     A_INT : ADR OF INTEGER;       {relative    machine   address   of 
                                   integer}
     AS_REAL : ADS OF REAL;        {segmented machine address of real}
BEGIN
     INT_VAR :=1;
     REAL_VAR :=3.1415;
     A_INT : ADR INT_VAR;          {A_INT=relative  machine address of 
                                   INT_VAR}
     AS_REAL :=ADS REAL_VAR        {AS_REAL=segmented machine  address 
                                   of REAL_VAR}
     WRITELN (A_INT^,AS_REAL^);    {write  values pointed at by  A_INT 
                                   and AS_REAL}
     AS_REAL.S := 16#0006;         {change  AS_REAL segment pointer to 
                                   0006 HEX}
     AS_REAL^ :=REAL_VAR;          {load address pointed at by AS-REAL 
                                   with REAL_VAR}
     REAL_VAR := 9.81;
     WRITELN (REAL_VAR,AS_REAL^);  {write   REAL_VAR  and  the   value 
                                   pointed at by AS_REAL}
     AS_REAL.S := 16#0007;         {change AS_REAL segment pointer  to 
                                   0007 HEX}
     AS_REAL^ := REAL_VAR;         {load address pointed at by AS_REAL 
                                   with REAL_VAR}
     REAL_VAR := 1.0;
     WRITELN  (REAL_VAR,AS_REAL^); {write   REAL_VAR  and  the   value 
                                   pointed at by AS_REAL}
     AS_REAL.S := 16#0008;         {etc.........    }
     AS_REAL^ := REAL_VAR;
     REAL_VAR := 0.15;
     WRITELN (REAL_VAR,AS_REAL^)
END.
{    THIS  PROGRAMME IS BASED ON THE EXAMPLE GIVEN ON PAGE 6.38 OF THE 
     PASCAL REFERENCE MANUAL.                                        }

                    B:MEM64
                         1 3.1415000E+00
                    9.8099990E+00 3.1415000E+00
                    1.0000000E+00 9.8099990E+00
                    1.5000000E+01 1.0000000E+00
                 A>
5.4.3 Memory usage outside of the default 64K segment in MS-FORTRAN

A technique to access more than 64K of data space in MS-FORTRAN is shown below. Named COMMON blocks, each of which may be up to 64K bytes in size can be used.

In the example, three COMMON blocks are used, each with 10,000 elements of four bytes each, thus allowing access to 120,000 bytes of data storage.

$STORAGE:2 
     PROGRAM BIGMEM
     COMMON /COM1/ARRAY1(10000)
     COMMON /COM2/ARRAY2(10000)
     COMMON /COM3/ARRAY3(10000) 
     DO 10 I=1,10000
       ARRAY1(I)=I
       ARRAY2(I)=I+10000
       ARRAY3(I)=I+20000 
10   CONTINUE
     OPEN(1,FILE='LST')
     DO 20 I=1,10000,1000
       WRITE(1,100)'ARRAY1(I)=',ARRAY1(I),'ARRAY2(I)=',ARRAY2(I), 
    1'ARRAY3(I)=',ARRAY3(I)
20   CONTINUE
100  FORMAT(1X,A11,F7.0,A11,F7.0,A11,F7.0)  
     END 
5.5 Fix for ASYNC so it will load default file ASYN.IEM when running under MS-DOS.
Using DDT:
.CW10
-rasync.mnu
 START         END
02C8:0000   02C8:51FF
-d5100
02C8:5100 3B 53 0B 49 1E 49 45 54 52 53 44 3F 1B 6E 49 6E ;S.I.IETRSD?.nIn
02C8:5110 49 6E 49 6E 49 6E 49 AC 49 A9 49 7F 03 05 08 0A InInInI.I.I.....
02C8:5120 0D 12 15 18 1B 17 55 24 55 CA 54 17 55 CA 54 CA ......U$U.T.U.T.
02C8:5130 54 D2 54 01 55 01 55 F8 54 00 49 45 2F 4D 4F 44 T.T.U.U.T.IE/MOD
02C8:5140 45 4D 49 45 4D 00 00 00 00 13 14 15 16 00 02 1F EMIEM...........
02C8:5150 01 7E 4E FA 8E 03 1F 05 9A 00 00 00 00 32 30 54 .~N..........20T
02C8:5160 31 32 33 34 35 36 37 33 30 30 00 31 32 33 34 35 1234567300.12345
02C8:5170 36 37 38 31 32 33 01 24 0D 23 01 D3 4E BF 91 0E 678123.$.#..N...
02C8:5180 23 01 D3 4E 0A 92 04 49 01 D3 4E 93 8F 05 49 01 #..N...I..N...I.
02C8:5190 D3 4E DE 8F 06 49 01 D3 4E 29 90 07 49 01 D3 4E .N...I..N)..I..N
02C8:51A0 74 90 08 49 01 D3 4E BF 90 0C 49 01 D3 4E 9A 91 t..I..N...I..N..
02C8:51B0 0D 49 01 D3 4E E5 91 OE 49 01 D3 4E 30 92 41 44 .I..N...I..N0.AD
-s513a
02C8:513A 49 41
02C8:513B 45 53
02C8:513C 2F 59
02C8:513D 4D 4e
02C8:513E 4F 43
02C8:513F 44 20
02C8:5140 45 20
02C8:5141 4D 20
02C8:5142 49 ^Z

E>d5100
02C8:5100 3B 53 0B 49 1E 49 45 54 52 53 44 3F 1B 6E 49 6E ;S.I.IETRSD?.nIn
02C8:5110 49 6E 49 6E 49 6E 49 AC 49 A9 49 7F 03 05 08 0A InInInI.I.I.....
02C8:5120 0D 12 15 18 1B 17 55 24 55 CA 54 17 55 CA 54 CA ......U$U.T.U.T.
02C8:5130 54 D2 54 91 55 91 55 F8 54 00 41 53 59 4E 43 20 T.T.U.U.T.ASYNC 
02C8:5140 20 20 49 45 4D 00 00 00 00 13 14 15 16 00 02 1F   IEM...........
02C8:5150 01 7E 4E FA 8E 03 1F 05 9A 00 00 00 00 32 30 54 .~N..........20T
02C8:5160 31 32 33 34 35 56 57 33 30 30 00 31 32 33 34 35 1234567300.12345
02C8:5170 36 37 38 31 32 33 01 24 0D 23 01 D3 4E BF 91 0E 678123.$.#..N...
02C8:5180 23 01 D3 4E 0A 92 04 49 01 D3 4F 93 8F 05 49 01 #..N...I..N...I.
02C8:5190 D3 4E DE 8F 06 49 01 D3 4E 29 90 07 49 01 D3 4E .N...I..N)..I..N
02C8:51A0 74 90 08 49 01 D3 4E BF 90 0C 49 01 D3 4E 9A 91 t..I..N...I..N..
02C8:51B0 0D 49 01 D3 4E E5 91 0E 49 01 D3 4E 30 92 41 44 .I..N...I..N0.AD
?
-wasync.mnu
-^C
*  No "/" allowed under MS-DOS
** Fixed
5.6 Creating ASCII Text Files

Under MS-DOS it is possible to create an ASCII text file (such as a batch file) directly from the keyboard.

From the A> prompt type:

               COPY CON A:FILENAME.EXT 

This will allow you to type onto the screen, the text you wish to create. After each line hit the RETURN key.

You can edit the current line you are typing by back spacing over any errors. When finished do a control-Z [^Z] then press RETURN. This will then close the disk file.

For CP/M-86 you will need PIP:

               PIP A:FILENAME.EXT=CON:"

PIP will then allow you to create a file as above but with the exception that after each line is entered and the RETURN key hit you must also do a control-J [^J] to force a line feed. When finished do a control-Z [^Z] to close the file.

5.7 CODEC PROGRAMMING

This describes what needs to be done in order to generate sound using the codec audio section of the Sirius 1.

Refer to technical reference manual for technical explanation of the CODEC.

Software has control over the following functions:

  1. Volume clock rate
  2. Volume level
  3. Codec clock rate
  4. Codec mode (Play/Record)
  5. SDA Initialisation
  6. SDA data transfers
5.7.1 Volume

Volume is controlled by the duty cycle of the ultra-audio chopper. The chop rate is generated by a 6522 VIA. This clock can be set once and left. The clock rate should be set at a rate above 20khz in order not to be heard as a tone in the audio output. The system normally sets this clock to the maximum rate the 6522 will generate.

     Volume clock rate setup example:

     INIT_VOLUME_CLOCK: proc;
     port$ptr= via_20;
     /* set shift register mode in acr to shift out on t2 */
     via(acr)= (via(acr) and not(1ch)) or 10h;
     /* set t2 to fastest clock rate */
     via(t2)= 1;
     /* init volume level to off value, 0= max,ff= off */
     /* 00,01,03,07,0f,3f,7f,ff are valid volume values */
     via(sr)= 0ffh;
     end INIT_VOLUME-CLOCK;


     Volume level set example:

     SET_VOLUME: proc(level);
        dcl level     byte;
     port$ptr= via_20;
     /* move new level into sr to control duty cycle */
     via(sr)= level;
     end SET_VOLUME
5.7.2 CODEC Clock

The codec clock rate determines the quality of the recorded message. The higher the clock rate the higher the quality. This clock should be adjusted to fit the need of the application as far as quality and data space require.

Typical clock rate is 16khz for good speech, and 32khz for very good speech quality. The data rate at 16khz is 2k bytes/second and at 32khz it is 4k bytes/second.

The codec clock is generated by a 6522 via using timer 1. The value stored in the 6522 timer is one half the period of the codec clock.

     N= ( 500,000/(F*8) )-1 or N= (62500/F)-1
     N is the 6522 timer value
     F is the desired clock frequency in Hz

Codec clock setup example:

     /* freq is the desired clock rate in hz */
     SET_CODEC_CLOCK: proc(freq);
        dcl freq     word;
     port$ptr= via_80;
     /* set new timer value for freq */
     via(tl)= (62500/freq)-1;
     end SET_CODEC_CLOCK;
5.7.3 Codec Mode Control

The codec can both encode speech input to digital data, and decode stored digital data back to speech output. The mode control for the codec is supplied by using the SM/DTR output of the SDA chip.

Example of codec mode control:

     /* set mode of code to play or record */
     /* 0 is play  1 is record */
     SET_CODEC_MODE: proc(mode);
        dcl mode    byte;
     if mode=0 then
          do;
          sda.r0=0;     /* select control reg 2 */
          sda.r1=5bh;   /* this sets SM/DTR low */
          end;

     else
          do;
          sda.r0=0;     /* select control reg 2 */
          sda.r1=58h;   /* this sets SM/DTR high */
          end;
     end SET_CODEC_MODE;
5.7.4 SDA Initialisation

The SDA performs the parallel to serial conversion and data buffering function for the CPU. This helps cut down the amount of time needed to service the CODEC data feeding and reading. The SDA must be initialised before any other codec operation is performed.

The functions that are setup are:

  1. set word length to 8 bits no parity, play mode
  2. set sync code to 0aah, on underflow send sync
  3. set byte transfer ready to 2 bytes

SDA initialisation example:

     SDA_INIT: proc;
     /* set word length to 8,play mode,2 byte ready */
     sda.r0=0;          /* select control reg 2 */
     sda.r1=5bh;
     /* set sync code to 0aah for quite pattern on underflow */
     sda.r0=80h;        /* select sync code */
     sda.r1=0aah;
     /* set external sync mode for par to ser operation */
     sda.r0=40h;        /* select control reg 3 */
     sda.r1=0dh;        /* clear TUF and CTS */
     sda.r0=40h;
     sda.r1=01h;        /* enable TUF and CTS*/
     end SDA_INIT;
5.7.5 SDA Data Transfer

The SDA is the interface the CPU uses to generate sound from the codec. This example is for educational purposes and is NOT the only method that can be used to drive the codec.

In order to record speech from the codec the CPU must:

  1. initialise the SDA
  2. set clock rate
  3. set volume to off
  4. set codec to record mode
  5. read data from SDA receive register
  6. set codec back to play mode

Example of recording data from codec:

     /* record a buffer of codec data at buf$ptr */
     /* record count bytes of data */
     RECORD: proc(buf$ptr,count);
          dcl buf$ptr   pointer;
          dcl count     word;
          dcl buffer(1) based buf$ptr;
          dcl i         word;
     call SET_VOLUME(0ffh);    /* set volume off */
     call SET_MODE(1);         /* set to record mode */
     i=0;
     do while (count [ | 0);   /* read in count bytes of data */
          /* wait for receive byte ready in r0 */
          do while (sda.r0 and 1) [|1; end;
          buffer(i)=dsa.r1;    /* read and store data byte */
          i=i+1;
       end;
       call SET_MODE(0);
     end RECORD;

Play back of codec data example:

     /* play a buffer of codec data back */
     /* buffer pointed at by buf$ptr */
     /* play count bytes */
     PLAY; proc(buf$ptr,count);
          dcl buf$ptr   pointer;
          dcl count     word;
          dcl i         word;
          call SET_MODE(0);    /* set play mode */
          do i=0 to count;
            /* wait for ready to send flag */
            do while (sda.r0 and 2)=0; end;
            sda.rl= buffer(i); /* store next byte */
          end;
     end PLAY;
5.8 Data Security

This sample program appends characters to a file. It closes the file and then re-opens it after writing every character to ensure that at most one character is lost if the system crashes.

name      test

code      segment public 'code'
assume    cs:code, ds:code

;  NOTE:  This  program assumes that an ASCII file named TEST  is 
;  located on the default drive.   TEST is the file that the data 
;  is appended to.

lf        equ  0ah
cr        equ  0dh
altz      equ  1ah

dir_con_io equ 06h
print     equ  09h
open      equ  0fh
close     equ  10h
write     equ  15h
setdma    equ  1ah

test :
          push es        ;save ptr to base page

          mov  ax,code   ;set up DS
          mov  ds,ax

          lea  dx, buffer     ;set up dma
          mov  ah, setdma
          int  21h

open_it:
          lea  dx, fcb
          mov  ah, open  ;open file
          int  21h
          inc  al
          jz   file_not_found

          mov  fcb_recsize, 1      ;set record size to 1 byte

;  set  the current block and current record to point at the last 
;  byte of the file since we want to append it.

          mov  ax, fcb_filesize
          xor  dx, dx
          mov  cx, 128
          div  cx
          mov  fcb_cur_block, ax
          mov  fcb_cur_rec, dl
              -- listing continued on next page --
; get a character from the keyboard

get_char_from_kb:
          mov  ah, dir_con_io
          mov  dl, 0ffh
          int  21h
          jz   get_char_from_kb

;  if the character is ALT-Z, then stop

          cmp  al, altz
          jz   end_prog

; write the character to the file

          mov  buffer, al
          mov  ah, write
          lea  dx, fcb
          int  21h

;  if the character was a carriage return, then write a line feed 
;  also

          cmp  byte ptr buffer, cr
          jnz  close_it

          mov  byte ptr buffer, lf
          mov  ah, write
          lea  dx, fcb
          int  21h

;   close the file to save what we just wrote in case the  system 
;   crashes

close_it:
          mov  ah, close
          lea  dx, fcb
          int  21h

;  go to open the file and get the next character

          jmp  open_it

file_not_found:
          lea  dx, not_found_msg
          mov  ah, print
          int  21h

end_prog:

return    proc far
;  do  a  far return to the first byte  of  the  basepage,  which 
;  contains an int 20 operation to terminate the program

              -- listing continued on next page --
          xor  ax, ax
          push ax
          ret

return    endp

fcb            db        0
fcb_name       db        'TEST      '
fcb_cur_block  dw        ?
fcb_recsize    dw        ?
fcb_filesize   dw        ?
               dw        ?
fcb_date       dw        ?
fcb_time       dw        ?
fcb_reserved   db        8 dup (?)
fcb_cur_rec    db        ?
fcb_rel_rec    db        4 dup (?)

buffer         db        0

not_found_msg   db       13,10,'The  sample  file  TEST  was  not 
                         found.',13,10,'$'

code      ends
          end
5.9 MS-Pascal Date and Time Program (input, output)

This program demonstrates the use of the MS-Pascal Date and Time procedures, as well as showing how to change the date by using the MS-DOS 1.25 Set Date function with the DOSXQQ function call.

PROGRAM Date_Time (INPUT,OUTPUT);

PROCEDURE DATE(VAR s: STRING   ); EXTERNAL;

PROCEDURE TIME(VAR s: STRING   );EXTERNAL;

FUNCTION DOSXQQ( command,parameter: WORD): BYTE;EXTERNAL;

VAR date_str,time_str: LSTRING(8);

FUNCTION Set_Date(CONST date_str: STRING):BOOLEAN;
(* Changes the system date if date_str is valid,  otherwise
 returns FALSE. *)

CONST setdate = QN2B;

VAR month_word,day_word,year_word,param: WORD;
    date_lstr: LSTRING(2);
    date_status,month_byte,day_byte: BYTE;

BEGIN
    Set_date: = FALSE;
    date_lstr.LEN: = 2;
    FOR VAR l:= 1 TO 2 DO date_lstr[l]: = date_str[l];
    IF NOT DECODE(date_lstr,month_word) THEN RETURN;
    FOR VAR l: = 4 TO 5 DO date_lstr[1-3]: = date_str[l];
    IF NOT DECODE(date_lstr,day_word) THEN RETURN;
    month_byte:= LOBYTE(month_word);
    day_byte:= LOBYTE(day_word);
    param:= BYWORD(month_byte,day_byte);
    FOR VAR l:= 7 TO 8 DO date_lstr[1-6]:= date_str[l];
    IF NOT DECODE(date_lstr,year_word) THEN RETURN;
    year_word:= year_word + 1900;
    CRCXQQ:= year_word;
    date_status:= DOSXQQ(setdate,param);
    IF date_status <> QNFF THEN
     Set_Date:= TRUE;
    END;

BEGIN
    time_str.LEN:= 8;
    TIME(time_str);
    WRITELN('The current system time is ',time_str);
    date_str.LEN:= 8;
    DATE(date_str);
    WRITELN('The current system date is ',date_str);
    WRITE('Enter  a  new date (mm-dd-yy) to change the  date,  or 
          else :');
    READLN(date_str);
    WRITELN;
    IF date_str.LEN > 0 THEN
      BEGIN
        IF Set_Date(date_str) THEN
         BEGIN
          DATE(date_str);
          WRITELN('The new system date is ',date_str);
         END
        ELSE
         WRITELN('Date ',date_str,'is not a valid date');
       END;  
      END.
5.10 Accessing System Time in dBASE II
ENTRY:BX - Pointer to length byte at start of string
         - 11 byte (space) string passed from dBASE
EXIT: string is passed back to dBASE with time
CHANGE:  All registers destroyed, but dBASE will return machine's 
     state when it regains control.

This program takes an 11 byte long string from dBASE and puts the MS-DOS time into it. The string must be of character type. If the string is not 11 bytes long, the routine is exited with no change. By typing DTIME at the system prompt before entering dBASE, the first part of the program loads the second half at address 65024 decimal in the current program segment. (Actually, any location above A400H is okay. Further, the dBASE command load [filename] could load an assembly language routine within dBase). This address is then used inside dBASE as the argument for the SET CALL TO command. Because the 1/100th seconds clock in MS-DOS returns either 00 or 50 it was felt to be of limited use and was not included in this program. A 12 hour clock with an AM or PM tag at the end was employed for ease of use. If a 24 hour clock is desired the conversion code can easily be eliminated. A SORT routine might overwrite the time code if it is large enough. If this is a problem, the code can be modified to use INT 27H to create a protected area in MS-DOS. A common sequence of commands to get the time would be:

A>DTIME

      A>DBASE

      Copyright (C) 1982 RSP Inc.

      *** dBASE II/86     Ver 2.4        1 July 1983

      .STORE"12345678901" TO TIME
      12345678901
      .SET CALL TO 65024
      .CALL TIME
      ? TIME
      5:23:00 PM

STOUT          GROUP          CODE
ASSUME         CS:STOUT,DS:STOUT             ;"STring OUT"

This first part loads GETTIME
CODE           SEGMENTPUBLIC 'CODE'
               ORG  100H         ;load over PSP
               MOV  CX,0A6H      ;prepare to move A6H bytes
               LEA  SI,GETTIME   ;put start of time code in SI
               MOV  DI,0FE00H    ;set destination above dBASE
               CLD
               REP  MOVSB        ;move it
               INT  20H          ;terminate

This is the actual time code

GETTIME        PROC NEAR
          
               NOP  ; entry target
               MOV  DI,BX      ;put address of length byte in DI
               XOR  BX,BX        ;clear it
               MOV  BL,0BH       ;put expected length in BL
               CMP  [DI],BL      ;is it 11 bytes long?
               JNZ  SHORT DONE   ;no, exit with no change

               INC  DI           ;move DI to start of string
               MOV  AH,2CH       ;get time in CX and DX request
               INT  21H          ;get it

               MOV  AL,CH        ;put hours in AL
               CMP  AL,0CH       ;after 12:00 noon?
               JGE  AMPM_FLAG    ;keep under 12, flag as PM
               CMP  AL,00        ;is it zero o'clock?
               JZ   SHORT MIDNIGHT  ;make it midnight
CONHOURS:      CALL CONVERT      ;convert to ASCII
               MOV  BH,0FFH      ;flag to OUT that this is hours
               CALL OUT          ;put hours in string

               MOV  AL,C         ;put minutes in AL
               CALL CONVERT
               CALL OUT

               MOV  AL,DH        ;put seconds in AL
               CALL CONVERT
               MOV  BH,0AAH      ;flag for OUT
               CALL OUT

               CMP  BL,0FFH      ;is it night?
               JZ   SHORT POUT
               MOV  [DI],BYTE PTR 'A'  ;give me an A
MOUT:          INC  DI
               MOV  [DI],BYTE PTR 'M'  ;give me an M
               JMP  SHORT DONE
POUT:          MOV  [DI],BYTE PTR 'P'  ;give me a P
               JMP  SHORT MOUT

DONE:          RET  ;all finished


CONVERT:       XOR  BH,BH        ;clear counter
               PUSH BX           ;save AM/PM flag

CONVERT2:      AAM               ;unpack AL
               ADD  AL,30H       ;bump to ASCII
               ADD  BH,01        ;loop count
               CMP  AH,0         ;quotient zero?
               JZ   SHORT CVRTDONE  ;then we're through
               MOV  BL,AL        ;save LSD
               MOV  AL,AH        ;for next unpack
               JMP  SHORT CONVERT2  ;do it again

ONEDIGIT:      MOV  AH,30H       ;MSD is zero
               POP  BX
               RET

CVRTDONE:      CMP  BH,01        ;one digit number?
               JZ   SHORT ONEDIGIT  ;put '0' in AH
               MOV  AH,AL        ;put MSD in AL for OUT
               MOV  AL,BL        ;put LSD in AL for OUT
               POP  BX
               RET

AMPM_FLAG:     MOV  BL,0FFH      ;flag as PM
               CMP  AL,0CH       ;is it after 12 noon?
               JG   SHORT CHOP   ;then keep hours under 12
               JMP  SHORT CONHOURS

CHOP:          SUB  AL,0CH
               JMP  SHORT CONHOURS

MIDNIGHT:      MOV  AL,0CH
               JMP  SHORT CONHOURS

OUT:           CMP  BH,0FFH      ;is it hours?
               JZ   SHORT ZEROKILL  ;might kill first zero
OUT2:          MOV  [DI],AH      ;move out first digit
               INC  DI           ;point to next string position
               MOV  [DI],AL      ;move out second digit
               INC  DI           ;point to next string position
               CMP  BH,0AAH      ;is it end of time numbers?
               JZ   SHORT BLANKOUT   ;send a space
               MOV  [DI],BYTE PTR ':'  ;send out colon
               INC  DI           ;advance to next position
OUT3:          RET

ZEROKILL:      XOR  BH,BH        ;clear flag that got us here
               CMP  AH,'0'
               JZ   SHORT ZEROFF
               JMP  SHORT OUT2


ZEROFF:        MOV  AH,' '
               JMP  SHORT OUT2


BLANKOUT:      XOR  BH,BH        ;clear flag that got us here
               MOV  [DI],BYTE PTR ' '  ;send space
               INC  DI
               JMP  SHORT OUT3

GETTIME        ENDP

CODE           ENDS

               END
5.11 Programming the 8253 Timer

The 8253 Timer, counter 2 is used for timer interrupt. Clock rate from Sirius hardware is 100 kHz.

For example, to obtain 100 microsecond interrupts we need to divide by 10. The 8253 can be used in a BCD or binary mode.

In assembler:

     org 100h

start:
     mov  bx,0e000h      ; ES points to I/O
     mov  es,bx
     mov  bx,23h         ; address mode register
     mov  al,0b5h        ; counter 2, mode 2, BCD     
     mov  es:[bx],al     ; write mode word
     mov  bx,22h         ; address counter 2
     mov  al,10h         ; least significant byte (LSB)
     mov  es:[bx],al
     mov  al,0           ; most significant byte (MSB)
     mov  es:[bx],al

The result is a 10 microsecond pulse every 100 microseconds.

Maximum time = 620 milliseconds (ms) approx. (MSB=0, LSB=0)
Minimum time = 20 microseconds (us) approx.  (MSB=0, LSB=2)
(Binary mode)

10^4 = 100 ms (0,0)
10^3 =  10 ms (10h,0)
10^2 =   1 ms (1,0)
10^1 = 100 us (0,10h)
5.12 Manipulating a Batch File
100 '----------------------------------------------------------------------
110 '---   START.BAS	by Keith Pickup					---
120 '---		Barson Computers (Sydney - Australia)		---
130 '---								---
140 '---   This program manipulates a batch file so that programs 	---
150 '---   may be executed by selecting them from a master menu.	---
160 '---   Parameters may be passed to the command string by tagging	---
170 '---   them to the program name string as in lines 710 & 720	---
180 '---   The program could also be compiled which would help		---
190 '---   speed it up.							---
200 '---								---
210 '---   This program requires an AUTOEXEC.BAT file to be created	---
220 '---   which contains the command 'MENU'. 				---
230 '---   MENU is in fact MENU.BAT which contains the following	---
240 '---   								---
250 '---	    MSBASIC START (or just START if compiled)		---
260 '---	    OPTION (which is a dummy command)			---
270 '---	    PAUSE **** Program Terminated ****			---
280 '---	    MENU (which calls the original .BAT file		---
290 '---								---
300 '---   The PAUSE allows programs like CHKDSK to display their	---
310 '---   information and wait for a response from the keyboard	---
320 '---   before re-loading the master menu program			---
330 '---								---
340 '---   Lastly there is a file called FINISH.BAT which contains	---
350 '---   no commands at all therefore allowing exit to the system	---
360 '---								---
370 '---   NOTE: The command 'dir/w|sort|more' relates to DOS 2.0	---
380 '---								---
390 '----------------------------------------------------------------------
400 '
410 ' *** define program variables ***
420 '
430 WIDTH 255:HEADING$="  **** MASTER MENU ****  "
440 ESC$=CHR$(27):REV$=ESC$+"p":ROFF$=ESC$+"q":CLR$=ESC$+"z":HOME$=ESC$+"H"
450 DIM SELECT$(10),PROG$(7),TEMP$(4),FK$(7):TOTAL.OPTIONS=7
460 FOR K%=1 TO TOTAL.OPTIONS:READ SELECT$(K%):NEXT K%
470 DATA "Sorted Directory Listing","Check disk space","Format new disk"
480 DATA "Copy diskettes","Edit ASCII file","Microsoft Basic"
490 DATA "Return to Operating System"
500 PAUSE$="pause **** Program Terminated ****"
510 PROG$(1)="dir/w|sort|more":PROG$(2)="chkdsk":PROG$(3)="format/e"
520 PROG$(4)="dcopy/e":PROG$(5)="edlin":PROG$(6)="msbasic":PROG$(7)="finish"
530 '
540 ' *** read current contents of the MENU.BAT file ***
550 '
560 OPEN "I",#1,"MENU.BAT"
570 FOR K%=1 TO 4
580 INPUT #1,TEMP$(K%)
590 NEXT K%:CLOSE 1
600 '
610 ' *** display menu for selection of option ***
620 '
630 PRINT CLR$;:KEYBASE=1:GOSUB 820
640 PRINT HOME$;TAB(40-(LEN(HEADING$)/2)+.5) REV$;HEADING$;ROFF$:PRINT:PRINT
650 FOR K%=1 TO TOTAL.OPTIONS
660 PRINT TAB(25);REV$;K%;ROFF$;"   ";SELECT$(K%):PRINT
670 NEXT K%
680 PRINT:PRINT TAB(25) "Please select option required ";
690 IP$="":IP$=INKEY$:IF IP$="" THEN 690
700 IP=VAL(IP$):IF IP < 1 OR IP > TOTAL.OPTIONS THEN 690
710 IF IP=5 THEN PRINT:PRINT TAB(25);:INPUT "Edit file name ";FILE.NAME$
720 IF IP=5 THEN PROG$(5)=PROG$(5)+" "+FILE.NAME$
730 TEMP$(2)=PROG$(IP):TEMP$(3)=PAUSE$
735 FOR I%=1 TO 7:PRINT ESC$+"41"+CHR$(I%)+CHR$(&HF0+I%):NEXT I%
740 '
750 ' *** write out new batch file replacing 'option' with program name ***
760 '
770 OPEN "O",#1,"MENU.BAT"
780 FOR K%=1 TO 4
790 PRINT #1,TEMP$(K%)
800 NEXT K%:CLOSE 1
810 PRINT CLR$:SYSTEM
820 '
830 ' *** set up for the 25th line ***
840 '
850 IF KEYBASE=1 THEN RESTORE 1010
860 IF KEYBASE=2 THEN RESTORE 1020
870 IF KEYBASE=3 THEN RESTORE 1030
880 FOR I%=1 TO 7:PRINT ESC$+"41"+CHR$(I%)+CHR$(&H30+I%):NEXT I%
890 READ FKCT
900 FOR I%=1 TO FKCT:READ FK$(I%):NEXT I%
910 GOSUB 920:RETURN
920 FKSZ=INT((80-(FKCT-1))/FKCT)
930 X9$="":C9$=ESC$+"q"+" "+ESC$+"p"
940 FOR I%=1 TO FKCT
950 B9$=LEFT$(FK$(I%),FKSZ):J9%=INT((FKSZ-LEN(B9$)+1)/2)
960 X9$=X9$+C9$+LEFT$(SPACE$(J9%)+B9$+SPACE$(FKSZ),FKSZ)
970 NEXT I%
980 X9$=ESC$+"p"+X9$+ESC$+"q"
990 PRINT ESC$"x1";ESC$"j";ESC$"Y8 ";ESC$"l";X9$;ESC$"Y  ";ESC$"k";
1000 RETURN
1010 DATA 7,"SORT DIR","CHK DISK","FORMAT","DCOPY","EDIT","MSBASIC","EXIT"
1020 DATA 7," "," "," "," "," "," "," "
1030 DATA 7," "," "," "," "," "," "," "
5.13 CALC (or UDCCALC) - Calculator Function

CALC, the calculator function (version 1.1 or later) on both MS- DOS and CP/M-86 work as follows:

Call up the calculator function by typing:

     CALC 

and striking SHIFT with CALC key.

Division

- no quirks

eg. 9:-6 (CALC KEY)

Multiplication

- one quirk - Your X key on the keyboard may not be configured so use the * key instead. (ie. SHIFT and 8 key).

eg. 9 X 6 (CALC KEY)

or 9 * 6 (CALC KEY)

Addition and Subtraction

- many quirks - Do not use the (CALC KEY) for the equal sign as it does not work. Only use it to clear the accumulator.

eg. 9 + 6 = would be 9 + 6 +

9 - 6 = would be 9 - 6 -

5.14 Directory Entries

The maximum number of directory entries in each version of the operating system is as follows:

CP/M 1.0                                               128

CP/M 1.1                                               128

MS-DOS 1.25/2.5 or earlier (floppy discs)              128

MS-DOS 1.25/2.5 (hard discs)                           no 
                                                       practical 
                                                       upper 
                                                       limit

Notes

  1. The maximum number of directory entries possible is not necessarily the same as maximum number of files.


    • CP/M-86 records are 128-byte blocks of data. When a file is created an extent (area) of disc is allocated to contain a maximum of 128 records i.e. 16K bytes of data. If files are larger than 16K bytes they will need extra extents.


    • The directory of a CP/M-86 disc contains an entry for each extent of each file. Therefore a file with more than one extent will have a multiple directory entry for each file displayed.

    • MS-DOS does not block records into 128 bytes each, nor does it use 16K file extents. A file can be scattered over the disc, not necessarily residing in contiguous sectors. Therefore the directory contains an entry for each block of file on disc.

      It is advisable to run the Check disc utility (CHKDSK.COM) to reclaim disc space after files have been deleted.
5.15 MS-DOS File Sizes and Disc Structure

Floppy disc

The CP/M-86 operating system blocks individual records into multiples of 128 bytes. eg. An 80 character record will always occupy 128 bytes of disc space.

MS-DOS on the other hand, does not do this. It does however, group records together into blocks or allocation units of 2K bytes (2048 bytes). These are the smallest units that can be written to or read from disc at any one time. Therefore a file will always occupy multiples of 2K bytes.

example

A file with fixed length records of 80 characters, containing 150 records.

          Data file = 80 * 150 bytes
                    = 12000 bytes

The disc space will then be allocated as follows:

Number of 2K byte blocks = size of data file divided by 2048
                         = 12000 divided by 2048
                         = 5.859
                         = 6  (rounding  upwards to  the  nearest 
                               whole block)

Total disc space = 6 * 2K blocks
                 = 6 * 2048 bytes
                 = 12228 bytes

The DIR command will give the file size as 12000 bytes however, the actual amount of disc space allocated for the file is 12228 bytes.

The CHKDSK command will give the sum of the actual disc space allocated for the files.

FILES               DIRECTORY SIZE                DISC SPACE
------------------------------------------------------------
COMMAND.COM              5737                          6144
  SETIO.COM              1012                          2048
 CHKDSK.COM              1976                          2048
  DCOPY.COM             15776                         16384
 FORMAT.COM             17132                         18432
  RDCPM.COM             11214                         12288
MSBASIC.COM             31360                         32768
UDCCALC.COM              4917                          6144
  EDLIN.COM              2432                          4096
 DISKID                  1536                          2048
                        -----                        ------
                        93092                        102400
                        -----                        ------

CHKDSK gives the value of 102400 bytes.

Hard Disc

The only difference with the Internal Winchester Sirius and the floppy disc drive Sirius is the size of the allocation units. On the floppy disc drive the allocation unit size is fixed at 2K. On the hard disc the user can specify different allocation units for each volume. Therefore files on disc can be multiples of 2K, 4K, 8K etc.