The MASM Forum

Miscellaneous => 16 bit DOS Programming => Topic started by: popcalent on February 07, 2024, 04:46:28 PM

Title: Double buffer in mode 13H
Post by: popcalent on February 07, 2024, 04:46:28 PM
Hello, all!

I'm programming in assembly for MSDOS, using TASM, and I'm trying to write a couple of subroutines that put a pixel in a buffer, and then dump the buffer on video memory. But it's not working. I can put pixel directly on video memory, but this is very slow when I have to render a big sprite or a picture. If I use the buffer, no pixel shows up on screen.

I declared the buffer after the data segment and before the code segment like this:
SBUFFER SEGMENT
  SCREEN_BUFFER DB 64000 DUP(?)
  SBUFFER ENDS

This is the subroutine that puts a pixel in the buffer:
;########################################################
;# SUBROUTINE: PUT_PIXEL_IN_BUFFER
;#
;# EXAMPLE: YELLOW PIXEL AT X=40, Y=20
;#
;#      MOV    MODE13H_X, 40
;#      MOV    MODE13H_Y, 20
;#      MOV    MODE13H_COLOR, 12
;#
;########################################################
PUT_PIXEL_IN_BUFFER PROC NEAR     
        PUSH    AX
        PUSH    BX
        PUSH    ES
        PUSH    DI
        MOV    AX, SEG SBUFFER
        MOV    ES, AX
        ASSUME  ES:SBUFFER
        MOV    DI, OFFSET SCREEN_BUFFER
        MOV    AX, MODE13H_Y
        MOV    BX, MODE13H_Y
        SHL    AX, 8
        SHL    BX, 6
        ADD    AX, BX
        ADD    AX, MODE13H_X
        ADD    DI, AX
        MOV    AL, MODE13H_COLOR
        MOV    ES:[DI], AL
        POP    DI
        POP    ES
        POP    BX
        POP    AX
        RET
        PUT_PIXEL_IN_BUFFER ENDP

Next is the subroutine that dumps the buffer on video memory:

;########################################################
;# SUBROUTINE: DUMP_VIDEO_BUFFER
;########################################################
DUMP_VIDEO_BUFFER PROC NEAR
        PUSH    AX
        PUSH    BX
        PUSH    DS
        PUSH    ES
        PUSH    SI
        PUSH    DI
        ;=============
        MOV    AX, 0A000H
        MOV    DS, AX
        MOV    AX, SEG SBUFFER
        MOV    ES, AX
        ASSUME  ES:SBUFFER
        MOV    DI, OFFSET SCREEN_BUFFER
        MOV    SI, 0
        ADD    DI, 9600
        ADD    SI, 9600
        ;=============
        DUMP_VIDEO_BUFFER_:
                MOV    AL, ES:[DI]
                CMP    AL, 16
                JE      NO_DUMP
                MOV    DS:[SI], AL
        NO_DUMP:
                INC    SI
                INC    DI
                CMP    SI, 0FA00H
                JL      DUMP_VIDEO_BUFFER_
        ;=============
        POP    DI
        POP    SI
        POP    ES
        POP    DS
        POP    BX
        POP    AX
        RET
        DUMP_VIDEO_BUFFER ENDP

Finally, I have a subroutine that waits for the end of a CRT trace:

;#################################################
;# WAIT FOR NEW VERTICAL RETRACE
;#################################################
WAIT_VR PROC    NEAR
        PUSH    AX
        PUSH    DX
        MOV    DX, 3DAH
        ;WAIT FOR BIT 3 TO BE ZERO
        WAIT_END_OLDVR:
                IN      AL, DX
                TEST    AL, 08H
                JNZ    WAIT_END_OLDVR
        ;WAIT FOR BIT 3 TO BE ONE
        WAIT_BEGIN_NEWVR:
                IN      AL, DX
                TEST    AL, 08H
                JZ      WAIT_BEGIN_NEWVR
        POP    DX
        POP    AX
        RET
        WAIT_VR ENDP

And this is how I call the subroutines:

        MOV    MODE13H_X, 40
        MOV    MODE13H_Y, 20
        MOV    MODE13H_COLOR, 12
        CALL    PUT_PIXEL_IN_VIDEO_BUFFER
        CALL    WAIT_VR
        CALL    DUMP_VIDEO_BUFFER

The result is that nothing shows up on screen. I've been troubleshooting this for hours now, and can't find what I'm doing wrong. I'm hoping a good samaritan will spot the mistake in my code... Thanks a lot!
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 07, 2024, 06:36:11 PM
Heh; just so happens I was looking at some of my old DOS code when I came across this. Ah, the goodbad old days of writing directly to screen memory ...

Your code looks fine. That is, until I did some arithmetic on your values. First of all, I'm wondering why you're offsetting your reads and writes to memory (both source and dest) by 9600. Also mystified by your left-shifting the same y-value by 8 and 6 and then adding them. I can only assume you know what you're doing there.

Taking your x- and y-values:

20 << 8 = 5120
20 << 6 = 1280
            40 +
---------------
result:   6440


That might explain it; you're skipping right over the data you wrote into your buffer by starting at offset 9600.

I can't see anything else that looks wrong. Not sure about your vertical-retrace code; have you tried it without that? I assume that's to avoid flicker, yes?
Title: Re: Double buffer in mode 13H
Post by: _japheth on February 07, 2024, 07:35:33 PM
One thing that strikes pretty instantly is this piece of code:

        NO_DUMP:
                INC    SI
                INC    DI
                CMP    SI, 0FA00H
                JL      DUMP_VIDEO_BUFFER_

JL is used for signed compares - and 0FA00h in 16 bit is a negative number. So your little loop will probably end just after one byte has been transfered. Better is to use JB.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 07, 2024, 08:03:21 PM
Quote from: NoCforMe on February 07, 2024, 06:36:11 PMFirst of all, I'm wondering why you're offsetting your reads and writes to memory (both source and dest) by 9600. Also mystified by your left-shifting the same y-value by 8 and 6 and then adding them. I can only assume you know what you're doing there.
I wrote this code 20 years ago, and perhaps it worked with other macros or code I don't have any more... I don't remember what the 9600 is... The left shifting is just to multiply the y coordinate times 320.

Quote from: _japheth on February 07, 2024, 07:35:33 PMOne thing that strikes pretty instantly is this piece of code:
JL is used for signed compares - and 0FA00h in 16 bit is a negative number. So your little loop will probably end just after one byte has been transfered. Better is to use JB.
Corrected. Thanks.

Ok. So I took away the two instructions that added 9600 to both SI and DI and it works now. It's still slow. It takes a little less than a second to dump the buffer (plus the time prior to that to put the sprites in the buffer). It's better than before, because it dumped every individual sprite one after the other, and now it does the whole screen at once. But still slow...

I'm blown away by very old msdos games that, when you moved to the next room, the screen changed instantly...
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 08, 2024, 04:51:32 AM
I fixed the code using the instruction MOVS, instead of moving pixel by pixel. In the original code, the CMP AL, 16 was meant to skip black pixels, but I got rid of it. I also disabled interrupts while the buffer is being copied. This is the new code (I skipped the pushes and the pops):
        CLI
        MOV     AX, 0A000H
        MOV     ES, AX                 ; Set ES to the video memory segment
        MOV     AX, SEG SBUFFER
        MOV     DS, AX                 ; Set DS to the segment of SBUFFER
        MOV     DI, OFFSET SCREEN_BUFFER  ; Set DI to the destination address in video memory
        MOV     SI, 0                  ; Set SI to the source index

        DUMP_VIDEO_BUFFER_:
                MOVS    BYTE PTR ES:[DI], BYTE PTR DS:[SI]  ;This also increments SI and DI
                CMP     SI, 0FA00H
                JB      DUMP_VIDEO_BUFFER_

        STI

