]> granicus.if.org Git - yasm/commitdiff
Make jmp with seg:off equ behave the same as NASM.
authorPeter Johnson <peter@tortall.net>
Sat, 19 Jan 2008 08:59:19 +0000 (08:59 -0000)
committerPeter Johnson <peter@tortall.net>
Sat, 19 Jan 2008 08:59:19 +0000 (08:59 -0000)
Formerly:
  foo equ 1:2
  jmp foo
would result in a far jump.  Now, an explicit "far" is required:
  jmp far foo
to generate a far jump.

In addition, the direct use of seg:off in immediates and effective
addresses will result in an error; the use of EQU'ed seg:off values
is still legal (and will still result in just the offset).  This
behavior is more sane and also matches NASM behavior.
Thus:
  foo equ 1:2
  mov ax, foo   ; okay, just 2
  mov ax, [foo] ; okay, just 2
  mov ax, 1:2   ; illegal
  mov ax, [1:2] ; illegal

svn path=/trunk/yasm/; revision=2028

14 files changed:
libyasm/insn.c
libyasm/insn.h
modules/arch/x86/tests/Makefile.inc
modules/arch/x86/tests/farbasic.asm
modules/arch/x86/tests/jmpfar.asm [new file with mode: 0644]
modules/arch/x86/tests/jmpfar.hex [new file with mode: 0644]
modules/arch/x86/tests/segoff-err.asm [new file with mode: 0644]
modules/arch/x86/tests/segoff-err.errwarn [new file with mode: 0644]
modules/arch/x86/tests/segoff.asm
modules/arch/x86/tests/segoff.hex
modules/arch/x86/x86id.c
modules/objfmts/bin/tests/bin-farabs.asm
modules/objfmts/bin/tests/bin-farabs.hex
modules/parsers/nasm/nasm-parse.c

index 7127b4108ec46fbf1527058941bb8a1c4de1dfbe..46a78dbe1a9c463ee34defcf070581b94ad3de52 100644 (file)
@@ -59,6 +59,7 @@ yasm_operand_create_reg(uintptr_t reg)
 
     retval->type = YASM_INSN__OPERAND_REG;
     retval->data.reg = reg;
+    retval->seg = 0;
     retval->targetmod = 0;
     retval->size = 0;
     retval->deref = 0;
@@ -74,6 +75,7 @@ yasm_operand_create_segreg(uintptr_t segreg)
 
     retval->type = YASM_INSN__OPERAND_SEGREG;
     retval->data.reg = segreg;
+    retval->seg = 0;
     retval->targetmod = 0;
     retval->size = 0;
     retval->deref = 0;
@@ -89,6 +91,7 @@ yasm_operand_create_mem(/*@only@*/ yasm_effaddr *ea)
 
     retval->type = YASM_INSN__OPERAND_MEMORY;
     retval->data.ea = ea;
+    retval->seg = 0;
     retval->targetmod = 0;
     retval->size = 0;
     retval->deref = 0;
@@ -111,6 +114,7 @@ yasm_operand_create_imm(/*@only@*/ yasm_expr *val)
         retval = yasm_xmalloc(sizeof(yasm_insn_operand));
         retval->type = YASM_INSN__OPERAND_IMM;
         retval->data.val = val;
+        retval->seg = 0;
         retval->targetmod = 0;
         retval->size = 0;
         retval->deref = 0;
