6

作为前言,我真的不想要我的问题的确切解决方案,只是指导。我不想让你给我密码。这不是家庭作业,这只是我正在尝试解决的一个练习。

我只是想让你告诉我如何访问 VDU 并在同一屏幕上直接更改字符。

屏幕分为25行80列。屏幕上显示的字符存储在称为 VDU 内存的特殊内存中(不要与普通内存混淆)。屏幕上显示的每个字符在 VDU 内存中占用两个字节。

这些字节中的第一个包含正在显示的字符的 ASCII 值,而第二个字节包含显示字符的颜色。例如,屏幕上第 0 行和第 0 列上出现的字符的 ASCII 值存储在位置 number 处0xB8000000

因此,该字符的颜色将出现在位置编号处0xB8000000 + 1。类似地,第 0 行字符的 ASCII 值,第 1 列将位于位置0xB8000000 + 2,其颜色位于0xB8000000 + 3

我的任务:

有了这些知识,编写一个程序,该程序在执行时会不断将屏幕上的每个大写字母转换为小写字母,并将每个小写字母转换为大写字母。该过程应该在用户从键盘敲击键的那一刻停止。这是一种名为 Dancing Dolls 的猖獗病毒的活动。(对于单色适配器,使用 0xB0000000 而不是 0xB8000000)。

真的,我没有任何想法来构建这段代码。我什至无法开始。

4

4 回答 4

5

您指的是曾经称为视频刷新缓冲区的东西。重要的是要说明Dancing DollsDOS的病毒

基本上,您期望视频内存位于0xB8000000程序内存上的地址。然而,现代操作系统(如 Linux/Windows/Mac OS X)提供了一种虚拟内存机制,可防止应用程序直接操作设备。因此,0xB8000000您的应用程序看到的地址0xb8000000不是对应于视频刷新缓冲区的物理地址。这个线程的最后一篇文章也有一些关于这个主题的有趣信息。

尽管如此,您感兴趣的技术仍然适用于 16 位 DOS,并且在《Assembly Language Step-by-step: Programming with DOS and Linux 》一书中进行了介绍。这本书在第 6 章中有一个很棒的部分,它解释了它是如何工作的。该部分名为Inspecting the Video Refresh Buffer with DEBUG,并且有一个有趣的示例显示如何使用debug.exe访问视频内存并对其进行修改。我在我的 Win 7 盒子的cmd.exe上成功测试了它。

但是,如果您想操作 Linux 终端的屏幕,请检查ncurses

它是一个函数库,用于管理应用程序在字符单元终端上的显示

于 2011-08-20T23:42:09.050 回答
4

我认为,最重要的是要意识到您的问题是关于一项(显然)曾经被设计为简单而有趣的练习。

但是,那是很久以前的事了,同时硬件和软件都在进步。访问物理内存不再容易。而且,至少对于初学者来说,这样做不再有趣。

因此,该练习的主要优点已被时间无情的熵作用所切断。

但是,对于绝对想要进行此练习的人来说,可以模拟曾经如此容易访问的硬件。透明地进行这样的模拟,以使学生的代码与真实的代码相同,这是相当困难的并且依赖于系统。但是,如果您(学生)只是同意显式调用一些“更新”例程来模拟硬件屏幕更新,那么它更接近于可管理 - 因此,旧练习可以重复使用!:-)

然后可以想象它main会进行一些初始化,并调用一个doTheDancingDolls带有参数的例程,该例程可以访问模拟的视频内存,以及前面提到的“更新”功能:

void doTheDancingDolls(
    curses::Console&            console,
    b8000::DisplayMemorySim&    memorySim
    )
{
    do
    {
        // Whatever - this is where you Do Things to the simulated video memory.

        // Then:
        memorySim.update( console );
    } while( !console.keyboard().keypressIsAvailable() );

    console.keyboard().getKey();    // Consume the keypress.
}

...memorySim对象以某种方式提供了指向模拟视频内存的必要指针——一个与练习中的 B8000000 地址相对应的指针。

这就是主要思想,如何为解决这个为现在过时的设备设计的练习找到一个起点。

再充实一点,这是视频内存模拟的一种可能实现(请注意,这不是解决您在练习中打算做的事情的代码,它只是解决您从未打算做的事情,但可能必须在今天):

文件[b8000.h]
#ifndef B8000_H
#define B8000_H
// A simulation of the early PC's 25*80 color text mode video memory.
// The structure of the memory is INTENTIONALLY not modelled in the
// exposed interface. A student can learn from imposing such structure.
//
// Copyright (c) 2011 Alf P. Steinbach.


//------------------------------------------ Dependencies:

#include "curses_plus_plus.h"       // curses::Console
#include <vector>                   // std::vector


//------------------------------------------ Interface:

namespace b8000 {
    typedef unsigned char   Byte;

    class DisplayMemorySim
    {
    private:
        std::vector< Byte >     buf_;

    public:
        enum { nColumns = 80, nLines = 25 };

        DisplayMemorySim(): buf_( 2*nLines*nColumns ) {}

        void* bufferPointer()               { return &buf_[0]; }
        void const* bufferPointer() const   { return &buf_[0]; }

        void update( curses::Console& console ) const
        {
            assert( console.nLines() >= nLines );
            assert( console.nColumns() >= nColumns );
            assert( console.colors().nColors() >= 16 );

            for( int y = 0;  y < nLines;  ++y )
            {
                for( int x = 0;  x < nColumns;  ++x )
                {
                    int const   iCell       = 2*(y*nColumns + x);
                    Byte const  charCode    = buf_[iCell];
                    Byte const  colors      = buf_[iCell + 1];
                    Byte const  fg          = colors & 0xF;
                    Byte const  bg          = colors >> 4;

                    console.colors().setFgBg( fg, bg );
                    console.putAt( x, y, charCode );
                }
            }
            console.updateScreen();
        }
    };
}    // namespace b8000

#endif

并且curses::Console该类可能是对curses 库的简单包装,例如……

文件[curses_plus_plus.h]
#ifndef CURSES_PLUS_PLUS_H
#define CURSES_PLUS_PLUS_H
// Sort of minimal C++ interface to the "curses" library.
// Copyright (c) 2011 Alf P. Steinbach.


//------------------------------------------ Dependencies:

#include "curses.h"
#if defined( _MSC_VER ) && defined( __PDCURSES__ )
#   pragma comment( lib, "pdcurses.lib" )
#endif

#ifdef  _MSC_VER
#   pragma warning( disable: 4355 ) // 'this' used in member initializer
#endif 

#include <assert.h>         // assert
#include <stddef.h>         // NULL


//------------------------------------------ Interface:

namespace curses {
    class Console;

    namespace detail {
        template< class Number > inline Number square( Number x ) { return x*x; }

        template< class Derived >
        struct SingleInstance
        {
            static int& instanceCount()
            {
                static int n    = 0;
                return n;
            }

            SingleInstance( SingleInstance const& );            // No such.
            SingleInstance& operator=( SingleInstance const& ); // No such.

            SingleInstance()
            {
                ++instanceCount();
                assert(( "can only have one instance at a time", (instanceCount() == 1) ));
            }

            ~SingleInstance() { --instanceCount(); }
        };
    }  // namespace detail

    namespace color {
        #ifdef  _WIN32      // Any Windows, 32-bit or 64-bit.
            int const   lightness   = 0x08;         //  Windows only.      8

            // The portable colors, expressed for Windows systems.
            int const   black           = COLOR_BLACK;                  // 0
            int const   blue            = COLOR_BLUE;                   // 1
            int const   green           = COLOR_GREEN;                  // 2
            int const   cyan            = COLOR_CYAN;                   // 3
            int const   red             = COLOR_RED;                    // 4
            int const   magenta         = COLOR_MAGENTA;                // 5
            int const   yellow          = COLOR_YELLOW | lightness;     // 6 + 8
            int const   white           = COLOR_WHITE | lightness;      // 7 + 8

            // Windows-specific colors.
            int const   gray            = COLOR_BLACK | lightness;
            int const   lightBlue       = COLOR_BLUE | lightness;
            int const   lightGreen      = COLOR_GREEN | lightness;
            int const   lightCyan       = COLOR_CYAN | lightness;
            int const   lightRed        = COLOR_RED | lightness;
            int const   lightMagenta    = COLOR_MAGENTA | lightness;
            int const   brown           = COLOR_YELLOW & ~lightness;    // A bit greenish.
            int const   lightGray       = COLOR_WHITE & ~lightness;
        #else
            // The portable colors, expressed for non-Windows systems.
            int const   black           = COLOR_BLACK;
            int const   blue            = COLOR_BLUE;
            int const   green           = COLOR_GREEN;
            int const   cyan            = COLOR_CYAN;
            int const   red             = COLOR_RED;
            int const   magenta         = COLOR_MAGENTA;
            int const   yellow          = COLOR_YELLOW;
            int const   white           = COLOR_WHITE;
        #endif
    }    // namespace color

