0
\$\begingroup\$

I have a project based on simulating operation of 8086 processor with memory and I/O devices using Proteus simulator.

Memory is eight 2K 6116 chips and I/O contains 2 seven-segment displays and a 4x3 keypad beside the address flip flops, logic gates for even and odd addresses and transceivers.

Also, I am interfacing memory using 74138 decoder and interfacing I/O with 8255.

The problem is that, during simulation, no value comes on address/data bus of processor, Meaning that control ports of 8086 have the values required as told in assembly code but every 20 bit address (16 bit address/data and 4 bit address A16..A19) take high Z value.

Is there anything wrong with internal memory start address (which is set to 0x00000) or assembly code?

.MODEL SMALL
.STACK 64
.DATA
    PORT_A EQU 60H;
    PORT_B EQU 62H;
    PORT_C EQU 64H;
    PORT_CON EQU 66H; 

.CODE

MAIN    PROC
        MOV AX, @DATA;
        MOV DS, AX;

        ;Write arbitary values to memory
        ;RAM1
        MOV AL, 8H;
        MOV BX, 14000H;
        MOV [BX], AL;

        ;RAM2
        MOV AL, 7H;
        MOV BX, 14800H;
        MOV [BX], AL;

        ;RAM3
        MOV AL, 6H;
        MOV BX, 15000H;
        MOV [BX], AL;

        ;RAM4
        MOV AL, 5H;
        MOV BX, 15800H;
        MOV [BX], AL;

        ;RAM5
        MOV AL, 4H;
        MOV BX, 16000H;
        MOV [BX], AL;

        ;RAM6
        MOV AL, 3H;
        MOV BX, 16800H;
        MOV [BX], AL;

        ;RAM7
        MOV AL, 2H;
        MOV BX, 17000H;
        MOV [BX], AL;

        ;RAM8
        MOV AL, 1H;
        MOV BX, 17800H;
        MOV [BX], AL;


        ;Load control word
        MOV AL, 92H; (selection)(mode 0)(port A input)(port B input)(port C output);
        OUT PORT_CON, AL;

PROG:   
        ;Interpret keypad
        IN AL, [PORT_A];
COL:    CMP AL, 2H;
        JNZ C1;
        MOV BX, 2H;
        JMP ROW;
C1:     JG C2;
        MOV BX, 3H;
        JMP ROW;
C2:     MOV BX, 1H;

ROW:    IN AL, [PORT_B];
        CMP AL, 2H;
        JNZ R1;
        MOV CX, 1H;
        JMP CALC;
R1:     JG R2;
        MOV CX, 2H;
        JMP CALC;
R2:     MOV CX, 0H;

CALC:   CMP CX, 0H;
        JZ ENDKEY;
        ADD BX, 3H;
        SUB CX, 1H;
        JMP CALC;

        ;Calculate multiply
ENDKEY: MOV DX, [BX];
        MOV AL, BL;
        MUL DL;

        ;Display output
        OUT PORT_C, AL;
        JMP PROG;

        MOV AH, 4CH;
        INT 21H;
MAIN    ENDP
        END MAIN
