Page 1 of 1

CPU is skipping an instruction

Posted: Sun Jan 25, 2015 3:41 pm
by ApproximateIdentity
I'm trying to write a basic bootsector which writes some stuff to the serial port. I was having some issues and finally realized that one of the lines in my source code was never being executed. It is the line ' movb %cl, %al # Character to transmit' below. When stepping through the code with bochs, it just skips that instruction. The same happens in qemu. I've checked with objdump that the instruction is in fact making it into the final binary. I presume I'm doing something pretty dumb here, but I'm at a total loss. Anyone here see the issue?

Here is my boot sector boot.s

Code: Select all

.section .boot

.start:

# Set up the serial port.
    movb    $0, %ah          # Initialize opcode
    movb    $0b11100011, %al # Parameter data.
    movw    $0, %dx          # COM1: port.
    int     $0x14

# Test the serial port.
    mov     $0x100, %cl
.loop:
    movw    $0, %dx          # Select COM1:
    # NEXT LINE IS THE PROBLEM!
    movb    %cl, %al         # Character to transmit
    movb    $1, %ah          # Transmit opcode
    int     $0x14

    dec     %cl
    jnz     .loop

# Infinite loop.
.idle:
    jmp .idle

.end:

# Fill out rest of the boot sector.
.skip 512 - 2 - (.end - .start)
.word 0xaa55
Here is my bochs config file:

Code: Select all

floppya: 1_44=boot.img, status=inserted
boot: floppy
display_library: sdl2
com1: enabled=1, mode=file, dev=logfile.log
magic_break: enabled=1
Here is my Makefile:

Code: Select all

build:
        as boot.s -o boot.o
        objcopy -O binary --set-section-flags .boot=alloc --only-section=.boot boot.o boot.img

install:
        dd if=boot.img of=/dev/sdb

run: bochs

bochs:
        bochs -f bochs.cfg

qemu:
        qemu-system-x86_64 -hda boot.img -nographic -serial file:logfile.log

clean:
        rm -f *.o boot.img *.log

Re: CPU is skipping an instruction

Posted: Sun Jan 25, 2015 4:07 pm
by iansjack
I don't see a

.code16

directive to let the assembler know that this is 16-bit code.

Re: CPU is skipping an instruction

Posted: Sun Jan 25, 2015 9:40 pm
by ApproximateIdentity
Thanks a lot that worked perfectly! Do you know why this issue materializes by simply skipping the instruction? Is that a normal thing?

Re: CPU is skipping an instruction

Posted: Sun Jan 25, 2015 10:23 pm
by Brendan
Hi,
ApproximateIdentity wrote:Thanks a lot that worked perfectly! Do you know why this issue materializes by simply skipping the instruction? Is that a normal thing?
For 32-bit code the machine code is decoded differently than it would've been for 16-bit code (especially operands). For example, "mov ax,0x1234" might become "mov eax,0xABCD1234" where the upper half (0xABCD) is the next instruction.

This causes "undefined by you" behaviour - it might cause the CPU to seem to skip the next instruction, or (even more likely) cause it to end up in the middle of an instruction and do something less expected (that may or may not make cause a crash).


Cheers,

Brendan

Re: CPU is skipping an instruction

Posted: Mon Jan 26, 2015 5:52 pm
by ApproximateIdentity
Thanks so much for the help you two. After looking directly at the bytes, I can see why the code almost works. It ends up being almost identical with the 16 and 32 bit options. I'm just most surprised that the machine doesn't crash. Is it standard for cpus to accept messed up input like this and then just keep going until something makes sense? Or is a special case because this is a valid instruction, but it's disabled when in 16 bit mode? I am a bit surprised by this.

edit: Or is this maybe this is simply undefined behavior for the cpu and bochs chooses to skip the instruction? (When I step through it, bochs doesn't execute the instruction at all as if it weren't there.)

This disassembled code (if anyone is curious):

Code: Select all

$ objdump -d --section=.boot -Maddr16,data16 boot16.o

boot16.o:     file format elf64-x86-64


Disassembly of section .boot:

0000000000000000 <.start>:
   0:   b4 00                   mov    $0x0,%ah
   2:   b0 e3                   mov    $0xe3,%al
   4:   ba 00 00                mov    $0x0,%dx
   7:   cd 14                   int    $0x14
   9:   b1 00                   mov    $0x0,%cl

000000000000000b <.loop>:
   b:   ba 00 00                mov    $0x0,%dx
   e:   88 c8                   mov    %cl,%al
  10:   b4 01                   mov    $0x1,%ah
  12:   cd 14                   int    $0x14
  14:   fe c9                   dec    %cl
  16:   75 f3                   jne    b <.loop>

0000000000000018 <.idle>:
  18:   eb fe                   jmp    18 <.idle>

000000000000001a <.end>:
        ...
 1fe:   55                      push   %bp
 1ff:   aa                      stos   %al,%es:(%rdi)

Code: Select all

$ objdump -d --section=.boot -Maddr16,data16 boot32.o

boot32.o:     file format elf64-x86-64


Disassembly of section .boot:

0000000000000000 <.start>:
   0:   b4 00                   mov    $0x0,%ah
   2:   b0 e3                   mov    $0xe3,%al
   4:   66 ba 00 00 cd 14       mov    $0x14cd0000,%edx
   a:   b1 00                   mov    $0x0,%cl

000000000000000c <.loop>:
   c:   66 ba 00 00 88 c8       mov    $0xc8880000,%edx
  12:   b4 01                   mov    $0x1,%ah
  14:   cd 14                   int    $0x14
  16:   fe c9                   dec    %cl
  18:   75 f2                   jne    c <.loop>

000000000000001a <.idle>:
  1a:   eb fe                   jmp    1a <.idle>

000000000000001c <.end>:
        ...
 1fc:   00 00                   add    %al,(%rax)
 1fe:   55                      push   %bp
 1ff:   aa                      stos   %al,%es:(%rdi)

Re: CPU is skipping an instruction

Posted: Mon Jan 26, 2015 7:34 pm
by Owen
You have the sequence of bytes

Code: Select all

66 ba 00 00 cd 14
The important thing is that the way the CPU interprets those bytes differs in 32-bit and 16-bit mode

In particular the processor has the concept of a default operand size, which matches the bitness of the mode. Every instruction has an operand size, which starts as the default operand size; this can be toggled with an operand size prefix. (Indeed, you can use multiple operand size prefixes if you want - it's pointless, however, and tends to make the CPU's instruction decoder cranky and slow)

Interpreting that sequence in in 32-bit mode:

Code: Select all

66 - Operand size prefix. The default operand size was 32-bit, so this switches to a 16-bit operand size 
ba - MOV to DX immediate
00 00 - 16-bit operand
cd - INT
14 - 8-bit immediate 0x14 (coding the interrupt vector)
Interpreting that sequence in 16-bit mode:

Code: Select all

66 - Operand size prefix. The default operand size was 16-bit, so this switches to a **32-bit** operand size
ba - MOV to EDX immediate 
00 00 cd 14 - 32-bit immediate
And therein lies your answer - the CPU executed the instruction mov $0x14CD0000, %edx