News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Double buffer in mode 13H

Started by popcalent, February 07, 2024, 04:46:28 PM

Previous topic - Next topic

_japheth

Quote from: popcalent on February 13, 2024, 04:52:25 AMHowever, the program is now not responding to any key presses. It's basically stalled.

Ok, and now? Do you expect us to make wild guesses of what the error may be, based on your "description" of what you've done and what's going wrong? Post the current source!
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

NoCforMe

Um, not quite. You're over-complicating things.

When your program gets called at your INT 15h entry point, that means that there's already a keystroke available. The key code is already in AL. So don't call CHECK_KEYBOARD; just use that key code to decide what to do by setting a flag (in your data segment) that can be checked by other parts of your program.

Interrupt service routines (ISRs) need to be lean and mean. The less you do there the better. No CALLs, just setting flags and then getting the hell out of there.

To set a flag in your data segment you'll need to do something like this:
PUSH ES
MOV AX, <your data segment>
MOV ES, AX
MOV ES:YourFlag, SomeValue
POP ES
but you probably already know that.
Assembly language programming should be fun. That's why I do it.

popcalent

Quote from: NoCforMe on February 13, 2024, 08:06:49 AMUm, not quite. You're over-complicating things.

When your program gets called at your INT 15h entry point, that means that there's already a keystroke available. The key code is already in AL. So don't call CHECK_KEYBOARD; just use that key code to decide what to do by setting a flag (in your data segment) that can be checked by other parts of your program.