Now there's no lag or flickering. The only problem is that my subroutine to put a sprite in the buffer, goes pixel by pixel, so there's a wait before I can see the image while the sprite is being transferred to the buffer.

This is the code of the subroutine that puts a sprite in the buffer:

;#################################################
;# SUBROUTINE: PRINT_256SPRITE
;#
;# EXAMPLE:
;#      MOV     MODE13H_DELETE, 1 (TO DELETE)
;#      MOV     MODE13H_X, 100
;#      MOV     MODE13H_Y, 105
;#      LEA     DI, SPRITE_ADDRESS
;#      CALL    WAIT_VR
;#      CALL    PRINT_256SPRITE
;#################################################
PRINT_256SPRITE PROC NEAR
        PUSH    AX
        PUSH    BX
        PUSH    CX
        PUSH    DX
        PUSH    DI

        MOV     CX, MODE13H_X  ;XCOORD
        MOV     DX, CX
        MOV     BX, MODE13H_Y ;YCOORD

        PRINT_256SPRITE_LOOP:
                CMP     BYTE PTR [DI],'$'       ;CHECK END OF SPRITE
                JE      PRINT_256SPRITE_END

                MOV     AL, BYTE PTR [DI]       ;GET PIXEL COLOR
                MOV     MODE13H_COLOR, AL
                CALL    PUT_PIXEL_IN_BUFFER     ;PUT PIXEL IN BUFFER

                INC     DI
                INC     MODE13H_X
                CMP     BYTE PTR [DI], '#'      ;CHECK END OF LINE
                JNE     PRINT_256SPRITE_LOOP
                MOV     MODE13H_X, CX           ;GO TO NEXT LINE
                INC     MODE13H_Y
                INC     DI
                JMP     PRINT_256SPRITE_LOOP

        PRINT_256SPRITE_END:
                MOV     MODE13H_DELETE, 0
                POP     DI
                POP     DX
                POP     CX
                POP     BX
                POP     AX
        RET
PRINT_256SPRITE ENDP

Perhaps there's a better way than transferring pixel by pixel, but then I'd have to make sure that the dimensions of the sprite are multiples of 8 of something. The only problem is that I won't be able to handle transparent pixels.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 08, 2024, 05:43:03 AM
You can do better than that for your byte-moving code. Take advantage of the x86's more advanced instructions. No need for a comparison to end the loop. In fact, no need for a loop at all:
    MOV    AX, 0A000h
    MOV    ES, EX
    MOV    AX, SEG Sbuffer
    MOV    DS, AX
    XOR    SI, SI
    MOV    DI, OFFSET SCREEN_BUFFER
    MOV    CX, <# of bytes to move>

; This moves DS:SI--> ES:DI, increments SI & DI, does it CX times:
    REP    MOVSB
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 08, 2024, 01:50:32 PM
Another opportunity for a speed-up: in your PRINT_256SPRITE routine, instead of calling PUT_PIXEL_IN_BUFFER for every single pixel, put that code in-line in your loop (PRINT_256SPRITE_LOOP), but minus the setup. Do your setup (setting your buffer pointer and the segment register) outside of the loop. Since you're using DI as your sprite-data pointer, use either SI or BX as a buffer pointer. That should reduce a lot of overhead, at the expense of slightly more code.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 09, 2024, 01:24:06 AM
Quote from: NoCforMe on February 08, 2024, 05:43:03 AMYou can do better than that for your byte-moving code. Take advantage of the x86's more advanced instructions. No need for a comparison to end the loop. In fact, no need for a loop at all:
    MOV    AX, 0A000h
    MOV    ES, EX
    MOV    AX, SEG Sbuffer
    MOV    DS, AX
    XOR    SI, SI
    MOV    DI, OFFSET SCREEN_BUFFER
    MOV    CX, <# of bytes to move>

; This moves DS:SI--> ES:DI, increments SI & DI, does it CX times:
    REP    MOVSB

I would fill CX with 320*200/4 and change MOVSB to MOVSD, moving aligned DWORDs will speed things up.
No need to wait for a vertical refresh if you're writing to the buffer, only when writing to the screen.
Also, as it is your code doesn't check for a sprite that is partly off the screen, is this deliberate?
Title: Re: Double buffer in mode 13H
Post by: jj2007 on February 09, 2024, 02:57:19 AM
Does movsd work in 16-bit code?
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 09, 2024, 03:15:17 AM
Quote from: jj2007 on February 09, 2024, 02:57:19 AMDoes movsd work in 16-bit code?
Yes, but I think it uses the operand size override 66H
It also requires a 386 or better :biggrin:
Title: Re: Double buffer in mode 13H
Post by: daydreamer on February 09, 2024, 04:53:02 AM
Hi popcalent
example of scrolling in mode 13h
and keyboard control and rossler with help of fpu
https://masm32.com/board/index.php?topic=9319.30 (https://masm32.com/board/index.php?topic=9319.30)
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 09, 2024, 08:09:56 AM
Thank you all for your replies. MOVSB won't work unless using 386 assembly, which I'm not.


Quote from: daydreamer on February 09, 2024, 04:53:02 AMHi popcalent
example of scrolling in mode 13h
and keyboard control and rossler with help of fpu
https://masm32.com/board/index.php?topic=9319.30 (https://masm32.com/board/index.php?topic=9319.30)

Thanks! Is this also taking care of the keyboard lag when using arrow keys to move a sprite?
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 09, 2024, 09:49:03 AM
Quote from: popcalent on February 09, 2024, 08:09:56 AMMOVSB won't work unless using 386 assembly, which I'm not.
Not true! Where did you get that idea? I've been using that (including REP MOVSB) since 8088 days.

What assembler are you using?
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 09, 2024, 10:17:53 AM
Quote from: NoCforMe on February 09, 2024, 09:49:03 AM
Quote from: popcalent on February 09, 2024, 08:09:56 AMMOVSB won't work unless using 386 assembly, which I'm not.
Not true! Where did you get that idea? I've been using that (including REP MOVSB) since 8088 days.

What assembler are you using?

I'm sorry. I meant MOVSD. If I'm not mistaken, MOVSB is the same as MOVS and forcing the parameters to be bytes, which is what I'm doing, correct? I can't remember if MOVSW works in < 386 assembly...
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 09, 2024, 10:40:34 AM
Both MOVSB and MOVSW will work with any x86 assembler; those have been in the instruction set since day 1.

You're correct, MOVSD requires 386 or better. But I don't think you need to mess with that in order to see a significant speed increase of your code. Why don't you try my suggestion and see what it does for you? (Including inlining your PUT_PIXEL_IN_BUFFER code.)

If you're moving an even number of bytes, then by all means use MOVSW instead of MOVSB. Take every advantage.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 09, 2024, 11:31:44 AM
Quote from: NoCforMe on February 09, 2024, 10:40:34 AMWhy don't you try my suggestion and see what it does for you? (Including inlining your PUT_PIXEL_IN_BUFFER code.)
Will do when I have a chance. Probably later tonight.

Quote from: NoCforMe on February 09, 2024, 10:40:34 AMIf you're moving an even number of bytes, then by all means use MOVSW instead of MOVSB. Take every advantage.
I'm copying a buffer the size of the 320x200 screen, so yes, it's an even number of bytes.
Title: Re: Double buffer in mode 13H
Post by: daydreamer on February 10, 2024, 04:08:26 AM
Quote from: popcalent on February 09, 2024, 08:09:56 AMThanks! Is this also taking care of the keyboard lag when using arrow keys to move a sprite?
no idea,I am spoiled by running Dosbox emulation on 3+ghz cpu,guess its capable of ca pentium cpu speed
depending on what kind of game you can make,simple physics engine ala moonlander and asteroids kind of control ,it always
add x,xspeed
add y,yspeed each frame
keyboard controls indirectly accelerate/decelerate xspeed and yspeed 
if you have fpu,might be faster to copy with fld/fstp 10 bytes at a time

