1 Programming Language အသစ္ထြင္ၾကမယ္ (၂) 24th April 2009, 8:32 am
sHa92
Founder
Adding Operands
ခုေလာေလာဆယ္ ကၽြန္ေတာ္တို႕ရဲ႕ Op-code TALK က ပုံေသတမ်ိဳးဘဲ print ထုတ္ေပးမွာ ျဖစ္ပါတယ္။ ဒီေတာ့ ပုံေသမဟုတ္ဘဲ ေပးထားတဲ့ number တခု screen ေပၚကိုရိုက္ထုတ္ေပးတဲ့ Op-code အသစ္တခုထပ္ ထည့္ၾကည့္ရေအာင္။
ဒီလို user (ကိုယ့္ language ကိုသုံးမဲ့ programmer) ကေပးတဲ့ data ကို print ထုတ္ျပဖို႔အတြက္ Op-code ကို data နဲ႔တြဲေပးဖို႔လိုပါတယ္။ အဲလို Data မိ်ဳးကို operand လို႔ေခၚတာ သိၾကမွာပါ။ ဒီေနရာမွာ ကိုယ့္ code မွာ operand ဘယ္ႏွစ္ခုသုံးခြင့္ေပးမလဲ ဆိုတာ ဆုံးျဖတ္ဖို႔လိုပါတယ္။ ဒီ tutorial မွာေတာ့ operand ၂ ခုလက္ခံေအာင္ ေရးထားပါတယ္။ ၂ ခုဆို assembly အတြက္ေတာ့ ေတာ္ေတာ္အလုပ္ျဖစ္ေနပါၿပီ။
ကဲ operand နဲ႔ Op-code တြဲေပးဖို႔ ေအာက္ကလို code တခ်ိဳ႕ျပင္ပါမယ္။
NUM ဆိုတဲ့ Op-code အသစ္တခုထပ္ထည့္ရေအာင္၊
Virtual Machine ထဲက switch ထဲမွာလဲ NUM ကို process လုပ္တဲ့ code ျဖည့္ထည့္ရပါမယ္၊
ကဲဒါဆို ကၽြန္ေတာ္တို႔ရဲ႕ code အသစ္ေလးကို စမ္းၾကည့္ရေအာင္။
ဒါဆို ကၽြန္ေတာ္တို႔ရဲ႕ script ကေအာက္ကလို print ထုတ္ေပးပါလိမ့္မယ္။
Hello, I am simplest language!
Current Number: 101
Current Number: 456
Adding Memory Support
Op-code တခုခ်င္းဆီမွာ data ကို operand အျဖစ္ထည့္ေပးလို႔ရတယ္။ ဒါေပမဲ့ Op-code တခုနဲ႔ တခုၾကား data ကို share လုပ္ခ်င္ရင္ ဘယ္လို လုပ္မလဲ။ global data pool တခုေတာ့ လိုေနပါတယ္။ ဒါမွ ပထမ Op-code က process လုပ္လို႔ထြက္လာတဲ့ data ကို ေနာက္ op-code က ယူသုံးလို႔ရမွာေလ။ ဒီအတြက္ Memory ဆိုတဲ့ array တခုကို Program class မွာသြားေၾကညာေပးရပါမယ္။
ဒီေနရာမွာ ေမးစရာက ဘာေၾကာင့္ global memory ကို Virtual Machine မွာမထည့္ဘဲ Program မွာသြားထည့္ရတာလဲ ဆိုတာပါ။ ဟုတ္တယ္ေလ၊ Global memory က Virtual Machine မွာအသုံးျပဳမွာ မဟုတ္လား။ ဒီလို ေၾကညာလဲ ရေတာ့ရပါတယ္။ ဒါေပမဲ့ Virtual Machine မွာ program ၂ ခုထက္ပို run တဲ့ အခါ ပထမ program သုံးသြားတဲ့ memory ကိုေနာက္ program က share သုံးရပါလိမ့္မယ္။ ဒီေတာ့ မလိုလားအပ္တဲ့ error ေတြတက္လာႏိုင္ပါတယ္။ ဒါေၾကာင့္ ကၽြန္ေတာ္တို႔က global memory ရဲ႕ scope ကို Program class level မွာဘဲထားၿပီး Program class တခုတိုင္း global memory pool တခုရွိေစခ်င္လို႕ global memory ကို Program class မွာေၾကညာရျခင္း ျဖစ္ပါတယ္။
pMemory ကို unsigned char အေနနဲ႔ဘဲ ေၾကညာထားပါတယ္။ ဒီေနရာမွာ 4-bytes ရွိတဲ့ int အျဖစ္ေပးရင္ ရေပမဲ့ 1 byte ရွိတဲ့ char data type ကို save သြားလုပ္ရင္လဲ 4 bytes စာယူပါလိမ့္မယ္။ ဒါေၾကာင့္အငယ္ဆုံး size ကိုေပးထားတာပါ။ ဒီေတာ့ 4-byte ရွိတဲ့ integer တခု save လုပ္ခ်င္ရင္ pMemory မွာ ၄ ေနရာယူပါလိမ့္မယ္။
Memory ကိုအသုံးျပဳဖို႔ function တခ်ိဳ႔လုိပါလိမ့္မယ္။
WriteMem() ဆိုတဲ့ function က memory array ေပၚမွာ data သြားေရးတာပါ။ Address ကေတာ့ ကိုယ္ေရးခ်င္တဲ့ address ေပါ့။ ဥပမာ၊ ပထမ integer value တခုသြားေရးခ်င္ရင္၊
program.WriteMem(0, 101, 4); ေပါ့။
ဒုတိယ integer တခုသြားေရးခ်င္ရင္ address 4 မွာေရးရပါမယ္ (ပထမ integer က address 0 ကေန 3 ထိေနရာယူထားတယ္ေလ)။
program.WriteMem(4, 456, 4); ေပါ့။
ReadMem ကေတာ့ WriteMem ရဲ႕ေျပာင္းျပန္ memory ကေနျပန္ဖတ္တဲ့ function ပါ။ Program() constructor ထဲမွာ ကၽြန္ေတာ္တို႔ရဲ႕ memory ကို ေဆာက္ေပးဖို႔လိုပါလိမ့္မယ္။ ခုကၽြန္ေတာ္တို႕ memory size ကို 10KB ေပးထားပါတယ္။ ဒီေတာ့ ကၽြန္ေတာ္တို႕ language မွာ 10KB ထက္ပိုၿပီး သိမ္းလို႔မရဘူးေပါ့ဗ်ာ။ ဒီထက္ပိုသိမ္းခ်င္ရင္ memory size ကိုျပင္ေပးရပါလိမ့္မယ္။ Destructor ထဲမွာ memory ကို delete လုပ္ခဲ့ဖို႔လဲ မေမ့ပါနဲ႔။
ဒီေနရာမွာ အားသာခ်က္တခုက global memory ကို 0 ေတြတခါတည္း initialize လုပ္ထားလို႔ရတာပါဘဲ (constructor ထဲမွာ “ZeroMemory(pMemory, 10240);” လို႔ေရးထားတယ္ေလ)။ C++ မွာဆို memory ကအစမွာ ေျဗာက္ေသာက္ျဖစ္ေနတာမို႕ variable ေၾကညာတိုင္းအၿမဲ initialize လုပ္ေပးရပါတယ္။ ဒါက Java လို Virtual Machine မွာ run တဲ့ programming language ေတြရဲ႕အားသာခ်က္တခုပါ။
ခုကၽြန္ေတာ္တို႔ဆီမွာ global memory ရွိၿပီမို႔ memory ကို access လုပ္တဲ့ Op-code တခ်ိဳ႕ေရးဖို႕ဘဲက်န္ပါေတာ့တယ္။ ဒီအတြက္ memory ကိုထဲကိုသြားသိမ္းမဲ့ OP_PUT_MEM ဆိုတဲ့ Op-code ကိုထည့္လိုက္ပါ။
ေနာက္ၿပီးရင္ VirtualMachine::Run() function ထဲမွာ OP_PUT_MEM အတြက္သြားေရးပါ။
ဒီေနရာမွာ Instruction ရဲ႕ ပထမ operand ကကိုယ္ေရးခ်င္တဲ့ memory address ျဖစ္ၿပီး ဒုတိယ operand ကကိုယ္သိမ္းခ်င္တဲ့ data ျဖစ္ပါတယ္။ သိမ္းတဲ့ data က integer မို႔ size ကေတာ့ 4 ပါ။ ၿပီးရင္ NUM ဆိုတဲ့ Op-code ကို operand ကို print လုပ္မဲ့အစား memory ထဲက data ကို print လုပ္တဲ့ function ေျပာင္းေရးၾကည့္ရေအာင္။
ဒီေနရာမွာ operand 0 က data မဟုတ္ေတာ့ဘဲ print ထုတ္မဲ့ memory ရဲ႕ address ျဖစ္သြားပါတယ္။ ကဲ ခုကၽြန္ေတာ္တို႕ရဲ႕ code ကို testing လုပ္ၾကည့္ရေအာင္။ main() function ကိုေအာက္ကအတိုင္းျပင္ပါမယ္။
Hello, I am simplest language!
Current Number: 101
Current Number: 456
ခုေလာေလာဆယ္ ကၽြန္ေတာ္တို႕ရဲ႕ Op-code TALK က ပုံေသတမ်ိဳးဘဲ print ထုတ္ေပးမွာ ျဖစ္ပါတယ္။ ဒီေတာ့ ပုံေသမဟုတ္ဘဲ ေပးထားတဲ့ number တခု screen ေပၚကိုရိုက္ထုတ္ေပးတဲ့ Op-code အသစ္တခုထပ္ ထည့္ၾကည့္ရေအာင္။
ဒီလို user (ကိုယ့္ language ကိုသုံးမဲ့ programmer) ကေပးတဲ့ data ကို print ထုတ္ျပဖို႔အတြက္ Op-code ကို data နဲ႔တြဲေပးဖို႔လိုပါတယ္။ အဲလို Data မိ်ဳးကို operand လို႔ေခၚတာ သိၾကမွာပါ။ ဒီေနရာမွာ ကိုယ့္ code မွာ operand ဘယ္ႏွစ္ခုသုံးခြင့္ေပးမလဲ ဆိုတာ ဆုံးျဖတ္ဖို႔လိုပါတယ္။ ဒီ tutorial မွာေတာ့ operand ၂ ခုလက္ခံေအာင္ ေရးထားပါတယ္။ ၂ ခုဆို assembly အတြက္ေတာ့ ေတာ္ေတာ္အလုပ္ျဖစ္ေနပါၿပီ။
ကဲ operand နဲ႔ Op-code တြဲေပးဖို႔ ေအာက္ကလို code တခ်ိဳ႕ျပင္ပါမယ္။
- Code:
class Instruction {
public:
OpCode code;
int operand[2];
Instruction(OpCode _c, int _p1, int _p2) : code(_c) {
operand[0] = _p1;
operand[1] = _p2;
};
};
NUM ဆိုတဲ့ Op-code အသစ္တခုထပ္ထည့္ရေအာင္၊
- Code:
enum OpCode {
OP_TALK,
OP_NUM,
OP_END,
};
Virtual Machine ထဲက switch ထဲမွာလဲ NUM ကို process လုပ္တဲ့ code ျဖည့္ထည့္ရပါမယ္၊
- Code:
void Run(Program& program) {
while (true)
{
Instruction inst = program.Next();
switch (inst.code)
{
case OP_TALK:
printf("Hello, I am simplest language!\n");
break;
case OP_NUM:
printf("Current Number: %d", inst.operand[0]);
break;
case OP_END:
return;
}
}
};
ကဲဒါဆို ကၽြန္ေတာ္တို႔ရဲ႕ code အသစ္ေလးကို စမ္းၾကည့္ရေအာင္။
- Code:
void main() {
Program MyProgram;
// my script :)
MyProgram.Add(Instruction(OP_TALK, 0, 0));
MyProgram.Add(Instruction(OP_NUM, 101, 0));
MyProgram.Add(Instruction(OP_NUM, 456, 0));
MyProgram.Add(Instruction(OP_END, 0, 0));
vm.Run(MyProgram);
};
ဒါဆို ကၽြန္ေတာ္တို႔ရဲ႕ script ကေအာက္ကလို print ထုတ္ေပးပါလိမ့္မယ္။
Hello, I am simplest language!
Current Number: 101
Current Number: 456
Adding Memory Support
Op-code တခုခ်င္းဆီမွာ data ကို operand အျဖစ္ထည့္ေပးလို႔ရတယ္။ ဒါေပမဲ့ Op-code တခုနဲ႔ တခုၾကား data ကို share လုပ္ခ်င္ရင္ ဘယ္လို လုပ္မလဲ။ global data pool တခုေတာ့ လိုေနပါတယ္။ ဒါမွ ပထမ Op-code က process လုပ္လို႔ထြက္လာတဲ့ data ကို ေနာက္ op-code က ယူသုံးလို႔ရမွာေလ။ ဒီအတြက္ Memory ဆိုတဲ့ array တခုကို Program class မွာသြားေၾကညာေပးရပါမယ္။
ဒီေနရာမွာ ေမးစရာက ဘာေၾကာင့္ global memory ကို Virtual Machine မွာမထည့္ဘဲ Program မွာသြားထည့္ရတာလဲ ဆိုတာပါ။ ဟုတ္တယ္ေလ၊ Global memory က Virtual Machine မွာအသုံးျပဳမွာ မဟုတ္လား။ ဒီလို ေၾကညာလဲ ရေတာ့ရပါတယ္။ ဒါေပမဲ့ Virtual Machine မွာ program ၂ ခုထက္ပို run တဲ့ အခါ ပထမ program သုံးသြားတဲ့ memory ကိုေနာက္ program က share သုံးရပါလိမ့္မယ္။ ဒီေတာ့ မလိုလားအပ္တဲ့ error ေတြတက္လာႏိုင္ပါတယ္။ ဒါေၾကာင့္ ကၽြန္ေတာ္တို႔က global memory ရဲ႕ scope ကို Program class level မွာဘဲထားၿပီး Program class တခုတိုင္း global memory pool တခုရွိေစခ်င္လို႕ global memory ကို Program class မွာေၾကညာရျခင္း ျဖစ္ပါတယ္။
- Code:
class Program {
public:
vector<Instruction> InstructionList;
int nCurrent;
unsigned char* pMemory;
...........
...........
};
pMemory ကို unsigned char အေနနဲ႔ဘဲ ေၾကညာထားပါတယ္။ ဒီေနရာမွာ 4-bytes ရွိတဲ့ int အျဖစ္ေပးရင္ ရေပမဲ့ 1 byte ရွိတဲ့ char data type ကို save သြားလုပ္ရင္လဲ 4 bytes စာယူပါလိမ့္မယ္။ ဒါေၾကာင့္အငယ္ဆုံး size ကိုေပးထားတာပါ။ ဒီေတာ့ 4-byte ရွိတဲ့ integer တခု save လုပ္ခ်င္ရင္ pMemory မွာ ၄ ေနရာယူပါလိမ့္မယ္။
Memory ကိုအသုံးျပဳဖို႔ function တခ်ိဳ႔လုိပါလိမ့္မယ္။
- Code:
class Program {
public:
...........
...........
// write data to specific memory address
void WriteMem(int address, void* data, int size) {
memcpy(pMemory + address, data, size);
};
// read data from the specific memory address
void ReadMem(int address, void* data, int size) {
memcpy(data, pMemory + address, size);
};
...........
...........
Program() : nCurrent(0) {
pMemory = new unsigned char[10240];
ZeroMemory(pMemory, 10240);
};
~Program() {
delete pMemory;
}
};
WriteMem() ဆိုတဲ့ function က memory array ေပၚမွာ data သြားေရးတာပါ။ Address ကေတာ့ ကိုယ္ေရးခ်င္တဲ့ address ေပါ့။ ဥပမာ၊ ပထမ integer value တခုသြားေရးခ်င္ရင္၊
program.WriteMem(0, 101, 4); ေပါ့။
ဒုတိယ integer တခုသြားေရးခ်င္ရင္ address 4 မွာေရးရပါမယ္ (ပထမ integer က address 0 ကေန 3 ထိေနရာယူထားတယ္ေလ)။
program.WriteMem(4, 456, 4); ေပါ့။
ReadMem ကေတာ့ WriteMem ရဲ႕ေျပာင္းျပန္ memory ကေနျပန္ဖတ္တဲ့ function ပါ။ Program() constructor ထဲမွာ ကၽြန္ေတာ္တို႔ရဲ႕ memory ကို ေဆာက္ေပးဖို႔လိုပါလိမ့္မယ္။ ခုကၽြန္ေတာ္တို႕ memory size ကို 10KB ေပးထားပါတယ္။ ဒီေတာ့ ကၽြန္ေတာ္တို႕ language မွာ 10KB ထက္ပိုၿပီး သိမ္းလို႔မရဘူးေပါ့ဗ်ာ။ ဒီထက္ပိုသိမ္းခ်င္ရင္ memory size ကိုျပင္ေပးရပါလိမ့္မယ္။ Destructor ထဲမွာ memory ကို delete လုပ္ခဲ့ဖို႔လဲ မေမ့ပါနဲ႔။
ဒီေနရာမွာ အားသာခ်က္တခုက global memory ကို 0 ေတြတခါတည္း initialize လုပ္ထားလို႔ရတာပါဘဲ (constructor ထဲမွာ “ZeroMemory(pMemory, 10240);” လို႔ေရးထားတယ္ေလ)။ C++ မွာဆို memory ကအစမွာ ေျဗာက္ေသာက္ျဖစ္ေနတာမို႕ variable ေၾကညာတိုင္းအၿမဲ initialize လုပ္ေပးရပါတယ္။ ဒါက Java လို Virtual Machine မွာ run တဲ့ programming language ေတြရဲ႕အားသာခ်က္တခုပါ။
ခုကၽြန္ေတာ္တို႔ဆီမွာ global memory ရွိၿပီမို႔ memory ကို access လုပ္တဲ့ Op-code တခ်ိဳ႕ေရးဖို႕ဘဲက်န္ပါေတာ့တယ္။ ဒီအတြက္ memory ကိုထဲကိုသြားသိမ္းမဲ့ OP_PUT_MEM ဆိုတဲ့ Op-code ကိုထည့္လိုက္ပါ။
- Code:
enum OpCode {
.......
OP_PUT_MEM, // write operand to global memory
.......
};
ေနာက္ၿပီးရင္ VirtualMachine::Run() function ထဲမွာ OP_PUT_MEM အတြက္သြားေရးပါ။
- Code:
Instruction inst = program.Next();
switch (inst.code)
{
......
case OP_PUT_MEM:
program.WriteMem(inst.operand[0], (void*)&(inst.operand[1]), 4);
break;
......
}
ဒီေနရာမွာ Instruction ရဲ႕ ပထမ operand ကကိုယ္ေရးခ်င္တဲ့ memory address ျဖစ္ၿပီး ဒုတိယ operand ကကိုယ္သိမ္းခ်င္တဲ့ data ျဖစ္ပါတယ္။ သိမ္းတဲ့ data က integer မို႔ size ကေတာ့ 4 ပါ။ ၿပီးရင္ NUM ဆိုတဲ့ Op-code ကို operand ကို print လုပ္မဲ့အစား memory ထဲက data ကို print လုပ္တဲ့ function ေျပာင္းေရးၾကည့္ရေအာင္။
- Code:
...........
case OP_NUM:
{
int data;
program.ReadMem(inst.operand[0], (void*) &data, 4);
printf("Current Number: %d", data);
}
break;
...........
...........
ဒီေနရာမွာ operand 0 က data မဟုတ္ေတာ့ဘဲ print ထုတ္မဲ့ memory ရဲ႕ address ျဖစ္သြားပါတယ္။ ကဲ ခုကၽြန္ေတာ္တို႕ရဲ႕ code ကို testing လုပ္ၾကည့္ရေအာင္။ main() function ကိုေအာက္ကအတိုင္းျပင္ပါမယ္။
- Code:
void main() {
Program MyProgram;
// my script :)
MyProgram.Add(Instruction(OP_TALK, 0, 0));
MyProgram.Add(Instruction(OP_PUT_MEM, 0, 101));
MyProgram.Add(Instruction(OP_PUT_MEM, 4, 456));
MyProgram.Add(Instruction(OP_NUM, 0, 0));
MyProgram.Add(Instruction(OP_NUM, 4, 0));
MyProgram.Add(Instruction(OP_END, 0, 0));
vm.Run(MyProgram);
};
Hello, I am simplest language!
Current Number: 101
Current Number: 456
Last edited by SYKO on 24th April 2009, 8:33 am; edited 1 time in total