That's why I deleted the IN AL, 60h line from CHECK_KEYBOARD. But it's possible that when I call the procedure AL is overwritten by something else (though it shouldn't...)

Quote from: NoCforMe on February 13, 2024, 08:06:49 AMInterrupt service routines (ISRs) need to be lean and mean. The less you do there the better. No CALLs, just setting flags and then getting the hell out of there.

To set a flag in your data segment you'll need to do something like this:
PUSH ES
MOV AX, <your data segment>
MOV ES, AX
MOV ES:YourFlag, SomeValue
POP ES
but you probably already know that.

Alright. I'll just set the flag to whatever AL is, leave the handler, then call CHECK_KEYBOARD from inside the main loop, and check the flag there. Thanks!

NoCforMe

Then change the ISR code to:
PUSH ES
MOV DX, <your data segment>
MOV ES, DX
MOV ES:Keystroke, AL
POP ES
or whatever (so you don't overwrite the keycode in AL).
Assembly language programming should be fun. That's why I do it.

popcalent

I can't get it to work. The program crashes, so I'm pasting the whole thing. I just made it shorter by handling only the ESC key. The program installs the handler, then enters the loop. When a key is pressed, the handler puts AL in PRESSED_KEY. The main loop is constantly checking for PRESSED_KEY. If it's ESC, then it calls EXIT_TO_DOS, if not it loops.

;#################################################
;#
;# PRESS_ESC.ASM
;#
;#################################################

;#################################################
;# DEFINITIONS
;#################################################
        KB_ESC          EQU     1h
;#################################################
;# STACK
;#################################################
STACKSG SEGMENT STACK
STACKSG ENDS
;#################################################
;# DATA
;#################################################
DATASG SEGMENT PARA 'Data'
OLD_INT15_VECTOR LABEL DWORD
OLD_INT15_OFF DW ?
OLD_INT15_SEG DW ?

PRESSED_KEY DB 0
DATASG ENDS

;#################################################
;# PROGRAM STARTS HERE
;#################################################
CODESG SEGMENT PARA 'Code'
PRESS_ESC PROC FAR
ASSUME CS:CODESG, DS:DATASG
MOV AX, DATASG
MOV DS, AX
;CALL SET_INT15H
;####################################
MAIN_LOOP:

MOV AL, PRESSED_KEY
CMP AL, KB_ESC
JNE CALL_EXIT_TO_MSDOS
MOV PRESSED_KEY, 0 ;For future use
JMP MAIN_LOOP

CALL_EXIT_TO_MSDOS:
CALL EXIT_TO_MSDOS

JMP MAIN_LOOP ;For future use
PRESS_ESC ENDP

;#################################################
;# SUBROUTINE: EXIT_TO_MSDOS
;#################################################
EXIT_TO_MSDOS PROC NEAR
FLUSH_KEYBOARD:
MOV  AH, 0 ;READ FROM KB BUFFER
INT  16H
CMP AH, KB_ESC
JNE FLUSH_KEYBOARD
POP AX
MOV AH, 4CH ; GO TO MSDOS
        INT 21H
RET
EXIT_TO_MSDOS ENDP



;#################################################
;# SUBROUTINE: SET_INT15H
;#################################################
SET_INT15H PROC NEAR
PUSH ES ;Save segment reg.
XOR AX, AX ;Point to "zero segment".
MOV ES, AX
MOV BX, 15h * 4 ;Address of INT 15h vector.
MOV AX, ES:[BX] ;Get offset.
MOV OLD_INT15_OFF, AX
MOV AX, ES:[BX + 2] ;Get segment.
MOV OLD_INT15_SEG, AX

CLI ;Disable interrupts.
MOV AX, OFFSET INT_15H_HANDLER
MOV ES:[BX], AX ;Plug in new offset.
MOV AX, CS ;Get our code segment.
MOV ES:[BX + 2], AX ;Plug it in.
STI ;Re-enable interrupts.
POP ES ;Restore segment reg.
RET
SET_INT15H ENDP

;#################################################
;# INTERRUPT HANDLER
;#################################################
INT_15H_HANDLER PROC NEAR
CMP AH, 4Fh
JNE EXIT_HANDLER

PUSH ES
MOV DX, DATASG
MOV ES, DX
MOV ES:PRESSED_KEY, AL
POP ES

EXIT_HANDLER:
JMP FAR PTR OLD_INT15_VECTOR
RET
INT_15H_HANDLER ENDP

CODESG ENDS
END PRESS_ESC


I'm sure the mistake is something stupid. I'm not an expert, nor do I claim to be one.

sinsi

    ;CALL SET_INT15HShould that be commented out? Your handler won't be called as it is  :sad:
Windows 11 is better :tongue: but 98SE was the bee's knees

popcalent

Quote from: sinsi on February 13, 2024, 12:19:32 PM    ;CALL SET_INT15HShould that be commented out? Your handler won't be called as it is  :sad:

Sorry, it's NOT commented. I commented it later to pinpoint the error, and forgot to uncomment it when I pasted the code here.

NoCforMe

Thanks for posting complete code.

You should save and restore DX (PUSH/POP) in your interrupt handler. My bad, should have put that in the code. That's not what's causing the crash, though, because as sinsi pointed out the interrupt handler never gets installed.

What's with the POP AX in the EXIT_TO_MSDOS routine? Seems like that would unbalance the stack.
Assembly language programming should be fun. That's why I do it.

popcalent

Quote from: NoCforMe on February 13, 2024, 12:44:04 PMThanks for posting complete code.

You should save and restore DX (PUSH/POP) in your interrupt handler. My bad, should have put that in the code. That's not what's causing the crash, though, because as sinsi pointed out the interrupt handler never gets installed.

What's with the POP AX in the EXIT_TO_MSDOS routine? Seems like that would unbalance the stack.

1) OK. Added PUSH/POP DX.
2) The extra POP AX shouldn't be there. I was doing something before with AX and I PUSH/POP'd it. However, I changed my mind, and forgot to delete that line. It's gone now.
3) The interrupt handler is installed. I commented the line to try to pinpoint the error, but then I forgot to uncomment it when I pasted the code here.

NoCforMe

Aargh, just thought of a problem: in giving you instructions on how to redirect an interrupt vector to your program, which does work, I neglected to think about the consequences of this. And here I'm not sure what they are.

The problem is that if you redirect that vector to your program, everything's OK while your program is running. But when it exits, that interrupt vector still points to the place where your ISR used to be, but is probably now occupied by something else. Probably garbage. Which may be why it's crashing, if it crashes upon exit.

I'm not sure what the protocol here is, exactly. Probably the right thing to do would be to restore the INT 15h interrupt vector to its original value before you exit.

