/* 
MEM.C -- walks DOS MCB chain(s): simple version
Andrew Schulman and Jim Kyle, July 1990
*/

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

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long ULONG;
typedef void far *FP;

#ifndef MK_FP
#define MK_FP(seg,ofs)  ((FP)(((ULONG)(seg) << 16) | (ofs)))
#endif

#ifdef __TURBOC__
#define ASM asm
#else
#define ASM _asm
#endif

#pragma pack(1)

typedef struct {
    BYTE type;          /* 'M'=in chain; 'Z'=at end */
    WORD owner;         /* PSP of the owner */
    WORD size;          /* in 16-byte paragraphs */
    BYTE unused[3];
    BYTE dos4[8];
    } MCB;

void fail(char *s) { puts(s); exit(1); }

MCB far *get_mcb(void)
{
    ASM mov ah, 52h
    ASM int 21h
    ASM mov dx, es:[bx-2]
    ASM xor ax, ax
    /* in both Microsoft C and Turbo C, far* returned in DX:AX */
}

mcb_chk(MCB far *mcb)
{
    for (;;)
        if (mcb->type == 'M')
            mcb = MK_FP(FP_SEG(mcb) + mcb->size + 1, 0);
        else
            return (mcb->type == 'Z');
}

void display(MCB far *mcb)
{
    char buf[80];
    sprintf(buf, "%04X    %04X    %04X (%6lu)", 
        FP_SEG(mcb), mcb->owner, mcb->size, (long) mcb->size << 4);
    if (! mcb->owner) 
        strcat(buf, "   free");
    puts(buf);
}

void walk(MCB far *mcb)
{
    printf("Seg     Owner   Size\n"); 
    for (;;)
        switch (mcb->type)
        {
            case 'M' : /* Mark : belongs to MCB chain */
                display(mcb);
                mcb = MK_FP(FP_SEG(mcb) + mcb->size + 1, 0);
                break;
            case 'Z' : /* Zbikowski : end of MCB chain */
                display(mcb);
                return;
            default :
                fail("error in MCB chain");
        }
}

#ifdef TRY_BUG
main(void)
{
    unsigned segm;
    ASM mov ah, 48h     /* Allocate Memory Block */
    ASM mov bx, 64h     /* get 100 paragraphs */
    ASM int 21h
    ASM jc done
    /* ax now holds initial segment of allocated block */
    ASM mov segm, ax
    printf("before: "); display(MK_FP(segm - 1, 0));

    ASM mov ax, segm
    ASM mov es, ax      /* now resize the block */
    ASM mov ah, 4Ah     /* Resize Memory Block */
    ASM mov bx, 0FFFFh  /* impossible (at least in real mode!) */
    ASM int 21h
    ASM jnc done        /* something seriously wrong if _didn't_ fail! */
    printf("after:  "); display(MK_FP(segm - 1, 0));
done:
    return 0;
}
#else
main(int argc, char *argv[])
{
    if (argc < 2)
        walk(get_mcb());            /* walk "normal" MCB chain */
    else
    {
        unsigned seg;
        sscanf(argv[1], "%04X", &seg);
        walk(MK_FP(seg, 0));       /* walk arbitrary MCB chain */
    }
    
    if (! mcb_chk(get_mcb()))
    {
        /* maybe do stack backtrace here, or dump registers */
        puts("Error in MCB chain - prepare for halt...");
        getchar();
    }
    else
        puts("MCB chain ok");
    
    return 0;
}
#endif