```
\$\endgroup\$
6
  • 4
    \$\begingroup\$ Please explain how Proteus would load and jump to run that code and at which address? I suppose it is not emulating MS-DOS? The code uses DOS functions, why? The code tries to load 16 bit registers with numbers larger than 16 bits, so accessed addresses are truncated. Does it really compile, and with which compiler? \$\endgroup\$
    – Justme
    Commented Oct 31, 2019 at 10:41
  • \$\begingroup\$ If you mean that where the code loads, I didn't change the default set up of proteus in which is set to 0x0744. since I'm new in assembly I didn't get your message about MS-DOS functions. could you explain detailed? \$\endgroup\$ Commented Oct 31, 2019 at 10:49
  • \$\begingroup\$ As @Justme correctly says, the INT 21 is an MS-DOS function call to terminate the program. Put simply, the INT x instruction acts like a subroutine call. The subroutine address is stored elsewhere for INT x but that's the effect you'll see. For what you're doing, you can't use that here to end your simulation. For more detail, search the Internet for 'INT 21 DOS' or similar. \$\endgroup\$
    – TonyM
    Commented Oct 31, 2019 at 11:19
  • \$\begingroup\$ is there anything else about the code and dos functions? after removing those parts of code still the same problems remains. 8086 processor doesn't put any values on AD[0..15] and A[16..19]. \$\endgroup\$ Commented Oct 31, 2019 at 11:36
  • 1
    \$\begingroup\$ How do you compile and link the code? What is the output format? There is nothing in the code that would know where in the memory it is loaded. Is 0x0744 the segment, offset, linear address or what? Why is it 0x744 instead of something else? Most likely the code does not even execute correctly or at all. \$\endgroup\$
    – Justme
    Commented Oct 31, 2019 at 12:28

1 Answer 1

3
\$\begingroup\$

Is there anything wrong with internal memory start address (which is set to 0x00000)

Yes. The 8088/8086 processor starts up with the CS segment register set to 0xFFFF and the PC register set to 0x0000. This means the segmented start address is always 0xFFFF:0x0000 and that translates to the 20-bit linear address (there are only 20 address pins on these devices) of 0xFFFF0. In short, the processor always starts very, very close to the end of the address space it supports.

Now, if there's nothing there in the memory system to respond at that start address, it's likely that the CPU will read 0xFF bytes. However, apparently you are using a simulator. So that means I've no idea what might be read at that location. The simulator might place some code there prior to starting simulation. Or it might place 0x00 there, instead. Who knows? But if 0xFF values are there, then the simulator will be provided an illegal instruction modifier when it reads the second byte and I, again, honestly have no idea how the simulator handles such cases.

If you are over-riding things, or if the simulator itself for some reason decides to use a segmented start address of 0x0000:0x0000, then that's where you want your code. But you really haven't discussed any of the details and I'm not privy to your educational tool behaviors. So this is the best I can offer as a guide towards finding out why you may be having trouble.

The problem is that, during simulation, no value comes on address/data bus of processor, Meaning that control ports of 8086 have the values required as told in assembly code but every 20 bit address (16 bit address/data and 4 bit address A16..A19) take high Z value.

Actually, you should be seeing a starting address on the bus of 0xFFFF0. Only the low-order four bits should be low (as in "0".) The rest should be high. Not high-Z. But high (as in "1".)

or assembly code?

I think there is a problem with the assembly code.

One commenter (Justme) has already mentioned that you are trying to load constant values requiring more than 16 bits into registers that can only hold 16 bits. I forget if the assembler issues a warning, or not. But I'm almost sure that it only uses the low-order 16 bits of the value, if it does assemble the code. This probably isn't keeping your address bus at high-Z, though. But it is still worth a mention.

There's another problem. The code is clearly written to use the small model -- see the very first line of your code. The old MASM toolset turns this into an .OBJ file and then a linkage step follows that up by creating an .EXE file from the .OBJ file.

The .EXE file includes provisions for code and data address "fix-ups" that DOS uses during the process of loading an .EXE into available memory. (The area available for DOS to load programs varies on the version of DOS and on the installed drivers operating at the time, as well as the history of allocated memory that wasn't later released.) The .EXE file also includes an added "magic code" at the beginning so that DOS has an added way of detecting that the file is an .EXE and not a .COM file that's been renamed. (There are other ways of detecting that fact. But this magic code is yet another added cross-check.)

The need for these fix-ups has to do with the inability of the 8088/8086 to support the arbitrary relocation of binary code that referenced (pretty much) anything outside of a default segment. For example, if code in segment A needed to jump to code in segment B, the CS register needed to be changed and this changed value had to be included in the binary version of the code. But the .EXE, prior to loading it into memory, didn't know anything about the precise location of segment A or segment B and therefore the .EXE file couldn't actually have the right values. Instead, a special section of the .EXE included "fixup records" that pointed at these locations and allowed DOS to patch up the code as it was loading it into memory for execution, so that the instructions used the right values during execution.

Historical Notes

The earliest version, DOS 1.0, didn't support memory management functions and it doesn't support .EXE programs. So, DOS 1.0 only supported a .COM program. These .COM programs, as stored on disk and prior to loading into memory, did not need any fix-up records. All of the code and data for a .COM program fit entirely within the maximum reach of a single segment of memory (65536 bytes.) So DOS 1.0 merely allocated all of the available memory (even memory that exceeds 65536 bytes) to the .COM program.

The first 20-bit address for this allocated memory was always "paragraph-aligned," so the low-order four bits was always zero. This allows all of the segment registers (for .COM programs), CS, DS, ES, and SS, to be set to the same value, with a guarantee that the offset (the low-order 16 bits that base off of the segment registers' values) starts at 0x0000. So DOS would set all four to the starting segment address value and then directly load the .COM file data into that memory starting at offset address 0x0100. (The first 0x0100 bytes was reserved for something DOS calls the "program segment prefix.") DOS would then jump to that offset address of 0x0100 to start execution of the .COM program.

No fix-ups were supported by DOS 1.0. So there were no adjustments made to the binary data found in the .COM file. It was simply assumed to have code residing at the address CS:0x0100 (which is where the binary file was loaded.)

I mentioned the program segment prefix (PSP.) This area, and the meaning of the bytes found in it, came from a prior operating system called CP/M. This earlier operating system had become popular on ALTAIR 8800 and IMSAI 8080 computers (based upon the i8080A and i8085A.) The guy (now a mostly nameless individual to all but a few today) who attended a Seattle area computer club around the same time as Bill Gates did, and who had coded up the early, pre-DOS code that was bought by Bill Gates and his father, had used CP/M as a design model for his own code for the 8088. So the PSP is an exact copy from CP/M days. It looks about like this:

    ;   PROGRAM SEGMENT PREFIX (PSP) DESCRIPTION
    ;   ==========================================================================
    ;   The PSP (program segment prefix) is always occupies the first part of
    ;   a .COM program's memory segment.  In the case of .EXE programs, it is
    ;   the first (and only) part of the memory segment passed in DS and ES
    ;   when the .EXE is started.  Either way, 256 bytes (0x100) are used for
    ;   this communication area.  These entries describe the information found
    ;   there.
    ;
    ;   The use of a SEGMENT AT here just keeps the assembler from generating
    ;   an instance of the data, while still assigning offsets.  Reference the
    ;   data here via an appropriate segment register which holds the correct
    ;   segment value in it.
    ;
    ;   For most programs, the only interesting part of the PSP is the buffer
    ;   used to hold a copy of the command line.  DOS copies the portion of
    ;   the command line that follows the program name into pspCmdTailText
    ;   and sets pspCmdTailCount to the number of characters placed there.
    ;   ==========================================================================
    
    PSP                 SEGMENT AT 0
    
                        ORG     0h
    
        pspInt20        dw        1 DUP(?)  ; INT 20h instruction.
        pspNextPara     dw        1 DUP(?)  ; segment addr of next paragraph.
                        db        1 DUP(?)  ; reserved.
        pspDispatcher   db        5 DUP(?)  ; long call to DOS.
        pspTermVector   dd        1 DUP(?)  ; Termination address (INT 22h).
        pspCtrlCVector  dd        1 DUP(?)  ; Control-C handler (INT 23h).
        pspCritVector   dd        1 DUP(?)  ; Critical error handler (INT 24h).
                        dw       11 DUP(?)  ; reserved.
        pspEnvironment  dw        1 DUP(?)  ; segment addr of environment.
                        dw       23 DUP(?)  ; reserved.
        pspFCB_1        db       16 DUP(?)  ; default FCB #1.
        pspFCB_2        db       16 DUP(?)  ; default FCB #2.
                        dd        1 DUP(?)  ; reserved.
        pspCmdTailCount db        1 DUP(?)  ; count of chars in command tail.
        pspCmdTailText  db      127 DUP(?)  ; text, starts 20h, ends 0Dh.
    
    PSP                 ENDS

The above code is from my own assembly template that I wrote and used back when there was no C compiler for DOS. So it dates to sometime in late 1981, when I first wrote it.

Subsequently, when DOS 2.0 became available, so also did support for .EXE files, which contained the fix-up records required to support memory models (these are software architecture models and are not hardware architecture models) used by DOS programs written using something other than assembly coding, at the time being the C language compiler tools that were beginning to become available for the new IBM PC hardware and DOS. This includes the small, medium, compact, large (and eventually, huge) memory models, which the C compilers then grew to support over time.

Much More Detail on Writing DOS Assembly Programs

It's probably quicker if I just post an .ASM source template that I've written and used over the years. Most of the needed documentation about how a program is loaded into memory by DOS and launched is included there. Some of it may be a little terse. But it gets the idea across. There's also a few (very old) web sites mentioned in that source code -- some are broken, some are still around. (It is what it is. Times change.)

Summary

So now I'm finally getting to the nub of my question for you. When you used that source code, what tools did you use to assemble and link it? Where did you get them? How did you use them? How did you load them into your simulator? Does your simulator provide built-in support for "booting DOS" and all of the details associated with loading an .EXE program you specify as a file? Or did you just take the .EXE file and treat it as a binary data dump, stuffing it into memory starting at location 0x0000:0x0000 and hope that it would get executed properly? (If this last thing, then there is little wonder the simulation didn't produce the results you expected.) And why did you include a DOS (0x21) termination call (AH=0x4C) at the end of the code? That makes no sense if there's no DOS running. Did you just copy and paste this stuff, then modify it without understanding??

If you want a file produced that is an exact binary image of code to be loaded into memory at some given location and you want to use the assembly tools to do it, then you will need the .TINY model and not the .SMALL model. If you use the .TINY model (or otherwise use options that make sure it is applied correctly -- there is more than one way to do this), then at least the assembly and linker tools will create a binary file without headers, magic codes, or fix-up records. That doesn't make things work. You have to do some extra stuff if there is no BIOS and this is just being loaded into memory. But at least you can make the tools produce something that will be very simply arranged binary code and data.

Even then, there are assumptions made by the assembly and linker tools when generating the .COM file. For one thing, the assembler will assume that any code you write will somehow be magically placed at address SEGMENT:0x0100, with all of the four segment registers already having their values correctly set. If you want your code placed at physical address 0x00000, there is nothing much to help you because the offset address can't be 0x0100 in that case. (Well, it can. But I'm not going to tell you how. You can work it out, if needed.) So it would be better if you decided to set CS=DS=ES=SS=0x0000 and set SP=0xFFFE (perhaps) and loaded the binary file at physical address 0x00100. You will need other code, though, which jumps correctly to this address using a far jump that modifies both the CS and also the PC registers to get there (after first clearing DS, ES, and SS.)

What's seriously missing from your question is all of the teaching materials that probably went along with this whole process you are trying to follow. If the problem assignment was well-crafted, then all of the necessary details to get the job done have already been provided and you have failed to share those details with us. You'll need to expand your question a lot more if you want to get more specific help. All I've been able to do is give you a tour. Unfortunately. But that's all you've left me able to attempt, for now.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.