VOGONS

Common searches


First post, by Peter Swinkels

User metadata
Rank Member
Rank
Member

EDIT: I updated the original code posted here.
EDIT 2: For some reason I linked to vbforums instead of YouTube. Duh.

Hi, I am trying to write a TSR using the instructions from this YouTube video: https://www.youtube.com/watch?v=PQXNpPul5oo&t=1960s

The result:

; A TSR for MS-DOS - By: Peter Swinkels, ***2021***

; Retrieves interrupt 0x08's vector.
CLI
MOV AH, 0x35
MOV AL, 0x08
INT 0x21

; Places the retrieved vector at another interrupt.
MOV DX, BX
PUSH ES
POP DS
MOV AH, 0x25
MOV AL, 0xC8
INT 0x21

; Places this TSR at interrupt 0x08.
PUSH CS
POP DS
MOV DX, TSR
MOV AH, 0x25
MOV AL, 0x08
INT 0x21

; Terminates and stays resident.
MOV AH, 0x31
MOV AL, 0x00
MOV DX, 0x00FF
INT 0x21

TSR:
; Disables hardware interrupts.
CLI

; Saves the registers.
PUSHA
PUSH DS

; Disables Num Lock.
MOV AX, 0x40
MOV DS, AX
MOV BX, 0x17
MOV AX, [BX]
AND AX, 0x6F
MOV [BX], AX

; Restores the registers.
POP DS
POPA

; Calls the redirected interrupt.
INT 0xC8

; Sends an End-Of Interrupt signal to the 8259 interrupt controller.
PUSH AX
MOV AL, 0x20
OUT 0x20, AL
POP AX

; Returns.
Show last 3 lines
STI
IRET

I see no relevant differences with what the video instructed to write and everything appears to make sense. Did I screw something up or doesn't DOSBox support this kind of TSR?

