Τετάρτη 22 Ιουλίου 2026

Snake and Ladder - A RosettaCode Example

https://rosettacode.org/wiki/Snake_and_ladder#M2000_Interpreter 



MODULE SNAKE_AND_LADDER {
ESCAPE OFF
BOARD=LIST:= 4:=14, 9:=31, 17:=7, 20:=38, 28:=84, 40:=59, 51:=67, 54:=34
APPEND BOARD, 62:=19, 63:=81, 64:=60, 71:=91, 87:=24, 93:=73, 95:=75, 99:=78
BOARD2=LIST
FOR I=1 TO 100
IF EXIST(BOARD, I) THEN BOARD2(EVAL(BOARD))=I
NEXT
FOR I=1 TO 100
IF NOT EXIST(BOARD, I) THEN BOARD(I)=I
IF NOT EXIST(BOARD2, I) THEN BOARD2(I)=I
NEXT
CLS
WW=SCALE.X/12
HH=SCALE.Y/12
DIM C()
REFRESH 1000
C()=NXN(10, WW, HH, WW, HH)
FOR I=1 TO 100
J=BOARD(I)
IF I<>J THEN
LADDER I, J, WW, HH
END IF
NEXT
NXN_LABELS 10, WW, HH, WW, HH
REFRESH 25
HOLD
LONG PLAYER1=0, COMPUTER=0
MIN=MIN(WW, HH)/2
ZARI=RANDOM(1, 6)
BOOLEAN COMPUTER_PLAY=TRUE'-RANDOM(0, 1)
EVERY 1000/8 {
IF PLAYER1>0 THEN
MOVE C(PLAYER1)#VAL(0)+WW DIV 2,C(PLAYER1)#VAL(1)+HH DIV 2
PEN 15 {WIDTH 8 {CIRCLE MIN}}
END IF
IF COMPUTER>0 THEN
MOVE C(COMPUTER)#VAL(0)+WW DIV 2,C(COMPUTER)#VAL(1)+HH DIV 2
PEN 11{WIDTH 8 {CIRCLE MIN}}
END IF
REFRESH 1000
IF COMPUTER_PLAY THEN
COMPUTER+=ZARI
MOVE SCALE.X/2, SCALE.Y/2
PEN 11 {LEGEND ""+ZARI,"ARIAL", HH/10,0,2} :REFRESH
WAIT 500
IF COMPUTER>101 THEN COMPUTER=101-COMPUTER+101
IF COMPUTER=101 THEN RELEASE:EXIT
IF BOARD(COMPUTER)<>COMPUTER THEN COMPUTER=BOARD(COMPUTER)
COMPUTER_PLAY~
ELSE.IF KEYPRESS(32) THEN
PLAYER1+=ZARI
MOVE SCALE.X/2, SCALE.Y/2
PEN 15 {LEGEND ""+ZARI,"ARIAL", HH/10,0,2 }:REFRESH
WHILE KEYPRESS(32) {WAIT 10}
IF PLAYER1>101 THEN PLAYER1=101-PLAYER1+101
IF PLAYER1=101 THEN RELEASE:EXIT
IF BOARD(PLAYER1)<>PLAYER1 THEN PLAYER1=BOARD(PLAYER1)
COMPUTER_PLAY~
END IF
ZARI=RANDOM(1, 6)
IF KEYPRESS(27) THEN EXIT
RELEASE
}
IF PLAYER1=101 THEN
MOVE SCALE.X/2, SCALE.Y/2
LEGEND "PLAYER WIN","ARIAL", HH/10,0,2
ELSE.IF COMPUTER=101 THEN
MOVE SCALE.X/2, SCALE.Y/2
LEGEND "COMPUTER WIN","ARIAL", HH/10,0,2
ELSE
WHILE KEYPRESS(27) {WAIT 10}
END IF
REFRESH 25
ESCAPE ON
FUNCTION NXN(N AS LONG, W AS LONG, H AS LONG, XT AS LONG, YT AS LONG)
LOCAL LONG I, J, K=1, NN=101, S=-1
LOCAL P(1 TO N*N), C()
C()=(#2266AA, #114400)
FOR I=1 TO N
MOVE XT, YT
FOR J=0 TO N-1
NN+=S
MOVE XT+J*W, YT
P(NN)=(XT+J*W, YT)
POLYGON C(K MOD 2), W, 0,0,H,-W,0,0,-H
K++
NEXT
NN+=S:NN-=N:S*=-1
K++
YT+=H
NEXT
=P()
END FUNCTION
SUB NXN_LABELS(N AS LONG, W AS LONG, H AS LONG, XT AS LONG, YT AS LONG)
LOCAL LONG I, J, K=1, NN=101, S=-1
FOR I=1 TO N
MOVE XT, YT
FOR J=0 TO N-1
NN+=S
MOVE XT+J*W, YT
K++
PATH{POLYGON 4, W, 0,0,H,-W,0,0,-H}
IF BOARD(NN)<NN THEN
STEP W/2,H/2 : PEN 0 {CIRCLE FILL 0, W/3,H/W}
STEP -W/2,-H/2
ELSE.IF BOARD(NN)>NN THEN
STEP W/2,H/2 : PEN 14 {CIRCLE FILL 14, W/3,H/W}
STEP -W/2,-H/2
ELSE.IF BOARD2(NN)<NN THEN
STEP W/2,H/2 : PEN 14 {CIRCLE FILL 14, W/3,H/W}
STEP -W/2,-H/2
ELSE.IF BOARD2(NN)>NN THEN
STEP W/2,H/2 : PEN 0 {CIRCLE FILL 0, W/3,H/W}
STEP -W/2,-H/2
ELSE
STEP W/2,H/2 : PEN 2 {CIRCLE FILL 2, W/3,H/W}
STEP -W/2,-H/2
END IF
STEP W DIV 2, H/6-H/24
PATH 2 {FORMLABEL ""+NN,"LUCIDA",H/20*2/3, 2}
NEXT
NN+=S:NN-=N:S*=-1
K++
YT+=H
NEXT
END SUB
SUB LADDER(P1 AS INTEGER, P2 AS INTEGER, WW AS LONG, HH AS LONG)
LOCAL Z
IF P1<P2 THEN
Z=14
ELSE
SWAP P1, P2 : Z=0
END IF
MOVE C(P1)#VAL(0)+WW DIV 2,C(P1)#VAL(1)+HH DIV 2
LOCAL LONG DX=C(P2)#VAL(0)-C(P1)#VAL(0), DY=C(P2)#VAL(1)-C(P1)#VAL(1)
IF DX=0 THEN DX=WW/2:IF DX=0 THEN DX=TWIPSX
IF DY=0 THEN DY=HH/2:IF DY=0 THEN DY=TWIPSY
IF WW DIV 4 = 0 THEN WW = 4
LOCAL LONG DXN=DX DIV (WW DIV 4): IF DXN=0 THEN DXN=TWIPSX*SGN(DY)
LOCAL DYN=(DY+HH/2) DIV (DX DIV DXN), J=POS.Y-HH/2
FOR I=POS.X+DXN TO C(P2)#VAL(0)+WW/2 STEP DXN
WIDTH 8 {DRAW TO I,J,Z}: J+=DYN
NEXT
WIDTH 8 {DRAW TO C(P2)#VAL(0)+WW DIV 2,C(P2)#VAL(1)+HH DIV 2, Z}
END SUB
}
SNAKE_AND_LADDER



Super Ellipse Plot

https://rosettacode.org/wiki/Superellipse#M2000_Interpreter

You can use: Font "Courier New": Window Mode, 12000, 10000;  for the exact output as in the rosettacode.org example.


Module Checkit {
Module Superellipse (a As Long, b As Long, n As Double) {
Const m_x = scale.x div 2
Const m_y = scale.y div 2
' myStep *2 because we set line Width 2 pixels
    Long x, m, myStep=twipsX*2, top=a div myStep
Dim y(0 to top) as long

    y(0) = b ' value for x = 0
    y(top) = 0 ' value for x = a
    ' 0,0 is in upper left corner

    Move m_x, m_y - y(0) ' set starting point
Pen #FFBBCC {
Width 2 {
For x = 1 To a-1 Step myStep
        y(m) = Int( Exp( Ln(1 - ((x / a) ^ n)) / n ) * b )
        draw to m_x + x, m_y - y(m):m++
    Next
    For x = a To 0 Step myStep
        draw to m_x + x, m_y + y(m):m--
    Next
    For x = 0 To a Step myStep
        m++:draw to m_x - x, m_y + y(m)
    Next
    For x = a To 0 Step myStep
        draw to m_x - x, m_y - y(m):m--
    Next
    }
    }
}
Cls #003300, 0
Pen #ccffdd


VirtualScreenWidth=800
VirtualScreenHeight=800
VirtualEllipseSemiHeight=150
VirtualEllipseSemiDiameter=200

Long a=VirtualEllipseSemiDiameter/VirtualScreenWidth*scale.x
Long b=VirtualEllipseSemiDiameter/VirtualScreenHeight*scale.y
Double n = 2.5
Refresh 2000 ' draw without refreshing the screen
Superellipse a, b, n
Refresh 25 ' final refresh
Cursor 0, height-1
Print Over $(6),"hit any key to end program"
Push Key$:Drop
Cls
}
Checkit


Παρασκευή 17 Ιουλίου 2026

Revision 7 Version 15 - Export test.exe from assembler

 This is another example with latest revision.


Static Function MachineCode
SplitHor(4/5)
Enum PESubsystem {
    Subsystem_GUI = 2
    Subsystem_CUI = 3
}
Assembler =getobject("","m2000.x86")
Assembler=>Subsystem=Subsystem_GUI
Assembler=>PEHeader=true
Buffer2export=MachineCode({
extern "kernel32", GetModuleHandleA
extern "user32",   LoadCursorA
extern "user32",   RegisterClassA
extern "user32",   UnregisterClassA
extern "user32",   CreateWindowExA
extern "user32",   DestroyWindow
extern "user32",   ShowWindow
extern "user32",   GetMessageA
extern "user32",   TranslateMessage
extern "user32",   DispatchMessageA
extern "user32",   DefWindowProcA
extern "user32",   PostQuitMessage


AppName:           db "ASM Window", 0
WindowTitle:       db "My first ASM window", 0
hInstance:         dd 0
hCursor:           dd 0
hWnd:              dd 0
wMsg:              dd 0


COLOR_APPWORKSPACE equ 12
SIZEOF_WNDCLASS    equ 40
SIZEOF_MSG         equ 28
WM_DESTROY         equ &H2
IDC_ARROW          equ &H7F00


main:
   push ebp
   mov  ebp, esp


   push dword 0
   call GetModuleHandleA
   mov [hInstance], eax


   push dword IDC_ARROW
   push dword 0
   call LoadCursorA
   mov [hCursor], eax


   ;;;;;; WND CLASS ;;;;;;;;


   sub esp, SIZEOF_WNDCLASS                ; WNDCLASS 40 Bytes
   
   mov dword [esp+0], 3                    ; style = CS_HREDRAW or CS_VREDRAW
   mov dword [esp+4], WndProc              ; lpfnWndProc = WndProc
   mov dword [esp+8], 0                    ; cbClsExtra = 0
   mov dword [esp+12], 0                   ; cbWndExtra = 0


   mov eax, [hInstance]
   mov dword [esp+16], eax                 ; hInstance


   mov dword [esp+20], 0                   ; hIcon = 0


   mov eax, [hCursor]
   mov dword [esp+24], eax                 ; hCursor


   mov dword [esp+28], COLOR_APPWORKSPACE  ; background color
   mov dword [esp+32], 0                   ; lpzszMenuName = 0
   mov dword [esp+36], AppName             ; lpszClassName = AppName


   push dword esp                          ; register the window class
   call RegisterClassA


   ;;;;;; CREATEWINDOW ;;;;;;;;


   push dword 0                            ; lParam
   push dword [hInstance]                  ; hInstance
   push dword 0                            ; hMenu
   push dword 0                            ; hWndParent
   push dword 250                          ; Height
   push dword 300                          ; Width
   push dword 0                            ; y
   push dword 0                            ; x
   push dword &HCF0000                     ; WS_OVERLAPPEDWND or WS_BORDER or WS_CAPTION
   push dword WindowTitle                  ; lpWindowName
   push dword AppName                      ; ClassName
   push dword &H40000                      ; dwExStyle
   call CreateWindowExA


   mov [hWnd], eax


   push dword 1                            ; show the window
   push dword [hWnd]
   call ShowWindow


   ;;;;;; MESSAGELOOP ;;;;;;;;


   sub  esp, SIZEOF_MSG                    ; space for MSG Struct
   mov  [wMsg], esp


   MessageLoop:
       push dword 0                        ; get a message from the queue
       push dword 0
       push dword 0
       push dword [wMsg]
       call GetMessageA


       test eax, eax
       jz   ExitMessageLoop


       push dword [wMsg]                   ; process the message
       call TranslateMessage
       push dword [wMsg]
       call DispatchMessageA


       jmp  MessageLoop


   ExitMessageLoop:
   add  esp, SIZEOF_MSG                    ; cleanup MSG struct


   push dword [hWnd]                       ; destroy the window
   call DestroyWindow


   push dword [hInstance]                  ; unregister the window class
   push dword AppName
   call UnregisterClassA


   add  esp, SIZEOF_WNDCLASS               ; cleanup WNDCLASS Struct
   pop  ebp
ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


WndProc:
   mov  eax, [esp+8]                       ; Message
   cmp  eax, WM_DESTROY                    ; WM_DESTROY?
   jne  WndProcNext
   push dword 0
   call PostQuitMessage


   WndProcNext:
       push dword [esp+16]                 ; copy arguments
       push dword [esp+16]
       push dword [esp+16]
       push dword [esp+16]
       call DefWindowProcA


ret 16
})
killit()
open "test.exe" for output as #f
put #f, Buffer2export
close #f
// Declare ShellExecute Lib "shell32.ShellExecuteW" {Long hWnd, Long lpszOp, lpszFile$, long lpszParams, long LpszDir, Long FsShowCmd }
// Print ShellExecute(0, 0, dir$+"test.exe", 0, 0, 1&)
//
win dir$+"test.exe"


Sub killit(name$="test.exe")
local out$=quote$(dir$+"out.txt")
Dos "del "+out$, 500;
name$=lcase(name$)
Dos format({tasklist /fi "IMAGENAME eq {0}" >}+out$, name$), 500;
local a$
document a$
load.doc a$,"out.txt"
local m=Paragraph(a$, 0)
If Forward(a$,m) then
While m
p=Paragraph$(a$,(m))
if lcase(left(p, len(name$)))=name$ then
Pid=val(trim(rightpart(p, " ")))
Dos "taskkill /F /PID "+pid;
Print "Pid=";Pid;" ...killed in action"
End if
End While
End If
End Sub
Function MachineCode(assembly as string)
if Assembler=>assemble(assembly, true) then
local OutPutSize=Assembler=>OutputSize
local mc
buffer code mc as byte*OutputSize
' feed the base address to Assembler
Assembler=>BaseAddress=&H400000
if Assembler=>assemble(assembly) then
' get a copy of final machine code
mc=>FillDataFromMem Assembler=>GetOutPtr
=mc
exit function
End if
End if
error Assembler=>LastErrorMessage
End Function
Sub SplitHor(ratio as single)
if ratio<0.15 or ratio>0.85 then ratio=0.5
local old=mode
window 6, window
back {
local x1=scale.x,y1=scale.y
x=scale.x*ratio:window 6, x, y1
}
mode old, x, y1
back {motion x1-scale.x, (y1-scale.y)/2}
end sub


Πέμπτη 16 Ιουλίου 2026

Revision 6 Version 15 - Added: Assembler x86

' x86.cls as  32 Bit X86 Assembler
' from Arne Elster 2007 / 2008
' I add hash table for find labels for now
' this is the first work


Assembler =getobject("","m2000.x86")


MachineCode= lambda Assembler (assembly)-> {
if Assembler=>assemble(assembly, true) then
' get the output size
OutPutSize=Assembler=>OutputSize
buffer code mc as byte*OutputSize
' feed the base address to Assembler
Assembler=>BaseAddress=mc(0)
if Assembler=>assemble(assembly) then
' get a copy of final machine code
mc=>FillDataFromMem Assembler=>GetOutPtr
=mc
exit
end if
end if
error Assembler=>LastErrorMessage
}
Example=MachineCode({
ASM_TEST_RAWDATA:
mov eax, [Data]
ret 16
Data:
dd 123454321
ASM_TEST_BUBBLESORT: ; this is another program
pushad
mov esi, [ebp+16] ; Arraylength
outer_loop:
mov ebx, [ebp+12] ; ArrPtr
mov edx, [ebp+16] ; Arraylength
xor edi, edi
inner_loop:
mov eax, [ebx+0] ; arr(j)
mov ecx, [ebx+4] ; arr(j+1)
cmp eax, ecx
jle byte next_loop ; swap if eax > ecx
mov [ebx+0], ecx ; swap arr(j), arr(j+1)
mov [ebx+4], eax
mov edi, 1 ; swapped
next_loop:
add ebx, 4
dec edx
jnz byte inner_loop ; i > 0 => still in inner
test edi, edi ; swapped?
jz  byte return ; no => sorted
dec esi
jnz byte outer_loop
return:
popad
ret &H10
ASM_TEST_CPUID:
pushad
mov edi, [ebp+12]
xor eax, eax
cpuid
mov [edi+0], ebx
mov [edi+4], edx
mov [edi+8], ecx
popad
ret 16
ASM_TEST_FDIV:
mov   eax, [ebp+20] ; Ptr to output float
fild  dword [ebp+12] ; st0 = numerator
fild  dword [ebp+16] ; st0 = divisor, st1 = numerator
fdivp ; st1 = st1 / st0, pop st0
fstp  float [eax] ; pop st0 to output float
ret    16
})
ASM_TEST_RAWDATA=assembler=>labeloffset("ASM_TEST_RAWDATA")
ALTERDATA=assembler=>labeloffset("data")
ASM_TEST_BUBBLESORT=assembler=>labeloffset("ASM_TEST_BUBBLESORT")
ASM_TEST_CPUID=assembler=>labeloffset("ASM_TEST_CPUID")
ASM_TEST_FDIV=assembler=>labeloffset("ASM_TEST_FDIV")
' a1: Numerator
' a2: Divisor
' a3: Ptr to result (float = single)


' **NEW** call using Code! used for ExecuteReadWrite
' call using Code used for ExecuteRead (no write bytes to buffer Example)
' **NEW** use ; to bypass error from eax<>0 - and use EAX to read the value
Execute Code! Example, ASM_TEST_RAWDATA;
Print Eax
Return Example, ALTERDATA:=uint(-11112222) as long '(unsigned)
Execute Code! Example, ASM_TEST_RAWDATA;
' eax has value of eax from last execut code when we use the ; symbol
' eax is signed value
Print EAX=-11112222


buffer Bytes12 as byte*12
'**NEW** we can pass max 4 byvalue long values.
Execute Code! Example, ASM_TEST_CPUID, Bytes12(0);
HEX "CPUID:";chr$(Bytes12[0,12])
long lngDividend=2, lngDivisor=5
single sngQuotient
' here we pass by reference sngQuotient passing by value the Variable Pointer
Execute Code! Example, ASM_TEST_FDIV,lngDividend, lngDivisor, VarPtr(sngQuotient);
Print sngQuotient=0.4~
N=100 ' try 1000
buffer MyData as Long*n
for i=0 to N-1
MyData[i]=Uint(random(1, 100000)-49999)
print Sint(MyData[i]),
next
print
Execute Code! Example, ASM_TEST_BUBBLESORT, MyData(0), N-1;
for i=0 to N-1
print sint(MyData[i]),
next
print


Τετάρτη 15 Ιουλίου 2026

Subtractive Generator

 from https://rosettacode.org/wiki/Subtractive_generator#M2000_Interpreter

Class Subtractive_generator {
private:
  long mod10=10^9, si=0, sj=0
  dim state(55) as long
  module subrand_seed (p1 as long) {
   long i, j=21, p2=1, d
   .state(0)=p1 mod .mod10
   for i=1 to 54
     if j>=55 then j-=55
     .state(j)=p2
     p2=p1-p2
     if p2<0 then p2+=.mod10
     p1=.state(j)
     j+=21
   next
   .si<=0
   .sj<=24
  }
public:
  property counter {
    value
  }=55&
  module advance (n) {
    for i=1 to n:d=.subrand():next  
  }
  function subrand {
    long x
    if .si=.sj then .subrand_seed 0
    .si--:if .si<0 then .si<=54
    .sj--:if .sj<0 then .sj<=54
    x=.state(.si)-.state(.sj)
    if x<0 then x+=.mod10
    .state(.si)=x
    =x
    .[counter]++
  }
class:
    module Subtractive_generator {
      .subrand_seed
    }
}
sg=Subtractive_generator(292929&)
sg.advance 220-55
sg1=sg ' copy of sg
for i=1 to 10
  print str$(sg.counter, "\r\[000\] = ")+sg.subrand()
next
print "== copy ==" ' print same numbers as before
for i=1 to 10
  print str$(sg1.counter, "\r\[000\] = ")+sg1.subrand()
next


r[220] = 467478574
r[221] = 512932792
r[222] = 539453717
r[223] = 20349702
r[224] = 615542081
r[225] = 378707948
r[226] = 933204586
r[227] = 824858649
r[228] = 506003769
r[229] = 380969305
== copy ==
r[220] = 467478574
r[221] = 512932792
r[222] = 539453717
r[223] = 20349702
r[224] = 615542081
r[225] = 378707948
r[226] = 933204586
r[227] = 824858649
r[228] = 506003769
r[229] = 380969305