Title: Re: Double buffer in mode 13H
Post by: popcalent on February 10, 2024, 07:26:26 AM
Quote from: daydreamer on February 10, 2024, 04:08:26 AMno idea,I am spoiled by running Dosbox emulation on 3+ghz cpu

I'm talking about the lag that happens after you press a key on any computer, even a modern one. For instance, you press 'a' on your favorite word processor and don't release, then just one 'a' appears on screen, and after a second of doing nothing the screen starts getting filled with a's until you release.

On a game, you would press an arrow key, the sprite would take a step towards that direction, stop for a second, then start walking. Of course, professional games don't do that, you press a key and the sprite starts walking without pausing for a second after the first step.
Title: Re: Double buffer in mode 13H
Post by: daydreamer on February 10, 2024, 07:42:14 AM
Quote from: popcalent on February 10, 2024, 07:26:26 AM
Quote from: daydreamer on February 10, 2024, 04:08:26 AMno idea,I am spoiled by running Dosbox emulation on 3+ghz cpu

I'm talking about the lag that happens after you press a key on any computer, even a modern one. For instance, you press 'a' on your favorite word processor and don't release, then just one 'a' appears on screen, and after a second of doing nothing the screen starts getting filled with a's until you release.

On a game, you would press an arrow key, the sprite would take a step towards that direction, stop for a second, then start walking. Of course, professional games don't do that, you press a key and the sprite starts walking without pausing for a second after the first step.
I use lowlevel in al,60h keyboard port and combine it with simple physics
in al,60h
         
          .IF al== VK_LEFT
            mov middx,-3
            mov middy,0
            .ENDIF
           .IF al==VK_RIGHT
            mov middx,3
            mov middy,0
            .ENDIF
            .IF al==VK_UP
            mov middy,-640
            mov middx,0
            .ENDIF
            .IF al==VK_DOWN
            mov middy,640
            mov middx,0
            .ENDIF
            cmp al,1
cmp al,1 =escape key =exit game loop
I have written a VK_codes.asm files with equates compatible with windows keycodes to make it easier to port code between windows and DOS 16 bit
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 10, 2024, 07:43:27 AM
So how are you accessing the keyboard in your program?

If you're using the DOS INT 21h interface, well, there's your problem; that's a high-level interface with lots of latency (time delay).

Your other choices are:
None of these are very hard to implement.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 10, 2024, 08:08:02 AM
Have a 128-byte table for keys and hook int 15h, AH=4Fh. The INT 9 code reads the scan code from port 60h and calls this function. Set or clear the byte in the array depending on whether it's a make code, then your main loop can just check the array for keys down. Easier then hooking INT 9 and having to worry about LEDs and weird key codes (like SysRq).
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 10, 2024, 08:37:05 AM
Are you sure about INT 15h? My book shows that as the cassette interface.
It's been a long time since I used any of that stuff, so I may be missing something here. Is AH=4FH a keyboard function?

I was thinking that hooking INT 9 would only involve peeking at the keycode, setting flags for certain keys only, then simply passing the request on to the real INT 9 handler. Basically a passive handler.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 10, 2024, 08:45:00 AM
INT 15h, AH=4Fh (https://fragglet.github.io/dos-help-files/alang.hlp/x_at_L8123.html)
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 10, 2024, 08:55:04 AM
Ah, nice clean interface there. That sounds like the way to go.

Invoked for each keystroke by the ROM BIOS's Int 09h keyboard
interrupt handler.
 
Input              Output
 
AH = 4Fh           If scan code consumed
AL = Scan code        Carry flag: clear
 
                   If scan code not consumed
                      Carry flag: set
                      AL = Unchanged or new scan code
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 12, 2024, 09:06:00 AM
Quote from: daydreamer on February 10, 2024, 07:42:14 AMI use lowlevel in al,60h keyboard port and combine it with simple physics
in al,60h

There's still a problem with IN AL, 60h. If the user goes too fast with his fingers (which is pretty common) and presses another arrow key before releasing the current one, the sprite stalls.

I'm thinking about implementing a two-level stack. When the user presses an arrow key, the program pushes a corresponding value into the stack (for instance, UP=1, DOWN=2, etc) , when the user releases an arrow key, the program pops the corresponding value out of the stack.

An example sequence would be this:

1) User doesn't push any arrow key. Stack is 00. Sprite doesn't move.
2) User pushes right arrow key. Stack is 0R. Sprite moves to the right.
3) User pushes left arrow key without releasing right arrow key. Stack is RL. Sprite moves to the left.
4) User releases right arrow key without releasing left arrow key. Stack is 0L. Sprite still moves to the left.

An alternative to step 4:
4) User releases left arrow key without releasing right arrow key. Stack is 0R. Sprite moves to the right.

I've done this with C before, and it works "most" of the time. Sometimes, you release the keys, and the sprite is stuck walking. I guess it happens when you release the key while the code is doing something else and can't register the release. I guess the solution is to do it through an interrupt. But perhaps it's not necessary in ASM since it's faster than C.



Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 12, 2024, 09:14:51 AM
Try hooking INT 15h as sinsi suggested; that might take care of that latency problem.

This code should do the hooking part:
OldINT15Vector LABEL DWORD
  OldINT15off DW ?
  OldINT15seg DW ?

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 OldINT15off, AX
MOV AX, ES:[BX + 2] ;Get segment.
MOV OldINT15seg, AX

CLI ;Disable interrupts.
MOV AX, OFFSET INT15handler
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.
. . .

INT15Handler PROC

CMP AH, 4Fh ;We're only interested in this subfunction.
JNE passon

; Set your flags or whatever here;
; key scan code is in AL.

passon: JMP FAR PTR OldINT15Vector ;Go to the original INT 15 handler.

INT15handler ENDP
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 12, 2024, 11:51:00 AM
QuoteThere's still a problem with IN AL, 60h. If the user goes too fast with his fingers (which is pretty common) and presses another arrow key before releasing the current one, the sprite stalls.
Are the arrow keys being used as a toggle? For example, when you press the right arrow key the sprite moves right, what happens when the key is released? Does the sprite keep moving or stop?
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 12, 2024, 05:20:34 PM
Quote from: sinsi on February 12, 2024, 11:51:00 AM
QuoteThere's still a problem with IN AL, 60h. If the user goes too fast with his fingers (which is pretty common) and presses another arrow key before releasing the current one, the sprite stalls.
Are the arrow keys being used as a toggle? For example, when you press the right arrow key the sprite moves right, what happens when the key is released? Does the sprite keep moving or stop?
Not as a toggle, but as a momentary switch. When the key is released, the sprite is supposed to stop.


I implemented a two level stack to prevent this from happening. This is similar to the 128-key table you proposed. This solves the problem with the user switching keys too fast. The problem now is that, most of the time, the sprite stops when the key is released, but sometimes it keeps moving. I'm not handling this as an interrupt (that's my next step), but as a procedure. My guess is that there are times the user releases a key, but the program is doing something else and doesn't register the release.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 12, 2024, 06:56:08 PM
Welcome to the world of tricky timing.

I think hooking interrupts is the way to go for you. All the interrupt hook will do is set a flag or two; it'll be up to your other code to check these flags--frequently!--to figure out what state it should be in.

I have confidence that you'll figure this out.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 04:52:25 AM
Quote from: NoCforMe on February 12, 2024, 09:14:51 AMTry hooking INT 15h as sinsi suggested; that might take care of that latency problem.

