Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Using Extended Memory On the PC AT


JAN89: USING EXTENDED MEMORY ON THE PC AT

Paul Thomson is a software engineer at Black Dot. He can be reached at 439 N Lake Shore Dr., Palatine, IL 60067.


The PC AT BIOS contains a service -- service 87h of interrupt 15h -- forcopying memory in protected mode, thereby allowing you to copy memory anywhere in the 16- Mbyte address range of the 80286. This means that you can copy memory from real-mode memory to extended memory and back again. Before calling this service routine, however, you must set up a data structure for protected-mode operation. This structure is called a global descriptor table. It contains six descriptors, each with 8 bytes. Two of these are segment descriptors that describe the source and target buffers. The rest of the descriptors are simply dummy descriptors that will be initialized by the BIOS routine. The 8-byte segment descriptors are shown in Table 1, below.

Table 1: Segment descriptor formats

  Descriptor        Format
  ------------------------

  16 bits           Null: Reserved for future use
   8 bits           Access rights byte: Contains attribute flags for the
                    segment (writable?, data segment?, etc.)
  24 bits           Physical address: Any real mode address must be
                    converted to a 24 bit physical address by the
                    following formula: (segreg * 16) + offset.
  16 bits           Segment length in bytes: Words to transfer * 2.

Before calling the service routine, you must set up the registers as shown in Table 2.

Table 2: Setup for registers

  AH          = 87h
  ES:SI       = Real mode address of
                global descriptor table

     CX       = Count of words to copy

There is also another interrupt 15h service -- service 88h -- which is useful for extended memory. Service 88h returns the number of Kbytes of extended memory (which starts at 0x100000) in AX. Since service 88h will not return an error when accessing non-existent memory, service 88h can be used to determine if the move will involve valid memory locations.

Listing One, page 108, shows a Microsoft C (compact or large model) version of the move routine (movphy), the top of memory routine (getext), and a test routine (main) while Listing Two, page 119, is an assembly language version. movphy will copy memory in 64K-byte chunks given the source and destination addresses in 24-bit format. getext, which must be called once (before movphy is called for the first time) sets up a static variable with the maximum physical address of extended memory. The test copies a string between extended and real-mode memory to show that the routine is working correctly. An error can be discovered by checking the routine return code (ax in C, ah in the assembler routine). Before you use the program, check that your extended memory is not occupied by vdisk; the program can wipe out your virtual disk files and/or render vdisk unusable.

_USING EXTENDED MEMORY ON THE PC AT_

by Paul Thomson

[LISTING ONE]

<a name="0041_0007">

/***
 *** ROUTINE FOR COPYING MEMORY USING PHYSICAL ADDRESSES ON THE PCAT.
 *** WRITTEN BY PAUL THOMSON.
 *** COMPILE USING MICROSOFT C V4.0, LARGE OR COMPACT MODEL.
 *** DO NOT USE THIS ROUTINE WITH A VIRTUAL DISK IN EXTENDED MEMORY.
 ***/

#include <stdio.h>
#include <dos.h>

#define PHYS(s) (((long)FP_SEG(s) << 4) + FP_OFF(s))    /* CALC PHYS ADDR */
#define BUFSZ 16                                        /* BUF SIZE IN BYTES */
#define PHY_ADDR 0x100000L                              /* BEG OF EXT MEMORY */

char *buf="ORIGINAL MESSAGE";

/* TEST MOVPHY BY COPYING A MESSAGE TO EXTENDED MEMORY AND BACK */
main()
{
    extsize();
    puts(buf);
    movphy(PHY_ADDR, PHYS(buf), BUFSZ/2);   /* MOVE TO EXTENDED MEMORY */
    sprintf(buf, "XXXXXXXXXXXXXXXX");       /* OVERWRITE BUFFER */
    puts(buf);
    movphy(PHYS(buf), PHY_ADDR, BUFSZ/2);   /* MOVE BACK FROM EXTENDED MEMORY */
    puts(buf);
}

static long maxext;                 /* HOLDS MAX ADDRESS OF EXTENDED MEMORY */
extsize()
{
    union REGS r;

    r.h.ah = 0x88;
    int86(0x15, &r, &r);            /* RETURNS SIZE OF EXTENDED MEM IN KBYTES */
    maxext = (r.x.ax + 1024L)*1024; /* FIND TOP OF EXTENDED MEMORY */
}

