Alter Ego
Alter Ego

Reputation: 39

Weird print string output from INT 0x10

Hi i have redirected divide overflow interrupt to point at my custom created interrupt which prints 'hello man here i am' on the screen, instead it print weird ascii characters. Can someone tell me why? here is the code

[ORG 100h]
jmp start
message:    dw      'hello man here i am'


prntstr:    push ax
            push bx
            push cx
            push dx
            push si
            push di
            push bp
            push ds
            push es
            push cs
            pop ds

            mov ah, 0x13
            mov al, 1
            mov bh, 0
            mov bl, 7
            mov dx,0x0a03
            mov cx,11
            push cs
            push es
            mov bp,message
            int 0x10
            pop es
            pop ds
            pop bp
            pop di
            pop si
            pop dx
            pop cx
            pop bx
            pop ax
            ret
tsr:        mov ah, 0
            int 0x16
            call prntstr
            iret
            ;mov ah,4ch
            ;mov al, 6
            ;int 0x21
            ;iret


divs:       mov ax,0x8569
            mov dl,2
            div dl
            ret


start:      xor ax,ax
            mov es,ax
            mov word[es:0*4], tsr
            mov [es:0*4+2],cs
            call divs

            mov ax,0x4c00
            int 0x21

one more thing i do not understand about the code is where i set offset at es:0*4 -- i assument that 00 is the location of the divide overflow interrupt? what is the 0*4 for coz anything multiplied by zero would mean the same, so why the 4 ? thanks in advance

Upvotes: 0

Views: 1390

Answers (2)

paxdiablo
paxdiablo

Reputation: 881263

As to you weird character problems, I think:

push cs
push es

should be:

push cs
pop  es

Otherwise:

  1. Your pushes and pops are unbalanced.
  2. Your es segment register is not set up for es:bp to point to the message correctly. It will print whatever is at offset message in the segment es was pointing to when your interrupt fired rather than in the code segment where your actual message is.
  3. It will also eventually crash.

For the 0*4 issue, I'm not sure. It's been a while since I did x86 but I know you can scale indirect addressing modes such as with:

mov eax, dwarray[edx*4]

to ensure the correct memory locations are accessed. This scaled edx up to the correct value before adding to the dwarray base address.

I don't think that was needed for immediate offsets though, so I suspect it was just boilerplate code for being able to change any interrupt by just replacing the 0 with the relevant interrupt number.

And, as an aside, you probably don't want to be changing interrupt vectors without ensuring that interrupts are disabled during the process. If an interrupt fires after you've written the offset of tsr, but before you've written the segment, the results will not be pretty.

Upvotes: 1

Alexey Frunze
Alexey Frunze

Reputation: 62048

You have multiple issues in your code. See the comments:

[ORG 100h]
jmp start
message:    db      'hello man here i am' ; chars are 8-bit, hence db, not dw
msglen      equ $ - message ; calculate message length

prntstr:    push ax
            push bx
            push cx
            push dx
            push si
            push di
            push bp
            push ds
            push es

            ;push cs ; not really needed here
            ;pop ds

            mov ah, 0x13
            mov al, 1
            mov bh, 0
            mov bl, 7
            mov dx,0x0a03
            mov cx,msglen ; use proper message length

            push cs
            pop es ; not "push es" - copy'n'paste bug !!!

            mov bp,message
            int 0x10

            pop es
            pop ds
            pop bp
            pop di
            pop si
            pop dx
            pop cx
            pop bx
            pop ax
            ret

tsr:
            call prntstr

            ; skip DIV (by advancing IP) to avoid infinite loop on DIV
            push bp
            mov bp, sp
            add word [bp+1*2], divend-divstart; IP (location of divstart) on the stack
            pop bp

            push ax ; save AX because int 0x16 will change it
            mov ah, 0
            int 0x16
            pop ax ; restore AX

            iret

divs:       mov ax,0x8569
            mov dl,2
divstart:
            div dl
divend:
            ret

start:
            mov  ax, 3
            int 0x10 ; clear screen by setting mode 3

            xor ax,ax
            mov es,ax

            cli                 ; update ISR address w/ ints disabled
            push word[es:0*4+2] ; preserve ISR address
            push word[es:0*4]
            mov word[es:0*4], tsr
            mov [es:0*4+2],cs
            sti

            call divs

            cli                 ; update ISR address w/ ints disabled
            pop  word[es:0*4]   ; restore ISR address
            pop  word[es:0*4+2]
            sti

            mov ax,0x4c00
            int 0x21

4 is a far pointer size (2 bytes for offset and 2 bytes for segment selector). So, for int 0 the address in the interrupt vector table will be 0*4, for int 1 it will be 1*4, for int n it will be n*4. In this particular case the multiplication is unnecessary, but it won't affect code generation anyway since the assembler will calculate and substitute 0 for 0*4 and 2 for 0*4+2.

Upvotes: 0

Related Questions