This code should do the hooking part:

So, I had CALL CHECK_KEYBOARD first thing in the main loop. The first thing in this subroutine is IN AL, 60h. Then, depending on what was in AL, do this or that. And that worked most of the time except for the few cases where I released a key and the sprite got stuck moving.

I put the code to hook up the handler in a subroutine and I call the subroutine before the main loop. I basically copied your code. Then, in the interrupt handler, I copied your code and I added CALL CHECK_KEYBOARD between JNE passon and passon: JMP FAR PTR OldINT15Vector. Inside the CHECK_KEYBOARD subroutine I deleted the IN AL, 60h, and left everything else the same.

I made it this way because I didn't want to cut and paste large chunks of code to avoid possible mess-ups. However, the program is now not responding to any key presses. It's basically stalled.
Title: Re: Double buffer in mode 13H
Post by: _japheth on February 13, 2024, 06:40:15 AM
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!
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 08:06:49 AM
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.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 09:05:51 AM
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!
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 09:13:51 AM
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).
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 11:57:47 AM
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.
Title: Re: Double buffer in mode 13H
Post by: 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:
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 12:43:50 PM
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.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 12:44:04 PM
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.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 12:51:16 PM
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.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 12:55:20 PM
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
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 01:06:17 PM
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).
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 13, 2024, 01:19:11 PM
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.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 01:33:55 PM
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
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 03:04:08 PM
ES:[BX]
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 03:49:55 PM
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.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 13, 2024, 04:05:59 PM
I put screen_address in the code segment, for ease of access. You have it in the data segment.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 04:10:53 PM
Quote from: sinsi on February 13, 2024, 04:05:59 PMI put screen_address in the code segment, for ease of access. You have it in the data segment.
Oh! I didn't catch that. I corrected it and it still doesn't work. I tried also printing a character and it won't. It prints it after setting interrupt 15h, but not inside the handler.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 13, 2024, 04:23:46 PM
You probably need a stack, at the moment there is none
STACKSG SEGMENT STACK
  db 4096 dup (?)  ;make a 4KB stack
        STACKSG ENDS       
Move the other vars from data to code as well - the code is written so JMP FAR PTR OLD_INT15_VECTOR is probably using the wrong value in DS (which the BIOS is using)
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 06:00:41 PM
Quote from: sinsi on February 13, 2024, 04:23:46 PMYou probably need a stack, at the moment there is none

Move the other vars from data to code as well - the code is written so JMP FAR PTR OLD_INT15_VECTOR is probably using the wrong value in DS (which the BIOS is using)
All done. No difference  :nie:
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 13, 2024, 06:59:52 PM
Quote from: sinsi on February 13, 2024, 04:23:46 PMMove the other vars from data to code as well - the code is written so JMP FAR PTR OLD_INT15_VECTOR is probably using the wrong value in DS (which the BIOS is using)
Yes; so if it's in the code segment, then you'd use JMP FAR PTR CS:OLD_INT15_VECTOR, right?

Gawd, it's been a long time since I coded any of this stuff ...
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 13, 2024, 07:18:05 PM
One major problem I finally saw with your code - you don't actually set 320x200 mode 13h
Title: Re: Double buffer in mode 13H
Post by: _japheth on February 13, 2024, 07:41:12 PM
Quote from: NoCforMe on February 13, 2024, 06:59:52 PMYes; so if it's in the code segment, then you'd use JMP FAR PTR CS:OLD_INT15_VECTOR, right?

Gawd, it's been a long time since I coded any of this stuff ...

OMG - what a mess!

Do you guys know how a type cast is supposed to work?

I'm asking vecause that "FAR PTR" behind the JMP instruction actually makes label OLD_INT15_VECTOR a far code label which surely isn't what you want to achieve. OLD_INT15_VECTOR is a normal variable holding a FAR code address, that's something completely different.

The assembler (masm or tasm) is smart enough to know that a DWORD variable in 16-bit will hold a FAR address, so no type cast is needed, just code JMP CS:[OLD_INT15_VECTOR].
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 08:03:50 PM
Quote from: sinsi on February 13, 2024, 07:18:05 PMOne major problem I finally saw with your code - you don't actually set 320x200 mode 13h
I did after you suggested printing a pixel to check if the handler executed.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 13, 2024, 08:23:54 PM
Quote from: _japheth on February 13, 2024, 07:41:12 PM
Quote from: NoCforMe on February 13, 2024, 06:59:52 PMYes; so if it's in the code segment, then you'd use JMP FAR PTR CS:OLD_INT15_VECTOR, right?

Gawd, it's been a long time since I coded any of this stuff ...

OMG - what a mess!

Do you guys know how a type cast is supposed to work?

I'm asking vecause that "FAR PTR" behind the JMP instruction actually makes label OLD_INT15_VECTOR a far code label which surely isn't what you want to achieve. OLD_INT15_VECTOR is a normal variable holding a FAR code address, that's something completely different.

The assembler (masm or tasm) is smart enough to know that a DWORD variable in 16-bit will hold a FAR address, so no type cast is needed, just code JMP CS:[OLD_INT15_VECTOR].

That's the problem with looking at someone else's block of code. I rewrote it with FASM and it worked as it should have but haven't got around to converting it to MASM (or is it TASM?)
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 13, 2024, 08:28:58 PM
Quote from: sinsi on February 13, 2024, 08:23:54 PMThat's the problem with looking at someone else's block of code. I rewrote it with FASM and it worked as it should have but haven't got around to converting it to MASM (or is it TASM?)
TASM
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 14, 2024, 05:39:57 AM
It seems to work now with JMP CS:[OLD_INT15_VECTOR]. However, I guess I need to uninstall my handler after exiting the program because if I execute it a second time it crashes.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 14, 2024, 09:21:37 AM
Quote from: popcalent on February 14, 2024, 05:39:57 AMI guess I need to uninstall my handler after exiting the program because if I execute it a second time it crashes.
Yes, you definitely do. Otherwise that interrupt vector is pointing straight into garbage.

I hope you're not getting too frustated here; this is just beginning to be fun.
Title: Re: Double buffer in mode 13H
Post by: FORTRANS on February 15, 2024, 03:06:26 AM
Hi,

   I am not sure this is of interest, but I replied to a question
about interrupt handling a while back.

"Interrupt Hooking in MS DOS"

https://masm32.com/board/index.php?topic=9494.msg103835#msg103835 (https://masm32.com/board/index.php?topic=9494.msg103835#msg103835)

Regards,

Steve N.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 15, 2024, 11:32:30 AM
Some useful stuff in there for sure.

Hey, I'd forgotten about those DOS (INT 21h) functions to get and set an interrupt vector (35h & 25h respectively). That's a more kosher way to handle it, rather than my method of just "peeking and poking" down there in the "zero segment", which'll work, but better to do it through the OS.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 15, 2024, 05:09:54 PM
Thanks.

I'll be working on this later this week and get back to you guys.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 18, 2024, 06:27:50 AM
I created the following procedure to uninstall my handler. It seems to work, but I thought I would post it here for others to see/use. Also, it's possible the handler has errors that didn't manifest during my tests.
UNSET_INT15H PROC NEAR
        PUSH    DX
        PUSH    ES                      ;Save segment reg.
        XOR    AX, AX                  ;Point to "zero segment".
        MOV    ES, AX
        MOV    BX, 15h * 4            ;Address of INT 15h vector.

        CLI                            ;Disable interrupts.
        MOV    AX, OLD_INT15_OFF      ; Restore original offset.
        MOV    ES:[BX], AX
        MOV    AX, OLD_INT15_SEG      ; Restore original segment.
        MOV    ES:[BX + 2], AX
        STI                            ;Re-enable interrupts.

        POP    ES                      ;Restore segment reg.
        POP    DX
        RET
        UNSET_INT15H ENDP