index ea3fe53919b24d30fad820f06132b2282acdfa66..905b2f96f7e8e2faa7c4af37f4b55917ca557243 100644 (file)
@@ -75,20 +75,20 @@ struct yasm_effaddr {
 /** An instruction operand (opaque type). */
 typedef struct yasm_insn_operand yasm_insn_operand;
 
+/** The type of an instruction operand. */
+typedef enum yasm_insn_operand_type {
+    YASM_INSN__OPERAND_REG = 1,     /**< A register. */
+    YASM_INSN__OPERAND_SEGREG,      /**< A segment register. */
+    YASM_INSN__OPERAND_MEMORY,      /**< An effective address
+                                     *   (memory reference). */
+    YASM_INSN__OPERAND_IMM          /**< An immediate or jump target. */
+} yasm_insn_operand_type;
+
 /** An instruction operand. */
 struct yasm_insn_operand {
     /** Link for building linked list of operands.  \internal */
     /*@reldef@*/ STAILQ_ENTRY(yasm_insn_operand) link;
 
-    /** Operand type. */
-    enum yasm_insn_operand_type {
-        YASM_INSN__OPERAND_REG = 1,     /**< A register. */
-        YASM_INSN__OPERAND_SEGREG,      /**< A segment register. */
-        YASM_INSN__OPERAND_MEMORY,      /**< An effective address
-                                         *   (memory reference). */
-        YASM_INSN__OPERAND_IMM          /**< An immediate or jump target. */
-    } type;
-
     /** Operand data. */
     union {
         uintptr_t reg;      /**< Arch data for reg/segreg. */
@@ -96,6 +96,8 @@ struct yasm_insn_operand {
         yasm_expr *val;     /**< Value of immediate or jump target. */
     } data;
 
+    yasm_expr *seg;         /**< Segment expression */
+
     uintptr_t targetmod;        /**< Arch target modifier, 0 if none. */
 
     /** Specified size of the operand, in bits.  0 if not user-specified. */
@@ -121,6 +123,9 @@ struct yasm_insn_operand {
      * "push strict dword 4", which sets this flag.
      */
     unsigned int strict:1;
+
+    /** Operand type. */
+    unsigned int type:4;
 };
 
 /** Base structure for "instruction" bytecodes.  These are the mnenomic
index 9069d2c270d3531e893b93c35e975d5347f6a833..b9f35d2b3c1c1e5c754c947578b26c5a312af1fe 100644 (file)
@@ -72,6 +72,8 @@ EXTRA_DIST += modules/arch/x86/tests/jmp64-5.asm
 EXTRA_DIST += modules/arch/x86/tests/jmp64-5.hex
 EXTRA_DIST += modules/arch/x86/tests/jmp64-6.asm
 EXTRA_DIST += modules/arch/x86/tests/jmp64-6.hex
+EXTRA_DIST += modules/arch/x86/tests/jmpfar.asm
+EXTRA_DIST += modules/arch/x86/tests/jmpfar.hex
 EXTRA_DIST += modules/arch/x86/tests/lds-err.asm
 EXTRA_DIST += modules/arch/x86/tests/lds-err.errwarn
 EXTRA_DIST += modules/arch/x86/tests/loopadsz.asm
@@ -144,6 +146,8 @@ EXTRA_DIST += modules/arch/x86/tests/segmov.asm
 EXTRA_DIST += modules/arch/x86/tests/segmov.hex
 EXTRA_DIST += modules/arch/x86/tests/segoff.asm
 EXTRA_DIST += modules/arch/x86/tests/segoff.hex
+EXTRA_DIST += modules/arch/x86/tests/segoff-err.asm
+EXTRA_DIST += modules/arch/x86/tests/segoff-err.errwarn
 EXTRA_DIST += modules/arch/x86/tests/shift.asm
 EXTRA_DIST += modules/arch/x86/tests/shift.hex
 EXTRA_DIST += modules/arch/x86/tests/simd-1.asm
index 0c2648dc0ae5f87fa926b3e4822a2d41c61eff8f..625ed14d827cba47b062f714ef3899b2c78de1ff 100644 (file)
@@ -2,7 +2,7 @@
 
 jmp 5:4
 
-jmp equval
+jmp far equval
 
 equval equ 6:7
 
diff --git a/modules/arch/x86/tests/jmpfar.asm b/modules/arch/x86/tests/jmpfar.asm
new file mode 100644 (file)
index 0000000..92ef261
--- /dev/null
@@ -0,0 +1,16 @@
+jmp 1234:5678          ; YASM: far jump
+jmp near 1234:5678     ; YASM: near jump; NASM: mismatch in operand sizes
+jmp far 1234:5678      ; YASM: far jump; NASM: mismatch in operand sizes
+;dw seg (1234:5678)
+far1 equ 1234:5678
+jmp far1               ; both: near jump
+jmp near far1          ; both: near jump
+jmp far far1           ; YASM: far jump; NASM: value referenced by FAR is not relocatable
+dw seg far1
+jmp far2               ; both: near jump
+jmp near far2          ; both: near jump
+jmp far far2           ; YASM: far jump; NASM: value referenced by FAR is not relocatable
+dw seg far2
+far2 equ 1234:5678
+;mov ax, [1234:5678]   ; YASM: invalid segment in effective address; NASM: invalid segment override
+;mov ax, 1234:5678     ; YASM: immediate does not support segment; NASM: invalid combination of opcode and operands
diff --git a/modules/arch/x86/tests/jmpfar.hex b/modules/arch/x86/tests/jmpfar.hex
new file mode 100644 (file)
index 0000000..f9d3ea4
--- /dev/null
@@ -0,0 +1,39 @@
+ea 
+2e 
+16 
+d2 
+04 
+e9 
+26 
+16 
+ea 
+2e 
+16 
+d2 
+04 
+e9 
+1e 
+16 
+e9 
+1b 
+16 
+ea 
+2e 
+16 
+d2 
+04 
+d2 
+04 
+e9 
+11 
+16 
+e9 
+0e 
+16 
+ea 
+2e 
+16 
+d2 
+04 
+d2 
+04 
diff --git a/modules/arch/x86/tests/segoff-err.asm b/modules/arch/x86/tests/segoff-err.asm
new file mode 100644 (file)
index 0000000..ac76927
--- /dev/null
@@ -0,0 +1,6 @@
+; all of these should be illegal
+
+jmp far[1:2]
+mov ax,[1:2]
+push dword [1:2]
+mov ax,1:2
diff --git a/modules/arch/x86/tests/segoff-err.errwarn b/modules/arch/x86/tests/segoff-err.errwarn
new file mode 100644 (file)
index 0000000..98a96c7
--- /dev/null
@@ -0,0 +1,4 @@
+-:3: error: invalid segment in effective address
+-:4: error: invalid segment in effective address
+-:5: error: invalid segment in effective address
+-:6: error: immediate does not support segment
index 987a7cf36ab571451f7d75728e12e61951cf3ef2..7a2091fe1a0894bbcc0ee2734ba1568dc7adcb02 100644 (file)
@@ -1,10 +1,5 @@
 ; all of these should be legal and should just result in the offset portion
 
-jmp far[1:2]
-mov ax,[1:2]
-push dword [1:2]
-mov ax,1:2
-
 foo equ 1:2
 jmp far[foo]
 mov ax,[foo]
index b83919ad5c09ff699a3a56c2b657f6a3c23779cd..7520242acb5cf451b1173f5281123c93a37679c5 100644 (file)
@@ -13,18 +13,3 @@ ff
 b8 
 02 
 00 
-ff 
-2e 
-02 
-00 
-a1 
-02 
-00 
-66 
-ff 
-36 
-02 
-00 
-b8 
-02 
-00 
index 13dcff60fd5d87827d64c8be1beb310fecbf0fe5..d17f045c1873d370b505563e3499cbeec8e6c139 100644 (file)
@@ -360,7 +360,6 @@ x86_finalize_jmpfar(yasm_bytecode *bc, yasm_bytecode *prev_bc,
     unsigned int mode_bits = id_insn->mode_bits;
     x86_jmpfar *jmpfar;
     yasm_insn_operand *op;
-    /*@only@*/ yasm_expr *segment;
 
     jmpfar = yasm_xmalloc(sizeof(x86_jmpfar));
     x86_finalize_common(&jmpfar->common, info, mode_bits);
@@ -368,13 +367,9 @@ x86_finalize_jmpfar(yasm_bytecode *bc, yasm_bytecode *prev_bc,
 
     op = yasm_insn_ops_first(&id_insn->insn);
 
-    if (op->type == YASM_INSN__OPERAND_IMM &&
-        yasm_expr_is_op(op->data.val, YASM_EXPR_SEGOFF)) {
-        /* SEG:OFF expression; split it. */
-        segment = yasm_expr_extract_segoff(&op->data.val);
-        if (!segment)
-            yasm_internal_error(N_("didn't get SEG:OFF expression in jmpfar"));
-        if (yasm_value_finalize_expr(&jmpfar->segment, segment, prev_bc, 16))
+    if (op->type == YASM_INSN__OPERAND_IMM && op->seg) {
+        /* SEG:OFF */
+        if (yasm_value_finalize_expr(&jmpfar->segment, op->seg, prev_bc, 16))
             yasm_error_set(YASM_ERROR_TOO_COMPLEX,
                            N_("jump target segment too complex"));
         if (yasm_value_finalize_expr(&jmpfar->offset, op->data.val, prev_bc,
@@ -383,13 +378,13 @@ x86_finalize_jmpfar(yasm_bytecode *bc, yasm_bytecode *prev_bc,
                            N_("jump target offset too complex"));
     } else if (op->targetmod == X86_FAR) {
         /* "FAR imm" target needs to become "seg imm:imm". */
-        if (yasm_value_finalize_expr(&jmpfar->offset,
-                                     yasm_expr_copy(op->data.val), prev_bc, 0)
-            || yasm_value_finalize_expr(&jmpfar->segment, op->data.val,
-                                        prev_bc, 16))
+        yasm_expr *e = yasm_expr_create_branch(YASM_EXPR_SEG,
+                                               yasm_expr_copy(op->data.val),
+                                               op->data.val->line);
+        if (yasm_value_finalize_expr(&jmpfar->offset, op->data.val, prev_bc, 0)
+            || yasm_value_finalize_expr(&jmpfar->segment, e, prev_bc, 16))
             yasm_error_set(YASM_ERROR_TOO_COMPLEX,
                            N_("jump target expression too complex"));
-        jmpfar->segment.seg_of = 1;
     } else
         yasm_internal_error(N_("didn't get FAR expression in jmpfar"));
 
@@ -785,8 +780,7 @@ x86_find_match(x86_id_insn *id_insn, yasm_insn_operand **ops,
                     break;
                 case OPT_ImmNotSegOff:
                     if (op->type != YASM_INSN__OPERAND_IMM ||
-                        op->targetmod != 0 ||
-                        yasm_expr_is_op(op->data.val, YASM_EXPR_SEGOFF))
+                        op->targetmod != 0 || op->seg)
                         mismatch = 1;
                     break;
                 case OPT_XMM0:
@@ -1173,6 +1167,9 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
                             yasm_internal_error(
                                 N_("invalid operand conversion"));
                         case YASM_INSN__OPERAND_MEMORY:
+                            if (op->seg)
+                                yasm_error_set(YASM_ERROR_VALUE,
+                                    N_("invalid segment in effective address"));
                             insn->x86_ea = (x86_effaddr *)op->data.ea;
                             if (info_ops[i].type == OPT_MemOffs)
                                 /* Special-case for MOV MemOffs instruction */
@@ -1197,6 +1194,9 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
                     }
                     break;
                 case OPA_Imm:
+                    if (op->seg)
+                        yasm_error_set(YASM_ERROR_VALUE,
+                                       N_("immediate does not support segment"));
                     if (op->type == YASM_INSN__OPERAND_IMM) {
                         imm = op->data.val;
                         im_len = size_lookup[info_ops[i].size];
@@ -1204,6 +1204,9 @@ x86_id_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
                         yasm_internal_error(N_("invalid operand conversion"));
                     break;
                 case OPA_SImm:
+                    if (op->seg)
+                        yasm_error_set(YASM_ERROR_VALUE,
+                                       N_("immediate does not support segment"));
                     if (op->type == YASM_INSN__OPERAND_IMM) {
                         imm = op->data.val;
                         im_len = size_lookup[info_ops[i].size];
index e26bcbc6695e3b54ea75f000e90a782eb6fda107..4d931c1e956d8fb99ba6d28c411c30e6e4a70bc6 100644 (file)
@@ -14,14 +14,16 @@ mov es, ax
 
 ; Use without seg should yield just the offset part.
 mov bx, keybuf
-mov bx, 0040h:001Eh            ; Illegal in NASM ("invalid combination...")
+;mov bx, 0040h:001Eh           ; Illegal
 
 ; Each of the below pairs should be equivalent (and legal) in Yasm.
 ; There are some differences from NASM here!
 
+; Defaults to near jump (on both NASM and Yasm)
+jmp keybuf
+
 ; Direct far jump.
-jmp keybuf             ; Results in a near jump in NASM
-jmp 0040h:001Eh                ; But Yasm sees the equ value as equivalent to this
+jmp 0040h:001Eh
 
 ; Force near (non-far) jump (just offset, no segment).
 jmp near keybuf
index 6f626b560538bbfb55eda2a864959ab6485da2b7..5ebd42252318d7414044fca8f477ba39e6a476a4 100644 (file)
@@ -9,36 +9,31 @@ c0
 bb 
 1e 
 00 
-bb 
-1e 
-00 
-ea 
-1e 
-00 
-40 
-00 
+e9 
+10 
+ff 
 ea 
 1e 
 00 
 40 
 00 
 e9 
-03 
+08 
 ff 
 e9 
-00 
+05 
 ff 
 e9 
-fd 
-fe 
+02 
+ff 
 e9 
-dc 
+e1 
 fe 
 e9 
-d9 
+de 
 0e 
 e9 
-d6 
+db 
 4e 
 eb 
 fe 
index db08275fb11ada1e7e07ca1aa7b287ff085b6523..88d2297bba84c383962f9b0c43ceb4d5de1cb41f 100644 (file)
@@ -35,8 +35,8 @@ RCSID("$Id$");
 
 typedef enum {
     NORM_EXPR,
-    DIR_EXPR,
-    DV_EXPR
+    DIR_EXPR,       /* Can't have seg:off or WRT anywhere */
+    DV_EXPR         /* Can't have registers anywhere */
 } expr_type;
 
 static yasm_bytecode *parse_line(yasm_parser_nasm *parser_nasm);
@@ -46,7 +46,7 @@ static yasm_bytecode *parse_times(yasm_parser_nasm *parser_nasm);
 static yasm_bytecode *parse_exp(yasm_parser_nasm *parser_nasm);
 static yasm_bytecode *parse_instr(yasm_parser_nasm *parser_nasm);
 static yasm_insn_operand *parse_operand(yasm_parser_nasm *parser_nasm);
-static yasm_effaddr *parse_memaddr(yasm_parser_nasm *parser_nasm);
+static yasm_insn_operand *parse_memaddr(yasm_parser_nasm *parser_nasm);
 static yasm_expr *parse_expr(yasm_parser_nasm *parser_nasm, expr_type type);
 static yasm_expr *parse_bexpr(yasm_parser_nasm *parser_nasm, expr_type type);
 static yasm_expr *parse_expr0(yasm_parser_nasm *parser_nasm, expr_type type);
@@ -475,7 +475,7 @@ parse_times(yasm_parser_nasm *parser_nasm)
     yasm_expr *multiple;
     yasm_bytecode *bc;
 
-    multiple = parse_expr(parser_nasm, DV_EXPR);
+    multiple = parse_bexpr(parser_nasm, DV_EXPR);
     if (!multiple) {
         yasm_error_set(YASM_ERROR_SYNTAX, N_("expression expected after %s"),
                        "TIMES");
@@ -526,7 +526,7 @@ parse_exp(yasm_parser_nasm *parser_nasm)
                         goto dv_done;
                     }
                 }
-                if ((e = parse_expr(parser_nasm, DV_EXPR)))
+                if ((e = parse_bexpr(parser_nasm, DV_EXPR)))
                     dv = yasm_dv_create_expr(e);
                 else {
                     yasm_error_set(YASM_ERROR_SYNTAX,
@@ -554,7 +554,7 @@ dv_done:
             unsigned int size = RESERVE_SPACE_val/8;
             yasm_expr *e;
             get_next_token();
-            e = parse_expr(parser_nasm, DV_EXPR);
+            e = parse_bexpr(parser_nasm, DV_EXPR);
             if (!e) {
                 yasm_error_set(YASM_ERROR_SYNTAX,
                                N_("expression expected after %s"), "RESx");
@@ -582,7 +582,7 @@ dv_done:
                 get_next_token();
             if (is_eol())
                 goto incbin_done;
-            start = parse_expr(parser_nasm, DV_EXPR);
+            start = parse_bexpr(parser_nasm, DV_EXPR);
             if (!start) {
                 yasm_error_set(YASM_ERROR_SYNTAX,
                                N_("expression expected for INCBIN start"));
@@ -594,7 +594,7 @@ dv_done:
                 get_next_token();
             if (is_eol())
                 goto incbin_done;
-            maxlen = parse_expr(parser_nasm, DV_EXPR);
+            maxlen = parse_bexpr(parser_nasm, DV_EXPR);
             if (!maxlen) {
                 yasm_error_set(YASM_ERROR_SYNTAX,
                     N_("expression expected for INCBIN maximum length"));
@@ -685,19 +685,18 @@ parse_operand(yasm_parser_nasm *parser_nasm)
     switch (curtok) {
         case '[':
         {
-            yasm_effaddr *ea;
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
+            op = parse_memaddr(parser_nasm);
 
             expect(']');
             get_next_token();
 
-            if (!ea) {
+            if (!op) {
                 yasm_error_set(YASM_ERROR_SYNTAX,
                                N_("memory address expected"));
                 return NULL;
             }
-            return yasm_operand_create_mem(ea);
+            return op;
         }
         case SEGREG:
             op = yasm_operand_create_segreg(SEGREG_val);
@@ -756,19 +755,32 @@ parse_operand(yasm_parser_nasm *parser_nasm)
         }
         default:
         {
-            yasm_expr *e = parse_expr(parser_nasm, NORM_EXPR);
+            yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR);
             if (!e)
                 return NULL;
-            return yasm_operand_create_imm(e);
+            if (curtok != ':')
+                return yasm_operand_create_imm(e);
+            else {
+                yasm_expr *off;
+                get_next_token();
+                off = parse_bexpr(parser_nasm, NORM_EXPR);
+                if (!off) {
+                    yasm_expr_destroy(e);
+                    return NULL;
+                }
+                op = yasm_operand_create_imm(off);
+                op->seg = e;
+                return op;
+            }
         }
     }
 }
 
 /* memory addresses */
-static yasm_effaddr *
+static yasm_insn_operand *
 parse_memaddr(yasm_parser_nasm *parser_nasm)
 {
-    yasm_effaddr *ea;
+    yasm_insn_operand *op;
     switch (curtok) {
         case SEGREG:
         {
@@ -780,48 +792,63 @@ parse_memaddr(yasm_parser_nasm *parser_nasm)
                 return NULL;
             }
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
-            if (ea)
-                yasm_ea_set_segreg(ea, segreg);
-            return ea;
+            op = parse_memaddr(parser_nasm);
+            if (op)
+                yasm_ea_set_segreg(op->data.ea, segreg);
+            return op;
         }
         case SIZE_OVERRIDE:
         {
             unsigned int size = SIZE_OVERRIDE_val;
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
-            if (ea)
-                ea->disp.size = size;
-            return ea;
+            op = parse_memaddr(parser_nasm);
+            if (op)
+                op->data.ea->disp.size = size;
+            return op;
         }
         case NOSPLIT:
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
-            if (ea)
-                ea->nosplit = 1;
-            return ea;
+            op = parse_memaddr(parser_nasm);
+            if (op)
+                op->data.ea->nosplit = 1;
+            return op;
         case REL:
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
-            if (ea) {
-                ea->pc_rel = 1;
-                ea->not_pc_rel = 0;
+            op = parse_memaddr(parser_nasm);
+            if (op) {
+                op->data.ea->pc_rel = 1;
+                op->data.ea->not_pc_rel = 0;
             }
-            return ea;
+            return op;
         case ABS:
             get_next_token();
-            ea = parse_memaddr(parser_nasm);
-            if (ea) {
-                ea->pc_rel = 0;
-                ea->not_pc_rel = 1;
+            op = parse_memaddr(parser_nasm);
+            if (op) {
+                op->data.ea->pc_rel = 0;
+                op->data.ea->not_pc_rel = 1;
             }
-            return ea;
+            return op;
         default:
         {
-            yasm_expr *e = parse_expr(parser_nasm, NORM_EXPR);
+            yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR);
             if (!e)
                 return NULL;
-            return yasm_arch_ea_create(p_object->arch, e);
+            if (curtok != ':')
+                return yasm_operand_create_mem(
+                    yasm_arch_ea_create(p_object->arch, e));
+            else {
+                yasm_expr *off;
+                get_next_token();
+                off = parse_bexpr(parser_nasm, NORM_EXPR);
+                if (!off) {
+                    yasm_expr_destroy(e);
+                    return NULL;
+                }
+                op = yasm_operand_create_mem(
+                    yasm_arch_ea_create(p_object->arch, off));
+                op->seg = e;
+                return op;
+            }
         }
     }
 }
@@ -869,14 +896,11 @@ static yasm_expr *
 parse_expr(yasm_parser_nasm *parser_nasm, expr_type type)
 {
     switch (type) {
-        case NORM_EXPR:
-            parse_expr_common(parse_bexpr, ':', parse_bexpr, YASM_EXPR_SEGOFF);
-        case DV_EXPR:
-            /* dataval expressions can't handle seg:off */
-            return parse_bexpr(parser_nasm, type);
         case DIR_EXPR:
             /* directive expressions can't handle seg:off or WRT */
             return parse_expr0(parser_nasm, type);
+        default:
+            parse_expr_common(parse_bexpr, ':', parse_bexpr, YASM_EXPR_SEGOFF);
     }
     /*@notreached@*/
     return NULL;