Double character is printing in a routine (solved)
Double character is printing in a routine (solved)
I am trying to work on the roots of a video driver, so I am implementing the following routine:
posn: dw 160*1+2*1 ; row 2 column 2
putc: push bp ; prolouge
mov sp, bp
sub sp, 4
push bp
push bx
mov bx, 0xB800 ; color video segment
mov es, bx
mov bp, [posn] ; position offset
mov byte [es:bp], al ; copy character
inc bp
mov byte [es:bp], 0xE0 ; make black on yellow
inc bp
mov [posn], bp ; save new position
pop bx
pop bp
mov bp, sp ; epilouge
pop bp
ret
I invoke the routine as follows:
mov al, 'x'
call putc
to print x
Whenever I print a string with it the first character prints twice like:
'HHello World!' starting at row 2 column 2. I am sure that I am not accidentally calling putc twice with H. I have used different letters.
I am assembling under nasm in FreeDOS in QEMU. What is going on? Shouldn't the bp be incremented each time the routine is called?
Thanks.
posn: dw 160*1+2*1 ; row 2 column 2
putc: push bp ; prolouge
mov sp, bp
sub sp, 4
push bp
push bx
mov bx, 0xB800 ; color video segment
mov es, bx
mov bp, [posn] ; position offset
mov byte [es:bp], al ; copy character
inc bp
mov byte [es:bp], 0xE0 ; make black on yellow
inc bp
mov [posn], bp ; save new position
pop bx
pop bp
mov bp, sp ; epilouge
pop bp
ret
I invoke the routine as follows:
mov al, 'x'
call putc
to print x
Whenever I print a string with it the first character prints twice like:
'HHello World!' starting at row 2 column 2. I am sure that I am not accidentally calling putc twice with H. I have used different letters.
I am assembling under nasm in FreeDOS in QEMU. What is going on? Shouldn't the bp be incremented each time the routine is called?
Thanks.
Last edited by brunsa2 on Sun Sep 30, 2007 9:22 am, edited 2 times in total.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
I find it rather awkward to reuse bp when you also use it for the stack frame and you have other registers at your disposal (bx, si, di)
next, you neglect to preserve ES and your epilogue makes no sense as you write to BP before popping it again.
Based on that, I suspect that you have more programming issues. Time to start the debugger.
next, you neglect to preserve ES and your epilogue makes no sense as you write to BP before popping it again.
Based on that, I suspect that you have more programming issues. Time to start the debugger.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
[es:bx] can be used just as well as [es:bp], so I don't see the problem other than a assembler supposedly complaining about something it should be capable of doing.
The four effective addresses you can use in 16-bit mode are:
[bx]
[bp]
[si]
[di]
[bx+si]
[bx+di]
[bp+si]
[bp+di]
if one of those can't be used, you should get a better assembler.
The four effective addresses you can use in 16-bit mode are:
[bx]
[bp]
[si]
[di]
[bx+si]
[bx+di]
[bp+si]
[bp+di]
if one of those can't be used, you should get a better assembler.
Your prolog and epilog are wrong. In Intel syntax use and and avoid using bp to store stuff other than the stack frame pointer. Actually, as you don't seem to need to use local variables, I don't see the need of the line at all (you can just use a register to store [posn], e.g. bx as already stated), and if not using local variables or arguments, you shouldn't need to set up a stack frame at all.
Regards,
John.
Code: Select all
push bp
mov bp, sp
Code: Select all
pop bp
ret
Code: Select all
sub sp, 4
Regards,
John.
I was able to make it use bx this time and it worked. However, nasm will often assemble mov bx, [posn] fine and all of the sudden decide it's illegal. The prologue and epilogue were obtained from Wikipedia, but something didn't seem right, and now I see why. I have pretty much rewritten the whole thing several times and tried many different things but it keeps displaying the first character twice.
Right now, putc is as follows:
I pass the character in al and the offset on the screen in cx. After each call I need to move the offset, but when I increment twice, the characters have spaces between them, and when I increment once, the characters are wrong and of different colors (because I'm writing attributes and characters in the wrong bytes); however, the double printing stopped in this routine.
I've tried
but this still double prints.
I became frustrated with C and assembly together recently, but should I use C again (I was hoping for an all-assembly kernel)? I've seen this implemented easily in C, but it stopped working for me. Meanwhile, the assembly version still is double printing or impractical.
Right now, putc is as follows:
Code: Select all
; al holds the character, cx holds offset
putc: push bp ; prolouge
mov sp, bp
sub sp, 2
push bx
push es
mov bx, 0xB800 ; video segment
mov es, bx
mov bx, cx ; where on the screen
mov byte [es:bx], al ; save character
mov byte [es:bx+1], 0xE0 ; save attribute (black on yellow)
add cx, 2
pop es
pop bx
mov bp, sp ; epilouge
pop bp
ret
I've tried
Code: Select all
mov bx, 0xB800
mov es, bx
mov bx, [posn]
mov byte [es:bx], al
mov byte [es:bx+1], al
add [posn], 2
but this still double prints.
I became frustrated with C and assembly together recently, but should I use C again (I was hoping for an all-assembly kernel)? I've seen this implemented easily in C, but it stopped working for me. Meanwhile, the assembly version still is double printing or impractical.
So despite what I said, you're still setting up your stack pointer wrong. And I ask again, why do you need a stack frame in this function? Also, C calling convention states that you don't need to preserve bx, so unless you're using a different one, you don't need to push/pop it.
Something like
should work, but considering you're still getting errors with a simple version of your putc, maybe the problem is in your string printing function instead?
Regards,
John.
Something like
Code: Select all
; character in al, offset ((x + y * 80) * 2) in cx, which is incremented by function
push es
push di
mov bx, 0xb800
mov es, bx
mov di, cx
mov ah, 0xe0 ; attribute
mov word [es:di], ax
add cx, 2
pop di
pop es
ret
Regards,
John.
The code was before I read the post because I made it earlier today.
I think that the problem may have been in the wrong prologue/epilogue from Wikipedia and my confusion between bp and si/di (I have been studing RISC processors lately, and that probably led to my confusion, or something...)
worked. Thank you.Code: Select all
; character in al, offset ((x + y * 80) * 2) in cx, which is incremented by function push es push di mov bx, 0xb800 mov es, bx mov di, cx mov ah, 0xe0 ; attribute mov word [es:di], ax add cx, 2 pop di pop es ret
I think that the problem may have been in the wrong prologue/epilogue from Wikipedia and my confusion between bp and si/di (I have been studing RISC processors lately, and that probably led to my confusion, or something...)