Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CMP flags going wrong in my emu?

98 views
Skip to first unread message

luserdroog

unread,
Sep 14, 2020, 12:20:43 AM9/14/20
to
I'm trying to fill out my toy forth interpreter and I think I have
a bug in my emulator for the CMP instruction and therefore probably
also SUB and SBB.

The problem is showing up in my comparator functions
CODE(<, less, POP(CX),POP(BX), MOVAXI(0xff,0xff), CMP(,R,BX,CX), JL,2,INC_(R,AX), PUSH(AX))
CODE(>, more, POP(CX),POP(BX), MOVAXI(0xff,0xff), CMP(,R,BX,CX), JG,2,INC_(R,AX), PUSH(AX))

When I test it with

WORD(test2a,test2a, enter, one, ten, less, dot,
one, ten, more, dot, ok)

no matter how I permute the arguments -- changing BX and CX in the CMP
instruction, or in the POP order, or in the test script -- the results
never change.

0 -1 OK

Here's the stack trace on the first one.

ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046d fl:0004 NC NO NS NZ
3b(073) cmpwt: d9(331) x:1 y:10 ->fffffff7
ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046f fl:0891 CA OV SN NZ
7c(174) jl: 02(002) <0>
ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:0471 fl:0891 CA OV SN NZ
ff(377) grp2w: c0(300) INC ->0000

The CMP produces fffffff7 which seems like the correct subtraction
extended to 32 bits. But the JL isn't taken.

Are my flags wrong after the cmpwt instruction?

luserdroog

unread,
Sep 14, 2020, 12:50:47 AM9/14/20
to
On Sunday, September 13, 2020 at 11:20:43 PM UTC-5, luserdroog wrote:

> Here's the stack trace on the first one.
>
> ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046d fl:0004 NC NO NS NZ
> 3b(073) cmpwt: d9(331) x:1 y:10 ->fffffff7
> ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046f fl:0891 CA OV SN NZ
> 7c(174) jl: 02(002) <0>
> ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:0471 fl:0891 CA OV SN NZ
> ff(377) grp2w: c0(300) INC ->0000
>
> The CMP produces fffffff7 which seems like the correct subtraction
> extended to 32 bits. But the JL isn't taken.
>
> Are my flags wrong after the cmpwt instruction?

I'm pretty sure this *is* wrong. I think I should have Carry but No
Overflow. The upper half is clearly the sign extension of the low 15
bits so it should not be considered an overflow, I think.

Alexei A. Frounze

unread,
Sep 14, 2020, 2:05:56 AM9/14/20
to
If you have to ask, it's quite possible.
Care to show your code for add/adc, sub/sbb/cmp?

I don't know why you're sign-extending 16-bit values
to 32 bits when performing a 16-bit operation.
It may contribute to the problem by itself.

Before you ask a question like this you should explain
your syntax a little bit. For example, in

CMP(,R,BX,CX)

I don't know if the operand order is the same as in the
intel/AMD x86 manual or the opposite like in the AT&T
assembly syntax.

Supposing the order is like in the intel syntax and you're doing:

CMP BX, CX

when BX=1 and CX=0xA, you should get:

difference = 0xFFF7 (unsigned) or -9 (signed), not stored anywhere

FLAGS.SF=1 (most significant (AKA sign) bit of the above difference)

