I have the following assembly code for 8086
MOV AL, [BX] OUT DX, AL
The bus clock frequency is 1MHz. Access to memory is done without WAIT, and to I/O with one WAIT tick. How I can calculate the duration of this program?
Retrocomputing Stack Exchange is a question and answer site for vintage-computer hobbyists interested in restoring, preserving, and using the classic computer and gaming systems of yesteryear. It only takes a minute to sign up.Sign up to join this community
I have the following assembly code for 8086
To start with basic timing for 8086 (*1) in CPU clocks is 21 clock cycles.
MOV AL,[BX] (*2)
13 cycles, consisting of 8 cycles for the instruction (MOV reg8,mem) plus 5 to calculate the address from [BX]. Since it's a byte access, no penalty for misalignment can happen.
OUT DX, AL
8 cycles, all for the instruction.
In addition it depends on the prefetch queue being filled or not, as code fetches are additional. Above code is 3 bytes (8A 07 EE), so two code fetches are necessary. Depending on the prior instructions they could be already loaded, adding zero cycles, or 4 additional cycles per fetch, adding 4 or 8 cycles total.
Since move's 13 clocks leaves a lot of room (only 4 are needed to get the data), the OUT usually can be fetched in parallel, leaving the total penalty here at 0 or 4 CPU clocks. In real world application the queue is rarely empty, as only a few high performance instructions can empty it when executed in sequence.
Base line: Prefetch wait is usually zero and thus doesn't matter, and if it does, one needs to analyze the instructions before that sequence.
The bus clock frequency is 1MHz.
Is that memory cycle or CPU clock? These are not the same and bus clock is an ambiguous term here.
Access to memory is done without WAIT, and to I/O with one WAIT tick.
What is a 'Wait tick'? Mind to specify it, preferably in CPU clocks?
How I can calculate the duration of this program?
As usual by adding up the clock cycles and multiply them by the clock period used.
*1 - If your question is about an 8088, as used in the original PC, then it's actually 29, as described here.
*2 - Please do not use white-spaces within parameters. Even if your assembler copes with it, it may introduce incompatibilities and hard to correct problems when using a different environment.
On the 8088 and 8086, execution involves two parallel processes--memory access and internal computation--and will be limited by the speed of whichever is slower. Generally, on the 8088 execution speed will be limited by memory access, while the 8086 will be better balanced. Every memory cycle on the 8088 or 8086 takes a minimum of four cycles, and on most machines they will usually take exactly four cycles except when e.g. accessing display-card memory, or when a DRAM refresh occurs [which may be thought of as introducing a few spontaneous memory accesses beyond those issued by the CPU; while there are situations where it may be possible to arrange DRAM refreshes to occur exactly once per loop with predictable timing, it's usually best to think of it as some additional overhead that can't be precisely predicted, but is small in any case].
Any time the main CPU isn't asking for a memory access and there fewer than four bytes (8088) or three two-byte words (8086) of code have been fetched ahead of the current execution point, the prefetch unit will initiate a code fetch, which will take four cycles. To figure out how long a sequence of instructions will take, one would need to figure out when these operations will occur, and then add to every instruction's execution time the amount of time it would spend waiting either for an instruction to be fetched, or for the bus unit to finish a prefetch operation.
If an instruction is preceded and followed by instructions like MUL which take a long time to execute without performing many memory accesses, then prefetch time will be irrelevant. If it's preceded and followed by long sequences of instructions like "add reg,immed16" which take longer to fetch than to execute, the internal execution time may be irrelevant. On the 8086, a sequence of "add reg,immed16" would each have an execution time of 4 cycles, but replacing instructions in the prefetch buffer would take eight, meaning that as the processor starts executing the last such instruction, the prefetch buffer will be empty, and it will get one word loaded into it during the instruction.
Although the nominal execution time of "mov al,[bx]" would be 13 cycles, execution under those circumstances would likely take 16 cycles (since a prefetch would likely start one the cycle before the MOV would have requested its memory access, forcing it to wait three cycles for the prefetch to complete) but during those 16 cycles, three instructions would have been prefetched. If the instruction was followed by a sequence of more big-but-fast instructions, the presence of those words in the prefetch buffer could eliminate eight cycles worth of waiting later on.
I am afraid that the answer is "it could take any time, depending on circumstances and the real WAIT condition". WAIT states have unpredictable length, so there is no exact way to answer your question.
The general answer, theoreticaly valid for every CPU (and really valid for the most of 8bit CPUs), is:
MOV AL,[BX]takes 12 + 5 = 17 T cycles,
OUT DX, ALtakes 12 T cycles (without WAIT!!!)
So together it will take 29 T cycles. (EDIT: My mistake, it's a timing for the 8088, not 8086)
You know the frequency of the clock, so one T takes
1/freq seconds. 1MHz means each T takes 1 µs.
Simply multiple, and you can see your code should take 29 µs.
BUT! It will be affected in the real environment by different influences, from real IC latency to internal pipelining mechanism, which allows to pre-decode the second instruction during the execution of the first one.