    class Colors
        : private detail::SingleInstance< Colors >
    {
    private:
        Colors( Colors const& );                // No such.
        Colors& operator=( Colors const& );     // No such.

        int     n_;
        int     nPairs_;
        int     rawIndexOfPair0_;

        int rawIndexFor( int fg, int bg ) const
        {
            return bg*n_ + fg;
        }

    public:
        int nColors() const         { return n_; }
        int nColorPairs() const     { return nPairs_; }

        int indexFor( int fg, int bg ) const
        {
            int const   rawIndex    = rawIndexFor( fg, bg );

            return 0?0
                : (rawIndex == rawIndexOfPair0_)?   0
                : (rawIndex == 0)?                  rawIndexOfPair0_
                :   rawIndex;
        }

        void setColorPair( int i )
        {
            ::color_set( short( i ), NULL );         //attrset( COLOR_PAIR( i ) );
        }

        void setFgBg( int fg, int bg )
        {
            setColorPair( indexFor( fg, bg ) );
        }

        Colors( Console& )
        {
            ::start_color();    // Initialize color support.

            // Although these look like global constants, they're global variables
            // that are initialized by the call to Curses' `start_color` (above).
            n_ = ::COLORS;  nPairs_ = ::COLOR_PAIRS;
            assert( detail::square( n_ ) <= nPairs_ );   // Our requirement.

            // Obtain curses' default colors, those are at color pair index 0.
            {
                short   fg0  = -1;
                short   bg0  = -1;

                ::pair_content( 0, &fg0, &bg0 );
                assert( fg0 != -1 );
                assert( bg0 != -1 );
                rawIndexOfPair0_ = rawIndexFor( fg0, bg0 );
            }

            // Initialize color pair table to support finding index for given colors.
            // The color pair at index 0 can't be specified (according to docs),
            // so trickery is required. Here like swapping index 0 to elsewhere.
            for( int fg = 0;  fg < n_;  ++fg )
            {
                for( int bg = 0;  bg < n_;  ++bg )
                {
                    int const i = indexFor( fg, bg );

                    if( i == 0 ) { continue; }      // It's initialized already.
                    ::init_pair( short( i ), short( fg ), short( bg ) );
                }
            }
        }
    };

    class Keyboard
        : private detail::SingleInstance< Keyboard >
    {
    private:
        WINDOW*     pCursesWin_;
        bool        isBlocking_;
        int         bufferedKeypress_;
        bool        hasBufferedKeypress_;

        void setBlocking( bool desiredBlocking )
        {
            if( isBlocking_ != desiredBlocking )
            {
                ::nodelay( pCursesWin_, !desiredBlocking );
                isBlocking_ = desiredBlocking;
            }
        }

    public:
        int getKey()
        {
            if( hasBufferedKeypress_ )
            {
                hasBufferedKeypress_ = false;
                return bufferedKeypress_;
            }

            setBlocking( true );
            return ::getch();
        }

        bool keypressIsAvailable()
        {
            if( hasBufferedKeypress_ )  { return true; }

            setBlocking( false );
            int const key = ::getch();
            if( key == ERR )            { return false; }

            hasBufferedKeypress_ = true;
            bufferedKeypress_ = key;
            return true;
        }

        Keyboard( WINDOW& pCursesWin )
            : pCursesWin_( &pCursesWin )
            , isBlocking_( true )
            , hasBufferedKeypress_( false )
        {
            ::keypad( pCursesWin_, true );  // Assemble multi-character sequences into key symbols.
            ::nodelay( pCursesWin_, false );
            assert( isBlocking_ == true );
        }

        ~Keyboard()
        {
            setBlocking( true );
        }
    };

    class Console
        : private detail::SingleInstance< Console >
    {
    private:
        ::WINDOW*   pCursesWin_;
        Colors      colors_;
        Keyboard    keyboard_;

        Console( Console const& );                    // No such.
        Console& operator=( Console const& );         // No such.

        static ::WINDOW* beginWin()
        {
            return ::initscr();
        }

    public:
        // At least with pdcurses in Windows, these numbers are for the
        // console window, i.e. not for its underlying text buffer.
        int nColumns() const    { return ::getmaxx( pCursesWin_ ); }
        int nLines() const      { return ::getmaxy( pCursesWin_ ); }

        Colors& colors() { return colors_; }
        Colors const& colors() const { return colors_; }

        Keyboard& keyboard() { return keyboard_; }
        Keyboard const& keyboard() const { return keyboard_; }

        void putAt( int x, int y, char const s[] )
        {
            ::mvaddstr( y, x, s );
        }

        void putAt( int x, int y, char c )
        {
            ::mvaddch( y, x, c );
        }

        void updateScreen() { ::refresh(); }

        Console()
            : pCursesWin_( beginWin() )
            , colors_( *this )
            , keyboard_( *pCursesWin_ )
        {
            ::cbreak();         // Immediate input (no line buffering).
            ::noecho();         // No input echo.
        }

        ~Console()
        {
            ::endwin();
        }       
    };
}    // namespace curses