Just for convenience, here's the SET_INT15H procedure again:
SET_INT15H PROC NEAR
        PUSH    DX
        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.
        POP    DX
        RET
        SET_INT15H ENDP

This is the handler code:
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    CS:[OLD_INT15_VECTOR]
        RET
        INT_15H_HANDLER ENDP

The handler puts the scan code of the pressed key in PRESSED_KEY, and then I take care of it in the main loop. The main loop sees the scan code and pushes a direction (R, L, U, D) into the buffer if the corresponding arrow key has been pressed, or pops a direction from the buffer if the corresponding arrow key has been released. The sprite moves in the direction stored in the first position of the buffer, or doesn't move if the buffer is empty.

Now, I know we've mentioned that the handler has to be as short as possible, but it occurred to me that if push/pop stuff into/out of the buffer outside the handler, it's possible that a key is pressed before the current key stroke is taken care of in the main loop. The only way to avoid this that I can think of is to manipulate the buffer inside the handler, but the code is pretty long for a handler... I'm not sure what to do. 
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 18, 2024, 12:57:52 PM
Quote from: NoCforMe on February 09, 2024, 10:40:34 AMWhy don't you try my suggestion and see what it does for you? (Including inlining your PUT_PIXEL_IN_BUFFER code.)

I finally got around fixing this issue. Here it is...
PRINT_256SPRITE PROC NEAR
        PUSH    AX
        PUSH    BX
        PUSH    CX
        PUSH    DX
        PUSH    ES
        PUSH    DI
        PUSH    DS
        PUSH    SI

        MOV     CX, MODE13H_X  ;XCOORD
        MOV     DX, CX
        MOV     BX, MODE13H_Y ;YCOORD

        ;PREPARE BUFFER AND BUFFER INDEX (SI)
        MOV     AX, SEG SBUFFER
        MOV     ES, AX
        ASSUME  ES:SBUFFER
        ;MOV     SI, OFFSET SCREEN_BUFFER

        ;FIND ABSOLUTE COORDINATE OF FIRST PIXEL
        MOV     AX, MODE13H_Y
        MOV     BX, MODE13H_Y
        SHL     AX, 8
        SHL     BX, 6
        ADD     AX, BX
        ADD     AX, MODE13H_X
        MOV     MODE13H_X, AX
        MOV     MODE13H_X2, AX          ;MODE13H_X2 HAS THE ORIGINAL ABSOLUTE COORD

        PRINT_256SPRITE_LOOP:
                CMP     BYTE PTR [DI],'$'       ;CHECK END OF SPRITE
                JE      PRINT_256SPRITE_END

                ;PREPARE BUFFER AND BUFFER INDEX (SI)
                ;MOV     AX, SEG SBUFFER
                ;MOV     ES, AX
                ;ASSUME  ES:SBUFFER
                MOV     SI, OFFSET SCREEN_BUFFER

                MOV     AX, MODE13H_X
                ADD     SI, AX
                MOV     AL, BYTE PTR [DI]       ;GET PIXEL COLOR
                MOV     ES:[SI], AL             ;PUT PIXEL COLOR

                INC     DI
                INC     MODE13H_X
                CMP     BYTE PTR [DI], '#'      ;CHECK END OF LINE
                JNE     PRINT_256SPRITE_LOOP

                MOV     AX, MODE13H_X2  ;RECOVER ABSOLUTE COORDINATE
                ADD     AX, 140h        ;ADD 320 TO GO TO NEXT LINE SAME X
                MOV     MODE13H_X2, AX  ;SAVE NEW ABSOLUTE COORDINATE IN X2
                MOV     MODE13H_X, AX   ;SAVE NEW ABSOLUTE COORDINATE IN X
                ;INC     MODE13H_Y
                INC     DI              ;SKIP '#'
                JMP     PRINT_256SPRITE_LOOP

        PRINT_256SPRITE_END:
                POP     SI
                POP     DS
                POP     DI
                POP     ES
                POP     DX
                POP     CX
                POP     BX
                POP     AX
        RET
PRINT_256SPRITE ENDP

I still need to figure out this part:

MOV     AL, BYTE PTR [DI]       ;GET PIXEL COLOR
MOV     ES:[SI], AL             ;PUT PIXEL COLOR

and see if I can do MOVSW because I'm not sure if all sprites have an even number of pixels per row.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 18, 2024, 01:19:23 PM
Quote from: popcalent on February 18, 2024, 12:57:52 PMI still need to figure out this part:

MOV    AL, BYTE PTR [DI]      ;GET PIXEL COLOR
MOV    ES:[SI], AL            ;PUT PIXEL COLOR

and see if I can do MOVSW because I'm not sure if all sprites have an even number of pixels per row.

You could switch SI and DI and use MOVSB, which will also increment both SI and DI. Should be a little bit faster.

You could use MOVSW to move most of your data, then if there's a leftover byte at the end use MOVSB. Would require a little more calculation code, but not much.

Something like this:
; Move words:
; Set up SI & DI.
MOV CX, <sprite data len>
SHR CX, 1 ;/2.
REP MOVSW

TEST <sprite data len>, 1 ;Even or odd?
JZ isEven ;  Even.

; Move extra byte @ end:
MOVSB

isEven: . . .
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 25, 2024, 05:54:17 PM
I haven't had a chance to post because I've been busy, but I'm still working on my keyboard interrupt program. At the moment, I made it so when I press a key, the handler checks if it's interrupt 4Fh, and if it is, it puts AL in PRESSED_KEY. It also prints AL plus 48, but this is for debugging and it will eventually go.

Then the main loop evaluates PRESSED_KEY, and if it's ESC, it exits the program, and, if it isn't, it loops. The problem is that sometimes I press ESC, the handler does but it's supposed to do, but the loop doesn't. I thought that this is because I press ESC after the loop evaluates PRESSED_KEY, then I release it before the loop evaluates PRESSED_KEY again. So the ESC scan code is never evaluated. However, I've tried pressing ESC and not releasing, and it takes a couple loops for the program to exit.

I thought that I could make it so if ESC is released, the handler does nothing, so the loop can evaluate ESC in the next loop. However, this doesn't solve anything... There are instances where I have to press ESC three or four times for the program to exit, and then the DOS prompt has three or four "\", each one resulting from each pressed ESC...

This is the code:

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

;#################################################
;# DEFINITIONS
;#################################################
        KB_ESC          EQU    01h
        REL_ESC          EQU    81h
;#################################################
;# STACK
;#################################################
STACKSG SEGMENT STACK
        db 4096 dup (?)  ;make a 4KB stack
        STACKSG ENDS
;#################################################
;# DATA
;#################################################
DATASG SEGMENT PARA 'Data'
        DATASG ENDS

;#################################################
;# PROGRAM STARTS HERE
;#################################################
CODESG SEGMENT PARA 'Code'
PRESS_ESC PROC FAR
        screen_address  dw 0
        OLD_INT15_VECTOR        LABEL DWORD
        OLD_INT15_OFF          DW ?
        OLD_INT15_SEG          DW ?
        PRESSED_KEY    DB 0
        ASSUME CS:CODESG, DS:DATASG
        MOV AX, DATASG
        MOV DS, AX
        CALL    MODE13H_SET
        CALL    SET_INT15H
        MAIN_LOOP:

                MOV    AL, PRESSED_KEY

                CMP    AL, KB_ESC
                JE      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
        CALL    MODE03H_SET
        CALL    UNSET_INT15H
        MOV AH, 4CH            ; GO TO MSDOS
        INT 21H
        RET
        EXIT_TO_MSDOS ENDP

