Добавил:
Studfiles2
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Курсовая работа2 / GenAsm
.cpp
// File: GenAsm.cpp
// Purpose: Генерация ассемблерного кода
//*****************************************************************************
#include "StdAfx.h"
#include "GenAsm.h"
#include "Utilities.h"
#include "TableMgr.h"
#include "Limits.h"
#include <assert.h>
//-----------------------------------------------------------------------------
CGenAsm::CGenAsm()
{
m_pFile = NULL;
m_NextLabel = 0;
}
//-----------------------------------------------------------------------------
std::string CGenAsm::NewLabel()
{
char buffer[128];
sprintf(buffer, "_ASM_Label_%u", m_NextLabel++);
return std::string(buffer);
}
//-----------------------------------------------------------------------------
const char * CGenAsm::GetConditionInstruction(TET_OPERATION op)
{
switch (op)
{
case TET_OP_REL_E_F:
case TET_OP_REL_E : return "je";
case TET_OP_REL_NE_F:
case TET_OP_REL_NE : return "jne";
case TET_OP_REL_G_F: return "ja";
case TET_OP_REL_G : return "jg";
case TET_OP_REL_GE_F: return "jae";
case TET_OP_REL_GE : return "jge";
case TET_OP_REL_L_F: return "jb";
case TET_OP_REL_L : return "jl";
case TET_OP_REL_LE_F: return "jbe";
case TET_OP_REL_LE : return "jle";
default:
assert(false);
return "Error!!!";
}
}
//-----------------------------------------------------------------------------
void CGenAsm::GenerateAsmFile(const char *FileName, CNameTable& NameTable, CTetradStream &Tetrads)
{
m_pFile = fopen(FileName, "w");
assert(m_pFile);
if(!m_pFile) return;
char sBuff[256];
UTIL::GetTimeStr(sBuff, sizeof(sBuff));
fprintf(m_pFile, "; Файл создан: \t %s \n", sBuff);
fprintf(m_pFile, "; --------------------------------------------\n");
WriteAsmPrefix();
WriteSegmentStack();
WriteSegmentData(NameTable);
WriteSegmentCode(Tetrads);
fclose(m_pFile);
}
//-----------------------------------------------------------------------------
void CGenAsm::WriteAsmPrefix()
{
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n .286");
fprintf(m_pFile, "\n DOSSEG");
fprintf(m_pFile, "\n .MODEL SMALL");
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n EXTRN _write_char: PROC");
fprintf(m_pFile, "\n EXTRN _write_int : PROC");
fprintf(m_pFile, "\n EXTRN _write_flt : PROC");
fprintf(m_pFile, "\n EXTRN _write_str : PROC");
fprintf(m_pFile, "\n EXTRN _write_ln : PROC");
fprintf(m_pFile, "\n EXTRN _read_char : PROC");
fprintf(m_pFile, "\n EXTRN _read_int : PROC");
fprintf(m_pFile, "\n EXTRN _read_flt : PROC");
fprintf(m_pFile, "\n EXTRN _read_str : PROC");
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n EXTRN _copy_str : PROC");
fprintf(m_pFile, "\n EXTRN _concat_str: PROC");
}
//-----------------------------------------------------------------------------
void CGenAsm::WriteSegmentStack()
{
/*
fprintf(m_pFile, "\n; **************************************");
fprintf(m_pFile, "\n; ********** Сегмент стека ************* \n");
fprintf(m_pFile, "\n Stack_Seg SEGMENT STACK");
fprintf(m_pFile, "\n DB 4096 dup(0)");
fprintf(m_pFile, "\n Stack_Seg ENDS");
*/
}
//-----------------------------------------------------------------------------
void CGenAsm::WriteSegmentData(CNameTable& NameTable)
{
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n; **************************************");
fprintf(m_pFile, "\n; *********** Сегмент данных ***********");
fprintf(m_pFile, "\n");
//fprintf(m_pFile, "\nData_Seg SEGMENT\n");
fprintf(m_pFile, "\n .DATA");
char * sSize = NULL;
char sBuff[512];
bool bLiter, bTemp;
unsigned index;
for (CNameTable::iterator it = NameTable.begin(); it != NameTable.end(); ++it)
{
bLiter = it->second.bLiter;
bTemp = it->second.bTemp;
index = it->second.index;
switch(it->second.type)
{
case TYPE_INTEGER:
sSize = "DW"; // для Integer выделяется слово
if (bLiter)
sprintf(sBuff, "%d", g_TableMgr.GetValueInt(index));
else
sprintf(sBuff, "0h ; %s", bTemp ? "временная": g_TableMgr.IDLexem(index));
break;
case TYPE_BOOLEAN: // 1 байт
sSize = "DB";
if (bLiter)
sprintf(sBuff, "%d", g_TableMgr.GetValueBool(index) ? 1 : 0);
else
sprintf(sBuff, "0h ; %s", bTemp ? "временная": g_TableMgr.IDLexem(index));
break;
case TYPE_REAL:
sSize = "DD";
if (bLiter)
sprintf(sBuff, "%f", g_TableMgr.GetValueReal(index));
else
sprintf(sBuff, "0h ; %s", bTemp ? "временная": g_TableMgr.IDLexem(index));
break;
case TYPE_STRING:
sSize = "DB";
if (bLiter)
sprintf(sBuff, "\"%s\",0", g_TableMgr.GetValueStr(index).c_str());
else
sprintf(sBuff, "256 dup(0) ; %s", bTemp ? "временная" : g_TableMgr.IDLexem(index));
break;
case TYPE_CHAR:
sSize = "DB";
if (bLiter)
sprintf(sBuff, "'%c'", g_TableMgr.GetValueChar(index));
else
sprintf(sBuff, "0 ; %s", bTemp ? "временная" : g_TableMgr.IDLexem(index));
break;
case TYPE_STACK:
// Стек может встретиться только как явно объявленная переменная
sSize = "DB";
if (bLiter)
assert(false);
else
sprintf(sBuff, "%d dup(0) ; %s", STACK_SIZE_BYTES, g_TableMgr.IDLexem(index));
break;
}
// Собственно записываем объявление переменной
fprintf(m_pFile, "\n%-16s %-10s %s", it->first.c_str(), sSize, sBuff);
}
//fprintf(m_pFile, "\n\nData_Seg ENDS");
}
//-----------------------------------------------------------------------------
void CGenAsm::WriteSegmentCode(CTetradStream &Tetrads)
{
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n; **************************************");
fprintf(m_pFile, "\n; ************* Сегмент кода ***********");
fprintf(m_pFile, "\n");
// fprintf(m_pFile, "\n Code_Seg SEGMENT");
// fprintf(m_pFile, "\n ASSUME CS: Code_Seg, DS: Data_Seg, SS: Stack_Seg");
// fprintf(m_pFile, "\n main PROC");
// fprintf(m_pFile, "\n");
// fprintf(m_pFile, "\n; Инициализация сегмента данных (1 шт.)");
// fprintf(m_pFile, "\n mov AX, Data_Seg");
// fprintf(m_pFile, "\n mov DS, AX");
fprintf(m_pFile, "\n .CODE");
fprintf(m_pFile, "\n PUBLIC _asmentry");
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n _asmentry PROC");
fprintf(m_pFile, "\n ; Собственно код...");
fprintf(m_pFile, "\n");
for (CTetradStream::iterator it = Tetrads.begin(); it != Tetrads.end(); ++it)
{
WriteTetrad(*it);
}
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n RET");
fprintf(m_pFile, "\n _asmentry ENDP");
fprintf(m_pFile, "\n END");
//fprintf(m_pFile, "\n mov AX, 4c00h");
//fprintf(m_pFile, "\n int 21h");
//fprintf(m_pFile, "\n");
//fprintf(m_pFile, "\n main ENDP");
//fprintf(m_pFile, "\n Code_Seg ENDS");
//fprintf(m_pFile, "\n END main");
}
//-----------------------------------------------------------------------------
void CGenAsm::WriteTetrad(STetrad &tet)
{
char sBuff[256];
std::string sLabel1, sLabel2, sLabel3;
tet.Print(sBuff);
fprintf(m_pFile, "\n");
fprintf(m_pFile, "\n ; %s", sBuff);
switch (tet.operation)
{
// Операции целочисленной арифметики -----------------
case TET_OP_ADD:
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n add AX, word ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n mov word ptr %s, AX", tet.result.c_str());
break;
case TET_OP_SUBTRACT:
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n sub AX, word ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n mov word ptr %s, AX", tet.result.c_str());
break;
case TET_OP_MULT:
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n imul word ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n mov word ptr %s, AX", tet.result.c_str());
break;
case TET_OP_DIV:
// В нашей реализации операций целочисленного деления быть не должно
assert(false);
// fprintf(m_pFile, "\n mov AX, %s", tet.operand1.c_str());
// fprintf(m_pFile, "\n sub AX, %s", tet.operand2.c_str());
// fprintf(m_pFile, "\n mov %s, AX", tet.result.c_str());
break;
case TET_OP_UMINUS :
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n neg AX");
fprintf(m_pFile, "\n mov word ptr %s, AX", tet.result.c_str());
break;
// Операции сравнения целых чисел --------------------
case TET_OP_REL_E :
case TET_OP_REL_NE :
case TET_OP_REL_G :
case TET_OP_REL_GE :
case TET_OP_REL_L :
case TET_OP_REL_LE :
sLabel1 = NewLabel();
sLabel2 = NewLabel();
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n cmp AX, word ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n %s %s", GetConditionInstruction(tet.operation), sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 0", tet.result.c_str());
fprintf(m_pFile, "\n jmp %s", sLabel2.c_str());
fprintf(m_pFile, "\n %s :", sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 1", tet.result.c_str());
fprintf(m_pFile, "\n %s :", sLabel2.c_str());
break;
// Операции сравнения вещественных чисел --------------------
case TET_OP_REL_E_F :
case TET_OP_REL_NE_F :
case TET_OP_REL_G_F :
case TET_OP_REL_GE_F :
case TET_OP_REL_L_F :
case TET_OP_REL_LE_F :
sLabel1 = NewLabel();
sLabel2 = NewLabel();
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fcompp");
fprintf(m_pFile, "\n fstsw ax");
fprintf(m_pFile, "\n sahf");
fprintf(m_pFile, "\n fwait");
fprintf(m_pFile, "\n %s %s", GetConditionInstruction(tet.operation), sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 0", tet.result.c_str());
fprintf(m_pFile, "\n jmp %s", sLabel2.c_str());
fprintf(m_pFile, "\n %s :", sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 1", tet.result.c_str());
fprintf(m_pFile, "\n %s :", sLabel2.c_str());
break;
// Операция приведения целого к вещественному --------
case TET_OP_ITOF :
fprintf(m_pFile, "\n fild word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
// Операция приведения символа к строке --------------
case TET_OP_CHAR_TO_STR:
fprintf(m_pFile, "\n mov BP, offset %s" , tet.result.c_str());
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n mov [BP], AL");
fprintf(m_pFile, "\n mov [BP+1], 0");
break;
// Операции вещественной арифметики ------------------
case TET_OP_ADD_F:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n fadd");
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
case TET_OP_SUBTRACT_F:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n fsub");
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
case TET_OP_MULT_F:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n fmul");
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
case TET_OP_DIV_F:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n fdiv");
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
case TET_OP_UMINUS_F:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fchs");
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
break;
// Операции вывода -----------------------------------
case TET_OP_WRITE_CHAR:
fprintf(m_pFile, "\n xor AX, AX");
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n push AX");
fprintf(m_pFile, "\n call _write_char");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_WRITE_INT:
fprintf(m_pFile, "\n push word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n call _write_int");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_WRITE_REAL:
fprintf(m_pFile, "\n push dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n call _write_flt");
fprintf(m_pFile, "\n add sp, 4");
break;
case TET_OP_WRITE_STR:
fprintf(m_pFile, "\n push offset %s", tet.operand1.c_str());
fprintf(m_pFile, "\n call _write_str");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_WRITELN:
fprintf(m_pFile, "\n call _write_ln");
break;
// Операции вывода -----------------------------------
case TET_OP_READ_CHAR:
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n call _read_char");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_READ_INT:
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n call _read_int");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_READ_REAL:
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n call _read_flt");
fprintf(m_pFile, "\n add sp, 2");
break;
case TET_OP_READ_STR:
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n call _read_str");
fprintf(m_pFile, "\n add sp, 2");
break;
// Операции присваивания -----------------------------
case TET_OP_ASSIGN_INT:
fprintf(m_pFile, "\n mov AX, word ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n mov word ptr %s, AX", tet.result.c_str());
break;
case TET_OP_ASSIGN_FP:
fprintf(m_pFile, "\n fld dword ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n fstp dword ptr %s", tet.result.c_str());
fprintf(m_pFile, "\n fwait");
break;
case TET_OP_ASSIGN_BOOL: // размеры совпадают, так что совместим
case TET_OP_ASSIGN_CH:
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, AL", tet.result.c_str());
break;
case TET_OP_ASSIGN_STR:
fprintf(m_pFile, "\n push offset %s", tet.operand1.c_str());
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n call _copy_str");
fprintf(m_pFile, "\n add sp, 4");
break;
case TET_OP_ASSIGN_STACK:
sLabel1 = NewLabel();
// fprintf(m_pFile, "\n mov BP, offset %s", tet.operand1.c_str()); // присваиваемое
// fprintf(m_pFile, "\n mov BX, offset %s", tet.result.c_str()); // результат
fprintf(m_pFile, "\n mov CX, %d", STACK_SIZE_BYTES-1);
fprintf(m_pFile, "\n mov SI, 0");
fprintf(m_pFile, "\n %s :" , sLabel1.c_str());
fprintf(m_pFile, "\n mov AL, %s[SI]", tet.operand1.c_str());
fprintf(m_pFile, "\n mov %s[SI], AL", tet.result.c_str());
fprintf(m_pFile, "\n inc SI");
fprintf(m_pFile, "\n loop %s" , sLabel1.c_str());
break;
// Операции передачи управления ----------------------
case TET_OP_DEF_LABEL :
fprintf(m_pFile, "\n %s :", tet.operand1.c_str());
fprintf(m_pFile, "\n");
break;
case TET_OP_GOTO :
fprintf(m_pFile, "\n jmp %s", tet.operand1.c_str());
break;
case TET_OP_GOTO_ZERO :
// Операции семейства "jcc" позволяют прыгать только на -127..128 байт
// что в данном случае может быть недостаточно
// поэтому прыгать будет ближним безусловным переходом
sLabel1 = NewLabel();
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n cmp AL, 0");
fprintf(m_pFile, "\n jne %s", sLabel1.c_str());
fprintf(m_pFile, "\n jmp %s", tet.operand2.c_str());
fprintf(m_pFile, "\n %s :" , sLabel1.c_str());
break;
// Операция инкремента целого числа ------------------
case TET_OP_INC_INT:
fprintf(m_pFile, "\n inc word ptr %s", tet.operand1.c_str());
break;
// Логическое И --------------------------------------
// Реализация почти идентична тому, что делает BC++ 3.1
case TET_OP_AND:
sLabel1 = NewLabel(); // переходим если FALSE
sLabel2 = NewLabel(); // конец
fprintf(m_pFile, "\n cmp byte ptr %s, 0", tet.operand1.c_str());
fprintf(m_pFile, "\n je %s" , sLabel1.c_str());
fprintf(m_pFile, "\n cmp byte ptr %s, 0", tet.operand2.c_str());
fprintf(m_pFile, "\n je %s" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 1", tet.result.c_str());
fprintf(m_pFile, "\n jmp %s" , sLabel2.c_str());
fprintf(m_pFile, "\n %s :" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 0", tet.result.c_str());
fprintf(m_pFile, "\n %s :" , sLabel2.c_str());
break;
// Логическое ИЛИ ------------------------------------
case TET_OP_OR:
sLabel1 = NewLabel();
sLabel2 = NewLabel();
sLabel3 = NewLabel();
fprintf(m_pFile, "\n cmp byte ptr %s, 0", tet.operand1.c_str());
fprintf(m_pFile, "\n jne %s" , sLabel1.c_str());
fprintf(m_pFile, "\n cmp byte ptr %s, 0", tet.operand2.c_str());
fprintf(m_pFile, "\n jne %s" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 0", tet.result.c_str());
fprintf(m_pFile, "\n jmp %s" , sLabel2.c_str());
fprintf(m_pFile, "\n %s :" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 1", tet.result.c_str());
fprintf(m_pFile, "\n %s :" , sLabel2.c_str());
break;
// Логическое НЕ -------------------------------------
// BC++ 3.1 делает нечто интересное... разбираться некогда, делаем втупую :)
case TET_OP_NOT:
sLabel1 = NewLabel();
sLabel2 = NewLabel();
fprintf(m_pFile, "\n cmp byte ptr %s, 0", tet.operand1.c_str());
fprintf(m_pFile, "\n je %s" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 0", tet.result.c_str());
fprintf(m_pFile, "\n jmp %s" , sLabel2.c_str());
fprintf(m_pFile, "\n %s :" , sLabel1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, 1", tet.result.c_str());
fprintf(m_pFile, "\n %s :" , sLabel2.c_str());
break;
// Конкатенация строк --------------------------------
case TET_OP_STR_CONCAT:
fprintf(m_pFile, "\n push offset %s", tet.result.c_str());
fprintf(m_pFile, "\n push offset %s", tet.operand2.c_str());
fprintf(m_pFile, "\n push offset %s", tet.operand1.c_str());
fprintf(m_pFile, "\n call _concat_str");
fprintf(m_pFile, "\n add sp, 6");
break;
// Операции со стеком --------------------------------
case TET_OP_STACK_PUSH:
// количество байт в стеке лежит в первом байте
fprintf(m_pFile, "\n xor AX, AX");
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n mov SI, AX");
fprintf(m_pFile, "\n inc SI");
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand2.c_str());
fprintf(m_pFile, "\n mov %s[SI], AL", tet.operand1.c_str());
fprintf(m_pFile, "\n mov AX, SI");
fprintf(m_pFile, "\n mov byte ptr %s, AL", tet.operand1.c_str());
break;
case TET_OP_STACK_TOP:
fprintf(m_pFile, "\n xor AX, AX");
fprintf(m_pFile, "\n mov AL, byte ptr %s", tet.operand1.c_str());
fprintf(m_pFile, "\n mov SI, AX");
fprintf(m_pFile, "\n mov AL, %s[SI]", tet.operand1.c_str());
fprintf(m_pFile, "\n mov byte ptr %s, AL", tet.result.c_str());
break;
case TET_OP_STACK_POP:
fprintf(m_pFile, "\n dec byte ptr %s", tet.operand1.c_str());;
break;
default:
assert(false);
fprintf(m_pFile, "\n ");
fprintf(m_pFile, "\n !!!!!! Error: unknown tetrad !!!!!");
fprintf(m_pFile, "\n ");
}
fprintf(m_pFile, "\n ");
}
Соседние файлы в папке Курсовая работа2