问题
I'm programming using MASM 6.11 on an old Windows 95 laptop, and I'm having a problem switching between data segments.
For the sake of organization I wanted to use a different data segment to hold all of the variables that are only used by my macros. That different data segment is also placed inside the macro file.
I thought I could specify a different data segment by just MOV
ing the new segment into DS, but that doesn't seem to be working. I get the following error repeated many times upon assembling.
error A20068: Cannot address with segment register
Here's an example .ASM
, and .MAC
file showing my program's basic layout.
;********************
; STACK SEGMENT
;********************
TheStack STACK SEGMENT
DB 64 DUP ('THESTACK') ;512 bytes for the stack
TheStack ENDS
;********************
; END STACK SEGMENT
;********************
;********************
; DATA SEGMENT
;********************
Data SEGMENT
var db ?
Data ENDS
;********************
; END DATA SEGMENT
;********************
;********************
; CODE SEGMENT
;********************
Code SEGMENT
assume CS:Code,DS:Data
MAIN PROC
;set Data to be the Data Segment
mov ax, Data
mov ds, ax
MAC3
;Return to DOS
mov ah,4ch ;setup the terminate process DOS service
mov al,0 ;ERRORLEVEL takes 0
int 21h ;return to DOS
MAIN ENDP
Code ENDS
;********************
; END CODE SEGMENT
;********************
END Start
And the .MAC file:
MAC1 MACRO
mov MacVar1,bx
ENDM
MAC2 MACRO
mov MacVar2,cx
ENDM
MAC3 MACRO
mov ax, MacData
mov ds, ax
MAC1
MAC2
mov ax, Data
mov ds, ax
ENDM
;********************
; DATA SEGMENT
;********************
MacData SEGMENT
macVar1 dw ?
macVar2 dw ?
MacData ENDS
;********************
; END DATA SEGMENT
;********************
回答1:
For the sake of organization I wanted to use a different data segment to hold all of the variables that are only used by my macros.
That sounds like a good way to make your macros less useful and harder to use. As well as destroying performance. What if you want to use a macro with a memory operand?
If you don't actually care about 8086, and just want 16-bit code that can run on a 386 or later, then you could still use this weird idea but with FS or GS segment overrides on instructions inside your macro, instead of changing DS.
Or even use ES for 8086 compatibility, but remember it's used by string instructions.
error A20068: Cannot address with segment register
You didn't say which instructions are generating those.
; this is valid:
mov ax, imm16
mov ds, ax
If that's not what your syntax means to MASM, then that's your problem. If mov ax, MacData
looks like a load to MASM, that's not good, but IDK why you get an error. Maybe it's trying to implicitly generate segment overrides based on where it thinks different variables are supposed to be?
See the x86 tag wiki for links to assembler manuals and lots of other stuff.
Segmented 16-bit code is not a good way to learn asm in general, IMO. Especially not learning the DOS system call API at the same time, since that's obsolete knowledge that you won't use while looking at the asm of real programs (while debugging or performance tuning). See this answer for more about how I think 16-bit code is a bad way to learn.
回答2:
As Peter Cordes said in his answer what you're doing a terrible idea. If you're going to the effort of programming in assembly language then you should be intelligently laying out your segments in order to minimize the amount of segment switching you need to do. If you ever do get a 8088-based PC then this becomes even more important because of how slow these CPUs were. Your Windows 95 laptop is at least 10 times faster than a 4.77 Mhz 8086 PC and could be 100 times faster.
In any case, your problem is that when you use a label in a memory operand the assembler needs to know what segment register to use for the instruction. You've told it, through the ASSUME directive, that CS points to the Code
segment and DS points to the Data
segment. However you haven't said what segment register points to the MacData
segment, so the assembler doesn't know what segment register to use for the macVar1
and macVar2
operands.
The simple fix would be to change the ASSUME'd DS value when you change DS:
MAC3 MACRO
mov ax, MacData
mov ds, ax
ASSUME DS:MacData
MAC1
MAC2
mov ax, Data
mov ds, ax
ASSUME DS:Data
ENDM
You could also change your memory operands to explicitly tell the assembler which segment register to use. This would let you make your macros more flexible so they don't have assume what DS value the rest of the code is using:
MAC1 MACRO
mov ds:MacVar1,bx
ENDM
MAC2 MACRO
mov ds:MacVar2,cx
ENDM
MAC3 MACRO
push ds
mov ax, MacData
mov ds, ax
MAC1
MAC2
pop ds
ENDM
来源:https://stackoverflow.com/questions/39187494/how-to-switch-between-multiple-data-segments-in-8086-assembly