#endif

为了驱动这一切,在主程序中,您将检查终端窗口(Windows“控制台窗口”)是否足够大,等等:

文件[main.cpp]
#include "string_util.h"            // strUtil::S
#include "b8000.h"                  // b8000::DisplayMemorySim
#include "curses_plus_plus.h"       // curses::Console

#include <algorithm>                // std::max
#include <assert.h>                 // assert
#include <iostream>                 // std::cerr, std::endl
#include <stdexcept>                // std::runtime_error, std::exception
#include <stdlib.h>                 // EXIT_SUCCESS, EXIT_FAILURE



void doTheDancingDolls(
    curses::Console&            console,
    b8000::DisplayMemorySim&    memorySim
    )
{
    do
    {
        // Whatever - this is where you Do Things to the simulated video memory.
        // The following three lines are just to see that something's going on here.
        using stringUtil::S;
        static int x = 0;
        console.putAt( 30, 5, S() << "I have now counted to " << ++x << "..." );

        // Then:
        //memorySim.update( console );
    } while( !console.keyboard().keypressIsAvailable() );

    console.keyboard().getKey();    // Consume the keypress.
}



bool throwX( std::string const& s ) { throw std::runtime_error( s ); }

void cppMain()
{
    using std::max;
    using stringUtil::S;

    curses::Console         console;
    enum
    {
        w = b8000::DisplayMemorySim::nColumns,
        h = b8000::DisplayMemorySim::nLines
    };

    (console.colors().nColors() >= 16)
        || throwX( "The console window doesn't support 16 colors." );
    (console.nColumns() >= w)
        || throwX( S() << "The console window has less than " << w << " columns." );
    (console.nLines() >= h)
        || throwX( S()  << "The console window has less than " << h << " lines." );

    namespace color = curses::color;
    console.colors().setFgBg( color::lightRed, color::yellow );
    console.putAt( 30, 0,        "  The Dancing Dolls!  " );
    console.putAt( 30, 1, S() << "  In " << h << "x" << w << " color mode." );
    console.putAt( 30, 3, S() << "  Press ANY key to start...  " );
    console.keyboard().getKey();
    console.putAt( 30, 3, S() << "  Press ANY key to stop...   " );

    b8000::DisplayMemorySim     displayMemorySim;
    doTheDancingDolls( console, displayMemorySim );
}

int main()
{
    using namespace std;

    try
    {
        cppMain();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        cout << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

这篇文章有点长,所以我没有为这S()件事添加代码(代码不多,但仍然如此)。

无论如何,这应该为实验提供一个起点。

免责声明:我只是把它拼凑在一起。它没有经过广泛的测试。例如,我可能误解了 curses 库的键盘处理 -。我希望它也能在 *nix-land 中工作,但我不知道。

干杯&hth.,

于 2011-08-21T10:04:07.747 回答
1
 #include<conio.h>
    #include<dos.h>
    #define MEMORY_SIZE 3999  // 2 bytes for each character  total char 2000

    void main(){

    char far *vidmem = 0xB8000000; // video memory address row & column zero
    int i,o=0;

    for(i=0;i<=MEMORY_SIZE;i+=2){
        if (o%2==0)
            *( vidmem + i) = 'A';
        else
            *( vidmem + i) = 'a';
    o++;
    }

    while(!kbhit()){

        for(i=0;i<=MEMORY_SIZE;i+=2){       
            if(*( vidmem +i) == 'A')
                *( vidmem + i) = 'a';
            else
                *( vidmem + i) = 'A';
        }
        delay(200); // wait for 200 ms
    }

    }

/* run and compile success fully in Turbo C++ v3.0 non error just warring during running programme u just want to disable it option>>compiler>>message>>portablity */ 
于 2012-08-18T18:33:52.997 回答
0

我们是在谈论实际对屏幕上显示的内容进行修改,还是只是将上述作业作为练习。

在后一种情况下,它只是在所描述的内存区域(0xb8000000 + 25(行)* 80(列)* 2(字节/显示的字符)上循环。读取每个显示字符的字符部分并将其从上/下转换为下/大写。转换只是通过简单的算术完成。看看http://www.asciitable.com/:例如:A = 0x41,a = 0x61 所以为了将一个转换为另一个,只需添加/删除 0x20读取值。

于 2011-08-21T01:18:46.280 回答