/* MOVE MEMORY USING PHYSICAL ADDRESSES */
movphy(target, source, wcount)
unsigned long target;           /* PHYSICAL 24 BIT TARGET ADDRESS */
unsigned long source;           /* PHYSICAL 24 BIT SOURCE ADDRESS */
int wcount;                     /* 16 BIT COUNT OF WORDS TO MOVE 0 - 32767 */
{
    int bcount;
    char gdt[48];               /* GLOBAL DESCRIPTOR TABLE (6 DESCRIPTORS*8) */
    char *g = gdt;              /* POINTER TO gdt FOR MACROS FP_SEG & FP_OFF */
    union REGS r;               /* HOLDS REGISTER VALUES FOR int86x CALL */
    struct SREGS s;             /* HOLDS SEG REGISTER VALUES FOR int86x CALL */

    if(wcount <= 0)             /* CHECK FOR WORD COUNT TOO BIG OR 0 */
        return(wcount);
    bcount = wcount*2;          /* SIZE IN BYTES TO MOVE */

    if(target+bcount >= maxext || source+bcount >= maxext)
        return(4);

    /* DESCRIPTORS 0 AND 1 ARE DUMMIES (NULL) */
    gdt[0]=gdt[1]=gdt[2]=gdt[3]=gdt[4]=gdt[5]=gdt[6]=gdt[7]=0;
    gdt[8]=gdt[9]=gdt[10]=gdt[11]=gdt[12]=gdt[13]=gdt[14]=gdt[15]=0;

    /* DESCRIPTOR 2: SOURCE */
    gdt[16] = bcount;           /* BYTE COUNT */
    gdt[17] = bcount>>8;
    gdt[18] = source;           /* PHYSICAL ADDRESS TO COPY FROM */
    gdt[19] = source>>8;
    gdt[20] = source>>16;
    gdt[21] = 0x93;             /* ACCESS RIGHTS BYTE */
    gdt[22] = gdt[23] = 0;      /* UNUSED ENTRIES */

    /* DESCRIPTOR 3: TARGET */
    gdt[24] = bcount;           /* BYTE COUNT */
    gdt[25] = bcount>>8;
    gdt[26] = target;           /* PHYSICAL ADDRESS TO COPY TO */
    gdt[27] = target>>8;
    gdt[28] = target>>16;
    gdt[29] = 0x93;             /* ACCESS RIGHTS BYTE */
    gdt[30] = gdt[31] = 0;      /* UNUSED ENTRIES */

    /* DESCRIPTORS 4 AND 5 ARE DUMMIES (NULL) */
    gdt[32]=gdt[33]=gdt[34]=gdt[35]=gdt[36]=gdt[37]=gdt[38]=gdt[39]=0;
    gdt[40]=gdt[41]=gdt[42]=gdt[43]=gdt[44]=gdt[45]=gdt[46]=gdt[47]=0;

    r.h.ah = 0x87;              /* AH = SERVICE 0x87 */
    r.x.cx = wcount;            /* CX = COUNT OF WORDS TO TRANSFER */
    s.es = FP_SEG(g);           /* ES:SI = SEGMENT:OFFSET OF GDT */
    r.x.si = FP_OFF(g);

    int86x(0x15, &r, &r, &s);   /* PERFORM BIOS INTERRUPT 0x15 */

    return(r.h.ah);
                                /* RETURN STATUS:
                                    0: SUCCESSFUL MOVE
                                    1: RAM PARITY ERROR
                                    2: EXCEPTION ERROR
                                    3: ADDRESS LINE 20 FAILED
                                    4: MEMORY OUT OF RANGE
                                    <0: WORD COUNT > 32767 */
}





<a name="0041_0008"><a name="0041_0008">
<a name="0041_0009">
[LISTING TWO]
<a name="0041_0009">

; ROUTINE FOR COPYING MEMORY USING PHYSICAL ADDRESSES ON THE PCAT.
; WRITTEN BY PAUL THOMSON.
; DO NOT USE THIS ROUTINE WITH A VIRTUAL DISK IN EXTENDED MEMORY.


.286p                       ; ALLOW 286 INSTRUCTIONS
code segment
assume cs:code,ds:code,es:code,ss:code

; TEST MOVPHY BY COPYING A MESSAGE TO EXTENDED MEMORY AND BACK
test proc near
    mov     ax,cs           ; ALLOW ACCESS OF DATA IN CODE SEG
    mov     ds,ax

    call    extsize        ; FIND TOP OF EXTENDED MEMORY

    mov     dx,offset mess1 ; PRINT MESSAGE
    mov     ah,9
    int     21h

    mov     dx,cs           ; CALCULATE PHYS ADDR FROM REAL ADDR OF MESSAGE BUF
    shr     dx,12           ; SI = BITS 0-15, DL = BITS 16-23
    mov     ax,cs
    shl     ax,4
    mov     si,offset mess1
    add     si,ax
    adc     dl,0
    push    si              ; SAVE PHYS ADDRESS FOR LATER
    push    dx

    mov     dh,10h          ; TOP OF EXTENDED MEMORY (100000h)
    mov     di,0            ; DI = BITS 0-15, DH = 16-23

    mov     cx,8            ; SIZE OF MESSAGE BUF IN WORDS
    call    movphy          ; MOVE MESSAGE TO EXTENDED MEMORY

    sub     bx,bx           ; OVERWRITE MESSAGE BUFFER