(I posted a similar topic at vbforums at https://www.vbforums.com/showthread.php?89225 … 467#post5525467)

Last edited by Peter Swinkels on 2021-07-11, 11:07. Edited 3 times in total.

Reply 4 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member

I fixed the typo. 0xC8 is now 0x08. The program now freezes DOSBox regardless of whether or not I acknowledge the interrupt. Anyone have any ideas?

Reply 6 of 41, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

I wonder which assembler you're using, because many DOS-era assemblers recognize the NNh form of hexadecimal, but not the 0xNN form.

Some general points: your use of CLI/STI in the interrupt handler is unnecessary because interrupts are always disabled when the handler is entered and IRET will restore the interrupt flag to its former state along with the other CPU flags. PUSHA/POPA are excessive, you only need push/pop AX and BX in addition to DS because they are the only registers your handler modifies. For your apparent intention to disable numlock, you need to mask off bit 5 of 40h:17h, but you're also masking 40h:18h entirely (which may indeed cause the keyboard to not work properly) by using a word pointer instead of a byte pointer, so I guess you want:

MOV AL,[BX]
AND AL,0DFh ; mask off bit 5 (numlock mode)
MOV [BX],AL

Lastly, there are debugger builds of DOSBox you can use to analyze what your assembled program is actually doing: DOSBox debugger

Reply 7 of 41, by Ringding

User metadata
Rank Newbie
Rank
Newbie

The cli at handler entry and the sti at exit don’t do anything useful, and I don’t get the cli at the very beginning. At least do an sti before exiting. Maybe one of the invoked int functions does this implicitly. I don’t remember and did not bother looking it up, but at least it looks strange.

Reply 9 of 41, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

You should pay attention to which type of program binary your assembler of choice is producing (COM or EXE), and if additional things (e.g. ORG 0x100 for COM, stack location for EXE, etc.) are needed for the type produced.

Reply 10 of 41, by Ringding

User metadata
Rank Newbie
Rank
Newbie

I’ve never used nasm for DOS, so I don’t know how to instruct it to generate a COM. But you are most likely missing the equivalent of "org 100h".

I used tasm and tlink and copied your code into a skeleton from the examples/usrguide directory, replaced all 0x with a "h" suffix, added one "offset" and checked the resulting binary with ndisasm.

Reply 11 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Nasm creates binaries suitable for *.com binaries by default and uses ORG 0x0 by default. @Ringding: And what did your check with ndisasm reveal?

Reply 12 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Okay, I made a few changes and decided to try to have my TSR display a character instead of modifying the Num Lock status. Code:

; A TSR for MS-DOS - By: Peter Swinkels, ***2021***
ORG 0x0100

; Retrieves interrupt 0x08's vector.
CLI
MOV AH, 0x35
MOV AL, 0x08
INT 0x21

; Places the retrieved vector at another interrupt.
MOV DX, BX
PUSH ES
POP DS
MOV AH, 0x25
MOV AL, 0xC8
INT 0x21

; Places this TSR at interrupt 0x08.
PUSH CS
POP DS
MOV DX, TSR
MOV AH, 0x25
MOV AL, 0x08
INT 0x21

; Terminates and stays resident.
MOV AH, 0x31
MOV AL, 0x00
MOV DX, 0x00FF
INT 0x21

TSR:
; Disables hardware interrupts.
CLI

; Saves the registers.
PUSHA
PUSH DS

; Displays a character.
MOV DL, 0x21
MOV AH, 0x02
INT 0x21

; Restores the registers.
POP DS
POPA

; Calls the redirected interrupt.
INT 0xC8

; Sends an End-Of Interrupt signal to the 8259 interrupt controller.
PUSH AX
MOV AL, 0x20
OUT 0x20, AL
POP AX

; Returns.
STI
IRET

It appears to work now. When I get around to it I am going to see if I can integrate some code I wrote that dumps all conventional memory to a file. That's why I wanted to create a TSR to begin with. Thank you all for your input!

Last edited by Peter Swinkels on 2021-06-19, 21:21. Edited 1 time in total.

Reply 13 of 41, by Ringding

User metadata
Rank Newbie
Rank
Newbie
Peter Swinkels wrote on 2021-06-16, 19:27:

@Ringding: And what did your check with ndisasm reveal?

That it looked ok 😉. I paid specific attention to taking the offset of the handler function.

Reply 14 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member

It appears I succeeded in making a stable TSR that dumps all conventional memory when the user presses the F12 key.

"mmdmptsr.asm"

; Memory Dumping TSR for MS-DOS v1.00 - By: Peter Swinkels, ***2021***
ORG 0x0100 ; Indicates that all relative pointers to data are moved forward by 0x0100 bytes.

RedirectedFrom EQU 0x08 ; Defines the interrupt to be redirected.
RedirectedTo EQU 0xFF ; Defines the redirected interrupt's new vector.

MOV AH, 0x35 ; Checks whether this TSR is already active by checking for a redirected interrupt.
MOV AL, RedirectedTo ;
INT 0x21 ;
MOV AX, ES ;
CMP AX, 0x0000 ;
JNE IsActive ;
CMP BX, 0x0000 ;
JNE IsActive ;

CLI ; Retrieves vector the vector for the interrupt to be redirected.
MOV AH, 0x35 ;
MOV AL, RedirectedFrom ;
INT 0x21 ;

MOV AX, DS ; Saves the data segment register.
MOV FS, AX ;

MOV DX, BX ; Places the retrieved vector at another interrupt.
PUSH ES ;
POP DS ;
MOV AH, 0x25 ;
MOV AL, RedirectedTo ;
INT 0x21 ;

PUSH CS ; Sets this TSR's interrupt vector.
POP DS ;
MOV DX, TSR ;
MOV AH, 0x25 ;
MOV AL, RedirectedFrom ;
INT 0x21 ;

MOV AH, 0x31 ; Terminates and stays resident.
MOV AL, 0x00 ;
MOV DX, 0x00FF ;
INT 0x21 ;



IsActive: ; Quits if the TSR is already active.
MOV AH, 0x4C ;
INT 0x21 ;

TSR:
CLI ; Disables the hardware interrupts.

PUSHA ; Saves the registers.
PUSH DS ;

MOV AX, FS ; Restores the data segment register.
MOV DS, AX ;

%INCLUDE "Memdump.asm" ; Includes the TSR's main body.

POP DS ; Restores the registers.
Show last 13 lines
POPA                      ;

INT RedirectedTo ; Calls the redirected interrupt.

PUSH AX ; Sends an End-Of Interrupt signal to the 8259 interrupt controller.
MOV AL, 0x20 ;
OUT 0x20, AL ;
POP AX ;

STI ; Re-enables the hardware interrupts.

IRET ; Returns.

"memdump.asm"

IN AL, 0x60           ; Skips memory dumping unless the F12 key is being pressed.
CMP AL, 0x58 ;
JNE Done ;

MOV AH, 0x3C ; Creates the output file.
MOV CX, 0x00 ;
LEA DX, OutputFile ;
INT 0x21 ;
JC Done ;

MOV BX, AX ; Closes the newly created output file.
MOV AH, 0x3E ;
INT 21h ;
JC Done ;

MOV AH, 0x3D ; Opens the output file for writing.
MOV AL, 0x01 ;
LEA DX, OutputFile ;
INT 0x21 ;
JC Done ;

MOV BX, AX ; Retrieves the filehandle.

MOV WORD [MemorySegment], 0x0000 ; Sets the first memory block.

MOV AX, DS ; Saves the current data segment.
MOV ES, AX ;

Dump:
ES ; Sets the memory block to be written to the output file.
MOV AX, [MemorySegment] ;
MOV DS, AX ;

MOV AH, 0x40 ; Writes the memory block to the output file.
MOV CX, 0xFFFF ;
MOV DX, 0x0000 ;
INT 0x21 ;
JC Done ;

ES ; Checks whether the last memory block has been reached.
MOV AX, [MemorySegment] ;
CMP AX, 0xF000 ;
JAE DumpFinished ;

ADD AX, 0x1000 ; Moves to the next memory block.
ES ;
MOV [MemorySegment], AX ;
JMP Dump

DumpFinished:
MOV AH, 0x3E ; Closes the output file.
INT 21h ;
JMP Done

OutputFile DB "MemDump.dat", 0x00
MemorySegment DW 0x0000

Done:

It would be interesting to hear from other users how my TSR behaves and whether or not the data collected is useful.

Reply 15 of 41, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

IMO, TSRs that react to a key press should hook the keyboard IRQ 1 handler (INT 9) because the interrupt only occurs when there is a key press or release. Using the timer IRQ 0 (INT 8) as you have is inefficient and adds unnecessary processing to the system.

Since you're posting in the DOSBox section of the forum, why not just use the DOSBox debugger to dump memory?

Reply 16 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member

I don't like the DOSBox debugger, a lot of steps. Writing my own software may be more work at first, but being able to press a single key to dump all conventional memory is worth it. Yeah, there is room for improvement in what I wrote. When I get around to it I will investigate using other methods.

Reply 17 of 41, by Ringding

User metadata
Rank Newbie
Rank
Newbie

OTOH using the timer interrupt may be more reliable because most games intercept the keyboard interrupt and never call the original handler, whereas I assume they continue calling the timer interrupt, even if they install their own handler.

Reply 18 of 41, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

There is all kinds of wrong in here.

I'd suggest setting DX correctly for func 31 to the end of your ISR rounded up , and put the ISR at the start of the app then all the init code after.

jmp start
my_isr:
...
end_isr:

start:
...
...
mov dx,end_isr
add dx,15
shr dx,4
mov ah,0x31
int 0x21

--/\-[ Stu : Bloody Cactus :: http://kråketær.com :: http://mega-tokyo.com ]-/\--

Reply 19 of 41, by Peter Swinkels

User metadata
Rank Member
Rank
Member
BloodyCactus wrote on 2021-06-20, 12:17:
There is all kinds of wrong in here. […]
Show full quote

There is all kinds of wrong in here.

I'd suggest setting DX correctly for func 31 to the end of your ISR rounded up , and put the ISR at the start of the app then all the init code after.

jmp start
my_isr:
...
end_isr:

start:
...
...
mov dx,end_isr
add dx,15
shr dx,4
mov ah,0x31
int 0x21

Thank you for your suggestion. I will look into that. Could you explain the calculation you're doing a little more?

The program may not be perfect, but it appears to be stable and work as expected. I have uploaded it at: https://github.com/PeterSwinkels/Memory-Dumpi … -TSR-for-MS-DOS .

EDIT:
Nevermind my earlier question. I figured it out and made the appropriate changes. Thank you for pointing that flaw out. Your earlier comment implied there were several things wrong, which other things need to be fixed as well according to you?