Hi!
I've recentely realized a function in C for displaying a win32 console screen:
//--------------------------------------------------------------------------------------------------
// Display a console screen in a 80 cols x 25 rows fixed size console
//--------------------------------------------------------------------------------------------------
// Structure for preparing output data
//--------------------------------------------------------------------------------------------------
// typedef struct _CHAR_INFO { // chi
// union { /* Unicode or ANSI character */
// WCHAR UnicodeChar;
// CHAR AsciiChar;
// } Char;
// WORD Attributes; // text and background colors
// } CHAR_INFO, *PCHAR_INFO;
//--------------------------------------------------------------------------------------------------
void DisplayCon(const CHAR_INFO * starget){
COORD bufferSize = {80, 25};
COORD TopLeft = {0,0};
SMALL_RECT Out_buf;
Out_buf.Left = 0;
Out_buf.Top = 0;
Out_buf.Right = 79;
Out_buf.Bottom = 24;
WriteConsoleOutput(wHnd,starget, bufferSize, TopLeft, &Out_buf);
return;
}
How do I define all these structures for calling the WriteConsoleOutput API,
and what is the MASM format for calling/invoking it?
In few words, how this function is going to look as a MASM procedure?
Thanks
they should already be defined for you in windows.inc :U
give me some time to scratch out some code...
BOOL WINAPI WriteConsoleOutput(
__in HANDLE hConsoleOutput,
__in const CHAR_INFO *lpBuffer,
__in COORD dwBufferSize,
__in COORD dwBufferCoord,
__inout PSMALL_RECT lpWriteRegion
);
helps if i use the right function - lol
BufHght EQU 25 ;buffer height (rows)
BufWdth EQU 80 ;buffer width (columns)
BufLen EQU BufHght*BufWdth ;string length (tchars)
.DATA?
hStdOut dw ?
CharBuf CHAR_INFO BufLen dup(<>)
;CHAR_INFO STRUCT
; Char CHARTYPE <>
; Attributes WORD ?
;CHAR_INFO ENDS
;CHARTYPE UNION
; UnicodeChar WORD ?
; AsciiChar db ?
;CHARTYPE ENDS
SizCord COORD <>
LocCord COORD <>
;COORD STRUCT
; x WORD ?
; y WORD ?
;COORD ENDS
WrRgn SMALL_RECT <>
;SMALL_RECT STRUCT
; Left WORD ?
; Top WORD ?
; Right WORD ?
; Bottom WORD ?
;SMALL_RECT ENDS
.CODE
INVOKE GetStdHandle,STD_OUTPUT_HANDLE
mov hStdOut,eax
INVOKE WriteConsoleOutput,
eax,
offset CharBuf,
offset SizCord,
offset LocCord,
offset WrRgn
sorry i don't have a little more time, Frank
i wanted to make a complete working example
but, you'll have to fill in the structures
i have shown their definitions (from windows.inc) in the comments
Thanks Dave. It is a good start. I have to prepare a small application
to read a file into a memory area, to process a little, and afterwards to fill
the fields before calling WriteConsoleOutput.
So let's start, it'll take a while :P at my slow pace :lol
Back to my pc for a while.
I put something together, but I probably still miss some SMALL_RECT
or COORD:
Microsoft (R) Macro Assembler Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Assembling: C:\masm32\examples\viewfmt\viewfmt.asm
C:\masm32\examples\viewfmt\viewfmt.asm(88) : error A2098:invalid operand for OFF
SET
C:\masm32\examples\viewfmt\viewfmt.asm(88) : error A2114:INVOKE argument type mi
smatch : argument : 5
C:\masm32\examples\viewfmt\viewfmt.asm(88) : error A2114:INVOKE argument type mi
smatch : argument : 4
C:\masm32\examples\viewfmt\viewfmt.asm(88) : error A2114:INVOKE argument type mi
smatch : argument : 3
_
Assembly Error
the prog I put together is:
;----------------------------------------------------------------------
; Reads a file containing a console screen format into an array
; of structure CHAR_INFO totalling 8000 bytes, and invoke the API
; to display it all at once: WriteConsoleOutput(). Wait a key and
; terminates the program.
;----------------------------------------------------------------------
; Input file: myfiles.txt
;----------------------------------------------------------------------
; Author: frktons @ MASM32 forum
; Date: 17/aug/2010.
;----------------------------------------------------------------------
include \masm32\include\masm32rt.inc
Console80x25 PROTO
.data
bytecount dd 8000 ; Bytes to read from input file
ConTitle db "Win32 Console 80 x 25 Fixed Size - Extended ASCII chart", 0
.data?
hFile HANDLE ?
charArray CHAR_INFO 2000 dup (<>)
wHnd HANDLE ?
rHnd HANDLE ?
inpbyte DWORD ?
.code
start:
mov hFile, fopen("myfiles.txt")
mov ebx, fread(hFile, offset charArray, bytecount)
fclose hFile
invoke AllocConsole
invoke Console80x25
invoke GetStdHandle, STD_INPUT_HANDLE
mov rHnd,eax
invoke ReadConsole, rHnd, ADDR inpbyte, 1, NULL, NULL
finish: invoke ExitProcess,0
ret
Console80x25 PROC
LOCAL windowSize:SMALL_RECT
LOCAL bufferSize:COORD
LOCAL startPoint:COORD
mov windowSize.Left, 0
mov windowSize.Right, 0
mov windowSize.Top, 79
mov windowSize.Bottom, 24
mov bufferSize.x, 80
mov bufferSize.y, 25
mov startPoint.x, 0
mov startPoint.y, 0
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov wHnd,eax
invoke SetConsoleTitle, ADDR ConTitle
invoke SetConsoleWindowInfo, wHnd, TRUE, ADDR windowSize
invoke SetConsoleScreenBufferSize, wHnd, DWORD PTR bufferSize
invoke WriteConsoleOutput, wHnd, offset charArray, offset bufferSize, offset startPoint, offset windowSize
ret
Console80x25 ENDP
end start
and guess where the errors are? Just in WriteConsoleOutput invoke.
What did I miss?
lemme seeeeee
you were trying to scroll, right ?
let me have a look.....
Quote from: dedndave on August 16, 2010, 11:38:06 PM
lemme seeeeee
you were trying to scroll, right ?
let me have a look.....
Hi Dave, I'm trying to display a complete console screen for the time being.
No scrolling at all. ::)
oh - now i remember - lol
i don't think i would put the structures local inside the proc
same is true of SetConsoleTitle, unless you want to change it each time
(generally, it is set at program init and left as is)
i would probably declare the structures as data, then pass the pointers to the proc
from there, you should be able to use PTR to point to the structure and access the different elements
AllocConsole is kind of a useless gizmo when called from a console app - lol
it creates a new console window, but the old one is rendered useless
or, at least, overly complicated to get back to :P
it is useful if you are running a GUI app and want a console window to run along side
the errors you are getting are because you can't use OFFSET with local variables
locals are on the stack, and are thus addressed with something like [ebp-4]
you may be able to use ADDR instead of OFFSET
although, you can run out of registers, too
in which case, you may have to use LEA and push the parms individually (you shouldn't have to do this)
but - if the structures are defined in the .data segment, you can use OFFSET
are you assembling this as a console app or GUI ?
oh - could you post your screen data file so i don't have to make one ?
thanks
frktons,
Most APIs are not sensitive to the direction flag (sometimes they clear it when they return), but I believe Console Write is sensitive. I have a wrapper that saves flags, clears direction, then calls Console Write, then restores flags. I did this for both Console Write and Console Read.
Dave.
generally, the DF is left cleared
if you set it, you should clear it when done
but, i don't see where Frank has set it
clearing and setting the DF, as well as PUSHF'ing and POPF'ing flags are surprisingly slow instructions
Now the pgm compiles but doesn't show anything worth seeing ::)
I attach the new version and the screen file for your tests.
The new code looks like this:
;----------------------------------------------------------------------
; Reads a file containing a console screen format into an array
; of structure CHAR_INFO totalling 8000 bytes, and invoke the API
; to display it all at once: WriteConsoleOutput(). Wait a key and
; terminates the program.
;----------------------------------------------------------------------
; Input file: myfile.txt
;----------------------------------------------------------------------
; Author: frktons @ MASM32 forum
; Date: 17/aug/2010.
;----------------------------------------------------------------------
include \masm32\include\masm32rt.inc
Console80x25 PROTO
.data
bytecount dd 8000 ; Bytes to read from input file
ConTitle db "Win32 Console 80 x 25 Fixed Size - Extended ASCII chart", 0
charArray CHAR_INFO 2000 dup (<>)
lp_charArray dd charArray[0]
hFile HANDLE 0
wHnd HANDLE 11
rHnd HANDLE 10
inpbyte DWORD 1
windowSize SMALL_RECT <>
bufferSize COORD <>
startPoint COORD <>
.data?
.code
start:
mov hFile, fopen("myfiles.txt")
mov ebx, fread(hFile, offset charArray, bytecount)
fclose hFile
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov wHnd,eax
mov windowSize.Left, 0
mov windowSize.Right, 0
mov windowSize.Top, 79
mov windowSize.Bottom, 24
mov bufferSize.x, 80
mov bufferSize.y, 25
mov startPoint.x, 0
mov startPoint.y, 0
invoke SetConsoleTitle, ADDR ConTitle
invoke Console80x25
invoke GetStdHandle, STD_INPUT_HANDLE
mov rHnd,eax
invoke ReadConsole, rHnd, ADDR inpbyte, 1, NULL, NULL
finish: invoke ExitProcess,0
ret
Console80x25 PROC
invoke SetConsoleWindowInfo, wHnd, TRUE, ADDR windowSize
invoke SetConsoleScreenBufferSize, wHnd, DWORD PTR bufferSize
invoke WriteConsoleOutput, wHnd, offset charArray, offset bufferSize, offset startPoint, offset windowSize
ret
Console80x25 ENDP
end start
and is attached in the zip file with the screen to display. Both need to stay on the same
folder. :P
well - it assembles
so, you got that going for you :bg
Quote from: dedndave on August 17, 2010, 12:28:33 AM
well - it assembles
so, you got that going for you :bg
And now let's try to make it work as well :lol
you are using ANSI chars, so each cell should look like this...
character db 'A',0 ;the 0 is a filler - if it were unicode, it would contain data
attributes dw Attrib ;refer to the link below
http://msdn.microsoft.com/en-us/library/ms682013%28VS.85%29.aspx
i am trying to write some code :P
if you make a new DAT file.....
Quote from: dedndave on August 17, 2010, 12:34:48 AM
you are using ANSI chars, so each cell should look like this...
character db 'A',0 ;the 0 is a filler - if it were unicode, it would contain data
attributes dw Attrib ;refer to the link below
http://msdn.microsoft.com/en-us/library/ms682013%28VS.85%29.aspx
i am trying to write some code :P
if you make a new DAT file.....
So far this kind of file has worked with PBCC and C with WriteConsoleOutput.
I guess it can work with MASM as well. I think the second byte for char and attrib
is just ignored, if it contains zero or space it should not change.
Anyway if you want to try with a file with zero after each meaningful byte here
you are, attached :P
ok - well - one of them should fly :U
Quote from: dedndave on August 17, 2010, 01:00:49 AM
ok - well - one of them should fly :U
From my point of view the one with zeros will not fly :P
well - it has some goofy attribute values
here is my code, so far
it does the same thing as yours - lol
let me play some more.....
removed attachment - see later post for working version
oops one mistake already...
ByteCount dd 4*BufLen ;Bytes to read from input file
I think this code:
ByteCount dd 2*BufLen
should be:
ByteCount dd 4*BufLen
each character on the screen (25*80) takes 4 bytes in the screen buffer
to display (2 for the char and 2 for the attribute).
lol - i think you're right, Frank :bg
2 changes:
mov windowSize.Left, 0
mov windowSize.Right, 79
mov windowSize.Top, 0
mov windowSize.Bottom, 24
left <> right
invoke WriteConsoleOutput, wHnd, offset charArray, DWORD PTR bufferSize, DWORD PTR startPoint, offset windowSize
the COORD parameters are the actual values, not the address
Quote from: sinsi on August 17, 2010, 01:44:54 AM
2 changes:
mov windowSize.Left, 0
mov windowSize.Right, 79
mov windowSize.Top, 0
mov windowSize.Bottom, 24
left <> right
invoke WriteConsoleOutput, wHnd, offset charArray, DWORD PTR bufferSize, DWORD PTR startPoint, offset windowSize
the COORD parameters are the actual values, not the address
Very well done, sinsi. Eventually I got my screen displayed.
Thanks a lot for your help. :U
@dedndave:
could you explain to me how did you get an executable that is 1/3 of mine?
You declared the structure CHAR_INFO in the .data? section, this is one,
and what else?
lol
i got it working, as well
i wrote a loop to fill the CharBuf with green "A"s, rather than reading the file
; mov hFile,fopen("myfiles.txt")
; mov ebx,fread(hFile,offset CharBuf,ByteCount)
; fclose hFile
mov ecx,2000
mov edi,offset CharBuf
test00:
mov dword ptr [edi],0A0041h
add edi,4
dec ecx
jnz test00
as for the size, i put the CharBuf buffer in the uninitialized data segment :bg
QuoteVery well done, sinsi. Eventually I got my screen displayed.
what am i ? - chopped liver - lol
Thanks dave, sinsi. :U
Going to sleep. I'll have to work on this brick and ReadConsoleInput
to build a working console menu old styled. :P
Holydays permitting, see you soon.
Frank
Quotewhat am i ? - chopped liver - lol
slurp! that's a good italian dish "fegato alla veneziana" with onions and herbs. :lol
it's all good, Frank :bg
here is my finished one
interesting to note that it works with either data file
frktons,
What exactly are you trying to do? Are you trying to print on the whole surface on the prompt?
yes - you can run it and see :P
Works very nice dave, good job.
I added this in at the end of PInit though:
mov cci.dwSize,sizeof cci
mov cci.bVisible,FALSE
INVOKE SetConsoleCursorInfo,hStdOut,addr cci
:P
Why?
;set structure values
mov word ptr SizCord.x,BufWdth
mov word ptr SizCord.y,BufHght
mov word ptr LocCord.x,0
mov word ptr LocCord.y,0
mov word ptr WrRgn.Left,0
mov word ptr WrRgn.Top,0
mov word ptr WrRgn.Right,BufWdth-1
mov word ptr WrRgn.Bottom,BufHght-1
Every word override adds a byte, so you have at least a qword of wasted bytes (66h), naughty.
Just define the structures already initialised...
SizCord COORD <80,25>
LocCord COORD <0,0>
WrRgn SMALL_RECT <0,0,79,24>
Quote from: sinsi on August 17, 2010, 07:45:23 AM
Why?
;set structure values
mov word ptr SizCord.x,BufWdth
mov word ptr SizCord.y,BufHght
mov word ptr LocCord.x,0
mov word ptr LocCord.y,0
mov word ptr WrRgn.Left,0
mov word ptr WrRgn.Top,0
mov word ptr WrRgn.Right,BufWdth-1
mov word ptr WrRgn.Bottom,BufHght-1
Every word override adds a byte, so you have at least a qword of wasted bytes (66h), naughty.
Just define the structures already initialised...
SizCord COORD <80,25>
LocCord COORD <0,0>
WrRgn SMALL_RECT <0,0,79,24>
Reducing the size of the overall process, and getting it a bit faster will be
the next step in the project, in my planning at least. So any comment on
how to reduce the size and optimize the speed is welcome. :U
Your comment is about reducing the size of the program, if I got it right,
I'll try ASAP and let you know. :bg
Edit: the size of the exe doesn't change a byte [2.560 bytes]. I'm going to try Polink to see what happens.
Edit2: with the good old polink I got an exe file of 1.536 bytes:
\masm32\polink /SUBSYSTEM:console /ALIGN:4096 WrCon2.obj
:P
Quote from: dedndave on August 17, 2010, 02:21:52 AM
it's all good, Frank :bg
....
interesting to note that it works with either data file
Good to know that the WriteConsoleOutput() API just ignores the second byte
passed [zero or space].
Not so much reducing the exe size, more like since they are constants you can define them initialised in the data section.
This saves the code to initialise them but unless your codesize is large (around the size of the file section alignment) there is no real benefit.
Mind you, if you do it for a lot of structures then it makes a difference - 4 bytes in .data versus ~12 in .code.
We are, after all, asm programmers who boast about small code sizes... :bdg
Quote from: sinsi on August 17, 2010, 11:17:07 AM
Not so much reducing the exe size, more like since they are constants you can define them initialised in the data section.
This saves the code to initialise them but unless your codesize is large (around the size of the file section alignment) there is no real benefit.
Mind you, if you do it for a lot of structures then it makes a difference - 4 bytes in .data versus ~12 in .code.
We are, after all, asm programmers who boast about small code sizes... :bdg
You mean that if I had 200-300 data structure in the program, initializing them
in .data section will get 1 kb more or less of gain vs .code section? And the overall speed of the
program will be affected as well?
I ask because the far away plan is to have this amount of data in the final project, I
mean 200-300 console screen defined and initialized inside the program without
external data files. All the data and code will be inside the program/libraries. Nothing
to read for displaying console screens, only proc calling that will design them
on the fly.
The idea is to try and use one structure already defined, then only change the members that need it (if any).
If you are going to be showing different screens, but they are all the same size, the only thing that changes is the address of the data - 0,0-79,24 don't.
Quote from: sinsi on August 17, 2010, 12:32:27 PM
The idea is to try and use one structure already defined, then only change the members that need it (if any).
If you are going to be showing different screens, but they are all the same size, the only thing that changes is the address of the data - 0,0-79,24 don't.
Yes sinsi, the idea is that all the console screens will have the same aspect [25 rows * 80 cols]
and a group of proc and data will create them as needed, using the same screen buffer, or more
than one probably, because I'll need
save_screen / restore_screen functions to swap
among the most used ones. I am in the design fase of the project, and from what I can imagine
for the time being, many repetitive data patterns can be replaced by code in order to have a more
compact program. [you are Assembly programmers, after all, and the size and speed count] :P
If I had, for example 10 console screens of 8000 bytes each and 5000 bytes of code, my hope
is to reduce the overall dimesions from 85000 bytes to 9000-10000 with a gain of 80-90%
in the size, without any performace loss. Not having to read from external files the data to be
displayed, the performance will probably be better than otherwise. ::)
By the way, the next thread I'll start on september will be about this task:
from xx kb to x kbtransforming data into code.
well - not knowing how it might be used, i wrote it that way
i am aware of override bytes and speed penalties
i wanted something that was simple and easy to understand - just to get it working
as for setting the console cursor to off, that doesn't always seem to work
but, if you want to use SetConsoleCursorInfo, it may be best to fill the structure with GetConsoleCursorInfo, first
then modify the values as desired prior to the SetConsoleCursorInfo call
you don't need "save screen" and "restore screen"
you can use the screen buffer pages (4 pages i think, in this mode - maybe 8 - i forget)
you can read/write one page while viewing another
then, just switch pages
it is very fast, and makes the program appear to be even faster - lol
you can usually do a pretty good job on a single-screen display using only 2 pages
if they flip to a menu or something, additional pages
Dave you are right in saying that the cci needs to be be filled before trying to turn the console cursor on or off. Here is the method I use to turn the cursor on or off & it works every time.
.data?
cci CONSOLE_CURSOR_INFO <>
.code
; -------------------------------------------------------------------------
hidecursor:
invoke GetConsoleCursorInfo,hConsoleOutput,addr cci
mov cci.bVisible,FALSE
invoke SetConsoleCursorInfo,hConsoleOutput,addr cci
ret
; -------------------------------------------------------------------------
showcursor:
invoke GetConsoleCursorInfo,hConsoleOutput,addr cci
mov cci.bVisible,TRUE
invoke SetConsoleCursorInfo,hConsoleOutput,addr cci
ret
; -------------------------------------------------------------------------
Call these procs from inside your prog & you have complete control.
well - it doesn't always work for all adapters - i am guessing, here :P
let me put it this way - i have seen it not work - lol
Quote from: dedndave on August 17, 2010, 02:58:40 PM
you don't need "save screen" and "restore screen"
you can use the screen buffer pages (4 pages i think, in this mode - maybe 8 - i forget)
you can read/write one page while viewing another
then, just switch pages
it is very fast, and makes the program appear to be even faster - lol
you can usually do a pretty good job on a single-screen display using only 2 pages
if they flip to a menu or something, additional pages
Well, this is one of the ways I think I'm going to do it. The
save_screen / restore screencould use this method or any suitable one, and I'll need a
save_area / restore_area functions
as well for displaying boxes on actual screen and restoring back the area of the screen buffer
that is temporarily swapped out with
save_area using the appropriate APIs.
Regarding the cursor on/off :
Quote from: Neil on August 17, 2010, 04:00:29 PM
Dave you are right in saying that the cci needs to be be filled before trying to turn the console cursor on or off. Here is the method I use to turn the cursor on or off & it works every time.
.data?
cci CONSOLE_CURSOR_INFO <>
.code
; -------------------------------------------------------------------------
hidecursor:
invoke GetConsoleCursorInfo,hConsoleOutput,addr cci
mov cci.bVisible,FALSE
invoke SetConsoleCursorInfo,hConsoleOutput,addr cci
ret
; -------------------------------------------------------------------------
showcursor:
invoke GetConsoleCursorInfo,hConsoleOutput,addr cci
mov cci.bVisible,TRUE
invoke SetConsoleCursorInfo,hConsoleOutput,addr cci
ret
; -------------------------------------------------------------------------
Call these procs from inside your prog & you have complete control.
I've used these APIs in PBCC and C programs and they work just fine. :U
yes - Neil's cursor procs are just what you need
notice that the structure is updated with current values each time
if you make changes in these values elsewhere in the program, the procs will still work with no surprises (i.e. bugs)
what you are talking about is the text-mode equivalent of a "Z-buffer"
and, the alternate pages may be used that way
it seemed a little simpler in the old DOS days, though :P
Quote from: dedndave on August 17, 2010, 10:59:08 PM
yes - Neil's cursor procs are just what you need
notice that the structure is updated with current values each time
if you make changes in these values elsewhere in the program, the procs will still work with no surprises (i.e. bugs)
what you are talking about is the text-mode equivalent of a "Z-buffer"
and, the alternate pages may be used that way
it seemed a little simpler in the old DOS days, though :P
Yes Dave, you are right. I'm trying to recreate the old DOS Menu/Screen/Boxes
and so on of my youth :P and it was easier in those times. But now we can
take advantage of Gbs of RAM and Tbs of Hard disk space, with processors that
are nearly 500/1000 times more powerful. So we loose something and we gain
something else. :lol
That's life anyway :bg