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:
- Volume clock rate
- Volume level
- Codec clock rate
- Codec mode (Play/Record)
- SDA Initialisation
- 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:
- set word length to 8 bits no parity, play mode
- set sync code to 0aah, on underflow send sync
- 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:
- initialise the SDA
- set clock rate
- set volume to off
- set codec to record mode
- read data from SDA receive register
- 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
- 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.