Maybe someone with more DOS expertise can give a better answer here.

Restoring the vector is just the opposite of re-vectoring:
CLI
XOR AX, AX ;Point to "zero segment".
MOV ES, AX
MOV AX, OLD_INT15_OFF
MOV BX, 15h * 4 ;Address of INT 15h vector.
MOV ES:[BX], AX ;Set vector offset.
MOV AX, OLD_INT15_SEG
MOV ES:[BX+2], AX ;Set vector segment.
STI
Assembly language programming should be fun. That's why I do it.

popcalent

Quote from: NoCforMe on February 13, 2024, 12:55:20 PMAargh, just thought of a problem: in giving you instructions on how to redirect an interrupt vector to your program, which does work, I neglected to think about the consequences of this. And here I'm not sure what they are.

It doesn't crash now after I fixed the issues you brought up. Anyway, when it crashed, it did when executing the program. Now it just stalls. So there's never a chance for the program to exit and return to MSDOS (and crash because the original interrupt vector is not restored).

sinsi

To see if your interrupt handler is actually called, try writing a few pixels to the screen
screen_address dw 0

;in your int handler
    push bx
    push es
    mov  ax,0a000h
    mov  es,ax
    mov  bx,cs:screen_address
    mov  byte ptr [es:bx],1
    add  cs:screen_address,1
    pop  es
    pop  bx
Be aware that when your handler returns, if you want the BIOS to continue processing the keystroke, you have to set the carry flag. You probably don't want to or else the key buffer will overflow and beep at you.
Windows 11 is better :tongue: but 98SE was the bee's knees

popcalent

Quote from: sinsi on February 13, 2024, 01:19:11 PMTo see if your interrupt handler is actually called, try writing a few pixels to the screen
screen_address dw 0

;in your int handler
    push bx
    push es
    mov  ax,0a000h
    mov  es,ax
    mov  bx,cs:screen_address
    mov  byte ptr [es:bx],1
    add  cs:screen_address,1
    pop  es
    pop  bx
Be aware that when your handler returns, if you want the BIOS to continue processing the keystroke, you have to set the carry flag. You probably don't want to or else the key buffer will overflow and beep at you.

I get a warning on this line:
mov  byte ptr [es:bx],1 Warning: ":" operator ignored
If I execute the program, there's no pixel. I also tried this code I had to print a pixel that 100% works, and it doesn't print a pixel:
 
        MOV     AH, 0Ch
        MOV     AL, COLOR
        MOV     CX, POS_X
        MOV     DX, POS_Y
        MOV     BH, 0   ;PAGE

NoCforMe

Assembly language programming should be fun. That's why I do it.

popcalent

Quote from: NoCforMe on February 13, 2024, 03:04:08 PMES:[BX]
It assembles without warning now, but the linker gives two errors:
Fixup overflow at CODESG:0060, target = DATASG:0000 in module ESC.ASM
Fixup overflow at CODESG:0069, target = DATASG:0000 in module ESC.ASM


These are the lines in CODESG:
005D  2E: 8B 1E 0005r                mov  bx,cs:screen_address
0062  26: C6 07 01                   mov  byte ptr es:[bx],1
0066  2E: 83 06 0005r 01             add  cs:screen_address,1

And this is DATASG:
0000                         DATASG SEGMENT PARA 'Data'
0000                                 OLD_INT15_VECTOR        LABEL DWORD
0000  ????                           OLD_INT15_OFF           DW ?
0002  ????                           OLD_INT15_SEG           DW ?
0004  00                             PRESSED_KEY     DB 0
0005  0000                           screen_address  dw 0
0007                                 DATASG ENDS


The program executes, but there's no pixel. I tried something simpler. Right after returning from CALL SET_INT15H, I print a character with this:
MOV     AH, 09H
MOV     BL, 7           ;COLOR
MOV     CX, 1           ;NUMBER OF CHARS
MOV     AL, 'A'         ;CHARACTER
INT     10H
Then inside the handler I repeat the same code but with character 'B'. The program prints an A, but not a B. So it definitely doesn't execute the handler.