CPU is skipping an instruction

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
ApproximateIdentity
Posts: 16
Joined: Sun Dec 08, 2013 4:14 pm

CPU is skipping an instruction

Post 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
User avatar
iansjack
Member
Member
Posts: 4707
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: CPU is skipping an instruction

Post by iansjack »

I don't see a

.code16

directive to let the assembler know that this is 16-bit code.
ApproximateIdentity
Posts: 16
Joined: Sun Dec 08, 2013 4:14 pm

Re: CPU is skipping an instruction

Post 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?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: CPU is skipping an instruction

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
ApproximateIdentity
Posts: 16
Joined: Sun Dec 08, 2013 4:14 pm

Re: CPU is skipping an instruction

Post 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)
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: CPU is skipping an instruction

Post 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
Post Reply