home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
PASCAL
/
PASTUT34
/
REGS.TXT
< prev
next >
Wrap
Text File
|
1993-06-12
|
19KB
|
509 lines
USING REGISTERS.
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
Before considering the use of registers in Turbo Pascal, it is useful
to appreciate the function and behaviour of registers and to use the
DOS facility DEBUG to inspect both the registers and memory.
Intel 8088, 8086, 80286 and 80386 microprocessors have 14 Registers,
which are used to store and process numbers quickly, within the
Central Processor Unit (CPU) itself. These are 16-bit registers,
although the first four may be accessed as 8-bit parts.
The word-sized registers are as follows:
Data registers (general purpose) - AX, BX, CX and DX
Pointer registers (Base and Stack) - BP and SP
Index registers (Source & Destination) - SI and DI
Segment registers (Code, Data, Stack & Extra) - CS, DS, SS and ES
Instruction pointer register - IP
Status register (Flags).
The function of each register.
══════════════════════════════
The AX (Accumulator) register is used to store data prior to the
execution of an instruction.
The BX (Base) register is used in some memory addressing schemes.
The CX (Count) register is often used as a loop counter.
The DX (Data) register is used to store 16-bit data or as an extension
to the AX register when handling 32-bit data.
The BP (Base Pointer) and SP (Stack Pointer) registers are used to
point to the Stack, a part of memory used for passing parameters to
subroutines, storing return addresses and saving register values for
later retrieval.
The SI (Source Index) and DI (Destination Index) registers are used in
complex addressing modes and for string manipulation.
The CS (Code Segment) register identifies the segment in which the
program resides. Absolute memory addresses of specific instructions
are calculated by combining the segment address in the CS register
with the offset address in the Instruction Pointer (IP) to get a
20-bit absolute address of the instruction - CS:IP.
The DS (Data Segment) register identifies the segment in which the
data (program variables) reside. In conjunction with another register
containing the offset data item (e.g. DS:BX), this register can also
be used to find the absolute address of that data item.
The SS (Stack Segment) register points to the stack segment and is
used in conjunction with the Stack Pointer (SP).
The ES (Extra Segment) register is used by string instructions.
Memory Usage.
═════════════
Referring to the Turbo Pascal Memory Map (page 210 of the Programmer's
Guide), the memory is used as follows:
The data segment (addressed through DS) contains all typed constants
followed by all global variables. The DS register is never changed
during program execution.
The stack segment register (SS) and the stack pointer (SP) are loaded
on entry to the program so that SS:SP points to the first byte past
the stack segment. The SS register is never changed during program
execution, but SP can move downward until it reaches the bottom of
the segment.
The ES (Extra Segment) register points to an extra segment of memory,
which may be used, with DS, to mark the source and destination of
string moves.
The IP (Instruction Pointer) register holds the offset address of the
next instruction to be executed and is used in conjunction with CS to
obtain the absolute address.
The Status register is a collection of nine bits (flags), which
indicate the outcome of arithmetical and logical operation as below:
The Carry flag (bit 0) is set to 1 if add causes a carry or subtract
causes a borrow.
The Parity flag (bit 2) is set to 1 for even parity, 0 for odd parity.
The Auxiliary Carry flag (bit 4) is used for BCD arithmetic.
The Zero flag (bit 6) is set to 1 if the result of an operation is
zero.
The Sign flag (bit 7) is set to 1 if the result of an arithmetical or
logical operation is negative.
The Trap flag (bit 8) is set by the processor to single-step through
the program instructions.
The Interrupt enable flag (bit 9) is set to 1 to enable interrupts.
The Direction flag (bit 10) is set to either increment or decrease the
index register by one.
The Overflow flag (bit 11) indicates an overflow condition in
arithmetic operations with signed numbers.
The DOS Debug facility and registers.
═════════════════════════════════════
A simple text program, which can be created by using the Turbo Pascal
editor, can be loaded into memory by means of the DOS debug utility
by typing:
debug <filespec>
where <filespec> can be the full file specification, including drive,
directory and filename.
The screen response is the minus sign (-) and then a number of debug
commands can be used, such as d followed by the offset address to
'dump' a section of memory or r to indicate the state of the various
registers as shown below:
debug test.txt
-d 0100
4DFC:0100 46 69 72 73 74 20 6C 69-6E 65 20 6F 66 20 74 65 First line of te
4DFC:0110 78 74 0D 0A 53 65 63 6F-6E 64 20 6C 69 6E 65 20 xt..Second line
4DFC:0120 66 6F 6C 6C 6F 77 73 0D-0A 54 68 69 72 64 20 6C follows..Third l
4DFC:0130 69 6E 65 20 69 73 20 6E-65 78 74 0D 0A 46 6F 75 ine is next..Fou
4DFC:0140 72 74 68 20 6C 69 6E 65-20 69 73 20 6C 61 73 74 rth line is last
4DFC:0150 0D 0A 1A 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4DFC:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4DFC:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-r
AX=0000 BX=0000 CX=0053 DX=0000 SP=2AB6 BP=0000 SI=0000 DI=0000
DS=4DFC ES=4DFC SS=4DFC CS=4DFC IP=0100 NV UP EI PL NZ NA PO NC
4DFC:0100 46 INC SI
-q
The last command q is used to quit debug.
It can be seen that the CS register displays the segment in which the
code resides, whilst the IP register indicates that the code starts at
the offset address 0100, which is the normal offset used by debug.
Finally, other debug commands can be used to write a new line to the
file. Enter (e) is used first to add a new line and then insert a
carriage return and line feed (OD OA). Then Name (n) is used to give
this file a new name (test2.txt) and Write (w) is employed to write
the new file, as shown below.
-e 0152 "One more line added"
-e 0165 0D 0A
-r cx
CX 0053
:67
-n test2.txt
-w
Writing 0067 bytes
This can then be checked as follows:
-d 0100
4DFC:0100 46 69 72 73 74 20 6C 69-6E 65 20 6F 66 20 74 65 First line of te
4DFC:0110 78 74 0D 0A 53 65 63 6F-6E 64 20 6C 69 6E 65 20 xt..Second line
4DFC:0120 66 6F 6C 6C 6F 77 73 0D-0A 54 68 69 72 64 20 6C follows..Third l
4DFC:0130 69 6E 65 20 69 73 20 6E-65 78 74 0D 0A 46 6F 75 ine is next..Fou
4DFC:0140 72 74 68 20 6C 69 6E 65-20 69 73 20 6C 61 73 74 rth line is last
4DFC:0150 0D 0A 4F 6E 65 20 6D 6F-72 65 20 6C 69 6E 65 20 ..One more line
4DFC:0160 61 64 64 65 64 0D 0A 00-00 00 00 00 00 00 00 00 added...........
4DFC:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-
Register Usage.
═══════════════
There are three ways in which registers are used in Turbo Pascal.
1. With debug to trace a Turbo Pascal program, observing the contents
of the registers, as they change with each machine code instruction.
The contents of the registers can also be inspected from the
Integrated Development Environment by means of the Window Menu (Alt-W)
and selecting R for Registers, the contents of which are then shown
in a window at the top right of the screen. The registers can also be
inspected in the watch window, as explained later in these notes.
2. To accept the return of function results.
3. In conjunction with Interrupt Service Routines.
1. Tracing a Turbo Pascal program.
──────────────────────────────────
When a Turbo Pascal program is compiled it is converted to machine code.
It is then possible to use the DOS debug facility to locate this .EXE
file in memory, find a particular instruction and trace its operation.
It is also possible to 'unassemble' the machine code and obtain the
assembly language instructions.
The process of locating a particular instruction is facilitated by the
use of a simple Inline machine code instruction, which creates three
consecutive NOP instructions ( No OPeration ). In machine code this
instruction is represented by the hexadecimal value 90. The occurrence
of 90 90 90 clearly indicates this point in the program.
A very simple Turbo Pascal program is now used to illustrate this point.
program Turbo_Assembly;
var
x,y : Integer;
begin
Inline($90/$90/$90);
x := 3;
y := x * x;
Inline($90/$90/$90)
end.
If this program is saved as RegsDemo.pas and compiled to RegsDemo.exe,
it can then be studied with the DOS debug facility as shown below:
C>debug { run debug }
-n RegsDemo.exe { set file name to be used by load }
-l { load file }
-s cs:0000 ffff 90 90 90 { search code segment for NOPs }
13A0:0008 { the first encounter }
13A0:001b { the second encounter }
-r ip { request the current value of IP }
IP 0000 { the value returned by debug }
:0008 { debug gives : prompt for new value }
-t { trace request for single instruction }
At this point the contents of all the registers are shown and the next
instruction - in this case NOP.
AX=0000 BX=0000 CX=0330 DX=0000 SP=4000 BP=0000 SI=0000 DI=0000
DS=1390 ES=1390 SS=1415 CS=13A0 IP=0009 NV UP EI PL NZ NA PO NC
13A0:0009 90 NOP
Further trace commands repeat this procedure for each instruction,
until the final three NOP instructions are encountered. The displays are
similar with some values remaining unchanged. The essential changes are
shown below.
-t
AX......
DS...... IP=000A
13A0:000A 90 NOP
-t
AX......
DS...... IP=000B
13A0:000B C7063E000300 MOV WORD PTR [003E],0003 DS:003E=0000
-t
AX......
DS...... IP=0011
13A0:0011 A13E00 MOV AX,[003E] DS:003E=0003
-t
AX=0003......
DS...... IP=0014
13A0:0014 F72E3E00 IMUL WORD PTR [003E]
-t
AX=0009......
DS...... IP=0018 ZR PE
13A0:0018 A34000 MOV [0040],AX DS:0040=0000
-t
AX=0009......
DS...... IP=001B
13A0:001B 90 NOP
The unassembled machine code is obtained by using the debug command
u <segment address> <offset address> as shown below:
-u 13A0:0000
13A0:0000 9A0000A313 CALL 13A3:0000
13A0:0005 55 PUSH BP
13A0:0006 89E5 MOV BP,SP
13A0:0008 90 NOP
13A0:0009 90 NOP
13A0:000A 90 NOP
13A0:000B C7063E000300 MOV WORD PTR [003E],0003
13A0:0011 A13E00 MOV AX,[003E]
13A0:0014 F72E3E00 IMUL WORD PTR [003E]
13A0:0018 A34000 MOV [0040],AX
13A0:001B 90 NOP
13A0:001C 90 NOP
13A0:001D 90 NOP
etc.
Although an understanding of machine code is required to fully
appreciate the above, it can be seen that the value 0003 is sent to
an address 003E and then put into the AX register and finally
(integer) multiplied with itself to give 0009 in the AX register.
2. Function results in Turbo Pascal.
────────────────────────────────────
Function results are returned through the registers, not the stack,
as shown in the table below:
Function result Registers
─────────────── ─────────
shortint, byte AL
integer, word AX
longint DX:AX (most significant word: least significant word)
real DX:BX:AX (most: middle: least significant word)
single, double )
extended, comp ) Maths coprocessor top-of-stack register
pointer DX:AX (segment:offset)
string Pointer on stack to temporary storage
────────────────────────────────────────────────────────────────────────
3. Interrupt Service Routines.
──────────────────────────────
Interrupts provide the means for I/O devices to communicate with the CPU.
An interrupt informs the processor that an external device requires
attention. This causes the processor to suspend its current activity in
order to respond to the interrupt. The processor, however, finishes
executing the last instruction, saves the address of the next instruction
on the stack, and then jumps to the special interrupt handling routine
in a designated part of memory. After executing this routine, the
processor returns to the suspended program by 'popping' the address of
the next instruction from the stack.
The pointers (or vectors) to the interrupt routines are stored in the
lowest 1024 bytes of memory in the Vector Interrupt Table. Interrupts can
be called by the assembly language instruction INT or from Turbo Pascal
by the Intr procedure.
Interrupts can be categorised as logical, hardware or software interrupts.
Logical interrupts include INT $00, divide-by-zero and INT $04, overflow.
Hardware interrupts include INT $09, keyboard and INT $0D hard disk
controller.
Software interrupts include calls to BIOS and DOS routines, such as
the BIOS INT $10 which relates to the Video Display and the much used
DOS INT $21, which can ascertain disk drive capacity, for example.
The BIOS and DOS interrupts require that certain values are placed in
the registers before the interrupt is called and generally return values
in these registers after the interrupt. The complete detail of these
register values must be found from a suitable reference, such as
Advanced MS-DOS Programming 2nd. Ed. by Ray Duncan, published by Microsoft.
DOS and BIOS Functions. Quick Reference by Que Corporation.
Interrupts called from Assembly Language programs feature in the section
on In-Line code.
For interrupts from Turbo Pascal, the Intr procedure is called (or the
MsDos procedure which is equivalent to Intr with an interrupt number of
$21). The 'call' and 'return' values in the registers are facilitated
by a variable of the type Registers, which is defined in the DOS unit as
type
Registers = record
case integer of
0: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: word);
1: (AL,AH,BL,BH,CL,CH,DL,DH: byte);
end;
end;
This is a variant record to map the 8-bit registers on top of their
16-bit equivalents.
If in the VAR declaration part of the program an instance of this
Registers type is declared, it is then possible to inspect the contents
of the registers from the Integrated Development Environment using the
Watch Window, as indicated below:
Var
Regs : Registers;
Then Alt-D, W, A (or Crtl-F7) to enter the watch variable as Regs,r
where the ,r indicates that the variable is a record type.
A typical example illustrates the use of BIOS interrupt $10 with the
register AH set to $0F before the call and returns AH = number of columns
on the screen, AL = the display mode and BH = the active display page.
The filename for this example is INTRDEMO.PAS and it is listed below:
program GetDisplayMode;
uses DOS, Crt;
var
Regs : Registers; { Registers is a record defined in DOS unit }
Mode : Byte;
Width : Byte;
Page : Byte;
DisplayMode : String[20];
begin
Regs.AX := $0F00; { Sets AH = 0F for Get Display Mode function }
Intr($10,Regs); { Interrupt 10 for ROM BIOS Video Services }
Mode := Lo(Regs.AX); { Display Mode returned in AL }
Width := Hi(Regs.AX); { Screen width returned in AH }
Page := Hi(Regs.BX); { Screen page returned in BH }
ClrScr; { Uses Crt }
Writeln('Display Mode is ',Mode);
Writeln('Screen Width is ',Width,' Columns');
Writeln('Screen Page is ',Page);
end.
────────────────────────────────────────────────────────────────────────
Another example shows the use of a DOS interrupt $21 with the AH
register set to $1C and the DL register to the drive number before the
call. The disk capacity is returned as shown in registers AL, CX, DX and
in DS:BX.
Program GetAllocationTableInfo;
uses DOS,Crt;
var
Regs : Registers;
Sectors_Cluster : Word;
Bytes_Sector : Word;
Clusters_Disk : Word;
Seg_Address : Word;
Off_Address : Word;
Disk_Type : Word;
Drive : Char;
begin
Write('Enter drive letter A,B or C: ');
Readln(Drive);
Drive := UpCase(Drive);
case Drive of
'A' : Regs.DL := 1;
'B' : Regs.DL := 2;
'C' : Regs.DL := 3
end;
Regs.AH := $1C;
Intr($21,Regs);
Sectors_Cluster := Lo(Regs.AH);
Bytes_Sector := Regs.CX;
Clusters_Disk := Regs.DX;
Seg_Address := Regs.DS;
Off_Address := Regs.BX;
ClrScr;
Disk_Type := MemW[Seg_Address:Off_Address];
Writeln('Disk type ');
case Disk_type of
240 : Writeln('Not identifiable');
248 : Writeln('Fixed disk');
249 : Writeln('Double sided, 9 sectors per track (720K)');
250 : Writeln('Double sided, 15 sectors per track (1.2M)');
252 : Writeln('Single sided, 9 sectors per track');
253 : Writeln('Double sided, 9 sectors per track (360K)');
254 : Writeln('Single sided, 8 sectors per track');
255 : Writeln('Double sided, 8 sectors per track')
end;
Writeln;
If Disk_Type <> 240 then
begin
Writeln('Sectors per cluster = ',Sectors_Cluster);
Writeln('Bytes per sector = ',Bytes_Sector);
Writeln('Clusters per disk = ',Clusters_Disk);
Writeln('Media descriptor address = ',Seg_Address,':',Off_Address);
Writeln
end;
MemW[Seg_Address:Off_Address] := 240;
end.
────────────────────────────────────────────────────────────────────────
REGS.TXT
Revised 20.1.93