I'm working on a project in TASM assembly and I have problems outputing color to specific cells of the DOS console when I use 32 bit registers in assembler (with .386
).
Here's an example of how I would normally do that:
BX loc of cell,
AH color / char of cell
mov ah , 01000000b ; Color Red to ah
mov ax,0b800h ; memory location of the console
mov es,ax ; to es
mov es:[bx] , ah ; mov data from ah to the cell of bx
this works but when I do .386
at the start to make it 32 bit this code stops working ... anyone know a way to fix the problem ?
SHORT :
The code need to set Color to cell . works without .386
and stops working with .386
Code segments :
.386
.Model small
.data
;all my data
.code
Start:
; all the code in there also the output code snippet I showed
end start
Fix by Ped7g :
put the line .386
inside the .code
segment
Fix by Margaret Bloom :
add USE16
modifier to MODEL
directive
Both are working. Thanks for the help
tasm 32 bit
If you don't specify the attributes of the code segment(s) explicitly the use of the .386
directive will set the default operand side to 32-bit.
Quoting the TASM manual:
Note that you can specify the model modifier in two places, for compatibility with MASM 5.2. If you don't use a model specifier, Turbo Assembler as.sumes the NEARSTACK modifier, and USE32 (if the 80386 or 80486 processor is selected).
This doesn't mean that you are allowed to use 32-bit registers1 but that the assembler will emit the instructions in a "specular" way.
All code in an x86 machine has a default operand size.
When running in 32-bit it is 32 bits, when running in 16-bit it is 16 bits (64-bit is a bit more involved, it is still 32 bits but can be overridden with the REX.W prefix).
The default operand size determines the default size of the immediate operands, i.e. the number of bytes that follow an opcode of an instruction that expects a value.
Instructions like mov ax, 0b800h
and mov eax, 0b800h
are encoded the same way: with the opcode B8
.
After this opcode, it follows the immediate operand of 16 or 32 bits.
The specific size depends on the default operand size.
To access the "other" size, i.e. specify a 16-bit version of an instruction in 32-bit code and vice-versa, an operand size override prefix exists (value 66
).
Put on a table
+-----------------------------------------+
| Default operand size |
+-------------------+---------------------+-------------------+
|Instruction | 16 | 32 |
+-------------------+---------------------+-------------------+
| | | |
|mov ax, 1234h | B8 34 12 | 66 B8 34 12 |
| | | |
|mov eax, 12345678h | 66 B8 78 56 34 12 | B8 78 56 34 12 |
| | | |
+-------------------+---------------------+-------------------+
See how mov ax, 0b800h
is encoded with 66 B8
when the assembler assumes a default operand size of 32-bit?
And that when executed in a 16-bit code the 66 B8
is decoded has having a 32-bit immediate?
This screw up the decoding of subsequent instructions.
The snipped posted when assembled as 32-bit code but executed as 16-bit code result in:
00000000 B440 mov ah,0x40
00000002 66B800B88EC0 mov eax,0xc08eb800
00000008 26678827 mov [es:edi],ah
Give each code segment the code-size attribute explicitly or set the default one with the MODEL
directive.
For example, if you use a SMALL
memory model:
.MODEL USE16 SMALL
1 You always are as far as the CPU is concerned, it's just that TASM refuses to assemble instructions not compliant with the processor family chosen.