;#################################################
;# SUBROUTINE: UNSET_INT15H
;#################################################
UNSET_INT15H PROC NEAR
        PUSH    DX
        PUSH    ES                      ;Save segment reg.
        XOR    AX, AX                  ;Point to "zero segment".
        MOV    ES, AX
        MOV    BX, 15h * 4            ;Address of INT 15h vector.

        CLI                            ;Disable interrupts.
        MOV    AX, OLD_INT15_OFF      ; Restore original offset.
        MOV    ES:[BX], AX
        MOV    AX, OLD_INT15_SEG      ; Restore original segment.
        MOV    ES:[BX + 2], AX
        STI                            ;Re-enable interrupts.

        POP    ES                      ;Restore segment reg.
        POP    DX
        RET
        UNSET_INT15H ENDP

;#################################################
;# SUBROUTINE: SET_INT15H
;#################################################
SET_INT15H PROC NEAR
        PUSH    DX
        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.
        POP    DX
        RET
        SET_INT15H ENDP

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

        CMP    AL, REL_ESC
        JE      EXIT_HANDLER
                ;PRINT SCAN CODE
                MOV    AH, 09H
                MOV    BL, 7          ;COLOR
                MOV    CX, 1          ;NUMBER OF CHARS
                ADD    AL, 48
                INT    10H
                SUB    AL, 48

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

        EXIT_HANDLER:
                JMP    CS:[OLD_INT15_VECTOR]
        RET
        INT_15H_HANDLER ENDP

;#################################################
;# SUBROUTINE: MODE13H_SET
;#################################################
MODE13H_SET PROC NEAR
        PUSH    AX
        MOV    AX, 13H
        INT    10H
        POP    AX
        RET
        MODE13H_SET ENDP

;#################################################
;# SUBROUTINE: MODE03H_SET
;#################################################
MODE03H_SET PROC NEAR
        PUSH    AX
        MOV    AX, 03H
        INT    10H
        POP    AX
        RET
        MODE03H_SET ENDP


CODESG ENDS
END PRESS_ESC
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 28, 2024, 08:06:35 AM
Problems:

I'm also not sure why you have your entry-point code set up as a PROC, since you will never return from it. Once you exit to DOS w/INT 21/AH=4Ch, that's the end of the show; no more of your code will execute after that, so there's no need of any RETs.

None of which might explain why you're having problems, but still things that should be fixed.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 12:05:47 PM
Thanks for your help.

Quote from: NoCforMe on February 28, 2024, 08:06:35 AMJust to be sure, I would use explicit segment overrides for that data that's in the code segment:
Corrected

Quote from: NoCforMe on February 28, 2024, 08:06:35 AMYour code segment has data at its entry point. That "data" will be executed as code, with who knows what effect. You should either move the data to the end of that segment, after your return to DOS,
  • You have no data in your DATA segment, so just get rid of it. (No requirement that a program have a data segment.)
Ok. I put all the data after the JMP MAIN_LOOP, which is a portion of code that is unreachable.

Quote from: NoCforMe on February 28, 2024, 08:06:35 AMI'm also not sure why you have your entry-point code set up as a PROC, since you will never return from it. Once you exit to DOS w/INT 21/AH=4Ch, that's the end of the show; no more of your code will execute after that, so there's no need of any RETs.
I know. It's just that I end every procedure with a RET out of good practice.

Quote from: NoCforMe on February 28, 2024, 08:06:35 AMNone of which might explain why you're having problems
Unfortunately, you're right.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 28, 2024, 12:25:32 PM
Hmm; OK, I went through your code again. If you didn't change the code at the end of your INT 15h handler, then that needs changed:
    PUSH   ES
    MOV    DX, DATASG
    MOV    ES, DX
    MOV    ES:PRESSED_KEY, AL
    POP    ES
should be just:
    MOV    CS:PRESSED_KEY, AL
Again, confusion over segments. Since that variable is in the code segment, just use the CS: segment override to access it.

Also, your interrupt handler should be as lean and mean as possible. Don't call any OS services from it, like INT 10. If you want to display a character, do it in your code outside of the ISR. You want to spend as little time as possible inside the ISR.

Other than that, I can't see anything wrong ...
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 01:42:59 PM
Quote from: NoCforMe on February 28, 2024, 12:25:32 PMAgain, confusion over segments. Since that variable is in the code segment, just use the CS: segment override to access it.
Yes, that's a remnant from when PRESSED_KEY was in DS.

Quote from: NoCforMe on February 28, 2024, 12:25:32 PMAlso, your interrupt handler should be as lean and mean as possible. Don't call any OS services from it, like INT 10. If you want to display a character, do it in your code outside of the ISR. You want to spend as little time as possible inside the ISR.
Yeah, that's there just for debugging, but it has to go. I actually just commented it.

Quote from: NoCforMe on February 28, 2024, 12:25:32 PMOther than that, I can't see anything wrong ...
Unfortunately, it doesn't work. Sometimes, I press ESC and the program exits immediately. Sometimes, I have to press ESC multiple times, then there are as many slashes on the DOS prompt as times I pressed ESC... How is this so complicated? All professional games and many homebrews have immediate response to cursor keys.What are they doing that is so obscure that I can't do?
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 28, 2024, 01:58:50 PM
Are you running under an emulator?
Are you building under an emulator?
TASM?

I've tested it with FASM and it works properly, even moves a sprite around (at 18 pixels per second :biggrin: )
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 28, 2024, 02:00:41 PM
I hear your frustration.

Scan code: you're apparently checking for a release code (81h) which may or may not come through INT 15h. Have you tried using the value 1, which is the scan code for ESC (http://www.manmrk.net/tutorials/basic/FBASIC/html/GfxScancodes.html)?
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 02:15:02 PM
Quote from: sinsi on February 28, 2024, 01:58:50 PMAre you running under an emulator?
Are you building under an emulator?
TASM?
Yes to both.

Quote from: sinsi on February 28, 2024, 01:58:50 PMI've tested it with FASM and it works properly,
The code I posted?
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 02:19:30 PM
Quote from: NoCforMe on February 28, 2024, 02:00:41 PMI hear your frustration.

Scan code: you're apparently checking for a release code (81h) which may or may not come through INT 15h. Have you tried using the value 1, which is the scan code for ESC (http://www.manmrk.net/tutorials/basic/FBASIC/html/GfxScancodes.html)?

The reason I check for release of ESC (81h) is because I thought my code wasn't working due to the fact that sometimes I would press ESC when the IP was after the "CMP AL, KB_ESC" in the MAIN_LOOP, and then I would release ESC when the IP was before the "CMP AL, KB_ESC" without giving the code in the MAIN_LOOP a chance to evaluate the scan code for ESC. So I thought I would not store the release code for ESC to make sure that, once I press ESC, it gets evaluated in the MAIN_LOOP (as opposed to being overwritten by the release code of ESC before being evaluated). The program doesn't work either way and it shows the same behavior.

And yes, I assume that the loop is so fast that there's no chance I press and release ESC within the same iteration of the loop, but I didn't know what else to try.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 28, 2024, 02:27:20 PM
OK, some (possibly) bad news, but maybe this'll help eventually:

