旧 dos 程序集中的硬件 VGA 文本模式 IO 问题

发布于 2024-11-27 00:43:31 字数 4211 浏览 2 评论 0原文

在阅读了大约 4 本关于汇编编程的不同书籍的至少前 3 或 4 章后,我已经到了可以使用 MASM 6.11 在 dosbox 控制台上放置“Hello World”的阶段。想象一下我的喜悦!!

我的程序的第一个版本使用 DOS Function 13h。 我的程序的第二个版本使用 BIOS Function 10h

我现在想使用直接硬件输出来执行第三个版本。我已经阅读了书籍中解释屏幕在 VGA 显示器上分为 80x25 的部分(不关心检测 CGA 之类的事情,所以我的程序使用内存地址 0B800h 来表示彩色 VGA,因为 DOSBox 很棒,而且我的愿望在我 90 岁之前转移到 Win Assembler)。我读到硬件屏幕上的每个字符都是 2 个字节(1 个用于属性,1 个用于字符本身,因此您有 80x25x2=4000 字节)。奇数字节描述属性,偶数字节描述 ASCII 字符。

但我的问题是这样的。无论我如何尝试,我都无法让我的程序输出一个简单的黑白(这只是属性,我假设我可以很容易地更改它)字符串(这只是一个字节数组)从顶部开始 5 行屏幕上,距左边缘 20 个字符(这只是距 4000 字节长的基于零的索引的空白字符数)。 (如果我的计算正确,即 5x80=400+20=420x2=840 是我的字符串在 4000 字节数组中的起始位置)

