Документація та інформація по "EnotVM32" (EnotVM32, Turbo Pascal 4.0, Version=1) Вихідний код оброблений https://chat.deepseek.com/ EnotVM32, Turbo Pascal 4.0, Version=1 ( Тема на форумі http://enotvm.frmbb.ru/viewtopic.php?id=31) uTpDt.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { uTpDt.pas — модуль типів та даних для EnotVM32 } { Copyright (c) 2024. Released under MIT License. } { Без змін логіки, лише форматування. } {==============================================================================} unit uTpDt; {Модуль типів та даних} interface Type Int8 = Shortint; { Shortint -128..127 signed 8-bit } Int16 = Integer; { Smallint -32768..32767 signed 16-bit } Int32 = Longint; { Longint -2147483648..2147483647 signed 32-bit } { типи, що будуть оптимізуватися } OptByte = Word; OptWord = Word; OptInt8 = Int16; OptInt16 = Int16; OptInt32 = Int32; type ShortString = string[255]; const { 232-239: EXTR вхідні дані } idYA = 232; idYB = 233; idYC = 234; idYD = 235; idYE = 236; idYF = 237; idYG = 238; idYH = 239; { 240-247: EXTR вихідні дані } idZA = 240; idZB = 241; idZC = 242; idZD = 243; idZE = 244; idZF = 245; idZG = 246; idZH = 247; { 248-255: резерв } idCMP1 = 254; idCMP2 = 255; const cEnotVMItIsCompiled = 1; { EnotVM скомпільовано за допомогою Turbo Pascal 4.0 } const cVersionEnotVM = 1; { EXTR 5 } cTypeOfAmbience = 1; { EXTR 11: 1=16bit, 2=32bit } cSizeOfRAMstack = 255; { EXTR 13 } const cSizeOfRAM = 49151; { EXTR 14: 48 КБ, індекси 0..49151 } var RAM : array[0..cSizeOfRAM] of Byte; RAMc : array[0..cSizeOfRAMstack] of Int32; RgESP : Int32; RegsDW : array[0..255] of Int32; RgEIPMain : OptInt16; const cSizeOfRAMext = 49151; { EXTR 15 } cMaxPagesOfRAMext = 10; { EXTR 16 } type TExtPage = array[0..cSizeOfRAMext] of Byte; PExtPage = ^TExtPage; var ExtRAMPages : array[0..cMaxPagesOfRAMext] of PExtPage; pSTOPEngine : OptByte; { 1=EndProgram, 2=ErrorOpCode, 3=APM } implementation end. uString.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { uString.pas — обробка рядків, write/writeln, read/readln } { Copyright (c) 2024. Released under MIT License. } {==============================================================================} unit uString; { модуль обробки рядків, вводу та виводу } interface uses uTpDt; procedure pWriteStr; procedure pWritelnStr; implementation procedure CheckTypeFormat(Addr: OptWord; var TypeF: OptByte; var FullSize: OptWord; var CurSize: OptWord; var BeginAddrDate: OptWord; var EndAddrDate: OptWord); begin TypeF := RAM[Addr]; case TypeF of 1: begin FullSize := RAM[Addr+1]; CurSize := RAM[Addr+2]; BeginAddrDate := Addr+3; end; 2: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+3] + (RAM[Addr+4] shl 8); BeginAddrDate := Addr+5; end; 3: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+4] + (RAM[Addr+5] shl 8); BeginAddrDate := Addr+7; end; 4: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+5] + (RAM[Addr+6] shl 8); BeginAddrDate := Addr+9; end; end; EndAddrDate := BeginAddrDate + CurSize - 1; end; procedure CheckTypeFormat2(Addr: OptWord; var BeginAddrDate: OptWord; var EndAddrDate: OptWord); var TypeF: OptByte; FullSize, CurSize: OptWord; begin TypeF := RAM[Addr]; case TypeF of 1: begin FullSize := RAM[Addr+1]; CurSize := RAM[Addr+2]; BeginAddrDate := Addr+3; end; 2: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+3] + (RAM[Addr+4] shl 8); BeginAddrDate := Addr+5; end; 3: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+4] + (RAM[Addr+5] shl 8); BeginAddrDate := Addr+7; end; 4: begin FullSize := RAM[Addr+1] + (RAM[Addr+2] shl 8); CurSize := RAM[Addr+5] + (RAM[Addr+6] shl 8); BeginAddrDate := Addr+9; end; end; EndAddrDate := BeginAddrDate + CurSize - 1; end; procedure pWriteStr; { EXTR 257 } var Strb1 : string; SizeStrb1 : OptWord; BeginAddrDate, EndAddrDate : OptWord; begin Strb1 := ''; SizeStrb1 := 0; CheckTypeFormat2(RegsDW[idYA], BeginAddrDate, EndAddrDate); while BeginAddrDate <= EndAddrDate do begin if SizeStrb1 < 255 then begin Strb1 := Strb1 + chr(RAM[BeginAddrDate]); inc(SizeStrb1); inc(BeginAddrDate); end else begin Write(Strb1); Strb1 := ''; SizeStrb1 := 0; end; end; if SizeStrb1 <> 0 then Write(Strb1); end; procedure pWritelnStr; { EXTR 256 } var Strb1 : string; SizeStrb1 : OptWord; BeginAddrDate, EndAddrDate : OptWord; begin Strb1 := ''; SizeStrb1 := 0; CheckTypeFormat2(RegsDW[idYA], BeginAddrDate, EndAddrDate); while BeginAddrDate <= EndAddrDate do begin if SizeStrb1 < 255 then begin Strb1 := Strb1 + chr(RAM[BeginAddrDate]); inc(SizeStrb1); inc(BeginAddrDate); end else begin Write(Strb1); Strb1 := ''; SizeStrb1 := 0; end; end; if SizeStrb1 <> 0 then Writeln(Strb1) else Writeln; end; end. uEmul0.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { uEmul0.pas — завантаження, ініціалізація, EXTR-функції, допоміжна RAM } { Copyright (c) 2024. Released under MIT License. } {==============================================================================} unit uEmul0; interface uses Dos, uTpDt; procedure LoadBIN(FileName: ShortString); procedure InitCPU; function IntToStr(N: Int32): ShortString; function IntToHEX(N: Int32; Digits: OptByte): ShortString; procedure pWriteln_GetTimeStr; { EXTR 3 } procedure pCONT; { EXTR 7 } procedure pWritelnYA; { EXTR 8 } procedure pWait00secTime_and_Writeln_GetTimeStr; { EXTR 9 } procedure pEndProgram; { EXTR 10 } procedure InitExtRAM; procedure CopyMainToExt(MainBeginAddr: OptWord; Count: OptWord; PageNum, ExtBeginAddr: OptWord); procedure CopyExtToMain(PageNum, ExtBeginAddr: OptWord; Count: OptWord; MainBeginAddr: OptWord); procedure CopyExtToExt(PageNum1, Ext1BeginAddr: OptWord; Count: OptWord; PageNum2, Ext2BeginAddr: OptWord); procedure CopyMainToMain(MainSrcAddr: OptWord; Count: OptWord; MainDstAddr: OptWord); implementation procedure LoadBIN(FileName: ShortString); var f : file; size : OptInt32; begin Assign(f, FileName); Reset(f, 1); size := FileSize(f); if size > SizeOf(RAM) then size := SizeOf(RAM); BlockRead(f, RAM, size); Close(f); end; procedure InitCPU; begin RgESP := cSizeOfRAMstack; RgEIPMain := 0; end; function IntToStr(N: Int32): ShortString; var S : string; Neg : Boolean; begin S := ''; Neg := N < 0; if N = 0 then begin IntToStr := '0'; Exit; end; if Neg then N := -N; while N > 0 do begin S := Chr(Ord('0') + (N mod 10)) + S; N := N div 10; end; if Neg then S := '-' + S; IntToStr := S; end; function IntToHEX(N: Int32; Digits: OptByte): ShortString; var b4 : array[0..3] of Byte absolute N; const HexChars: array[0..15] of Char = '0123456789ABCDEF'; var S : string; begin S := ''; S := HexChars[(b4[0] mod 16)] + S; S := HexChars[(b4[0] div 16)] + S; if (b4[1] <> 0) or (b4[2] <> 0) or (b4[3] <> 0) then begin S := HexChars[(b4[1] mod 16)] + S; S := HexChars[(b4[1] div 16)] + S; end; if (b4[2] <> 0) or (b4[3] <> 0) then begin S := HexChars[(b4[2] mod 16)] + S; S := HexChars[(b4[2] div 16)] + S; end; if (b4[3] <> 0) then begin S := HexChars[(b4[3] mod 16)] + S; S := HexChars[(b4[3] div 16)] + S; end; while Length(S) < Digits do S := '0' + S; IntToHEX := S; end; procedure pWriteln_GetTimeStr; { EXTR 3 } var Hour, Minute, Second, Sec100 : Word; begin GetTime(Hour, Minute, Second, Sec100); Writeln(Hour, ':', Minute, ':', Second, '.', Sec100); end; procedure pCONT; { EXTR 7 } var wrems : string; begin repeat Writeln('Will Enter ''cont'' or ''CONT'' for continue'); Readln(wrems); until (wrems = 'cont') or (wrems = 'CONT'); end; procedure pWritelnYA; { EXTR 8 } begin Writeln(IntToStr(RegsDW[idYA]) + ' ($' + IntToHEX(RegsDW[idYA], 8) + ')'); end; procedure pWait00secTime_and_Writeln_GetTimeStr; { EXTR 9 } var Hour, Minute, Second, Sec100 : Word; begin Writeln('Wait Time 00 Sec'); repeat GetTime(Hour, Minute, Second, Sec100); until Second = 0; Writeln(Hour, ':', Minute, ':', Second, '.', Sec100); end; procedure pEndProgram; { EXTR 10 } begin pSTOPEngine := 1; { pEndProgram } end; procedure InitExtRAM; var i : OptInt16; begin for i := 0 to cMaxPagesOfRAMext do begin New(ExtRAMPages[i]); FillChar(ExtRAMPages[i]^, SizeOf(TExtPage), 0); end; end; procedure CopyMainToExt(MainBeginAddr: OptWord; Count: OptWord; PageNum, ExtBeginAddr: OptWord); begin Move(RAM[MainBeginAddr], ExtRAMPages[PageNum]^[ExtBeginAddr], Count); end; procedure CopyExtToMain(PageNum, ExtBeginAddr: OptWord; Count: OptWord; MainBeginAddr: OptWord); begin Move(ExtRAMPages[PageNum]^[ExtBeginAddr], RAM[MainBeginAddr], Count); end; procedure CopyExtToExt(PageNum1, Ext1BeginAddr: OptWord; Count: OptWord; PageNum2, Ext2BeginAddr: OptWord); begin Move(ExtRAMPages[PageNum1]^[Ext1BeginAddr], ExtRAMPages[PageNum2]^[Ext2BeginAddr], Count); end; procedure CopyMainToMain(MainSrcAddr: OptWord; Count: OptWord; MainDstAddr: OptWord); begin Move(RAM[MainSrcAddr], RAM[MainDstAddr], Count); end; end. uEmul.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { uEmul.pas — ядро віртуальної машини, диспетчер команд 16-бітного режиму } { Copyright (c) 2024. Released under MIT License. } {==============================================================================} unit uEmul; interface uses Dos, uEmul0, uString, uTpDt; procedure StartEngine; implementation procedure Engine16bitB4; var RgEIP : OptInt16; tmp1, tmp2 : OptByte; Adr1, Adr2 : OptInt16; V1 : OptInt32; begin pSTOPEngine := 0; RgEIP := RgEIPMain; repeat tmp1 := RAM[RgEIP]; case tmp1 of $00: { NOP } begin Inc(RgEIP); end; $01: { mov Rg32,V } begin Adr1 := RgEIP + 2; RegsDW[RAM[(RgEIP+1)]] := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8) or (OptInt32(RAM[Adr1+2]) shl 16) or (OptInt32(RAM[Adr1+3]) shl 24); Inc(RgEIP, 6); end; $02: { mov Rg32,Rg32 } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $03: { mov Rg32,[DWA1] } begin Adr1 := RgEIP + 2; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RegsDW[RAM[RgEIP+1]] := OptInt32(RAM[Adr2]) or (OptInt32(RAM[Adr2+1]) shl 8) or (OptInt32(RAM[Adr2+2]) shl 16) or (OptInt32(RAM[Adr2+3]) shl 24); Inc(RgEIP, 6); end; $04: { mov Rg32,[Rg32] } begin Adr1 := RegsDW[RAM[(RgEIP+2)]]; RegsDW[RAM[(RgEIP+1)]] := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8) or (OptInt32(RAM[Adr1+2]) shl 16) or (OptInt32(RAM[Adr1+3]) shl 24); Inc(RgEIP, 3); end; $05: { mov [DWA1],Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); V1 := RegsDW[RAM[RgEIP+5]]; RAM[Adr2] := Byte(V1); RAM[Adr2+1] := Byte(V1 shr 8); RAM[Adr2+2] := Byte(V1 shr 16); RAM[Adr2+3] := Byte(V1 shr 24); Inc(RgEIP, 6); end; $06: { mov [Rg32],Rg32 } begin Adr1 := RegsDW[RAM[(RgEIP+1)]]; V1 := RegsDW[RAM[(RgEIP+2)]]; RAM[Adr1] := Byte(V1); RAM[Adr1+1] := Byte(V1 shr 8); RAM[Adr1+2] := Byte(V1 shr 16); RAM[Adr1+3] := Byte(V1 shr 24); Inc(RgEIP, 3); end; $07: { mov Rg32, word [DWA1] } begin Adr1 := RgEIP + 2; RegsDW[RAM[RgEIP+1]] := RAM[Adr1] or (RAM[Adr1+1] shl 8); Inc(RgEIP, 6); end; $08: { mov [DWA1], word Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); V1 := RegsDW[RAM[RgEIP+5]]; RAM[Adr2] := Byte(V1); RAM[Adr2+1] := Byte(V1 shr 8); Inc(RgEIP, 6); end; $09: { mov Rg32, byte [DWA1] } begin Adr1 := RgEIP + 2; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RegsDW[RAM[RgEIP+1]] := RAM[Adr2]; Inc(RgEIP, 6); end; $0A: { mov [DWA1], byte Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RAM[Adr2] := RegsDW[RAM[RgEIP+5]]; Inc(RgEIP, 6); end; $0B: { CALLa DWA1 } begin Dec(RgESP); RAMc[RgESP] := RgEIP + 5; RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8); end; $0C: { CALLr Rg32 } begin Dec(RgESP); RAMc[RgESP] := RgEIP + 2; RgEIP := RegsDW[RAM[(RgEIP+1)]]; end; $0D: { RET } begin RgEIP := RAMc[RgESP]; Inc(RgESP); end; $0E: { GOTOa } begin RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8); end; $0F: { GOTOr } begin RgEIP := RegsDW[RAM[RgEIP+1]]; end; $10: { GOTOsaIs } begin if RegsDW[idCMP1] = RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $11: { GOTOsaNotIs } begin if RegsDW[idCMP1] <> RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $12: { GOTOsaMoreIs } begin if RegsDW[idCMP1] >= RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $13: { GOTOsaLess } begin if RegsDW[idCMP1] < RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $14: { GOTOsrIs } begin if RegsDW[idCMP1] = RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $15: { GOTOsrNotIs } begin if RegsDW[idCMP1] <> RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $16: { GOTOsrMoreIs } begin if RegsDW[idCMP1] >= RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $17: { GOTOsrLess } begin if RegsDW[idCMP1] < RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $18: { PUSHr } begin Dec(RgESP); RAMc[RgESP] := RegsDW[RAM[(RgEIP+1)]]; Inc(RgEIP, 2); end; $19: { POPr } begin RegsDW[RAM[(RgEIP+1)]] := RAMc[RgESP]; Inc(RgESP); Inc(RgEIP, 2); end; $1A: { PUSHrsvr } begin Adr1 := RAM[RgEIP+1]; tmp2 := RAM[RgEIP+2] - Adr1; Dec(RgESP, tmp2 + 1); for tmp1 := 0 to tmp2 do RAMc[RgESP + tmp1] := RegsDW[Adr1 + tmp1]; Inc(RgEIP, 3); end; $1B: { POPrsvr } begin Adr1 := RAM[RgEIP+1]; tmp2 := RAM[RgEIP+2] - Adr1; for tmp1 := 0 to tmp2 do RegsDW[Adr1 + tmp1] := RAMc[RgESP + tmp1]; Inc(RgESP, tmp2 + 1); Inc(RgEIP, 3); end; $1C: { ADDrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] + RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1D: { SUBrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] - RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1E: { MULrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] * RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1F: { DIVrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] div RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $20: { MODrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] mod RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $21: { INCr } begin Inc(RegsDW[RAM[RgEIP+1]]); Inc(RgEIP, 2); end; $22: { DECr } begin Dec(RegsDW[RAM[RgEIP+1]]); Inc(RgEIP, 2); end; $23: { ANDrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] and RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $24: { ORrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] or RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $25: { XORrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] xor RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $26: { NOTr } begin RegsDW[RAM[RgEIP+1]] := not RegsDW[RAM[RgEIP+1]]; Inc(RgEIP, 2); end; $27: { SHLrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] shl RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $28: { SHRrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] shr RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $29: { CMPrr } begin RegsDW[idCMP1] := RegsDW[RAM[RgEIP+1]]; RegsDW[idCMP2] := RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $2B: { mov Rg32, word [Rg32] } begin Adr1 := RegsDW[RAM[(RgEIP+2)]]; RegsDW[RAM[(RgEIP+1)]] := RAM[Adr1] or (RAM[Adr1+1] shl 8); Inc(RgEIP, 3); end; $2C: { mov [Rg32], word Rg32 } begin Adr1 := RegsDW[RAM[(RgEIP+1)]]; V1 := RegsDW[RAM[(RgEIP+2)]]; RAM[Adr1] := Byte(V1); RAM[Adr1+1] := Byte(V1 shr 8); Inc(RgEIP, 3); end; $2D: { mov Rg32, byte [Rg32] } begin RegsDW[RAM[(RgEIP+1)]] := RAM[(RegsDW[RAM[(RgEIP+2)]])]; Inc(RgEIP, 3); end; $2E: { mov [Rg32], byte Rg32 } begin RAM[RegsDW[RAM[(RgEIP+1)]]] := RegsDW[RAM[(RgEIP+2)]]; Inc(RgEIP, 3); end; $2A: { OpEXTR } begin Adr1 := RAM[RgEIP+1]; Adr2 := RAM[RgEIP+2]; Inc(RgEIP, 3); case Adr2 of 0: { 0-255 } case Adr1 of 0: { EXTR_NOP } begin end; 1: { CheckEXTR } begin RegsDW[idZA] := 0; case RegsDW[idYA] of 0..16, 256..257, 512..515, 768..771: RegsDW[idZA] := 1; end; end; 2: { HALT_ } Halt(RegsDW[idYA]); 3: { Writeln_GetTimeStr } pWriteln_GetTimeStr; 4: { Readln0 } Readln; 5: { VersionEnotVM } RegsDW[idZA] := cVersionEnotVM; 6: { Writeln0 } Writeln; 7: { CONT } pCONT; 8: { WritelnYA } pWritelnYA; 9: { Wait00secTime_and_Writeln_GetTimeStr } pWait00secTime_and_Writeln_GetTimeStr; 10: { EndProgram } pEndProgram; 11: { TypeOfAmbience } RegsDW[idZA] := cTypeOfAmbience; 12: { APM } pSTOPEngine := 3; 13: { SizeOfRAMstack } RegsDW[idZA] := cSizeOfRAMstack; 14: { SizeOfRAM } RegsDW[idZA] := cSizeOfRAM; 15: { SizeOfRAMext } RegsDW[idZA] := cSizeOfRAMext; 16: { MaxPagesOfRAMext } RegsDW[idZA] := cMaxPagesOfRAMext; 17: RegsDW[idZA] := cEnotVMItIsCompiled; end; 1: { 256-511 STRING } case Adr1 of 0: { WritelnStr } pWritelnStr; 1: { WriteStr } pWriteStr; end; 2: { 512-767 ExtrRAM } case Adr1 of 0: { CopyMainToMain } Move(RAM[RegsDW[idYC]], RAM[RegsDW[idYE]], RegsDW[idYB]); 1: { CopyMainToExt } Move(RAM[RegsDW[idYC]], ExtRAMPages[RegsDW[idYF]]^[RegsDW[idYE]], RegsDW[idYB]); 2: { CopyExtToMain } Move(ExtRAMPages[RegsDW[idYD]]^[RegsDW[idYC]], RAM[RegsDW[idYE]], RegsDW[idYB]); 3: { CopyExtToExt } Move(ExtRAMPages[RegsDW[idYD]]^[RegsDW[idYC]], ExtRAMPages[RegsDW[idYF]]^[RegsDW[idYE]], RegsDW[idYB]); end; 3: { 768-1023 GOTOu... } case Adr1 of 0: { GOTOuaMoreIs } begin if (Word(RegsDW[idCMP1] shr 16) > Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) >= Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := OptInt32(RAM[(RgEIP)]) or (OptInt32(RAM[(RgEIP+1)]) shl 8) else Inc(RgEIP, 4); end; 1: { GOTOuaLess } begin if (Word(RegsDW[idCMP1] shr 16) < Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) < Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := OptInt32(RAM[(RgEIP)]) or (OptInt32(RAM[(RgEIP+1)]) shl 8) else Inc(RgEIP, 4); end; 2: { GOTOurMoreIs } begin if (Word(RegsDW[idCMP1] shr 16) > Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) >= Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := RegsDW[RAM[(RgEIP)]] else Inc(RgEIP, 1); end; 3: { GOTOurLess } begin if (Word(RegsDW[idCMP1] shr 16) > Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) < Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := RegsDW[RAM[(RgEIP)]] else Inc(RgEIP, 1); end; end; end; { case Adr2 } end; { $2A } else pSTOPEngine := 2; { ErrorOpCode } end; { case tmp1 } until pSTOPEngine <> 0; RgEIPMain := RgEIP; end; procedure StartEngine; begin LoadBIN('BOOT.bin'); InitCPU; Engine16bitB4; end; end. uMain.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { uMain.pas – ініціалізація, запуск та завершення програми } { Copyright (c) 2024. Released under MIT License. } {==============================================================================} unit uMain; interface uses uEmul, uEmul0; procedure InitProgram; procedure StartProgram; procedure FinalProgram; implementation procedure InitProgram; begin InitExtRAM; end; procedure FinalProgram; begin end; procedure StartProgram; begin StartEngine; end; end. EnotVM32.pas: {Обробка вихідного коду від https://chat.deepseek.com/} {==============================================================================} { ProjectP.pas – головна програма EnotVM32 } { Copyright (c) 2024. Released under MIT License. } {==============================================================================} Program EnotVM32; uses Crt, uMain; Begin { Delay(500); } { за потреби розкоментувати } InitProgram; StartProgram; FinalProgram; end. { $ N + } Вихідний код оброблений https://chat.qwen.ai EnotVM32, Turbo Pascal 4.0, Version=1 ( Тема на форумі http://enotvm.frmbb.ru/viewtopic.php?id=31) uTpDt.pas: {Обробка вихідного коду від https://chat.qwen.ai} unit uTpDt; { Модуль типів та даних } interface type { Базові цілочисельні типи } Int8 = Shortint; { -128..127, signed 8-bit } Int16 = Integer; { Smallint -32768..32767, signed 16-bit } Int32 = Longint; { -2147483648..2147483647, signed 32-bit } { Типи, які будуть оптимізуватися } OptByte = Word; OptWord = Word; OptInt8 = Int16; OptInt16= Int16; OptInt32= Int32; ShortString = string[255]; const { 232-239: EXTR вхідні дані } idYA = 232; idYB = 233; idYC = 234; idYD = 235; idYE = 236; idYF = 237; idYG = 238; idYH = 239; { 240-247: EXTR вихідні дані } idZA = 240; idZB = 241; idZC = 242; idZD = 243; idZE = 244; idZF = 245; idZG = 246; idZH = 247; { 248-255: Резерв } idCMP1 = 254; idCMP2 = 255; const { Середовище компіляції EnotVM } { 1 = Turbo Pascal Version 4.0 Copyright (c) 1987 Borland International } cEnotVMItIsCompiled = 1; const cVersionEnotVM = 1; { EXTR 5 } cTypeOfAmbience = 1; { повертає ZA = 1 - 16бит; ZA = 2 - 32бит. EXTR 11 } cSizeOfRAMstack = 255; { EXTR 13 } cSizeOfRAM = 49151; { EXTR 14. 48 КБ = 49152 байт, індекси від 0 до 49151 } var RAM: array[0..cSizeOfRAM] of Byte; RAMc: array[0..cSizeOfRAMstack] of Int32; RgESP: Int32; RegsDW: array[0..255] of Int32; RgEIPMain: OptInt16; const cSizeOfRAMext = 49151; { EXTR 15. 48 КБ = 49152 байт, індекси від 0 до 49151 } cMaxPagesOfRAMext= 10; { EXTR 16. Кількість сторінок, від 0 до ... } type { Одна сторінка розширеної пам'яті } TExtPage = array[0..cSizeOfRAMext] of Byte; PExtPage = ^TExtPage; var { Розширена пам'ять — масив покажчиків } ExtRAMPages: array[0..cMaxPagesOfRAMext] of PExtPage; pSTOPEngine: OptByte; { 1 = EndProgram, 2 = ErrorOpCode, 3 = APM. EXTR 12 = APM } implementation end. uString.pas: {Обробка вихідного коду від https://chat.qwen.ai} unit uString; { Модуль, в якому обробляються рядки, а також ввід та вивід на екран (write/writeln, Read/Readln) } interface uses uTpDt; procedure pWriteStr; procedure pWritelnStr; implementation procedure CheckTypeFormat(Addr: OptWord; var TypeF: OptByte; var FullSize: OptWord; var CurSize: OptWord; var BeginAddrDate: OptWord; var EndAddrDate: OptWord); { 16bit } begin TypeF := RAM[Addr]; case TypeF of 1: begin FullSize := RAM[Addr+1]; CurSize := RAM[Addr+2]; BeginAddrDate := Addr+3; end; 2: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+3] + (RAM[Addr+4] shl 8)); BeginAddrDate := Addr+5; end; 3: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+4] + (RAM[Addr+5] shl 8)); BeginAddrDate := Addr+7; end; 4: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+5] + (RAM[Addr+6] shl 8)); BeginAddrDate := Addr+9; end; end; EndAddrDate := BeginAddrDate + CurSize - 1; end; procedure CheckTypeFormat2(Addr: OptWord; var BeginAddrDate: OptWord; var EndAddrDate: OptWord); { 16bit } var TypeF: OptByte; FullSize: OptWord; CurSize: OptWord; begin TypeF := RAM[Addr]; case TypeF of 1: begin FullSize := RAM[Addr+1]; CurSize := RAM[Addr+2]; BeginAddrDate := Addr+3; end; 2: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+3] + (RAM[Addr+4] shl 8)); BeginAddrDate := Addr+5; end; 3: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+4] + (RAM[Addr+5] shl 8)); BeginAddrDate := Addr+7; end; 4: begin FullSize := (RAM[Addr+1] + (RAM[Addr+2] shl 8)); CurSize := (RAM[Addr+5] + (RAM[Addr+6] shl 8)); BeginAddrDate := Addr+9; end; end; EndAddrDate := BeginAddrDate + CurSize - 1; end; procedure pWriteStr; { EXTR 257 } var Strb1: string; SizeStrb1: OptWord; BeginAddrDate: OptWord; EndAddrDate: OptWord; begin Strb1 := ''; SizeStrb1 := 0; CheckTypeFormat2(RegsDW[idYA], BeginAddrDate, EndAddrDate); while BeginAddrDate <= EndAddrDate do begin if SizeStrb1 < 255 then begin Strb1 := Strb1 + chr(RAM[BeginAddrDate]); inc(SizeStrb1); inc(BeginAddrDate); end else begin Write(Strb1); Strb1 := ''; SizeStrb1 := 0; end; end; if SizeStrb1 <> 0 then Write(Strb1); end; procedure pWritelnStr; { EXTR 256 } var Strb1: string; SizeStrb1: OptWord; BeginAddrDate: OptWord; EndAddrDate: OptWord; begin Strb1 := ''; SizeStrb1 := 0; CheckTypeFormat2(RegsDW[idYA], BeginAddrDate, EndAddrDate); while BeginAddrDate <= EndAddrDate do begin if SizeStrb1 < 255 then begin Strb1 := Strb1 + chr(RAM[BeginAddrDate]); inc(SizeStrb1); inc(BeginAddrDate); end else begin Write(Strb1); Strb1 := ''; SizeStrb1 := 0; end; end; if SizeStrb1 <> 0 then Writeln(Strb1) else Writeln; end; end. uEmul0.pas: {Обробка вихідного коду від https://chat.qwen.ai} unit uEmul0; interface uses Dos, uTpDt; procedure LoadBIN(FileName: ShortString); procedure InitCPU; function IntToStr(N: Int32): ShortString; function IntToHEX(N: Int32; Digits: OptByte): ShortString; procedure pWriteln_GetTimeStr; { EXTR 3 } procedure pCONT; { EXTR 7 } procedure pWritelnYA; { EXTR 8 } procedure pWait00secTime_and_Writeln_GetTimeStr; { EXTR 9 } procedure pEndProgram; { EXTR 10 } { Extr RAM } procedure InitExtRAM; procedure CopyMainToExt(MainBeginAddr: OptWord; Count: OptWord; PageNum, ExtBeginAddr: OptWord); procedure CopyExtToMain(PageNum, ExtBeginAddr: OptWord; Count: OptWord; MainBeginAddr: OptWord); procedure CopyExtToExt(PageNum1, Ext1BeginAddr: OptWord; Count: OptWord; PageNum2, Ext2BeginAddr: OptWord); procedure CopyMainToMain(MainSrcAddr: OptWord; Count: OptWord; MainDstAddr: OptWord); implementation procedure LoadBIN(FileName: ShortString); var f: file; size: OptInt32; begin Assign(f, FileName); Reset(f, 1); size := FileSize(f); if size > SizeOf(RAM) then size := SizeOf(RAM); BlockRead(f, RAM, size); Close(f); end; procedure InitCPU; begin RgESP := cSizeOfRAMstack; RgEIPMain := 0; end; function IntToStr(N: Int32): ShortString; var S: string; Neg: Boolean; begin S := ''; Neg := N < 0; if N = 0 then begin IntToStr := '0'; Exit; end; if Neg then N := -N; while N > 0 do begin S := Chr(Ord('0') + (N mod 10)) + S; N := N div 10; end; if Neg then S := '-' + S; IntToStr := S; end; function IntToHEX(N: Int32; Digits: OptByte): ShortString; var b4: array[0..3] of Byte absolute N; const HexChars: array[0..15] of Char = '0123456789ABCDEF'; var S: string; begin S := ''; S := HexChars[(b4[0] mod 16)] + S; S := HexChars[(b4[0] div 16)] + S; if (b4[1] <> 0) or (b4[2] <> 0) or (b4[3] <> 0) then begin S := HexChars[(b4[1] mod 16)] + S; S := HexChars[(b4[1] div 16)] + S; end; if (b4[2] <> 0) or (b4[3] <> 0) then begin S := HexChars[(b4[2] mod 16)] + S; S := HexChars[(b4[2] div 16)] + S; end; if (b4[3] <> 0) then begin S := HexChars[(b4[3] mod 16)] + S; S := HexChars[(b4[3] div 16)] + S; end; { доповнюємо нулями зліва } while Length(S) < Digits do S := '0' + S; IntToHEX := S; end; procedure pWriteln_GetTimeStr; { EXTR 3 } var Hour, Minute, Second, Sec100: Word; begin GetTime(Hour, Minute, Second, Sec100); Writeln(Hour, ':', Minute, ':', Second, '.', Sec100); end; procedure pCONT; { EXTR 7 } var wrems: string; begin Repeat Writeln('Will Enter ', #39, 'cont', #39, ' or ', #39, 'CONT', #39, ' for continue'); Readln(wrems); until (wrems = 'cont') or (wrems = 'CONT'); end; procedure pWritelnYA; { EXTR 8 } begin Writeln(IntToStr(RegsDW[idYA]) + ' ($' + IntToHEX(RegsDW[idYA], 8) + ')'); end; procedure pWait00secTime_and_Writeln_GetTimeStr; { EXTR 9 } var Hour, Minute, Second, Sec100: Word; begin Writeln('Wait Time 00 Sec'); Repeat GetTime(Hour, Minute, Second, Sec100); until Second = 0; Writeln(Hour, ':', Minute, ':', Second, '.', Sec100); end; procedure pEndProgram; { EXTR 10 } begin pSTOPEngine := 1; { pEndProgram } end; { Extr RAM } procedure InitExtRAM; var i: OptInt16; begin for i := 0 to cMaxPagesOfRAMext do begin New(ExtRAMPages[i]); FillChar(ExtRAMPages[i]^, SizeOf(TExtPage), 0); end; end; procedure CopyMainToExt(MainBeginAddr: OptWord; Count: OptWord; PageNum, ExtBeginAddr: OptWord); begin Move(RAM[MainBeginAddr], ExtRAMPages[PageNum]^[ExtBeginAddr], Count); end; procedure CopyExtToMain(PageNum, ExtBeginAddr: OptWord; Count: OptWord; MainBeginAddr: OptWord); begin Move(ExtRAMPages[PageNum]^[ExtBeginAddr], RAM[MainBeginAddr], Count); end; procedure CopyExtToExt(PageNum1, Ext1BeginAddr: OptWord; Count: OptWord; PageNum2, Ext2BeginAddr: OptWord); begin Move(ExtRAMPages[PageNum1]^[Ext1BeginAddr], ExtRAMPages[PageNum2]^[Ext2BeginAddr], Count); end; procedure CopyMainToMain(MainSrcAddr: OptWord; Count: OptWord; MainDstAddr: OptWord); begin Move(RAM[MainSrcAddr], RAM[MainDstAddr], Count); end; end. uEmul.pas: {Обробка вихідного коду від https://chat.qwen.ai} unit uEmul; interface uses Dos, uEmul0, uString, uTpDt; procedure StartEngine; implementation procedure Engine16bitB4; var RgEIP: OptInt16; tmp1, tmp2: OptByte; Adr1, Adr2: OptInt16; V1: OptInt32; begin pSTOPEngine := 0; RgEIP := RgEIPMain; repeat tmp1 := RAM[RgEIP]; case tmp1 of $00: { NOP } begin Inc(RgEIP); end; $01: { MOVrv: mov Rg32, V } begin Adr1 := RgEIP + 2; RegsDW[RAM[(RgEIP+1)]] := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8) or (OptInt32(RAM[Adr1+2]) shl 16) or (OptInt32(RAM[Adr1+3]) shl 24); Inc(RgEIP, 6); end; $02: { MOVrr: mov Rg32, Rg32 } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $03: { MOVrm: mov Rg32, [DWA1] } begin Adr1 := RgEIP + 2; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RegsDW[RAM[RgEIP+1]] := OptInt32(RAM[Adr2]) or (OptInt32(RAM[Adr2+1]) shl 8) or (OptInt32(RAM[Adr2+2]) shl 16) or (OptInt32(RAM[Adr2+3]) shl 24); Inc(RgEIP, 6); end; $04: { MOVrmr: mov Rg32, [Rg32] } begin Adr1 := RegsDW[RAM[(RgEIP+2)]]; RegsDW[RAM[(RgEIP+1)]] := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8) or (OptInt32(RAM[Adr1+2]) shl 16) or (OptInt32(RAM[Adr1+3]) shl 24); Inc(RgEIP, 3); end; $05: { MOVmr: mov [DWA1], Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); V1 := RegsDW[RAM[RgEIP+5]]; RAM[Adr2] := Byte(V1); RAM[Adr2+1] := Byte(V1 shr 8); RAM[Adr2+2] := Byte(V1 shr 16); RAM[Adr2+3] := Byte(V1 shr 24); Inc(RgEIP, 6); end; $06: { MOVmrr: mov [Rg32], Rg32 } begin Adr1 := RegsDW[RAM[(RgEIP+1)]]; V1 := RegsDW[RAM[(RgEIP+2)]]; RAM[Adr1] := Byte(V1); RAM[Adr1+1] := Byte(V1 shr 8); RAM[Adr1+2] := Byte(V1 shr 16); RAM[Adr1+3] := Byte(V1 shr 24); Inc(RgEIP, 3); end; $07: { MOV2rm: mov Rg32, word [DWA1] } begin Adr1 := RgEIP + 2; RegsDW[RAM[RgEIP+1]] := RAM[Adr1] or (RAM[Adr1+1] shl 8); Inc(RgEIP, 6); end; $08: { MOV2mr: mov [DWA1], word Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); V1 := RegsDW[RAM[RgEIP+5]]; RAM[Adr2] := Byte(V1); RAM[Adr2+1] := Byte(V1 shr 8); Inc(RgEIP, 6); end; $09: { MOV1rm: mov Rg32, byte [DWA1] } begin Adr1 := RgEIP + 2; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RegsDW[RAM[RgEIP+1]] := RAM[Adr2]; Inc(RgEIP, 6); end; $0A: { MOV1mr: mov [DWA1], byte Rg32 } begin Adr1 := RgEIP + 1; Adr2 := OptInt32(RAM[Adr1]) or (OptInt32(RAM[Adr1+1]) shl 8); RAM[Adr2] := RegsDW[RAM[RgEIP+5]]; Inc(RgEIP, 6); end; $0B: { CALLa DWA1 } begin Dec(RgESP); RAMc[RgESP] := (RgEIP + 5); RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8); end; $0C: { CALLr Rg32 } begin Dec(RgESP); RAMc[RgESP] := (RgEIP + 2); RgEIP := RegsDW[RAM[(RgEIP+1)]]; end; $0D: { RET } begin RgEIP := RAMc[RgESP]; inc(RgESP); end; $0E: { GOTOa } begin RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8); end; $0F: { GOTOr } begin RgEIP := RegsDW[RAM[RgEIP+1]]; end; $10: { GOTOsaIs } begin if RegsDW[idCMP1] = RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $11: { GOTOsaNotIs } begin if RegsDW[idCMP1] <> RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $12: { GOTOsaMoreIs } begin if RegsDW[idCMP1] >= RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $13: { GOTOsaLess } begin if RegsDW[idCMP1] < RegsDW[idCMP2] then RgEIP := OptInt32(RAM[(RgEIP+1)]) or (OptInt32(RAM[(RgEIP+2)]) shl 8) else Inc(RgEIP, 5); end; $14: { GOTOsrIs } begin if RegsDW[idCMP1] = RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $15: { GOTOsrNotIs } begin if RegsDW[idCMP1] <> RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $16: { GOTOsrMoreIs } begin if RegsDW[idCMP1] >= RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $17: { GOTOsrLess } begin if RegsDW[idCMP1] < RegsDW[idCMP2] then RgEIP := RegsDW[RAM[(RgEIP+1)]] else Inc(RgEIP, 2); end; $18: { PUSHr } begin Dec(RgESP); RAMc[RgESP] := RegsDW[RAM[(RgEIP+1)]]; Inc(RgEIP, 2); end; $19: { POPr } begin RegsDW[RAM[(RgEIP+1)]] := RAMc[RgESP]; inc(RgESP); Inc(RgEIP, 2); end; $1A: { PUSHrsvr (кілька регістрів) } begin Adr1 := RAM[RgEIP+1]; tmp2 := RAM[RgEIP+2] - Adr1; Dec(RgESP, tmp2 + 1); for tmp1 := 0 to tmp2 do RAMc[RgESP+tmp1] := RegsDW[Adr1+tmp1]; Inc(RgEIP, 3); end; $1B: { POPrsvr (кілька регістрів) } begin Adr1 := RAM[RgEIP+1]; tmp2 := RAM[RgEIP+2] - Adr1; for tmp1 := 0 to tmp2 do RegsDW[Adr1+tmp1] := RAMc[RgESP+tmp1]; inc(RgESP, tmp2 + 1); Inc(RgEIP, 3); end; $1C: { ADDrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] + RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1D: { SUBrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] - RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1E: { MULrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] * RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $1F: { DIVrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] div RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $20: { MODrr } begin RegsDW[RAM[RgEIP+1]] := (RegsDW[RAM[RgEIP+1]] mod RegsDW[RAM[RgEIP+2]]); Inc(RgEIP, 3); end; $21: { INCr } begin Inc(RegsDW[RAM[RgEIP+1]]); Inc(RgEIP, 2); end; $22: { DECr } begin Dec(RegsDW[RAM[RgEIP+1]]); Inc(RgEIP, 2); end; $23: { ANDrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] and RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $24: { ORrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] or RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $25: { XORrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] xor RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $26: { NOTr } begin RegsDW[RAM[RgEIP+1]] := not RegsDW[RAM[RgEIP+1]]; Inc(RgEIP, 2); end; $27: { SHLrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] shl RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $28: { SHRrr } begin RegsDW[RAM[RgEIP+1]] := RegsDW[RAM[RgEIP+1]] shr RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; $29: { CMPrr } begin RegsDW[idCMP1] := RegsDW[RAM[RgEIP+1]]; RegsDW[idCMP2] := RegsDW[RAM[RgEIP+2]]; Inc(RgEIP, 3); end; { $2A: OpEXTR; Початок } $2A: begin { 232-239: EXTR вхідні дані } { idYA=232; idYB=233; idYC=234; idYD=235; idYE=236; idYF=237; idYG=238; idYH=239; } { EXTR вихідні дані: idZA=240; idZB=241; idZC=242; idZD=243; idZE=244; idZF=245; idZG=246; idZH=247 } Adr1 := RAM[RgEIP+1]; Adr2 := RAM[RgEIP+2]; Inc(RgEIP, 3); case Adr2 of 0: { 0-255: Процедури для тестів емулятору та інше } case Adr1 of 0: { EXTR_NOP } begin end; 1: { CheckEXTR } begin RegsDW[idZA] := 0; case RegsDW[idYA] of 0..16, 256..257, 512..515, 768..771: RegsDW[idZA] := 1; end; end; 2: { HALT_ } HALT(RegsDW[idYA]); 3: { Writeln_GetTimeStr } pWriteln_GetTimeStr; 4: { Readln0 } Readln; 5: { VersionEnotVM } RegsDW[idZA] := cVersionEnotVM; 6: { Writeln0 } Writeln; 7: { CONT } pCONT; 8: { WritelnYA } pWritelnYA; 9: { Wait00secTime_and_Writeln_GetTimeStr } pWait00secTime_and_Writeln_GetTimeStr; 10: { EndProgram } pEndProgram; 11: { TypeOfAmbience } RegsDW[idZA] := cTypeOfAmbience; 12: { APM } pSTOPEngine := 3; 13: { SizeOfRAMstack } RegsDW[idZA] := cSizeOfRAMstack; 14: { SizeOfRAM } RegsDW[idZA] := cSizeOfRAM; 15: { SizeOfRAMext } RegsDW[idZA] := cSizeOfRAMext; 16: { MaxPagesOfRAMext } RegsDW[idZA] := cMaxPagesOfRAMext; 17: RegsDW[idZA] := cEnotVMItIsCompiled; end; 1: { 256-511: STRING } case Adr1 of 0: { 256: WritelnStr } pWritelnStr; 1: { 257: WriteStr } pWriteStr; end; 2: { 512-767: ExtrRAM } case Adr1 of 0: { 512: CopyMainToMain } Move(RAM[RegsDW[idYC]], RAM[RegsDW[idYE]], RegsDW[idYB]); 1: { 513: CopyMainToExt } Move(RAM[RegsDW[idYC]], ExtRAMPages[RegsDW[idYF]]^[RegsDW[idYE]], RegsDW[idYB]); 2: { 514: CopyExtToMain } Move(ExtRAMPages[RegsDW[idYD]]^[RegsDW[idYC]], RAM[RegsDW[idYE]], RegsDW[idYB]); 3: { 515: CopyExtToExt } Move(ExtRAMPages[RegsDW[idYD]]^[RegsDW[idYC]], ExtRAMPages[RegsDW[idYF]]^[RegsDW[idYE]], RegsDW[idYB]); end; 3: { 768-1023: GOTOu... } case Adr1 of 0: { 768: GOTOuaMoreIs } begin if (Word(RegsDW[idCMP1] shr 16) > Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) >= Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := OptInt32(RAM[(RgEIP)]) or (OptInt32(RAM[(RgEIP+1)]) shl 8) else Inc(RgEIP, 4); end; 1: { 769: GOTOuaLess } begin if (Word(RegsDW[idCMP1] shr 16) < Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) < Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := OptInt32(RAM[(RgEIP)]) or (OptInt32(RAM[(RgEIP+1)]) shl 8) else Inc(RgEIP, 4); end; 2: { 770: GOTOurMoreIs } begin if (Word(RegsDW[idCMP1] shr 16) > Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) >= Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := RegsDW[RAM[(RgEIP)]] else Inc(RgEIP, 1); end; 3: { 771: GOTOurLess } begin if (Word(RegsDW[idCMP1] shr 16) < Word(RegsDW[idCMP2] shr 16)) or ((Word(RegsDW[idCMP1] shr 16) = Word(RegsDW[idCMP2] shr 16)) and (Word(RegsDW[idCMP1] and $FFFF) < Word(RegsDW[idCMP2] and $FFFF))) then RgEIP := RegsDW[RAM[(RgEIP)]] else Inc(RgEIP, 1); end; end; end; end; { OpEXTR; Кінець } $2B: { MOV2rmr: mov Rg32, word [Rg32] } begin Adr1 := RegsDW[RAM[(RgEIP+2)]]; RegsDW[RAM[(RgEIP+1)]] := (RAM[Adr1] or (RAM[Adr1+1] shl 8)); Inc(RgEIP, 3); end; $2C: { MOV2mrr: mov [Rg32], word Rg32 } begin Adr1 := RegsDW[RAM[(RgEIP+1)]]; V1 := RegsDW[RAM[(RgEIP+2)]]; RAM[Adr1] := Byte(V1); RAM[Adr1+1] := Byte(V1 shr 8); Inc(RgEIP, 3); end; $2D: { MOV1rmr: mov Rg32, byte [Rg32] } begin RegsDW[RAM[(RgEIP+1)]] := RAM[(RegsDW[RAM[(RgEIP+2)]])]; Inc(RgEIP, 3); end; $2E: { MOV1mrr: mov [Rg32], byte Rg32 } begin RAM[RegsDW[RAM[(RgEIP+1)]]] := RegsDW[RAM[(RgEIP+2)]]; Inc(RgEIP, 3); end; else pSTOPEngine := 2; { ErrorOpCode } end; until pSTOPEngine <> 0; { 1=EndProgram, 2=ErrorOpCode, 3=APM } RgEIPMain := RgEIP; end; procedure StartEngine; begin LoadBIN('BOOT.bin'); InitCPU; Engine16bitB4; end; end. uMain.pas: {Обробка вихідного коду від https://chat.qwen.ai} unit uMain; interface uses uEmul, uEmul0; procedure InitProgram; procedure StartProgram; procedure FinalProgram; implementation procedure InitProgram; begin InitExtRAM; end; procedure FinalProgram; begin end; procedure StartProgram; begin StartEngine; end; end. ENOTVM32.pas: {Обробка вихідного коду від https://chat.qwen.ai} Program ENOTVM32; uses Crt, uMain; begin InitProgram; StartProgram; FinalProgram; end. Документація та опис від автору EnotVM32 "01 Загальний опис EnotVM32.txt": Загальний опис EnotVM32. Емулятор EnotVM32, задумувався, враховуючи ряд факторів, які я може й не всі й пригадаю. :-) Але основні все таки спробую. Це: * Мінімальний набір команд процесору EnotVM32, який може бути легко перенесений на інші платформи та середовища розробки. * Щоб була схожість команд EnotVM32 на АСМ32, також для можливого відносно легкого перенесення коду з АСМ32 на EnotVM32, та навпаки. * Вихідний код емулятору доступний. * Використовувати можливості FASM, його макросів, для розробки програм для EnotVM32. Можливо й використовувати інші асемблери, окрім FASM. Тощо. Для написання програм для EnotVM32, використовуйте FASM та файл макросів Macros.asm, який описує ідентифікатори регістрів та макроси команд EnotVM32. Програма стартує з нульової адреси, при необхідності, це можна змінити в вихідному коді EnotVM32, але краще не треба. :-) Розмір оперативної пам'яті регулюється константою cSizeOfRAM. Розмір масиву стеку константою cSizeOfRAMstack. Що примітно, що розмір стеку вказується в 32 бітних даних, а не в байтах. І стек має окремий масив. Ну думаю, для загального опису цього достатньою. Тонкощі читайте в інших файлах документації по "EnotVM32". Особливу увагу зверніть на ось це: EnotVM32 є 32-бітним емулятором, але в 16-бітних середовищах, де немає сенсу для для 32-бітних переходів,EnotVM32 використовує лише частину адреси переходів, тобто 16-бітні переходи, для збереження швидкості емулятора. "02 Команди EnotVM32.txt": Опис, особливості та документація по командам віртуального процесору "EnotVM32". Регістрів 256. Назви регістрів дві літери від "AA" до "JV". Це так звані довгі назви регістрів. Ті назви регістрів, які не можуть бути використані, з-за того, що є зарезервовані FASM або іншим яким середовищем, до них додається "_" в кінці назви. В FASM наприклад, це AH_,AL_,AS_,AT_ і.д. дивіться в файлі Macros.asm. Існує також короткі назви регістрів для зручності. Від "A" до "Z", що відповідає довгій назві від "AA" до "AZ". Також існують регістри зарезервовані під інше. YA,YB,YC,YD,YE,YF,YG,YH, - зарезервовані для передачі даних при виклику EXTR (Системна інструкція для виклику розширень). ZA,ZB,ZC,ZD,ZE,ZF,ZG,ZH - Зарезервовані для повернення даних після виклику EXTR. CMP1,CMP2 - для використання їх в командах порівняння та переходів процесору "EnotVM32", таких як, GOTOsaIs, GOTOsaNotIs, GOTOsaMoreIs, GOTOsaLess, GOTOsrIs, GOTOsrNotIs, GOTOsrMoreIs, GOTOsrLess і т.д. Регістри зберігаються у вигляді масиву RegsDW:array[0..255] of Int32; (окрім RgESP та RgEIP) і тому у кожного регістра є цифровий ідентифікатор. Ідентифікатори регістрів, дивіться в Macros.asm. Також дивіться мнемоніку написання кожної команди, у вигляді макросів. Коротко A=0 до Z=25,CMP1=254,CMP2=255. В вихідних файлах ідентифікатори регістрів з додаванням "id" до регістру. В основному команди емулятору побудовані так, щоб усі операції, що тільки можливо, виконувались через 32-бітні регістри. Арифметичні операції виконуються як знакові (Int32) (Int32=Longint PAS|DELPHI). Логічні операції працюють на рівні бітів. Емулятор стартує з адреси 0, тобто при загрузці файлу, - RgEIP:=0. І програма починає робити. Довжина пам'яті налаштовується cSizeOfRAM. Для стеку є окремий масив RAMc:array[0..cSizeOfRAMstack] of Int32; його довжина налаштовується окремо cSizeOfRAMstack. При початку загрузки встановлюється RgESP:=cSizeOfRAMstack. Стек адресує елементи типу Int32 (4 байти). RgESP індексує елементи, а не байти. Регістри RgESP,RgEIP:Int32, недоступні, як звичайні регістри, тобто через команди типа MOV.Доступні вони будуть, через ($2A; EXTR). Опис команд самого процесору. Значення в опису команд. Rg1,Rg2 - ід-регістру відповідно "EnotVM32", (значення 0.255) V - числове 32 бітне значення -2147483648..2147483647 Rg32 - регістр аналог 32 бітного асемблеру (eax,ebx,ecx...) DWA - (DWord Adress), 32 бітна адреса Код десятичний; код шістнадцятирічний; Назва макросу (формат)в модулі Macros.asm який для FASM; довжина команди в байтах; аналог команди в FASM (можливо приблизний); опис команди. 00; $00; nop_; 1; nop; Пуста команда не виконує ніяких дій. ----------- команди MOV для 32 бітних регістрів. На відміну від асемблеру де робота з 32,16 та 8 бітними даними робиться, через одну команду MOV. В "EnotVM32", це робиться. окремими командами MOV..., MOV2..., MOV1 відповідно. 01; $01; MOVrv Rg1,V ; 6 ; mov Rg32,V ; Присвоює регістру Rg1 значення V. 02; $02; MOVrr Rg1,Rg2 ; 3 ; mov Rg32,Rg32 ; Присвоює регістру Rg1 значення регістру Rg2. 03; $03; MOVrm Rg1,DWA1 ; 6 ; mov Rg32,[DWA1] ; Присвоює регістру Rg1, значення, яке знаходиться по адресі DWA1. 04; $04; MOVrmr Rg1,Rg2 ; 3 ; mov Rg32,[Rg32] ; Присвоює регістру Rg1, значення, яке знаходиться по адресі регістру Rg2. 05; $05; MOVmr DWA1,Rg1 ; 6 ; mov [DWA1],Rg32 ; Присвоює пам'яті з адресою DWA1 значення, регістру Rg1. 06; $06; MOVmrr Rg1,Rg2 ; 3 ; mov [Rg32],Rg32 ; Присвоює пам'яті, з адресою в Rg1, значення, регістру Rg2. ----------- команди MOV для 16 бітних регістрів. 07; $07; MOV2rm Rg1,DWA1 ; 6 ; mov Rg32, word [DWA1]; Присвоює регістру Rg1, значення, яке знаходиться по адресі DWA1. 08; $08; MOV2mr DWA1,Rg1 ; 6 ; mov [DWA1], word Rg32; Присвоює пам'яті з адресою DWA1 значення, регістру Rg1. ----------- команди MOV для 8 бітних регістрів. 09; $09; MOV1rm Rg1,DWA1 ; 6 ; mov Rg32, byte [DWA1] ; Присвоює регістру Rg1, значення, яке знаходиться по адресі DWA1. 10; $0A; MOV1mr DWA1,Rg1 ; 6 ; mov [DWA1],byte Rg32 ; Присвоює пам'яті з адресою DWA1 значення, регістру Rg1. ----------- Команди викликів підпрограм та переходи. 11; $0B; CALLa DWA1 ; 5 ; CALL DWA1 ; Викликає підпрограму по адресі DWA1. 12; $0C; CALLr Rg1 ; 2 ; CALL Rg32 ; Викликає підпрограму по адресі, яка знаходиться в регістрі Rg1. 13; $0D; RET ; 1 ; RET ; Повертає з підпрограми. 14; $0E; GOTOa DWA1 ; 5 ; JMP DWA1 ; Безумовний перехід по адресі DWA1. 15; $0F; GOTOr Rg1 ; 2 ; JMP Rg1 ; Безумовний перехід по адресі, значення, якої в регістрі Rg1. ----------- Умовні переходи.ЗНАКОВІ. Умовні переходи порівнюють лише регістр CMP1 з регістром CMP2. Завантажувати дані регістри можна командами присвоєння регістрі MOV ($01 - $06) або однієї командою CMPrr {$29} можна завантажити відразу CMP1 та CMP2. Не забувайте данні порівняння знакові. Умовні переходи які роблять порівняння CMP1 та CMP2 та перехід. 16; $10; GOTOsaIs DWA1 ; 5 ;if ... then jump; Якщо CMP1=CMP2 то перехід, на адресу DWA1 17; $11; GOTOsaNotIs DWA1 ; 5 ; (дивись вище) ; Якщо CMP1<>CMP2 то перехід, на адресу DWA1 18; $12; GOTOsaMoreIs DWA1; 5 ; (дивись вище) ; Якщо CMP1>=CMP2 то перехід, на адресу DWA1 19; $13; GOTOsaLess DWA1 ; 5 ; (дивись вище) ; Якщо CMP1CMP2 то перехід, на адресу, яка вказана в Rg1 22; $16; GOTOsrMoreIs Rg1 ; 2 ; (дивись вище) ; Якщо CMP1>=CMP2 то перехід, на адресу, яка вказана в Rg1 23; $17; GOTOsrLess Rg1 ; 2 ; (дивись вище) ; Якщо CMP1 CMP2 then jump addr 12h if CMP1 >= CMP2 then jump addr 13h if CMP1 < CMP2 then jump addr 14h–17h — ті ж самі, але адреса в регістрі Стек. 18h PUSHr Rg1 push(Rg1) 19h POPr Rg1 Rg1 = pop() 1Ah PUSHrsvr Rg1,Rg2 push(Rg1..Rg2) 1Bh POPrsvr Rg1,Rg2 pop(Rg1..Rg2) Арифметика та логіка. Формат: Rg1 = (Rg1 op Rg2) | Opcode | Операція | | ------ | -------- | | 1C | ADD | | 1D | SUB | | 1E | MUL | | 1F | DIV | | 20 | MOD | | 21 | INC | | 22 | DEC | | 23 | AND | | 24 | OR | | 25 | XOR | | 26 | NOT | | 27 | SHL | | 28 | SHR | | ------ | -------- | CMP. 29h CMPrr Rg1, Rg2 CMP1 = Rg1; CMP2 = Rg2 Розширення. 2Ah EXTR Системна команда для виклику: * внутрішніх процедур * API емулятора * розширень (аналог BIOS / interrupt-подібного механізму) Особливості архітектури. * 256 регістрів * відсутність FLAGS (замість цього CMP1/CMP2) * явні розміри доступу до пам’яті (byte/word/dword) * стек працює з Int32 елементами * простий декодер команд (opcode + аргументи) Висновок. **EnotVM32** — це: * проста * швидка * добре контрольована VM-архітектура з елементами: * RISC (чіткі інструкції) * x86 (гнучкість доступу до пам’яті) "04 Документація по командам MOV від ChatGPT.txt": Документація по командам MOV від ChatGPT ( https://chatgpt.com/ ) Регістр < значення | Opcode | Формат | Опис | | ------ | ----------------- | ---------------------- | | 01 | `mov Rg32, imm32` | Завантаження константи | | 02 | `mov Rg32, Rg32` | Регістр < регістр | Регістр < пам’ять (за адресою) | Opcode | Формат | Опис | | ------ | ------------------------ | -------------------------- | | 03 | `mov Rg32, [imm32]` | Читання DWORD | | 07 | `mov Rg32, word [imm32]` | Читання WORD (zero-extend) | | 09 | `mov Rg32, byte [imm32]` | Читання BYTE (zero-extend) | Регістр < пам’ять (через регістр) | Opcode | Формат | Опис | | ------ | ----------------------- | ------------------ | | 04 | `mov Rg32, [Rg32]` | DWORD | | 2B | `mov Rg32, word [Rg32]` | WORD (zero-extend) | | 2D | `mov Rg32, byte [Rg32]` | BYTE (zero-extend) | Пам’ять < регістр (за адресою) | Opcode | Формат | Опис | | ------ | ------------------------ | ----------- | | 05 | `mov [imm32], Rg32` | Запис DWORD | | 08 | `mov [imm32], word Rg32` | Запис WORD | | 0A | `mov [imm32], byte Rg32` | Запис BYTE | Пам’ять < регістр (через регістр) | Opcode | Формат | Опис | | ------ | ----------------------- | ----- | | 06 | `mov [Rg32], Rg32` | DWORD | | 2C | `mov [Rg32], word Rg32` | WORD | | 2E | `mov [Rg32], byte Rg32` | BYTE | "05 Встановлення, використання та написання програм для EnotVM32.txt": Встановлення, використання та написання програм для EnotVM32. Встановлення. Опис для диску "C:\". Розпакуйте архів EnotVM32 на диск "C" повинно вийти ось так: C:\EnotVM32\BP4_001\ ATT.BGI CGA.BGI EGAVGA.BGI HERC.BGI PC3270.BGI EnotVM32.EXE Descript.ion Папка "BP4_001", BP4 - означає, що він компільований Turbo Pascal Version 4.0 Copyright (c) 1987 Borland International цифри це версія EnotVM32 Виберіть будь-який приклад з C:\EnotVM32\BP4_001\Examples\ файл "BOOT.bin" та скопіююйте його в папку C:\EnotVM32\BP4_001\ тепер можна запускати EnotVM32.EXE і він буде виконувати програму BOOT.bin Розробка програм. Розробка програм буде описуватись для flat assembler version 1.73.25 (FASM). Встановіть FASM цієї версії або будь якої іншої з офіційного сайту https://flatassembler.net/ https://flatassembler.net/download.php - посилання для скачування останньої версії. https://flatassembler.net/fasm17335.zip - flat assembler 1.73.35 for DOS ( fasm17335.7z - http://forumstatic.ru/files/001c/77/27/72607.7z ) або https://flatassembler.net/fasmw17335.zip - flat assembler 1.73.35 for Windows ( fasmw17335.7z - http://forumstatic.ru/files/001c/77/27/91859.7z ) створіть папку C:\FASMdos\ та розпакуйте fasm17335.zip або створіть папку C:\FASMwin\ та розпакуйте fasmw17335.zip Перевірьте, щоб був обов'язково файл макросів Macros.asm та BOOT.asm, - файл для написання програми для EnotVM32. Створимо... EditDos.Bat, який дозволяє редагувати BOOT.asm в режимі ДОС. EditWin.Bat, який дозволяє редагувати BOOT.asm в режимі Windows. Або ж ви можете використовувати будь який інший текстовий редактор який вам до вподоби. Якщо ви використовували один з цих редакторів та компілювали (Ctrl+F9) програму з'явиться (оновиться) файл BOOT.bin і після цього можна запустити EnotVM32.EXE для роботи BOOT.bin Якщо ж ви використовували будь який інших редактор, то програму треба компілювати. Для цього створені ComplDos.Bat та ComplWin.Bat відповідно для компіляції DOS-компілятором або Windows-компілятором і після цього можна запустити EnotVM32.EXE для роботи BOOT.bin Примітка. Якщо для компіляції ви використовуєте ComplDos.Bat або ComplWin.Bat, то зверніть увагу, на те, що перед компіляцією вони видаляють BOOT.bin, згідно строки "del BOOT.bin" в цих файлах. Якщо таке не потрібно, то, закоментуйте цю строку вставивши на її початок "REM ", тобто повинно буди "REM del BOOT.bin", або просто видаліть її повністю. Звісно при бажання можна створити Bat-файл і зробити, що відразу компілювала та запускала EnotVM32. Результат всього створеного буде в архіві EnotVM32. "99 Вдячність.txt": Автор EnotVM32 вдячний за допомогу в розробці та описам EnotVM32 штучним інтелектам На початку розоробки, двигун - ChatGPT ( https://chatgpt.com/ ) Декілька моментів в допомозі було від gemini.google ( https://gemini.google.com/ ) https://chat.qwen.ai за Вихідний код оброблений та документацію. https://chat.deepseek.com/ за Вихідний код оброблений та документацію. Надалі користуюсь https://chat.deepseek.com/ Документація користувача EnotVM32 від https://chat.deepseek.com/ 1. Загальний опис EnotVM32 — це проста 32?бітна віртуальна машина (емулятор), написана на Turbo Pascal 4.0. Вона дозволяє виконувати програми, скомпільовані у власний бінарний формат (`BOOT.bin`). Проєкт відкритий, поширюється під ліцензією MIT. Основні цілі: - мінімальний набір команд, легкий для перенесення на інші платформи; - схожість на асемблер x86 (спрощене перенесення коду); - використання макросів FASM для написання програм; - відкритий вихідний код емулятора. 2. Встановлення та перший запуск 1. Розпакуйте архів `EnotVM32` у теку `C:\EnotVM32\BP4_001\`. У ній вже знаходяться: - `EnotVM32.EXE` – готова програма; - файли `*.BGI` для графіки (не обов’язкові для текстових програм); - папка `Examples\` з прикладами. 2. Виберіть приклад (наприклад, `Examples\hello\BOOT.bin`) та скопіюйте його в `BP4_001\`. 3. Запустіть `EnotVM32.EXE` — він прочитає `BOOT.bin` і почне виконання. 3. Розробка власних програм (за допомогою FASM) Для написання програм потрібен flat assembler (FASM) версії 1.73 або інші версії. 3.1. Встановлення FASM - Завантажте з офіційного сайту (https://flatassembler.net/download.php): - `fasm17335.zip` (для DOS) або `fasmw17335.zip` (для Windows). - Розпакуйте, наприклад, у `C:\FASMdos\` або `C:\FASMwin\`. 3.2. Структура проєкту У папці з прикладами ви знайдете: - `Macros.asm` – набір макросів, що визначають імена регістрів та мнемоніки команд EnotVM32 (обов’язковий файл). - `BOOT.asm` – вихідний текст програми. - `BOOT.bin` – скомпільований образ (оновлюється після компіляції). 3.3. Компіляція Варіант А – з інтегрованим редактором (DOS / Windows) Використовуйте готові bat?файли (є в комплекті прикладів): - `EditDos.bat` – редагування та компіляція через DOS?версію FASM. - `EditWin.bat` – те саме для Windows. У редакторі натисніть `Ctrl+F9` – буде створено `BOOT.bin`. Варіант Б – ручна компіляція - `ComplDos.bat` або `ComplWin.bat` автоматично викличуть FASM. - Увага: ці bat-файли спочатку видаляють старий `BOOT.bin`. Якщо це не потрібно, закоментуйте рядок `del BOOT.bin` (додайте `REM` на початку). Після компіляції скопіюйте `BOOT.bin` у папку з `EnotVM32.EXE` та запустіть емулятор. 4. Архітектура EnotVM32 4.1. Регістри - 256 32-бітних регістрів (масив `RegsDW[0..255]`). - Довгі назви: від `AA` до `JV` (деякі зарезервовані FASM мають суфікс `_`, наприклад `AH_`, `AL_`). - Короткі назви: від `A` до `Z` (відповідають `AA` – `AZ`). - Спеціальні регістри: - `CMP1` (254), `CMP2` (255) – для операцій порівняння. - `YA..YH` (232–239) – вхідні параметри для системних викликів `EXTR`. - `ZA..ZH` (240–247) – вихідні значення після `EXTR`. - Регістри `RgEIP` (вказівник інструкцій) та `RgESP` (вказівник стеку) безпосередньо не доступні через `MOV`, але будуть доступні через майбутні розширення `EXTR`. 4.2. Пам’ять - Основна пам’ять: масив байтів `RAM[0..cSizeOfRAM]`, за замовчуванням 48 КБ (49152 байти, адреси 0..49151). Розмір можна змінити в `uTpDt.pas` (константа `cSizeOfRAM`). - Розширена пам’ять: до 11 сторінок (`cMaxPagesOfRAMext = 10`) по 48 КБ кожна. Сторінки створюються динамічно під час ініціалізації. Доступ через спеціальні `EXTR`-команди копіювання. 4.3. Стек - Окремий масив `RAMc[0..cSizeOfRAMstack] of Int32` (за замовчуванням 256 елементів). - `RgESP` адресує елементи Int32, а не байти. Ініціалізується значенням `cSizeOfRAMstack` (вершина стеку). - Команди `PUSH`/`POP` змінюють `RgESP` на 1. 4.4. Типи даних - Усі регістри — знакові 32-бітні цілі (діапазон –2147483648 … 2147483647). - Арифметичні операції (`ADD`, `SUB`, `MUL`, `DIV`, `MOD`) виконуються як знакові. - Логічні операції (`AND`, `OR`, `XOR`, `NOT`, `SHL`, `SHR`) — побітові. - Зсуви `SHL`/`SHR` працюють з 32?бітним операндом; кількість зсувів береться з регістра. 5. Система команд Повний перелік команд (опкод, мнемоніка, довжина, опис) наведено у файлі `02 Команди EnotVM32.txt`. Нижче – стислий довідник. 5.1. NOP `00` – `nop_` (1 байт) – нічого не робить. 5.2. Команди пересилання (MOV) Усі варіанти доступу до пам’яті: безпосередня адреса (`[imm32]`) або через регістр (`[Rg]`), а також розмір даних: 32 біти (MOV), 16 біт (MOV2), 8 біт (MOV1). 32?бітні (регістр < значення/пам’ять): - `01` MOVrv Rg, imm32 - `02` MOVrr Rg1, Rg2 - `03` MOVrm Rg, [imm32] - `04` MOVrmr Rg1, Rg2 (Rg1 := [Rg2]) - `05` MOVmr [imm32], Rg - `06` MOVmrr Rg1, Rg2 ([Rg1] := Rg2) 16?бітні (читання з розширенням до 32 біт): - `07` MOV2rm Rg, [imm32] - `2B` MOV2rmr Rg1, Rg2 - `08` MOV2mr [imm32], Rg (записуються молодші 16 біт) - `2C` MOV2mrr Rg1, Rg2 8?бітні (читання з розширенням до 32 біт): - `09` MOV1rm Rg, [imm32] - `2D` MOV1rmr Rg1, Rg2 - `0A` MOV1mr [imm32], Rg - `2E` MOV1mrr Rg1, Rg2 5.3. Переходи та виклики - `0B` CALLa imm16 – виклик підпрограми за абсолютною адресою. - `0C` CALLr Rg – виклик за адресою в регістрі. - `0D` RET – повернення. - `0E` GOTOa imm16 – безумовний перехід. - `0F` GOTOr Rg – безумовний перехід через регістр. 5.4. Умовні переходи (знакові) Порівнюються регістри `CMP1` і `CMP2`. Їх можна завантажити командою `CMPrr` (`29`) або будь?яким `MOV`. - `10` GOTOsaIs imm16 – якщо CMP1 = CMP2 - `11` GOTOsaNotIs imm16 – якщо CMP1 ? CMP2 - `12` GOTOsaMoreIs imm16 – якщо CMP1 ? CMP2 - `13` GOTOsaLess imm16 – якщо CMP1 < CMP2 - `14`-`17` – ті самі умови, але адреса переходу в регістрі (GOTOsr...). 5.5. Стекові команди - `18` PUSHr Rg – покласти регістр у стек. - `19` POPr Rg – витягти зі стеку. - `1A` PUSHrsvr Rg1, Rg2 – зберегти регістри з діапазону Rg1..Rg2. - `1B` POPrsvr Rg1, Rg2 – відновити діапазон. 5.6. Арифметичні та логічні операції (регістр–регістр) `1C` ADD, `1D` SUB, `1E` MUL, `1F` DIV, `20` MOD, `21` INC, `22` DEC, `23` AND, `24` OR, `25` XOR, `26` NOT, `27` SHL, `28` SHR. Усі, крім `INC`, `DEC`, `NOT`, мають довжину 3 байти (`opcode + Rg1 + Rg2`). `INC`, `DEC`, `NOT` – 2 байти. 5.7. Порівняння `29` CMPrr Rg1, Rg2 – записує Rg1 у CMP1, Rg2 у CMP2. 5.8. Системний виклик EXTR `2A` EXTR word_value – виклик функцій розширення (аналог переривань). Детальний опис функцій – у наступному розділі. 6. Системні виклики EXTR Команда `EXTR` (опкод `2Ah`) приймає 16?бітний параметр, який задає групу та номер функції. Деякі функції використовують регістри `YA..YH` для вхідних даних, а результати повертають у `ZA..ZH`. Нижче – перелік реалізованих функцій. 6.1. Група 0 (загальні) | Номер | Назва | Вхідні | Вихідні | Опис | |-------|-------------------------------------|--------|---------|------| | 0 | EXTR_NOP | – | – | Порожня операція (для тестів) | | 1 | CheckEXTR | YA = номер команди | ZA=1 існує, ZA=0 не існує | Перевірка наявності EXTR?команди | | 2 | HALT_ | YA = код виходу | – | Негайне завершення програми (Halt) | | 3 | Writeln_GetTimeStr | – | – | Вивести поточний час (год:хв:сек.соті) | | 4 | Readln0 | – | – | Очікування натискання Enter | | 5 | VersionEnotVM | – | ZA = версія | Версія EnotVM (константа cVersionEnotVM) | | 6 | Writeln0 | – | – | Вивести порожній рядок | | 7 | CONT | – | – | Запит “Will Enter 'cont' or 'CONT' for continue” і очікування вводу | | 8 | WritelnYA | – | – | Вивести значення YA у десятковому та шістнадцятковому вигляді | | 9 | Wait00secTime_and_Writeln_GetTimeStr| – | – | Чекати, поки лічильник секунд стане 0, потім вивести час | | 10 | EndProgram | – | – | Коректне завершення роботи двигуна | | 11 | TypeOfAmbience | – | ZA = 1 (16-біт) або 2 (32-біт) | Тип середовища | | 12 | APM | – | – | Обробка повідомлень (сумісність), встановлює pSTOPEngine=3 | | 13 | SizeOfRAMstack | – | ZA = розмір стеку (в елементах) | | | 14 | SizeOfRAM | – | ZA = cSizeOfRAM | Розмір основної пам’яті | | 15 | SizeOfRAMext | – | ZA = cSizeOfRAMext | Розмір сторінки розширеної пам’яті | | 16 | MaxPagesOfRAMext | – | ZA = cMaxPagesOfRAMext | Кількість сторінок (від 0 до …) | | 17 | (середовище компіляції) | – | ZA = cEnotVMItIsCompiled | Тип компілятора (1 = TP 4.0) | 6.2. Група 1 – рядковий ввід/вивід (256–511) | Номер | Назва | Вхідні | Вихідні | Опис | |-------|--------------|--------|---------|------| | 256 | WritelnStr | YA = адреса рядка у спеціальному форматі | – | Вивести рядок з переводом рядка | | 257 | WriteStr | YA = адреса рядка | – | Вивести рядок без переводу рядка | Рядки в пам’яті зберігаються у форматі: - 1 байт – тип (`1`…`4`), - далі поля довжини та поточної довжини, - потім власне символи. 6.3. Група 2 – копіювання пам’яті (512–767) Усі функції використовують регістри: - `YB` – кількість байт (Count) - `YC`, `YD`, `YE`, `YF` – адреси/номери сторінок (див. нижче) | Номер | Назва | Параметри | |-------|-----------------|-------------------------------------------------------------------| | 512 | CopyMainToMain | YC = Src, YB = Count, YE = Dst | | 513 | CopyMainToExt | YC = MainSrc, YB = Count, YF = PageDst, YE = ExtDst | | 514 | CopyExtToMain | YD = PageSrc, YC = ExtSrc, YB = Count, YE = MainDst | | 515 | CopyExtToExt | YD = PageSrc, YC = ExtSrc, YB = Count, YF = PageDst, YE = ExtDst | Примітка: перекриття ділянок при копіюванні обробляється коректно (використовується процедура `Move`). 6.4. Група 3 – беззнакові умовні переходи (768–1023) Ці команди порівнюють пару `CMP1`/`CMP2` як беззнакові 32?бітні числа (старше слово, потім молодше). | Номер | Назва | Дія | |-------|----------------|---------------------------------------------------------------| | 768 | GOTOuaMoreIs | Якщо CMP1 ? CMP2 (беззнаково) — перехід за 16?бітною адресою | | 769 | GOTOuaLess | Якщо CMP1 < CMP2 — перехід | | 770 | GOTOurMoreIs | Те саме, але адреса у регістрі, вказаному після EXTR | | 771 | GOTOurLess | Аналогічно | 7. Приклад найпростішої програми Файл `BOOT.asm` (з використанням макросів `Macros.asm`): asm format binary as 'bin' include 'Macros.asm' ; Вивести "Hello, World!" та завершити MOVrv A, hello_string ; YA := адреса рядка EXTR 256 ; WritelnStr EXTR 10 ; EndProgram hello_string: db 1, 13, 13 ; тип 1, повна довжина=13, поточна=13 db 'Hello, World!' Після компіляції отриманий `BOOT.bin` скопіюйте до `EnotVM32.EXE` і запустіть. 8. Ліцензія Проєкт поширюється під "MIT License". Повний текст ліцензії додається окремим файлом `LICENSE`. Ви можете вільно використовувати, змінювати та розповсюджувати як вихідний код, так і скомпільовані програми. --- Документація підготовлена на основі оригінальних описів та вихідного коду. Детальна документація користувача та розробника EnotVM32 від https://chat.qwen.ai Повна документація EnotVM32 1. Загальний опис EnotVM32 — це віртуальний 32-бітний процесор з регістрово-стековою архітектурою. Емулятор створений з акцентом на простоту перенесення на інші платформи, схожість із синтаксисом ASM32 та можливість використання асемблера FASM (Flat Assembler) для написання програм. Особливість архітектури: EnotVM32 є 32-бітним емулятором, але у 16-бітних середовищах (наприклад, Turbo Pascal / DOS) для збереження швидкодії використовуються 16-бітні адреси переходів. --- 2. Архітектура віртуального процесора 2.1. Регістри У процесора 256 регістрів загального призначення. Кожен регістр має розмірність Int32 (32-бітне знакове ціле). Короткі імена: `A` ... `Z` (ідентифікатори `0` ... `25`). Довгі імена: `AA` ... `JV`. (Якщо ім'я зайняте середовищем FASM, додається `_`, наприклад `AH_`, `AL_`). Спеціальні регістри для EXTR: `YA` ... `YH` (232 ... 239) — для передачі вхідних параметрів. `ZA` ... `ZH` (240 ... 247) — для повернення результатів. Регістри порівняння: `CMP1` (254) та `CMP2` (255). Використовуються для умовних переходів. Службові регістри: `RgEIP` — вказівник інструкції (лічильник команд). `RgESP` — вказівник стеку. (Вони недоступні для прямого читання/запису через звичайні команди `MOV`). 2.2. Пам'ять та Стек Основна пам'ять (RAM): Лінійний масив байтів. Розмір: 48 КБ (49152 байт, індекси `0..49151`). Виконання програми завжди починається з адреси `0`. Стек (RAMc): Окремий масив для стеку. Розмір: 255 елементів. Важливо: Стек зберігає елементи типу `Int32` (4 байти). `RgESP` індексує елементи, а не байти! Команди `PUSH/POP` змінюють `RgESP` на `1`. 2.3. Розширена пам'ять Для роботи з об'ємами даних, що перевищують 48 КБ, передбачена розширена пам'ять. Розмір однієї сторінки: 48 КБ. Кількість сторінок: до 10 (індекси `0..cMaxPagesOfRAMext`). --- 3. Система команд (ISA) 3.1. Команди пересилання даних (MOV) На відміну від класичного x86, де `MOV` універсальний, в EnotVM32 розмірність даних задається окремою командою: 32-бітні (DWORD): `MOVrv`, `MOVrr`, `MOVrm`, `MOVrmr`, `MOVmr`, `MOVmrr` 16-бітні (WORD): `MOV2rm`, `MOV2rmr`, `MOV2mr`, `MOV2mrr` (доповнюються нулями до 32 біт). 8-бітні (BYTE): `MOV1rm`, `MOV1rmr`, `MOV1mr`, `MOV1mrr` (доповнюються нулями до 32 біт). 3.2. Керування потоком `CALLa DWA1` / `CALLr Rg1` — виклик підпрограми (адреса або регістр). `RET` — повернення з підпрограми. `GOTOa DWA1` / `GOTOr Rg1` — безумовний перехід (аналог `JMP`). 3.3. Умовні переходи Порівнюються тільки регістри `CMP1` та `CMP2` (знакове порівняння). `GOTOsa...` — перехід за абсолютною адресою. `GOTOsr...` — перехід за адресою з регістру. Умови: `Is` (=), `NotIs` (<>), `MoreIs` (>=), `Less` (<). 3.4. Робота зі стеком `PUSHr Rg1` / `POPr Rg1` — для одного регістру. `PUSHrsvr Rg1, Rg2` / `POPrsvr Rg1, Rg2` — збереження/відновлення діапазону регістрів (дуже зручно для прологів/епілогів функцій). 3.5. Арифметика та логіка Працюють лише з 32-бітними регістрами. Формат: `Rg1 = Rg1 op Rg2`. Арифметика: `ADD`, `SUB`, `MUL`, `DIV`, `MOD`, `INC`, `DEC`. Логіка: `AND`, `OR`, `XOR`, `NOT`, `SHL`, `SHR`. `CMPrr Rg1, Rg2` — завантажує `Rg1` в `CMP1`, а `Rg2` в `CMP2` для подальших умовних переходів. --- 4. Системні виклики (EXTR) Команда `$2A` (`EXTR Adr1, Adr2`) — це аналог переривань (INT) або BIOS. Вона викликає розширення емулятора. Адресація відбувається за групами за номером типа word, який "розбивається" на старший та молодший байти Adr1 та Adr2 (`Adr2` визначає групу, `Adr1` — номер функції). Група 0 (0-255): Системні функції | Код | Опис | | | 0 | `EXTR_NOP` (Пуста команда для тестів) | | 1 | `CheckEXTR` (Перевірка існування команди EXTR) | | 2 | `HALT` (Негайний вихід, код виходу в `YA`) | | 3 | `Writeln_GetTimeStr` (Вивести поточний час) | | 4 | `Readln0` (Очікування натискання Enter) | | 5 | `VersionEnotVM` (Повертає версію емулятора в `ZA`) | | 6 | `Writeln0` (Вивести порожній рядок) | | 7 | `CONT` (Пауза: очікує введення 'cont' або 'CONT') | | 8 | `WritelnYA` (Вивести вміст регістра `YA` у Dec та Hex) | | 9 | `Wait00secTime...` (Очікування 00 секунди та вивід часу) | | 10 | `EndProgram` (Коректне завершення роботи двигуна) | | 11-17| Інформація про середовище, розміри RAM та стеку. | Група 1 (256-511): Робота з рядками 256: `WritelnStr` — вивести рядок з переходом на новий. 257: `WriteStr` — вивести рядок без переходу. Вказівник на структуру рядка передається через регістр `YA`. Група 2 (512-767): Робота з розширеною пам'яттю 512: `CopyMainToMain` (Копіювання в основній пам'яті). 513: `CopyMainToExt` (З основної в розширену). 514: `CopyExtToMain` (З розширеної в основну). 515: `CopyExtToExt` (Між сторінками розширеної пам'яті). Група 3 (768-1023): Беззнакові умовні переходи Аналогічні переходам з Групи 1, але порівняння `CMP1` та `CMP2` відбувається як беззнакових 32-бітних чисел (`GOTOuaMoreIs`, `GOTOuaLess`, `GOTOurMoreIs`, `GOTOurLess`). --- 5. Формати рядків (для EXTR 256/257) Рядки в EnotVM32 зберігаються у пам'яті зі спеціальним заголовком, який вказує на тип та розмір. Програма зчитує байт за адресою з `YA`: Тип 1: 8-бітні розміри (FullSize, CurSize). Тип 2: 16-бітні розміри. Тип 3: 24-бітні розміри. Тип 4: 32-бітні розміри. Дані рядка починаються після заголовка і можуть бути більшими за 255 байт (емулятор виводить їх частинами). --- 6. Встановлення та запуск емулятора 1. Розпакування: Розпакуйте архів `EnotVM32` на диск `C:\`. Структура папки має виглядати так: `C:\EnotVM32\BP4_001\` (де `BP4` вказує на те, що емулятор скомпільований у Turbo Pascal 4.0). 2. Підготовка програми: Перейдіть у папку `C:\EnotVM32\BP4_001\Examples\`, виберіть будь-який приклад (файл `BOOT.bin`) і скопіюйте його в корінь папки `C:\EnotVM32\BP4_001\`. 3. Запуск: Запустіть файл `EnotVM32.EXE`. Емулятор автоматично завантажить `BOOT.bin` в пам'ять і почне виконання з адреси 0. --- 7. Розробка програм для EnotVM32 (FASM) Для написання програм рекомендується використовувати flat assembler (FASM) версії 1.73.25 або новішої. Налаштування середовища: 1. Завантажте FASM з офіційного сайту (https://flatassembler.net/). 2. Розпакуйте його у папку `C:\FASMdos\` (для DOS) або `C:\FASMwin\` (для Windows). 3. Переконайтеся, що у папці з вашим проектом обов'язково присутні файли: `Macros.asm` — файл макросів, який описує ідентифікатори регістрів (`AA`, `YA`, `CMP1` тощо) та мнемоніки команд EnotVM32. `BOOT.asm` — шаблонний файл для написання вашої програми. Компіляція: Ви можете редагувати `BOOT.asm` у будь-якому текстовому редакторі. Для зручності створені bat-файли: `EditDos.Bat` / `EditWin.Bat` — для відкриття редактора. `ComplDos.Bat` / `ComplWin.Bat` — для компіляції. Важливо: Bat-файли компіляції за замовчуванням видаляють старий `BOOT.bin` (`del BOOT.bin`). Якщо ви хочете зберегти стару версію, закоментуйте цей рядок (`REM del BOOT.bin`). Після успішної компіляції з'явиться оновлений файл `BOOT.bin`, який готовий до запуску в `EnotVM32.EXE`. ;Macros.asm: ; Файл макросів для EnotVM32 ;Довга назва регістрів AA=0 AB=1 AC=2 AD=3 AE=4 AF=5 AG=6 AH_=7 AI=8 AJ=9 AK=10 AL_=11 AM=12 AN=13 AO=14 AP=15 AQ=16 AR=17 AS_=18 AT_=19 AU=20 AV=21 AW=22 AX_=23 AY=24 AZ=25 BA=26 BB=27 BC=28 BD=29 BE=30 BF=31 BG=32 BH_=33 BI=34 BJ=35 BK=36 BL_=37 BM=38 BN=39 BO=40 BP_=41 BQ=42 BR=43 BS=44 BT_=45 BU=46 BV=47 BW=48 BX_=49 BY=50 BZ=51 CA=52 CB=53 CC=54 CD=55 CE=56 CF=57 CG=58 CH_=59 CI=60 CJ=61 CK=62 CL_=63 CM=64 CN=65 CO=66 CP=67 CQ=68 CR=69 CS_=70 CT=71 CU=72 CV=73 CW=74 CX_=75 CY=76 CZ=77 DA=78 DB_=79 DC=80 DD_=81 DE=82 DF_=83 DG=84 DH_=85 DI_=86 DJ=87 DK=88 DL_=89 DM=90 DN=91 DO=92 DP_=93 DQ_=94 DR=95 DS_=96 DT_=97 DU_=98 DV=99 DW_=100 DX_=101 DY=102 DZ=103 EA=104 EB=105 EC=106 ED=107 EE=108 EF=109 EG=110 EH=111 EI=112 EJ=113 EK=114 EL=115 EM=116 EN=117 EO=118 EP=119 EQ_=120 ER=121 ES_=122 ET=123 EU=124 EV=125 EW=126 EX=127 EY=128 EZ=129 FA=130 FB=131 FC=132 FD=133 FE=134 FF=135 FG=136 FH=137 FI=138 FJ=139 FK=140 FL=141 FM=142 FN=143 FO=144 FP=145 FQ=146 FR=147 FS_=148 FT=149 FU=150 FV=151 FW=152 FX=153 FY=154 FZ=155 GA=156 GB=157 GC=158 GD=159 GE=160 GF=161 GG=162 GH=163 GI=164 GJ=165 GK=166 GL=167 GM=168 GN=169 GO=170 GP=171 GQ=172 GR=173 GS_=174 GT=175 GU=176 GV=177 GW=178 GX=179 GY=180 GZ=181 HA=182 HB=183 HC=184 HD=185 HE=186 HF=187 HG=188 HH=189 HI=190 HJ=191 HK=192 HL=193 HM=194 HN=195 HO=196 HP=197 HQ=198 HR=199 HS=200 HT=201 HU=202 HV=203 HW=204 HX=205 HY=206 HZ=207 IA=208 IB=209 IC=210 ID=211 IE=212 IF_=213 IG=214 IH=215 II=216 IJ=217 IK=218 IL=219 IM=220 IN_=221 IO=222 IP=223 IQ=224 IR=225 IS=226 IT=227 IU=228 IV=229 IW=230 IX=231 IY=232 IZ=233 JA_=234 JB_=235 JC_=236 JD=237 JE_=238 JF=239 JG_=240 JH=241 JI=242 JJ=243 JK=244 JL_=245 JM=246 JN=247 JO_=248 JP_=249 JQ=250 JR=251 JS_=252 JT=253 JU=254 JV=255 ; коротка назва регістрів A=0 B=1 C=2 D=3 E=4 F=5 G=6 H=7 I=8 J=9 K=10 L=11 M=12 N=13 O=14 P=15 Q=16 R=17 S=18 T=19 U=20 V=21 W=22 X=23 Y=24 Z=25 ;{232-239}{EXTR вход.дан.} YA=232 YB=233 YC=234 YD=235 YE=236 YF=237 YG=238 YH=239 ;{240-247}{EXTR виход.дан.} ZA=240 ZB=241 ZC=242 ZD=243 ZE=244 ZF=245 ZG=246 ZH=247 ;{248-255}{резерв} та CMP CMP1=254 CMP2=255 ; команди процесора Енот Macro nop_ {db $00} ;{NOP} Macro MOVrv Rg1,V { ; {mov Rg32,V} db $01 db Rg1 dd V} Macro MOVrr Rg1,Rg2 { ;{mov Rg32,Rg32} db $02 db Rg1 db Rg2} Macro MOVrm Rg1,DWA1 {;{mov Rg32,[DWA1]} db $03 db Rg1 dd DWA1} Macro MOVrmr Rg1,Rg2 { ;{mov Rg32,[Rg32]} db $04 db Rg1 db Rg2} Macro MOVmr DWA1,Rg1 { ;{mov [DWA1],Rg32} db $05 dd DWA1 db Rg1} Macro MOVmrr Rg1,Rg2 { ;{mov [Rg32],Rg32} db $06 db Rg1 db Rg2} Macro MOV2rm Rg1,DWA1 {;{mov Rg32, word [DWA1]} db $07 db Rg1 dd DWA1} Macro MOV2mr DWA1,Rg1 { ;{mov [DWA1], word Rg32;} db $08 dd DWA1 db Rg1} Macro MOV1rm Rg1,DWA1 { ;{mov Rg32, byte [DWA1]} db $09 db Rg1 dd DWA1} Macro MOV1mr DWA1,Rg1 { ;{mov [DWA1],byte Rg32} db $0A dd DWA1 db Rg1} Macro CALLa DWA1 { db $0B dd DWA1} Macro CALLr Rg1 { db $0C db Rg1} Macro RET_ {db $0D} Macro GOTOa DWA1 { db $0E dd DWA1} Macro GOTOr Rg1 { db $0F db Rg1} ; ЗНАКОВІ ПОРІВНЯННЯ Macro GOTOsaIs DWA1 { ; Якщо CMP1=CMP2 то перехід, на адресу DWA1 db $10 dd DWA1} Macro GOTOsaNotIs DWA1 {; Якщо CMP1<>CMP2 то перехід, на адресу DWA1 db $11 dd DWA1} Macro GOTOsaMoreIs DWA1 { ; Якщо CMP1>=CMP2 то перехід, на адресу DWA1 db $12 dd DWA1} Macro GOTOsaLess DWA1 { ; Якщо CMP1CMP2 то перехід, на адресу, яка вказана в Rg1 db $15 db Rg1} Macro GOTOsrMoreIs Rg1 {; Якщо CMP1>=CMP2 то перехід, на адресу, яка вказана в Rg1 db $16 db Rg1} Macro GOTOsrLess Rg1 { ; Якщо CMP1=CMP2 то перехід, на адресу DWA1 EXTR 768 dd DWA1 } Macro GOTOuaLess DWA1 { ; Якщо CMP1=CMP2 то перехід, на адресу, яка вказана в Rg1 EXTR 770 db Rg1} Macro GOTOurLess Rg1 { ; Якщо CMP1 1 ; Використаємо YC як лічильник MOVrv YC, 1 ; YC = 1 (константа для порівняння) CMPrr YA, YC ; CMP1 = YA, CMP2 = 1 GOTOsaLess done ; якщо YA < 1, закінчити (але YA=5, тому не спрацює відразу) ; множимо результат на YA і зменшуємо YA MULrr YB, YA ; YB = YB * YA DECr YA ; YA-- ; перевірити, чи YA >= 1 CMPrr YA, YC ; CMP1 = YA, CMP2 = 1 GOTOsaMoreIs fact_loop ; якщо YA >= 1, продовжуємо done: MOVrr YA, YB ; YA = результат WritelnYA ; виведе "120 ($00000078)" Readln0 EndProgram ; Пояснення: ; Використовуємо `YB` для накопичення добутку. Спочатку `YA = 5`, `YB = 1`. ; У циклі `YB = YB * YA`, потім `YA--`. Коли `YA` стане менше 1, виходимо. ; Умова `GOTOsaMoreIs` перевіряє `YA >= 1`? (CMP1=YA, CMP2=1). Це працює, ; бо макрос `GOTOsaMoreIs` — знакове порівняння «більше або дорівнює». ; Для додатних чисел коректно. "05 CopyTo" ; Програма від https://chat.deepseek.com/ ; Копіювання даних між основною та розширеною пам’яттю ; Приклад записує рядок в основну пам’ять (за адресою 1000), ; копіює його в розширену сторінку 0, потім назад в основну ; пам’ять за адресою 2000 та виводить звідти. format binary as 'bin' include 'Macros.asm' ; Створимо рядок за адресою 1000 в основній пам'яті MOVrv YA, 1000 ; YA = dst (куди копіювати рядок) MOVrv YB, src_str ; YB = адреса джерела (наш рядок у коді) MOVrv YC, src_len ; YC = кількість байт для копіювання MOVrv YE, 1000 ; YE = dst (дублюємо для CopyMainToMain) ; Насправді макрос CopyMainToMain використовує idYC, idYB, idYE, ; тому встановимо їх: MOVrv YC, src_str ; YC = джерело MOVrv YB, src_len ; YB = кількість MOVrv YE, 1000 ; YE = призначення CopyMainToMain ; копіюємо рядок у RAM[1000..1000+src_len-1] ; Копіюємо з основної пам'яті (адреса 1000) у розширену сторінку 0, зміщення 0 MOVrv YC, 1000 ; YC = адреса джерела в RAM MOVrv YB, src_len ; YB = кількість MOVrv YF, 0 ; YF = номер сторінки (page) MOVrv YE, 0 ; YE = зміщення в сторінці CopyMainToExt ; Копіюємо назад з розширеної сторінки 0, зміщення 0 -> основна RAM за адресою 2000 MOVrv YD, 0 ; YD = номер сторінки джерела MOVrv YC, 0 ; YC = зміщення в сторінці джерела MOVrv YB, src_len ; YB = кількість MOVrv YE, 2000 ; YE = адреса призначення в RAM CopyExtToMain ; Виведемо рядок, який тепер лежить за адресою 2000 MOVrv YA, 2000 WritelnStr ; вивести рядок Readln0 EndProgram ; Рядок-джерело (використовуємо VarStrB для зручності) src_str: VarStrB 15, 'Hello from RAM!' src_len = $ - src_str ; обчислимо реальну довжину даних (включаючи заголовок VarStrB) "06 GOTOuaMoreIs" ; Програма від https://chat.deepseek.com/ ; Беззнакове порівняння та перехід (EXTR 768–771) ; Порівнює два 32?бітних беззнакових числа та переходить, ; якщо перше більше або рівне другому. format binary as 'bin' include 'Macros.asm' MOVrv A, 4000000000 ; A (0xEE6B2800) – більше за максимальне знакове позитивне MOVrv B, 2000000000 ; B CMPrr A, B ; CMP1=A, CMP2=B GOTOuaMoreIs addr_yes; (перехід, якщо CMP1 >= CMP2 беззнаково) ; Сюди потрапимо лише якщо умова хибна MOVrv YA, msg_no WritelnStr ; виведення строк типа String на екран з вводом Readln0 ; Натиснути "Enter" для продовження. Аналог "Readln;" в TP. EndProgram ; ставиться наприкінці програми для виходу з двигуна Енот. addr_yes: MOVrv YA, msg_yes WritelnStr ; виведення строк типа String на екран з вводом Readln0 ; Натиснути "Enter" для продовження. Аналог "Readln;" в TP. EndProgram ; ставиться наприкінці програми для виходу з двигуна Енот. msg_yes: VarStrB 11,'First is >=' msg_no: VarStrB 10,'First is <' ; Результат: виведе `First is >=`. "07 Recur Factorial" ; Програма від https://chat.deepseek.com/ ; Рекурсивний факторіал — якщо все ж хочеться рекурсію ; Цей варіант ближчий до мого першого прикладу, але виправлений ; із використанням макросів та без помилок. format binary as 'bin' include 'Macros.asm' MOVrv YA, 5 CALLa factorial MOVrr YA, ZA ; результат повертаємо в YA WritelnYA Readln0 EndProgram factorial: ; Зберігаємо регістри, які будемо псувати PUSHr YA PUSHr YB PUSHr YC MOVrv YC, 1 ; константа 1 CMPrr YA, YC ; порівняти n (YA) з 1 GOTOsaNotIs recurse ; якщо n != 1, йдемо в рекурсію ; n == 1 -> повернути 1 MOVrv ZA, 1 GOTOa return recurse: ; n > 1 MOVrr YB, YA ; зберегти n у YB DECr YA ; YA = n-1 CALLa factorial ; викликати factorial(n-1), результат у ZA ; ZA = (n-1)! MULrr ZA, YB ; ZA = ZA * n (тобто (n-1)! * n) ; результат уже в ZA return: ; Відновлюємо регістри у зворотному порядку POPr YC POPr YB POPr YA RET_ ; Пояснення: ; Тут використано регістр `ZA` для повернення результату ; (як це часто прийнято в EnotVM). Зверніть увагу на правильне ; збереження/відновлення регістрів, які змінюються всередині процедури ; (`YA`, `YB`, `YC`). Макрос `JMP` не визначений, тому використано `GOTOa` ; з міткою — але у вас немає макросу `JMP`. Я використав `GOTOa return`, ; що є безумовним переходом на мітку. Це працює.