Looks like using INT 15/AH = 4Fh might not be the way to go. I just looked at a page (https://stanislavs.org/helppc/int_15-4f.html) which says:
QuoteAH = 4F
AL = scan code
CF = set to 1 (via STC instruction)

on return

AH = 80h, CF set  (PC, PCjr)
  = 86h, CF set  (XT BIOS 11/8/82, AT BIOS 1/10/84)
AL = CF set, new scan code
  = CF clear, original scancode

- available with XT BIOS after 11/8/82, AT BIOS after 1/10/84
- called by INT 9, makes allowance for keyboard translation
- normally returns the scan code in AL, with CF set
- if function returns with CF clear, INT 9 ignores keystroke
- do not rely on this function being called for each INT 9 since
  any user INT 9 handler can exit prematurely and circumvent
  this function

So maybe you do need to hook the actual keyboard BIOS interrupt (INT 9) and store keycodes there. I know that's do-able.

Also, question for @sinsi: Are we doing wrong here by JMPing to the old INT 15h vector? Should we instead be just doing an IRET inside the ISR? (Assuming we continue using INT 15h.)
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 28, 2024, 02:41:04 PM
Which emulator? Some of them are known for timing issues which can affect keyboard input.
The FASM code I have uses a 128-byte key array, not just a single byte, but when I converted your code it exited as soon as ESC was pressed.
Title: Re: Double buffer in mode 13H
Post by: _japheth on February 28, 2024, 02:53:23 PM

Hello,

it's an interesting "race condition" problem. You're writing to variable PRESSED_KEY in both your main loop and your interrupt procedure. That cannot work without some "synchronization". The best and simplest approach of course is NOT to write to that variable in your main loop.

Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 28, 2024, 03:16:16 PM
OK, another approach: instead of hooking any interrupts, just poll the keyboard in a tight loop looking for your keystrokes:
QuoteINT 16h, AH = 01

Returns:
    ZF = 0 if a key pressed (even Ctrl-Break)
    AX = 0 if no scan code is available
    AH = scan code
    AL = ASCII character or zero if special function key

Tight loop:
tloop: MOV AH, 1
INT 16H
TEST AX, AX
JZ tloop

; Key code/char. is in AH/AL:

(This one doesn't wait for a keystroke but returns immediately.)

BTW, forget about hooking INT 9, as keycodes are nowhere to be found (the ISR places the latest keystroke in a circular buffer).
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 28, 2024, 03:23:19 PM
Quote from: _japheth on February 28, 2024, 02:53:23 PMHello,

it's an interesting "race condition" problem. You're writing to variable PRESSED_KEY in both your main loop and your interrupt procedure. That cannot work without some "synchronization".
If I remember the BIOS code, the first instruction is STI, then a read from port 60, then an INT 15 call, so an overwrite is possible.
QuoteThe best and simplest approach of course is NOT to write to that variable in your main loop.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 03:52:41 PM
Quote from: sinsi on February 28, 2024, 02:41:04 PMWhich emulator? Some of them are known for timing issues which can affect keyboard input.
The FASM code I have uses a 128-byte key array, not just a single byte, but when I converted your code it exited as soon as ESC was pressed.
I'm using dosbox. I have a 386 PC104 board here that I want to assemble with some parts to have an MSDOS working computer, but I need to find the time to do so.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 03:56:40 PM
Quote from: _japheth on February 28, 2024, 02:53:23 PMHello,

it's an interesting "race condition" problem. You're writing to variable PRESSED_KEY in both your main loop and your interrupt procedure. That cannot work without some "synchronization". The best and simplest approach of course is NOT to write to that variable in your main loop.


Without the variable write in the main loop, it seems to work (I just tried it a couple of times). However, as of now, the program only checks if ESC has been pressed, and if so, it exits. The aim is to evaluate the cursor keys. The motivation to write the variable with a zero in the main loop is that the next iteration of the loop does not evaluate the same value of the variable, even when no new key has been pressed. Without that write, the loop would evaluate a scan code, a release code, and every subsequent iteration of the loop would evaluate that same release code until a new key is pressed.
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 28, 2024, 04:05:01 PM
Quote from: NoCforMe on February 28, 2024, 03:16:16 PMOK, another approach: instead of hooking any interrupts, just poll the keyboard in a tight loop looking for your keystrokes:
tloop: MOV AH, 1
INT 16H
TEST AX, AX
JZ tloop

; Key code/char. is in AH/AL:

Iirc, this was my first approach. However the problem is that when you press a cursor key, the sprite moves towards the direction of the cursor key, stalls, then it moves without stalling. If you press another cursor key while another one is pressed, then the sprite stalls.

If you don't understand what I mean by "moves, stalls, moves without stalling", try pressing a letter key without releasing anywhere where you can enter text (I just tried here while writing this comment). You'll see the letter appears once, then it stalls, then a bunch of that letter appear without interruption.
Title: Re: Double buffer in mode 13H
Post by: _japheth on February 28, 2024, 04:36:35 PM
Quote from: popcalent on February 28, 2024, 03:56:40 PMWithout the variable write in the main loop, it seems to work (I just tried it a couple of times). However, as of now, the program only checks if ESC has been pressed, and if so, it exits. The aim is to evaluate the cursor keys. The motivation to write the variable with a zero in the main loop is that the next iteration of the loop does not evaluate the same value of the variable, even when no new key has been pressed. Without that write, the loop would evaluate a scan code, a release code, and every subsequent iteration of the loop would evaluate that same release code until a new key is pressed.

For gaming, the best approach - as sinsi already has mentioned - is to maintain an array of 128 bits/bytes that tell you the status of all keys at any time

Here's an example how you could implement that in your int 15h interrupt proc, using 80386 instructions:

push ax
mov ah, 0
btr ax,7          ; pressed or released?
jc released
bts cs:[keybittab], ax
jmp done
released:
btr cs:[keybittab], ax
done:
pop ax

with the bit array defined as:
keybittab dw 8 dup (?)

And then, if you want to know the current status of the ESC key, just code

bt cs:[keybittab], 1
jc is_pressed

Title: Re: Double buffer in mode 13H
Post by: NoCforMe on February 29, 2024, 11:54:02 AM
I want to back up a bit here and go back over some ground again. Hopefully this won't add to the confusion here.

Thing is, I may have misunderstood what the OP is trying to do here (and others may have as well).

This all concerns keyboard handling for his game program. I'm now wondering if using any of the BIOS functions is the wrong way to go here. Why? Because apparently the OP needs to handle the case where one key is pressed and then another key is pressed without releasing the first key. I believe this falls under the heading of key "rollover", and I don't think the BIOS handles this correctly for his application. (I could be wrong here.)

So let me ask @popcalent: is this the case? Do you need to be able to handle a new keypress with another key already held down?

Another reason not to use the BIOS is that it automatically goes into "typematic" mode when a key is held down, generating multiple keypresses from that key. I wonder if there's some way to disable this behavior when the game program is running.

If it's true that this kind of keyboard handling is required, then do the DOS experts here know whether this case can be handled correctly using BIOS functions (like INT 15h/AH = 4Fh)?

If not, then how about this: go to the lowest-level interface and access the keyboard through port 60 (and 64) to get the immediate status. Here's a page describing that interface (https://www.plantation-productions.com/Webster/www.artofasm.com/DOS/ch20/CH20-2.html).
Title: Re: Double buffer in mode 13H
Post by: popcalent on February 29, 2024, 05:25:26 PM
Quote from: NoCforMe on February 29, 2024, 11:54:02 AMSo let me ask @popcalent: is this the case? Do you need to be able to handle a new keypress with another key already held down?
Yes. This is the case. I was almost able to achieve this using a buffer as described here:
Quote from: popcalent on February 12, 2024, 09:06:00 AM.
, but the problem was that sometimes the program did not register key releases and the sprite kept moving.
Title: Re: Double buffer in mode 13H
Post by: sinsi on February 29, 2024, 05:57:43 PM
QuoteAnother reason not to use the BIOS is that it automatically goes into "typematic" mode when a key is held down, generating multiple keypresses from that key
Nope, it's the controller in hardware. That's one of the many reasons to use low-level BIOS routines.

If th OP wants to handle more than one key, a 128-slot buffer is really the only way.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 01, 2024, 07:00:50 AM
Well, that might be true.

I'm getting curious enough about this issue to maybe try some coding. Problem is that I don't have any kind of DOS emulator. Is there something I can install on my Windows 7 computah that will properly emulate DOS? DosBox? vDos?

What I'm thinking of doing is coding a key-logging program to just spit out all keystroke events to a file. Should be low enough latency to capture all keyboard events. That might be able to prove a method that will work for the OP.
Title: Re: Double buffer in mode 13H
Post by: FORTRANS on March 01, 2024, 11:49:44 PM
Hi,

Quote from: NoCforMe on March 01, 2024, 07:00:50 AMWell, that might be true.

I'm getting curious enough about this issue to maybe try some coding. Problem is that I don't have any kind of DOS emulator. Is there something I can install on my Windows 7 computah that will properly emulate DOS? DosBox? vDos?

   I am using Oracle's VirtualBox with Windows 8.1.  Not exactly DOS but
a version of OS/2 that runs DOS programs.  Seems to work here.

   Or, if your system allows, just boot up DOS from a bootable media.
That has worked with older computers for me.

Regards,

Steve N.
Title: Re: Double buffer in mode 13H
Post by: daydreamer on March 02, 2024, 06:39:19 AM
Quote from: FORTRANS on March 01, 2024, 11:49:44 PMI am using Oracle's VirtualBox with Windows 8.1.  Not exactly DOS but
a version of OS/2 that runs DOS programs.  Seems to work here.

   Or, if your system allows, just boot up DOS from a bootable media.
That has worked with older computers for me.
I am using dosbox an switch to energy setting that stops turbo increase clock freqency,to stop it from change speed

But even when i had my old 300 mhz p2 cpu,dont you return to same old problem old dos games run superfast if you boot dos ?
Now my slowest is 2 ghz cpu
OP intention seem to run it on old 386
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 02, 2024, 06:35:23 PM
OK, here's something to maybe chew on: I finally got a test program working in DOSBox. It's a key-logging program that uses INT 15h/AH=4Fh to capture keycodes to a buffer, then to write them to a log file after ESC is pressed (or the buffer is full).

Interesting, to say the least: first of all, I'm not sure how much of the behavior of this app is due to just the weirdness of running DOSBox on a (fairly) modren computah (Intel Core 2 Duo). Anyhow, here's what I've seen:

You get (at least) 2 keycodes per key, one for pressing, the other for releasing. If you just hit ESC right away you get this:

Key: 009C
Key: 0001

(keycodes in hex, obviously)

OK, fine. But look what happens if I run the logger, hit the right arrow key ONCE, then ESC:

Key: 009C
Key: 00E0
Key: 004D
Key: 00E0
Key: 00CD
Key: 0001


The cursor keys generate 2 keycodes each for press & release. But what's weird is that the ESC press keycode is first in the buffer, even though it was pressed AFTER the arrow key, while the release code is at the end as expected.

Have I created a time machine here? I've been over and over the code, and I'm 98% sure it's correct. (Source & executable attached here.)

Anyhow, play around with this and see what you think. Maybe someone else can figure this out. You can try testing key rollover with this (hold down one key and press another).

About the source: fairly straightforward 16-bit code. The complicated stuff at the end is my sprintf() function 'cause I like to see formatted output; you can just ignore it (or use it if you like).
Title: Re: Double buffer in mode 13H
Post by: sinsi on March 02, 2024, 07:37:33 PM
The controller needs some way of distinguishing between two keys that are the same (e.g. ctrl).
That's what the E0 is, a prefix byte.
In your case the keyboard sent the make code for the keypad right arrow (E0 4D) then the break code (E0 CD). If you are totally bypassing the BIOS, see what byte sequence the pause key sends.

Key: 009C esc released   edit: WRONG it's the enter key
Key: 00E0 prefix
Key: 004D right pressed
Key: 00E0 prefix
Key: 00CD right released
Key: 0001 esc presewed
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 02, 2024, 07:46:15 PM
OK, I get the prefix codes; that's what I expected.

What I can't wrap my head around is the sequence of these codes. First of all, you say that the first code (9C) is the ESC release code; are you sure? Shouldn't that be the make code? How could the release code come before the make code?

You looked at my source, yes? I'm buffering the keycodes in the order they occur from INT 15h/AH=4Fh, and I'm writing them to the log file in the same order. So how come they're ... out of order?
Title: Re: Double buffer in mode 13H
Post by: sinsi on March 02, 2024, 07:54:39 PM
Blind as a bat lol.

9C is the release code for enter
Makes sense when you think about it...
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 02, 2024, 08:20:26 PM
... ohhhhh, <Enter> from starting the program at the DOSBox prompt? And 01 is the make code for ESC. (Because that's what I'm looking for to exit the key-logging loop.)

OK, my head doesn't hurt so much now. My only disappointment is that there's no time machine there ...
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 03, 2024, 03:52:17 AM
More proof of concept: I ran my keylogger again. Hit the following keys as quickly as possible:

Got this in the log file:
Key: 00E0 - r.arrow down
Key: 004D -    "    "
Key: 00E0 - l.arrow down
Key: 004B -    "    "
Key: 00E0 - r.arrow up
Key: 00CD -    "    "
Key: 00E0 - l.arrow up
Key: 00CB -    "    "
Key: 0001 - <Esc>


So it works. OP, you can use something like this scheme in your game. Suggestion: set a flag or three, use them to drive your sprite-moving code.

(I think this can be done without a 128-key map, though that is one option. Anything beyond this in game design is far out of my wheelhouse.)

Oh, one more thing: in the INT 15h ISR, add this (the XOR AL, AL, which also clears the carry flag) to throw the keystrokes away so you don't get a million keys coming up when the program exits:
INT15_ISR LABEL FAR

; We're looking for AH=4Fh:
CMP AH, 4Fh
JNE isr99

; Stash keycode:
MOV CS:Keycode, AL
XOR AL, AL ;Throw keycode away.
isr99: JMP CS:[OldINT15Vector]
Title: Re: Double buffer in mode 13H
Post by: daydreamer on March 04, 2024, 02:13:37 AM
in my code I have .inc file with lots of VK_ESCAPE codes for DOS,so you can use it for easy port between windows 32 or 64 bit and DOS 16 bit
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 04, 2024, 09:34:38 AM
Quote from: popcalent on February 28, 2024, 03:52:41 PMI'm using dosbox.
Same here.

Here's something for ya: a working demo that moves a sprite around on the screen. Proves that the INT 15h/AH=4Fh interface works. It's pretty dumbass: uses all 4 cursor keys to move the sprite 1 pixel at a time (the right cursor moves it more just for ha-has). I'm simply erasing the sprite for each move, then redrawing it.

I was thinking about that this am: how in a real game you'd need to implement transparency in the sprite, which would complicate things. Where I'm able to do a block move here (using REP MOVSW), you'd have to do a pixel-by-pixel move, watching out for your transparency color. It could be done, but would be slower.

Anyhow, lemme know whatcha think.
Title: Re: Double buffer in mode 13H
Post by: NoCforMe on March 05, 2024, 09:03:10 AM
Here's something else for you, @popcalent: this little utility shows you all 256 colors in color mode 13. Works for me in DOSBox. Hit any key to toggle between showing the color #s and just showing color blocks. ESC exits.

Heh; what d'you think of my nice blocky number font? Created it myself with my font maker program.
Title: Re: Double buffer in mode 13H
Post by: popcalent on March 15, 2024, 08:21:11 AM
Hey!

I've been disappeared for a while. Sorry about that. I've been dealing with other stuff. I'll try to get back to this project as soon as possible. Thank you very much to all who contributed and will continue to help!