6

序文として、私は本当に私の問題の正確な解決策を望んでいません。ただのガイダンスです。私はあなたにコードを教えてほしくありません。これは宿題ではなく、私が解決しようとしている演習です。

VDU にアクセスして、同じ画面で直接キャラクターを変更する方法を教えてください。

画面は 25 行 80 列に分割されています。画面に表示される文字は、VDU メモリ (通常のメモリと混同しないでください) と呼ばれる特別なメモリに格納されます。画面に表示される各文字は、VDU メモリで 2 バイトを占有します。

これらのバイトの最初のバイトには表示される文字の ASCII 値が含まれ、2 番目のバイトには文字が表示される色が含まれます。たとえば、画面の 0 番目の行と 0 番目の列にある文字の ASCII 値は、ロケーション number に格納されます0xB8000000

したがって、この文字の色は位置 number に存在します0xB8000000 + 1。同様に、行 0、列 1 の文字の ASCII 値は位置0xB8000000 + 2にあり、その色は にあり0xB8000000 + 3ます。

私の仕事:

この知識を使用して、実行時に画面上のすべての大文字を小文字に、すべての小文字を大文字に変換し続けるプログラムを作成します。この手順は、ユーザーがキーボードからキーを押した瞬間に停止する必要があります。これはダンシングドールと呼ばれる蔓延するウイルスの活動です。(モノクロ アダプターの場合は、0xB8000000 の代わりに 0xB0000000 を使用します)。

本当に、このコードを作成する考えがありません。私は始めることさえも行き詰まっています。

4

4 に答える 4

5

かつてビデオ リフレッシュ バッファと呼ばれていたものについて言及しています。Dancing Dollsは DOS 用ウイルスであると述べておくことは重要です。

基本的に、ビデオ メモリが0xB8000000プログラムのメモリ上のアドレスにあると想定しています。ただし、最新のオペレーティング システム (Linux/Windows/Mac OS X など) は、アプリケーションがデバイスを直接操作できないようにする仮想メモリ メカニズムを提供します。したがって、アプリケーションが認識するアドレスは、ビデオ リフレッシュ バッファに対応する0xB8000000物理アドレスではありません。このスレッドの最後の投稿には、このテーマに関する興味深い情報もあります。0xb8000000

それにもかかわらず、あなたが興味を持っている手法は 16 ビット DOS でも有効であり、本Assembly Language Step-by-step: Programming with DOS and Linux で説明されています。この本の第6章には、これがどのように機能するかを正確に説明する素晴らしいセクションがあります。このセクションの名前は で、 debug.exeを使用してビデオ メモリにアクセスし、それを変更するInspecting the Video Refresh Buffer with DEBUG方法を示す興味深い例があります。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 アドレスに対応するポインターです。

これが主なアイデアであり、現在のANTIQUATED機器用に設計されたこの演習を解決するための開始点を取得する方法です。

もう少し具体化して、ビデオ メモリ シミュレーションの 1 つの可能な実装を次に示します (これは、演習で実行することを意図していたことに対処するコードではないことに注意してください。しかし、おそらく今日でなければならないでしょう):

ファイル[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 ライブラリcurses::Consoleの単純なラッパーである可能性があります。たとえば…

ファイル[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(バイト/表示された文字)) をループするだけです。表示された各文字の文字部分を読み取り、それを上位/下位から下位に変換します。 /upper case. 変換は、単純な算術演算によって簡単に行われます. http://www.asciitable.com/を参照してください: 例: A = 0x41, a = 0x61 したがって、一方を他方に変換するには、単に 0x20 を追加/削除します読み取り値。

于 2011-08-21T01:18:46.280 に答える