FLAGS.ZF=0 (the above difference isn't 0)

FLAGS.CF=1 (in unsigned comparison 1 < 0xA holds true)

FLAGS.OF=0 (mathematical difference: 1 - 0xA = -9, correctly represented)

JL will jump when SF!=OF. This should be the case here.

It's very likely that your signed overflow computation is busted.
This may help: https://stackoverflow.com/a/8037485/968261

Alex

luserdroog

unread,
Sep 14, 2020, 2:21:04 AM9/14/20
to
It's a custom macro syntax which targets the "from" forms R selects
MOD=3 (register to register) and BX is in the REG field and CX is in
the REG/MEM field. So, as correctly surmised below, BX is the
Destination and CX is the Source. So the comparison should be the
same as the subtraction BX-CX.

> Supposing the order is like in the intel syntax and you're doing:
>
> CMP BX, CX
>
> when BX=1 and CX=0xA, you should get:
>
> difference = 0xFFF7 (unsigned) or -9 (signed), not stored anywhere
>
> FLAGS.SF=1 (most significant (AKA sign) bit of the above difference)
>
> FLAGS.ZF=0 (the above difference isn't 0)
>
> FLAGS.CF=1 (in unsigned comparison 1 < 0xA holds true)
>
> FLAGS.OF=0 (mathematical difference: 1 - 0xA = -9, correctly represented)
>
> JL will jump when SF!=OF. This should be the case here.
>
> It's very likely that your signed overflow computation is busted.
> This may help: https://stackoverflow.com/a/8037485/968261
>
> Alex

Thanks. Yep, it's that stupid overflow again lol.

luserdroog

unread,
Sep 14, 2020, 2:51:13 AM9/14/20
to
On Monday, September 14, 2020 at 1:05:56 AM UTC-5, Alexei A. Frounze wrote:
> On Sunday, September 13, 2020 at 9:20:43 PM UTC-7, luserdroog wrote:
> > Here's the stack trace on the first one.
> >
> > ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046d fl:0004 NC NO NS NZ
> > 3b(073) cmpwt: d9(331) x:1 y:10 ->fffffff7
> > ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:046f fl:0891 CA OV SN NZ
> > 7c(174) jl: 02(002) <0>
> > ax:ffff cx:000a dx:0000 bx:0001 sp:f000 bp:1ffc si:0e1a di:0000 ip:0471 fl:0891 CA OV SN NZ
> > ff(377) grp2w: c0(300) INC ->0000
> >
> > The CMP produces fffffff7 which seems like the correct subtraction
> > extended to 32 bits. But the JL isn't taken.
> >
> > Are my flags wrong after the cmpwt instruction?
>
> If you have to ask, it's quite possible.
> Care to show your code for add/adc, sub/sbb/cmp?
>

The code is at:

https://github.com/luser-dr00g/8086/blob/fd410940a9fd226dea5d70a8e3264fabee4595f3/a8086.c

and an explanation of the code is at:

https://codegolf.stackexchange.com/a/52902/2381

(this includes a fully preprocessed and indent-ed addbt
(add byte "to") operator function.

The CMP operator sets the destination pointer to a sink,
loads variables x and y from dest and source, and calls
SUB which does ... hang on, computer rebooted. now I gotta
delete the X.lock to fire up emacs ... ah, that's better...
SUB does

#define SUB z=d?x-y:y-x; LOGFLAGS MATHFLAGS RESULT

where the flags are set with

// flags set by logical operators
#define LOGFLAGS *fl=0; \
*fl |= ( (z&(w?0x8000:0x80)) ?SF:0) \
| ( (z&(w?0xffff:0xff))==0 ?ZF:0) ;

// additional flags set by math operators
#define MATHFLAGS *fl |= ( (z&(w?0xffff0000:0xff00)) ?CF:0) \
| ( ((z^x)&(z^y)&(w?0x8000:0x80)) ?OF:0) \
| ( ((x^y^z)&0x10) ?AF:0); \
SETPF

I don't know where I came up with that expression for OF.
It's gibberish to me now.




Alexei A. Frounze

unread,
Sep 14, 2020, 4:51:24 AM9/14/20
to
On Sunday, September 13, 2020 at 11:51:13 PM UTC-7, luserdroog wrote:
> // additional flags set by math operators
> #define MATHFLAGS *fl |= ( (z&(w?0xffff0000:0xff00)) ?CF:0) \
> | ( ((z^x)&(z^y)&(w?0x8000:0x80)) ?OF:0) \
> | ( ((x^y^z)&0x10) ?AF:0); \
> SETPF
>
> I don't know where I came up with that expression for OF.
> It's gibberish to me now.

It's making sure that the result's sign is the same
as the signs of x and y. If that isn't true, OF gets set.
That works for addition, z = x + y,
but not subtraction, z = x - y.
Unless you exchange things a bit...

add: z = x + y
(z^x)&(z^y)&...

sub: z = x - y
OR, speaking in terms of addition...
sub: x = z + y
(x^z)&(x^y)&...

IOW, for the purposes of OF calculation you need to
exchange x and z in that formula of yours.

Alex

luserdroog

unread,
Sep 14, 2020, 5:51:34 AM9/14/20
to
Thanks a bunch! It works great now. My forth can now
print numbers in any base from 2 to 36.

Alexei A. Frounze

unread,
Sep 14, 2020, 6:36:43 AM9/14/20
to
Sorry, I hit "send" too soon without a clarification.

You need to exchange x with z in MATHFLAGS for sub only.
I think now you have add broken.

Alex

luserdroog

unread,
Sep 14, 2020, 1:07:19 PM9/14/20
to
Ok. Thanks again. I'm parameterizing it so there's a separate formula
for ADDFLAGS vs SUBFLAGS.

luserdroog

unread,
Sep 14, 2020, 1:22:23 PM9/14/20
to
On Monday, September 14, 2020 at 1:21:04 AM UTC-5, luserdroog wrote:
> On Monday, September 14, 2020 at 1:05:56 AM UTC-5, Alexei A. Frounze wrote:
> > On Sunday, September 13, 2020 at 9:20:43 PM UTC-7, luserdroog wrote:

> > Before you ask a question like this you should explain
> > your syntax a little bit. For example, in
> >
> > CMP(,R,BX,CX)
> >
> > I don't know if the operand order is the same as in the
> > intel/AMD x86 manual or the opposite like in the AT&T
> > assembly syntax.
>
> It's a custom macro syntax which targets the "from" forms R selects
> MOD=3 (register to register) and BX is in the REG field and CX is in
> the REG/MEM field. So, as correctly surmised below, BX is the
> Destination and CX is the Source. So the comparison should be the
> same as the subtraction BX-CX.
>

I said this wrong. These are "to" forms, not "from". The first argument
to my CMP macro can be F to create a "from" opcode or blank (as above)
to create a "to" opcode. Elsethread I mentioned the expanded operator
function listed in my codegolf answer, that should be "addbf" (the "from"
form) not "addbt". Sigh. addbf (add byte from) being opcode 0x00.

luserdroog

unread,
Sep 14, 2020, 5:38:49 PM9/14/20
to
Do I also need to swap x and y if the direction is
reversed and z = y - x?
then y = z + x.
sthg like:
((d?x:y)^z)&(x^y)&...

Alexei A. Frounze

unread,
Sep 14, 2020, 6:53:55 PM9/14/20
to
I didn't follow the meaning of "direction" in your code,
but yes, if you're swapping the diminuend and the subtrahend.

That gives you 3 cases:

z=x+y:
(z^x)&(z^y)&...

z=x-y (mathematically equivalent to x=z+y):
(x^z)&(x^y)&...

z=y-x (mathematically equivalent to y=x+z):
(y^x)&(y^z)&...

Putting it all together:

(((is_add||sub_dir)?z:y)^x) &
(((is_add||!sub_dir)?z:x)^y) & ...

Alex

wolfgang kern

unread,
Sep 15, 2020, 4:32:03 AM9/15/20
to
On 14.09.2020 19:15, luserdroog wrote:
...
>>> CMP(,R,BX,CX)

3b d9 CMP bx,cx ;does a faked SUB (bx-cx)
flags affected: CY,PAR,AUX,Z,S,O
usable conditional instructions after this:
Jcc(short or long)/SETcc/CMOVcc/(FCMOVcc is a bit different)

x86 Conditions:
O code 70/40/80/90 if OF=0
NO code 71 41/81/91 if OF=1
C aka B aka NAE aka < code 72/42/82/92 if CF=1 if below/if carry
NC aka NB aka AE aka >= code 73/43/83/93 if CF=0 if not below/if
above or equal

Z aka E aka = code 74/44/84/94 if ZF=1 if zero/if equal
NZ aka NE aka <> code 75 and so on if ZF=0 if not zero/if
not equal

NA aka BE aka <= code 76 if CF=1 or ZF=1 if not
above/if below or equal

A aka NBE aka > aka NCNZ code 77 if CF=0 and ZF=0 if not
below or equal/if above

S aka SM aka - code 78 if SF=1 (usable after
unsigned cmp/sub/add...)

NS aka SP aka + code 79 if SF=0
P aka PE code 7A if PE=1 parity even
(works on low byte only)

NP aka PO code 7B if PE=0 prity odd ("-")
L aka NGE aka <0 code 7C if SF<>OF if less/if not
greater or equal **

NL aka GE aka >=0 code 7D if SF=OF if not less/ if
greater or equal **

NG aka LE aka <=0 code 7E if SF<>OF or ZF=1 if less
or equal/if not greater **

G aka NLE aka >0 code 7F/4F/9F/9F if ZF=0 and SF=OF if not
less or equal/if greater **

** this four are made for signed compare...
ie:
70 xx JO xx
0f 80 xx xx JO xxxx
0f 40/r CMOVO r
0f 90/0/rm SETO r/rm
...
> I said this wrong. These are "to" forms, not "from". The first
> argument to my CMP macro can be F to create a "from" opcode or blank
> (as above) to create a "to" opcode. Elsethread I mentioned the
> expanded operator function listed in my codegolf answer, that should
> be "addbf" (the "from" form) not "addbt". Sigh. addbf (add byte from)
> being opcode 0x00.

Why easy when you can make it complicated ?
what's wrong with the assembler syntax used by NASM,FASM,..(long list)
and also AMD and Intel in their docs ?

sorry if the lines wrap
__
wolfgang

luserdroog

unread,
Sep 15, 2020, 12:17:29 PM9/15/20
to
The whole idea of this project was to build something running on top
of the 8086 emulator. This was to exercise the emulator and shake
out bugs, but also to motivate implementing more opcodes.

For the assembler part, I wanted to put the assembled bytes into a C
data structure. I also wanted complete control over the exact encoding
of the opcodes, mostly so I could make sure it was using codes that
are actually implemented in the emulator.

For these reasons, the best choice I could think of was to make the
assembler with C preprocessor macros that yield a comma separated
list of byte values. One advantage of this approach is that I can
design the *machine code* to be (somewhat) readable. I put opcodes
in hex, M/R/R-M bytes in octal, and offsets in decimal.

"We choose to do these things not because they are easy, but because
they are hard."

olcott

unread,
Sep 16, 2020, 4:19:43 PM9/16/20
to
This is a real "emu"
https://i.insider.com/5d31dff836e03c28ef338ee2?width=1300&format=jpeg&auto=webp

--
Copyright 2020 Pete Olcott

0 new messages