top:
    mov     al,mess2[bx]
    mov     mess1[bx],al
    inc     bx
    cmp     bx,16
    jl      top
    mov     dx,offset mess1

    mov     ah,9            ; PRINT OVERWRITTEN MESSAGE BUFFER
    int     21h

    pop     dx              ; GET PHYS BUFFER ADDRESS FROM BEFORE
    pop     di              ; DI = BITS 0-15, DH = BITS 16-23
    mov     dh,dl

    mov     dl,10h          ; TOP OF EXTENDED MEMORY (100000h)
    mov     si,0            ; DI = BITS 0-15, DH = 16-23

    mov     cx,8            ; SIZE OF MESSAGE BUF IN WORDS
    call    movphy          ; MOVE MESSAGE BACK FROM EXTENDED MEMORY

    mov     dx,offset mess1 ; PRINT RESTORED MESSAGE
    mov     ah,9
    int     21h

    mov     ah,4ch          ; EXIT
    int     21h
test endp
mess1       db 'ORIGINAL MESSAGE',0dh,0ah,'$'
mess2       db 'XXXXXXXXXXXXXXXX'

; extsize - GET PHYSICAL ADDRESS OF TOP OF EXTENDED MEMORY
; ADDRESS RETURNED IN max_hi,max_lo
extsize proc near
    mov     ah,88h
    int     15h
    mov     cx,1024
    add     ax,cx
    mul     cx
    mov     max_hi,dl
    mov     max_lo,ax
    ret
extsize endp
max_hi      db ?
max_lo      dw ?

; movphy - MOVE MEMORY USING PHYSICAL ADDRESSES
; CALLED WITH:
;   dh:di = physical 24 bit target address.
;   dl:si = physical 24 bit source address.
;   cx = word count
; STATUS RETURNED IN ah:
;   0: successful move
;   1: RAM parity error
;   2: exception error
;   3: address line 20 failed
;   4: memory out of range
;   255: word count > 32767

public movphy
movphy proc near
    push    ds
    mov     ax,cs           ; ALLOW ACCESS OF gdt IN CODE SEGMENT
    mov     ds,ax
    mov     es,ax           ; es = SEGMENT OF gdt FOR int 15h

    mov     ax,cx           ; CALCULATE MAXIMUM TARGET ADDRESS
    shl     ax,1
    mov     bl,dh
    add     ax,di
    adc     bl,0
    cmp     bl,max_hi       ; CHECK IF TARGET ADDRESS OUT OF RANGE
    jl      $target_ok
    jg      $bad_range
    cmp     ax,max_lo
    jge     $bad_range
$target_ok:
    mov     ax,cx           ; CALCULATE MAXIMUM SOURCE ADDRESS
    shl     ax,1
    mov     bl,dl
    add     ax,si
    adc     bl,0
    cmp     bl,max_hi       ; CHECK IF SOURCE ADDRESS OUT OF RANGE
    jl      $source_ok
    jg      $bad_range
    cmp     ax,max_lo
    jl      $source_ok
$bad_range:
    mov     ah,4            ; IF ACCESSING NON-EXISTENT MEMORY, RETURN ERROR 4
    jmp     $xend
$source_ok:

    cmp     cx,0            ; CHECK FOR WORD COUNT TOO BIG OR 0
    jg      $wcount_ok
    mov     ax,cx           ; RETURN 255 IF WORD COUNT > 32767
    jmp     $xend
$wcount_ok:

;   DESCRIPTORS 0,1,4,5 ARE DUMMIES (NULL)
    sub     ax,ax

    mov     gdt,ax          ; DESCRIPTOR 0
    mov     gdt+2,ax
    mov     gdt+4,ax
    mov     gdt+6,ax

    mov     gdt+8,ax        ; DESCRIPTOR 1
    mov     gdt+10,ax
    mov     gdt+12,ax
    mov     gdt+14,ax

    mov     gdt+32,ax       ; DESCRIPTOR 4
    mov     gdt+34,ax
    mov     gdt+36,ax
    mov     gdt+38,ax

    mov     gdt+40,ax       ; DESCRIPTOR 5
    mov     gdt+42,ax
    mov     gdt+44,ax
    mov     gdt+46,ax

    mov     gdt+22,ax       ; UNUSED ENTRIES IN DESCRIPTOR 2
    mov     gdt+30,ax       ; UNUSED ENTRIES IN DESCRIPTOR 3

    mov     ax,cx           ; CHANGE WORD COUNT TO BYTE COUNT
    shl     ax,1
    mov     gdt+16,ax       ; BYTE COUNT DESCRIPTOR 2
    mov     gdt+24,ax       ; BYTE COUNT DESCRIPTOR 3

    mov     gdt+18,si       ; PHYSICAL ADDRESS TO COPY FROM
    mov     al,dl
    mov     ah,93h          ; ACCESS RIGHTS BYTE
    mov     gdt+20,ax

    mov     gdt+26,di       ; PHYSICAL ADDRESS TO COPY TO
    mov     al,dh
    mov     ah,93h          ; ACCESS RIGHTS BYTE
    mov     gdt+28,ax

;   MAKE DOS CALL
    mov     ah,87h          ; SELECT SERVICE 87h
    mov     si,offset gdt   ; ES:SI = SEGMENT:OFFSET OF GLOBAL DESCRIPTOR TABLE
    int     15h             ; PERFORM MEMORY MOVE
$xend:
    pop     ds
    ret

    gdt dw 24 dup(?)        ; GLOBAL DESCRIPTOR TABLE

movphy  endp
code ends
end











Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.