如何将属性与字符分开(我让它部分工作,但它只是显示每隔一个字符,然后显示一堆随机垃圾(这就是我认为我需要某种字节对来表示属性和文本的方式),或者如何设置它以便两者一起被识别。一旦计算完成,我就可以控制屏幕上的文本位置。

我尝试在网上查找这个看似简单的问题,但找不到解决方案。 DOS 和 x86 汇编可以告诉我如何在不使用 BIOS 或 DOS 功能的情况下仅使用硬件来完成这个简单的小程序,

如果可能的话,我真的很欣赏一个简单的代码片段或一些网站或免费电子书的参考。我不想买一个。关于 dos 控制台编程的大书,当我很快转向 Windows 时,这本书将毫无用处。我专注于此的唯一原因是因为我想学习真正的汇编,而不是一些宏语言或一些声称是汇编的自命不凡的高级语言。

我正在尝试建立一个例程库,使汇编更容易学习,这样人们就不必费力去阅读 10 本书的所有 3 到 6 章的理论书籍,这些章节本质上是一遍又一遍地解释相同的内容,而实际上所需要的就足够了了解如何获取一些输出、为变量赋值、获取一些输入以及执行一些循环和决策。该理论可以稍后出现,当他们进行循环和决策时,大多数人已经完成了足够的汇编程序以掌握所有理论。我相信汇编语言的教学应该与任何其他语言没有什么不同,从一个简单的 hello world 程序开始,然后获取输入等。我想让这成为可能。但是,嘿,我只是一个初学者,当我了解更多时,也许我的教学会改变。

另请注意,我知道问题不在于 DOSBox,因为我有一台运行真正的 MS-DOS V6.2 的非常旧的 PC,并且该程序仍然无法工作(但提供几乎相同的输出)。事实上,DOSBox 实际上运行我的一些旧程序甚至比 True dos 还要好。 Gem 桌面就是一个例子。只是想在人们尝试提出模拟器问题之前先解决这个问题。不可能,用这么简单的程序就不行。不,恐怕问题是我的小大脑没有完全理解需要什么。

有谁可以帮忙吗!!


下面是我使用的程序(MASM 6.1 Under DOSBox on Win 7 64-bit)。它使用 BIOS 中断 10h 功能 13h 子功能 0。我想使用直接硬件 IO 执行相同的操作。


.model small
.stack
.data           ;part of the program containing data
    ;Constants - None
    ;Variables
    MyMsg   db    'Hello World'

.code
Main:
GetAddress:
    mov ax,@data        ;Gets address of data segment
    mov es,ax           ;Loads segment address into es regrister
    mov bp,OFFSET MyMsg ;Load Offset into DX

SetAttributes:
    mov bl,01001111b    ;BG/FG Colour attributes
    mov cx,11           ;Length of string in data segment

SetRowAndCol:
    mov dh,24       ;Set the row to start printing at
    mov dl,68       ;Set the column to start printing at

GetFunctionAndSub:
    mov ah,13h      ;BIOS Function 10h - String Output
    mov al,0        ;BIOS Sub-Function (0-3)

Execute:
    int 10h         ;BIOS Interrupt 10h

EndProg:
    mov ax,4c00h    ;Terminate program return 0 to OS
    int 21h         ;DOS Interrupt 21h

end Main
end

我希望采用易于解释的格式。这是我目前的工作。我已经快明白了但它只打印属性,在屏幕上获取字符是一个问题。 (有时,当我稍微修改它时,我会得到带有随机属性的每个第二个字符(我想我知道原因的技术细节,但不知道足够的汇编程序来修复它))。


.model small
.stack
.data
    ;Constants
    ScreenSeg   equ     0B800h

    ;Variables
    MyMsg   db  'Hello World'
    StrLen  equ $-MyMsg

.code
Main:               

SetSeg:
    mov ax, ScreenSeg   ;set segment register:
    mov ds, ax

InitializeStringLoop:   ;Display all characters: - Not working :( Y!
    mov cx, StrLen      ;number of characters.
    mov di, 00h         ;start from byte 'h'

OutputString:
    mov [di], offset byte ptr MyMsg[di]
    add di, 2           ;skip over next attribute code in vga memory.
    loop OutputString

InitializeAttributeLoop:;Color all characters: - Atributes are working fine.
    mov cx, StrLen      ;number of characters.
    mov di, 01h         ;start from byte after 'h'

;Assuming I have all chars with same attributes - fine for now - later I would make this
;into a procedure that I will just pass the details into. - But for now I just want a
;basic output tutorial.

OutputAttributes:
    mov [di], 11101100b     ;light red(1100) on yellow(1110)
    add di, 2               ;skip over next ascii code in vga memory.
    loop OutputAttributes

EndPrg:
    mov ax, 4C00h
    int 21h
end Main

当然,我想将说明减少到最基本的要点。 (出于适当的教学目的,在教导他人时较少涉及)。这就是我没有将 MOVSB/W/D 等与 REP 一起使用的原因。我选择使用标准 MOV、INC、ADD 等易于解释的手动循环。这些说明足够基本并且易于向新人解释。因此,如果可能的话,我想让它尽可能接近这个。

我知道本质上所有似乎错误的都是实际字符串处理程序的循环。它不允许我按照我想要的方式增加地址。这对我来说很尴尬,因为我实际上是一个使用 C++、C#、VB 和 Delphi 的优秀程序员(很久以前))。我知道你不会认为我什至无法在汇编程序中获得正确的循环,但它是一种完全不同的语言。高级语言中有 2 或 3 个循环,并且根据指令,在汇编程序中执行循环的方法似乎有无限的组合。所以我说“简单循环”,但实际上它并不简单。

我希望有人能帮助我,你会挽救我的装配职业,并确保我最终成为一名优秀的装配老师。预先感谢您,特别是您阅读本文。

After reading about at least the first 3 or 4 chapters of about 4 different books on assembly programming I got to a stage where I can put "Hello World" on a dosbox console using MASM 6.11. Imagine my delight!!

The first version of my program used DOS Function 13h.
The second version of my program used BIOS Function 10h

I now want to do the third version using direct hardware output. I have read the parts of the books that explain the screen is divided into 80x25 on a VGA monitors (not bothered about detecting CGA and all that so my program uses memory address 0B800h for colour VGA, because DOSBox is great and all, and my desire to move to Win Assembler sometime before im 90 years old). I have read that each character on the hardware screen is 2 bytes (1 for the attribute and one for the character itself, therefore you have 80x25x2=4000 bytes). The odd bytes describe the attribute, and the even bytes the ASCII character.

But my problem is this. No matter how I try, I cant get my program to output a simple black and white (which is just the attribute, I assume I can change this reasonably easily) string (which is just an array of bytes) 5 lines from the top of the screen, and 20 characters in from the left edge (which is just the number of blank characters away from a zero based index with 4000 bytes long). (if my calc is correct that is 5x80=400+20=420x2=840 is the starting position of my string within the array of 4000 bytes)

How do I separate the attribute from the character (I got it to work partially but it only shows every second character, then a bunch of random junk (thats how I figured I need some sort of byte pair for the attribute and text), or how do I set it up such that both are recognised together. How do I control he position of the text on the screen once the calcs are made. Where am I going wrong.

I have tried looking around the web for this seemingly simple question but am unable to find a solution. Is there anyone who used to program in DOS and x86 Assembly that can tell me how to do this easy little program by not using BIOS or DOS functions, just with the hardware.

I would really appreicate a simple code snippet if possible. Or a refrence to some site or free e-book. I dont want buying a big book on dos console programming which will end up useless when I move to windows shortly. The only reason I am focused on this is because I want to learn true assembly, not some macro language or some pretensious high level language that claims to be assembly.

I am trying to build a library of routines that will make Assembly easier to learn so people dont have to work though all the 3 to 6 chapters across 10 books of theory esentially explaining again and again the same stuff when really all that is needed is enough to know how to get some output, assign values to variables, get some input, and do some loops and decisions. The theory can come along later, and by the time they get to loops and decisions most people will have done enough assembler to have all the theory anyway. I beleive assembly should be taught no different than any other language starting with a simple hello world program then getting input ect. I want to make this possible. But hey, I'm just a beginner, maybe my taughts will change when I learn more.

One other note, I know for a fact the problem is NOT with DOSBox as I have a very old PC running true MS-DOS V6.2 and the program still doesnt work (but gives almost identical output). In fact, DOSBox actually runs some of my old programs even better than True dos. Gem desktop being one example. Just wanted to get that cleared before people try suggesting its a problem with the emulator. It cant be, not with such simple programs. No im afraid the problem is with my little brain not fully understanding what is needed.

Can anyone out there please help!!


Below is the program I used (MASM 6.1 Under DOSBox on Win 7 64-bit). It uses BIOS Intrrupt 10h Function 13h sub function 0. I want to do the very same using direct hardware IO.


.model small
.stack
.data           ;part of the program containing data
    ;Constants - None
    ;Variables
    MyMsg   db    'Hello World'

.code
Main:
GetAddress:
    mov ax,@data        ;Gets address of data segment
    mov es,ax           ;Loads segment address into es regrister
    mov bp,OFFSET MyMsg ;Load Offset into DX

SetAttributes:
    mov bl,01001111b    ;BG/FG Colour attributes
    mov cx,11           ;Length of string in data segment

SetRowAndCol:
    mov dh,24       ;Set the row to start printing at
    mov dl,68       ;Set the column to start printing at

GetFunctionAndSub:
    mov ah,13h      ;BIOS Function 10h - String Output
    mov al,0        ;BIOS Sub-Function (0-3)

Execute:
    int 10h         ;BIOS Interrupt 10h

EndProg:
    mov ax,4c00h    ;Terminate program return 0 to OS
    int 21h         ;DOS Interrupt 21h

end Main
end

I want to have this in a format that is easy to explain. So here is my current workings. I've almost got it. But it only prints the attributes, getting the characters on screen is a problem. (Ocasionally when I modify it slightly, I get every second character with random attributes (I think I know the technicalities of why, but dont know enough assembler to fix it)).


.model small
.stack
.data
    ;Constants
    ScreenSeg   equ     0B800h

    ;Variables
    MyMsg   db  'Hello World'
    StrLen  equ $-MyMsg

.code
Main:               

SetSeg:
    mov ax, ScreenSeg   ;set segment register:
    mov ds, ax

InitializeStringLoop:   ;Display all characters: - Not working :( Y!
    mov cx, StrLen      ;number of characters.
    mov di, 00h         ;start from byte 'h'

OutputString:
    mov [di], offset byte ptr MyMsg[di]
    add di, 2           ;skip over next attribute code in vga memory.
    loop OutputString

InitializeAttributeLoop:;Color all characters: - Atributes are working fine.
    mov cx, StrLen      ;number of characters.
    mov di, 01h         ;start from byte after 'h'

;Assuming I have all chars with same attributes - fine for now - later I would make this
;into a procedure that I will just pass the details into. - But for now I just want a
;basic output tutorial.

OutputAttributes:
    mov [di], 11101100b     ;light red(1100) on yellow(1110)
    add di, 2               ;skip over next ascii code in vga memory.
    loop OutputAttributes

EndPrg:
    mov ax, 4C00h
    int 21h
end Main

Of course I want to reduce the instructions used to the bare bones essentials. (for proper tuition purposes, less to cover when teaching others). Hense the reason I did not use MOVSB/W/D ect with REP. I opted instead for an easy to explain manual loop using standard MOV, INC, ADD ect. These are instructions that are basic enough and easy to explain to newcommers. So if possible I would like to keep it as close to this as possible.

I know esentially all that seems to be wrong is the loop for the actual string handler. Its not letting me increment the address the way I want it to. Its embarasssing to me cause I am actually quite a good progammer using C++, C#, VB, and Delphi (way back when)). I know you wouldnt think that given I cant even get a loop right in assembler, but it is such a different language. There are 2 or 3 loops in high level languages, and it seems there are an infinate combination of ways to do loops in assembler depending on the instructions. So I say "Simple Loop", but in reality there is little simple about it.

I hope someone can help me with this, you would be saving my assembly carreer, and ensuring I eventually become a good assembly teacher. Thanks in advance, and especially for reading this far.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

用心笑 2024-12-04 00:43:31

典型的约定是使用ds:si作为源,使用es:di作为目标。

所以它最终会类似于(未经测试):

  mov ax, @data
  mov ds, ax
  mov ax, ScreenSeg
  mov es, ax
  ...
  mov si, offset MyMsg
OutputString:
  mov al, byte ptr ds:[si]
  mov byte ptr es:[di], al
  add si, 1           ; next character from string
  add di, 2           ; skip over next attribute code in vga memory.
  loop OutputString

The typical convention would be to use ds:si as source, and es:di as destination.

So it would end up being similar to (untested):

  mov ax, @data
  mov ds, ax
  mov ax, ScreenSeg
  mov es, ax
  ...
  mov si, offset MyMsg
OutputString:
  mov al, byte ptr ds:[si]
  mov byte ptr es:[di], al
  add si, 1           ; next character from string
  add di, 2           ; skip over next attribute code in vga memory.
  loop OutputString
子栖 2024-12-04 00:43:31

如果您还没有 Masm32 软件包,我建议您获取它。它主要是为了在“今天的Windows”中轻松使用汇编语言,这非常好,而且还会教你很多关于Asm的知识,并且还展示了在哪里可以获得之前回复中提到的不可缺少的Intel芯片手册。

我在 80 年代开始编程,所以我明白为什么你对它的本质如此感兴趣,我知道我很怀念它。如果更多的人从那里开始,这会给他们带来很大的回报。你们的服务非常棒!

我正在玩你所说的直接硬件,我还了解到Windows已经改变了一些DOS服务,BIOS服务也发生了变化,所以有些服务不再工作了。事实上,我正在编写一个小型 .com 程序,并在命令提示符窗口中从 Win7 运行它,打印一条消息并等待按键,考虑到它是 2012 年的 Win7,这非常酷!

事实上,BIOS 10h - 0Eh 不起作用,所以我尝试 Dos 21h 02h 写入屏幕,它起作用了。代码如下,因为它是一个 .com(命令程序),我认为它可能对您有用。

;这使得 .com 程序(64k 限制、代码、数据和所有
;必须适合这个空间。用于小型公用事业和
;适合非常快速的任务。事实上DOS命令主要是
;像这样的小型 .com 程序(除了更有用)!
;
;使用 Masm 进行组装
; c:\masm32\bin\ml /AT /c bfc.asm
;使用 Masm 的 Link16 链接
; c:\masm32\bin\link16 bfc.obj,bfc.com;
;
; Link16是制作这个16位.com(命令)文件的关键

SEGMT SEGMENT
org 100h

Start:
        push    CS
        pop     DS

        MOV SI, OFFSET Message
Next:
        MOV ah, 02h             ; Write Char to Standard out
        MOV dl, [si]            ; Char
        INT 21h                 ; Write it
        INC si                  ; Next Char
        CMP byte ptr[si], 0     ; Done?
        JNE Next                ; Nope

WaitKey:
        XOR ah, ah              ; 0
        INT 16h                 ; Wait for any Key
ExitHere:        
        MOV ah, 4Ch             ; Exit with Return Code
        xor al, al              ; Return Code
        INT 21h

Message db  "It Works in Windows 7!", 0

SEGMT ENDS
END Start

I would suggest getting the Masm32 Package if you don't already have it. It is mainly geared towards easily using Assembly Language in "Windows today", which is very nice, but also will teach you a lot about Asm and also shows where to get the Intel Chip manuals that were mentioned in an earlier reply that are indispensable.

I started programming in the 80's and so I can see why you are so interested in the nitty gritty of it, I know I miss it. If more people were to start out there, it would pay off for them very much. You are doing a great service!

I am playing with exactly what you are talking about, Direct Hardware, and I have also learned that Windows has changed some of the DOS services and BIOS services have changed too, so that some don't work any more. I am in fact writing a small .com program and running it from Win7 in a Command Prompt Window, Prints a msg and waits for a key, Pretty cool considering it's Win7 in 2012!

In fact it was BIOS 10h - 0Eh that did not work and so I tried Dos 21h 02h to write to the screen and it worked. The code is below because it is a .com (Command Program) i thought it might be of use to you.

; This makes a .com program (64k Limit, Code, Data and all
; have to fit in this space. Used for small utilities and
; good for very fast tasks. In fact DOS Commands are mostly
; small .com programs like this (except more useful)!
;
; Assemble with Masm using
; c:\masm32\bin\ml /AT /c bfc.asm
; Link with Masm's Link16 using
; c:\masm32\bin\link16 bfc.obj,bfc.com;
;
; Link16 is the key to making this 16bit .com (Command) file

SEGMT SEGMENT
org 100h

Start:
        push    CS
        pop     DS

        MOV SI, OFFSET Message
Next:
        MOV ah, 02h             ; Write Char to Standard out
        MOV dl, [si]            ; Char
        INT 21h                 ; Write it
        INC si                  ; Next Char
        CMP byte ptr[si], 0     ; Done?
        JNE Next                ; Nope

WaitKey:
        XOR ah, ah              ; 0
        INT 16h                 ; Wait for any Key
ExitHere:        
        MOV ah, 4Ch             ; Exit with Return Code
        xor al, al              ; Return Code
        INT 21h

Message db  "It Works in Windows 7!", 0

SEGMT ENDS
END Start
迷鸟归林 2024-12-04 00:43:31

我曾经做过你所说的所有事情。试图记住细节。迈克尔·亚伯拉什(Michael Abrash)是一个你应该在谷歌上搜索的名字。例如,Mode-X 200 x 200(240x200?)256 色模式非常受欢迎,因为它打破了 16 色边界,当时游戏看起来非常好。

我认为金属寄存器编程是可行的,但很痛苦,您可能需要获取您感兴趣的芯片的程序员参考/数据表。随着时间的推移,从 CGA 到 EGA,再到 VGA,再到 VESA,工作方式也发生了变化。我认为通常您使用 int Something 调用来访问页面框架,然后您可以直接填写它。 VESA 我认为是这样工作的,VESA 在显卡支持方面是一个很大的救星,在此之前你必须为每个芯片编写自己的驱动程序(如果你不想要丑陋的标准模式)。

我会查看 mode-x 或 vesa,然后从那里开始。无论如何,你需要有一点黑客才能解决其中的一些问题,很难找到完整且准确的数据表/程序员参考手册,你总是必须只插入一些字节来看看会发生什么。开始填充那些应该是页面框架的内存块,直到您在屏幕上看到一些变化...

除了像图形编程黑皮书这样的粗俗书籍之外,我在我的图书馆中没有看到任何特定的图形编程书籍。这段时间的尾声。我有 BIOS 和 Dos 程序员参考资料,也使用了 ralf Browns 列表。我确信我有流行视频芯片的手册副本(在互联网出现之前,你记得你用挂着一根电线的电话打电话给一个人,那个人拿了一本印刷手册,有时装订得很好,有时只是一个订书钉)如果是的话,请将其放入信封中并邮寄给您,这是您唯一的副本,除非您通过复印机运行它)。我有成堆的印刷品,很遗憾地说,我不会通过它们来回答这个问题。不过,我会牢记这个问题,并查看更多信息以获取更多信息,实际上我可能有一些方便的旧程序,绘制分形和其他类似的东西(直接对显卡/内存进行实用)。

编辑。

我知道您正在寻找文本模式的东西,这是一种图形模式,但它可能会或可能不会阐明您正在尝试做的事情。 int 调用和直接填充页面和调色板内存的组合。

http://dwelch.s3.amazonaws.com/fly.tar.gz

I used to do all of what you are talking about. Trying to remember the details. Michael Abrash is a name you should be googling for. Mode-X for example a 200 something by 200 (240x200?) 256 color mode was very popular as it broke the 16 color boundary and at the time the games looked really good.

I think that the on the metal register programming was doable but painful and you likely need to get the programmers reference/datasheet for the chip you are interested in. As time passed from CGA to EGA to VGA to VESA the way things worked changed as well. I think typically you use int something calls to get access to the page frame then you could fill that in directly. VESA I think worked that way, VESA was a big livesaver when it came to video card support, you used to have to write your own drivers for each chip before then (if you didnt want the ugly standard modes).

I would look at mode-x or vesa and go from there. You need to have a bit of a hacker inside to get through some of this anyway, it is very rare to find a datasheet/programmers reference manual that is complete and accurate, you always have to just shove some bytes around to see what happens. Start filling those memory blocks that are supposed to be the page frames until you see something change on the screen...

I dont see any specific graphics programming books in my library other than the abrash books like the graphics programming black book, which was at the tail end of this period of time. I have bios and dos programmers references and used ralf browns list too. I am sure that I had copies of the manuals for popular video chips (before the internet remember you called a human on that phone thing with a cord hanging out of it, the human took a printed manual, sometimes nicely bound sometimes just a staple in the corner if that, put it in an envelope and mailed it to you and that was your only copy unless you ran it through the copier). I have stacks of printed stuff that, sorry to say, am not going to go through to answer this question. I will keep this question in my mind though and look around some more for info, actually I may have some of my old programs handy, drawing fractals and other such things (direct as practical to the video card/memory).

EDIT.

I know you are looking for text mode stuff, and this is a graphics mode but it may or may not shed some light on what you are trying to do. combination of int calls and filling pages and palette memory directly.

http://dwelch.s3.amazonaws.com/fly.tar.gz

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文