Проблема Танцующих кукол: на GCC Linux - PullRequest
5 голосов
/ 20 августа 2011

В качестве предисловия, я действительно не хочу точного решения моей проблемы, только руководство.Я не хочу, чтобы вы дали мне код.Это не домашняя работа, это просто упражнение, которое я пытаюсь решить.

Я просто хочу, чтобы вы рассказали мне, как получить доступ к VDU и напрямую сменить персонажа на том же экране.

Экранразделен на 25 строк и 80 столбцов.Символы, отображаемые на экране, хранятся в специальной памяти, называемой памятью VDU (не путать с обычной памятью).Каждый символ, отображаемый на экране, занимает два байта в памяти VDU.

Первый из этих байтов содержит значение ASCII отображаемого символа, тогда как второй байт содержит цвет, в котором отображается символ.Например, значение ASCII символа, присутствующего в нулевой строке и нулевом столбце на экране, сохраняется в номере местоположения 0xB8000000.

Следовательно, цвет этого символа будет присутствовать в номере местоположения 0xB8000000 + 1.Аналогично, значение ASCII символа в строке 0, столбце 1 будет в местоположении 0xB8000000 + 2, а его цвет в 0xB8000000 + 3.

Моя задача:

С этим знанием напишите программу, которая при выполнении будет преобразовывать каждую заглавную букву на экране в маленькую букву и каждую маленькую букву в заглавную букву.Процедура должна остановиться, как только пользователь нажмет клавишу на клавиатуре.Это деятельность безудержного вируса под названием «Танцующие куклы».(Для монохромного адаптера используйте 0xB0000000 вместо 0xB8000000).

На самом деле у меня нет никакой идеи для создания этого кода.Я застрял даже на том, чтобы начать.

Ответы [ 4 ]

5 голосов
/ 21 августа 2011

Вы имеете в виду то, что когда-то называлось буфер обновления видео . Важно отметить, что Dancing Dolls был вирусом для DOS .

Обычно вы ожидаете, что видеопамять будет находиться по адресу 0xB8000000 в памяти вашей программы. Однако , современные операционные системы (такие как Linux / Windows / Mac OS X) предоставляют механизм виртуальной памяти, который не позволяет приложениям манипулировать устройствами напрямую. Таким образом, адрес 0xB8000000, который видит ваше приложение, не является физическим адресом 0xb8000000, который соответствует буферу обновления видео . Последний пост этой темы также содержит некоторую интересную информацию по этому вопросу.

Тем не менее , интересующая вас техника по-прежнему действительна для 16-битной DOS и описана в книге Язык ассемблера, шаг за шагом: программирование в DOS и Linux, В этой книге есть большой раздел в главе 6, где объясняется, как именно это работает. Раздел называется Inspecting the Video Refresh Buffer with DEBUG и содержит интересный пример, который показывает, как использовать debug.exe для доступа к видеопамяти и ее изменения. Я успешно проверил его на cmd.exe моего Win 7 box.

Но если вы хотите манипулировать экраном терминала Linux, отметьте ncurses :

Это библиотека функций, которые управляют отображением приложения на терминалах символьных ячеек

4 голосов
/ 21 августа 2011

Я думаю, самое важное - это знать, что ваш вопрос касается упражнения, которое (очевидно) когда-то было разработано, чтобы быть легким и интересным .

Тем не менее, это было давно, и между тем и аппаратное и программное обеспечение прогрессировало. Это больше не легко получить доступ к физической памяти. И, по крайней мере, для начинающих, это уже не интересно.

Таким образом, основные достоинства упражнения были отрублены неумолимым энтропийным действием времени.

Тем не менее, для того, кто абсолютно хочет выполнить это упражнение, это легко доступное оборудование может быть смоделировано. Выполнение такого моделирования прозрачно, чтобы код ученика был таким же, как если бы он был на самом деле, довольно сложно и зависит от системы. Однако, если вы (студент) просто согласны явно вызвать какое-либо & ldquo; update & rdquo; чтобы имитировать обновление экрана аппаратного обеспечения, оно становится практически управляемым - и, таким образом, старые упражнения можно использовать повторно! : -)

Затем можно представить, что main выполняет некоторую инициализацию вещей и вызывает подпрограмму doTheDancingDolls с аргументами, которые предоставляют доступ к моделируемой видеопамяти, и вышеупомянутым & ldquo; update & rdquo; Функциональность:

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.
}

& hellip; где объект memorySim каким-то образом обеспечивает необходимый указатель на моделируемую видеопамять & ndash; указатель, соответствующий адресу 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 , например. как & hellip;

Файл [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 & ldquo; окно консоли & rdquo;) и т. Д.:

Файл [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() (это не так много кода, но все же).

В любом случае, это должно послужить отправной точкой для экспериментов.

Отказ от ответственности: Я только что сделал это вместе. Это не очень тщательно протестировано. Я, например, мог неправильно понять, что такое библиотека проклятий & rsquo; обработка клавиатуры -. И я * m надеюсь , что он будет работать и в * nix-land, но я не знаю.

Приветствия и hth.,

1 голос
/ 18 августа 2012
 #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 */ 
0 голосов
/ 21 августа 2011

Мы говорим о фактическом выполнении изменений того, что отображается на вашем экране, или мы просто принимаем вышеуказанное задание в качестве упражнения.

В последнем случае это просто цикл по описанной области памяти (0xb8000000 + 25 (строки) * 80 (столбцы) * 2 (байты / отображаемый символ). нижний в нижний / верхний регистр. Преобразование просто сделано простой арифметикой. Посмотрите на http://www.asciitable.com/: Например: A = 0x41, a = 0x61 Поэтому для преобразования одного в другое просто добавьте / удалите 0x20 из прочитанного значения.

...