【问题标题】:Dancing Dolls Problem : on GCC linux跳舞娃娃问题:在 GCC linux 上
【发布时间】:2023-04-01 13:30:01
【问题描述】:

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

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

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

第一个字节包含正在显示的字符的 ASCII 值,而第二个字节包含显示字符的颜色。例如,屏幕上第 0 行第 0 列的字符的 ASCII 值存储在位置号0xB8000000

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

我的任务:

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

我真的不知道如何构建这段代码。我什至无法开始。

【问题讨论】:

  • 你说的是哪个操作系统?
  • 请在您的问题中使用描述性标题。想象一下,每个人都会问“请帮帮我”,你认为我们会如何排序。把它放在一个简洁的问题中也是一个很好的练习,让你首先更好地理解你的问题。
  • 您可以添加对谜题来源的引用吗? (是 Y. Kanetkar 的“Les us C”吗?)在this link 上,您的问题有一个完全相同的副本,包括大写字母,您也是吗?
  • +1 反对匿名投票者。
  • +1 用于就有趣主题提出相关问题。

标签: c gcc terminal dos virus


【解决方案1】:

您指的是曾经被称为视频刷新缓冲区的东西。请务必声明 Dancing Dolls was a virus 用于 DOS

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

尽管如此,您感兴趣的技术仍然适用于 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

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

【讨论】:

  • +1 回答这个问题。技术细节:我在 Windows 7 上找不到 MS-DOS 或 debug.exe,除了我有一个 cmd.exe 的快捷方式,我设置 MS-DOS 图标只是为了好玩......另外,我很确定 OP 显示为 0xB8000000 的地址实际上是取自 DOS 时代所谓的“远指针”的符号,因此它意味着 B800:0000(段选择器 B800,偏移量 0000),这意味着物理地址十六进制 B800* 10 + 0000 = B8000。但是,在 Windows 7 中访问这些地址中的任何一个都会失败。
  • 确实,我把 cmd.exe 误认为是 MS-DOS。我将回顾示例中的步骤,并让你们知道我做了什么。
  • 我目前正在使用带有 SP1 的 Windows 7 Ultimate 32 位版本,并且我已经安装了 VS2010 Express,但我不知道 debug.exe 的确切来源.示例如下:启动cmd.exe 并执行cls 以清除屏幕。执行debug 打开它。在debug 中,通过执行r es 设置ES 寄存器,然后输入b800。之后,通过执行d es:0来转储该位置的内存,然后再次执行命令d查看下一块内存,您将能够看到当时的提示文本 调试已执行。
【解决方案2】:

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

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

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

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

然后可以想象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 地址。

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

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

文件[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 中工作,但我不知道。

干杯,

【讨论】:

  • +1 表示一种有趣的方法。对于初学者来说可能有点太多了,但它非常巧妙。顺便说一句,这是一道 C 题。
  • 哦。嗯。感谢“C”。我没注意到。重新“巧妙”,不,它只是简单的编程(包括可能解决不存在的问题,只是因为编写代码来解决它比深入研究如此记录的库以找出它们如何工作或如何工作要快意味着工作)。 :-)
【解决方案3】:
 #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 */ 

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 2014-04-02
      • 2014-03-19
      • 2022-06-11
      相关资源
      最近更新 更多