Κυριακή 26 Ιουλίου 2026

Make Animation through Image Sequences

A small program written in M2000 produce in a folder a sequence of png files (each image for each frame). Png files are lossless so we get the quality of the original frame. 

We can use Kdenlive to process the sequence as a video clip (we have to adjust the time for each image, we can give one video frame per image).



We apply a smart blurr effect to drop some pixels....


This is the code (included in zip file with the Orguss.png)

If not Exist("Orguss.png") then Error "Orguss.png not exit"
module SetFolder (that$) {
dir user
try ok {dir that$}
' if we don't have the folder we make it
if not ok then subdir that$
'we can delete any files or we overwrite them
' we send to two commands to cmd.exe
' with ";" we don't see the window of cmd.exe
' dos statement not work if we set a named user.
' so the named user if the folder exist can:
' 1) leave it and we get overwritten files or choose another name
' 2) change the export folder name.
dos "cd "+dir$+"  && del *.bmp";
}
module saveBMP (file$) {
ox=pos.x:oy=pos.y
move 0,0
that$=""
copy scale.x, scale.y to that$
image that$ Export file$+".bmp"
move ox, oy
}
module title.display (caption$, at as long=0, f$="ARIAL BLACK", sz as long=128){
module formlabel1 {
legend letter$, letter$, number, 0, number, 1
'formlabel letter$, letter$, number, number
}
move scale.x/2,at
Font.sizeY=size.y("|", f$, sz)
width 4 {
color {
formlabel1 caption$, f$, sz, 2
};
'move 0,at
'fill scale.x,Font.sizeY*2/3, 7,12, 0
move 0, Font.sizeY*2/3+at
fill scale.x, Font.sizeY/3, 11,3, 0

color {};
move scale.x/2, at
pen 0 {
color {
formlabel1 caption$, f$, sz, 2
}
}
}
}
module title.display (caption$, at as long=0, f$="ARIAL BLACK", sz as long=128){
module formlabel1 {
legend letter$, letter$, number, 0, number, 0
}

Font.sizeY=size.y("|", f$, sz)
move scale.x/2,at+Font.sizeY
width 4 {
color {
formlabel1 caption$, f$, sz, 2
};
move 0,at+Font.sizeY
fill scale.x,Font.sizeY*2/3, 7,12, 0
move 0, Font.sizeY*2/3+at
fill scale.x, Font.sizeY/3, 11,3, 0

color {};
move scale.x/2, at+Font.sizeY
pen 0 {
color {
formlabel1 caption$, f$, sz, 2
}
}
}
}
module title.displaY (caption$, at as long=0, f$="ARIAL BLACK", sz as long=128){
module formlabel1 {
legend letter$, letter$, number, 0, number, 0
}

Font.sizeY=size.y("|", f$, sz)
move scale.x/2,at+Font.sizeY/2
width 4 {
color {
formlabel1 caption$, f$, sz, 2
};
move 0,at
fill scale.x,Font.sizeY*2/3, 7,12, 0
move 0, Font.sizeY*2/3+at
fill scale.x, Font.sizeY/3, 11,3, 0
color {};
move scale.x/2, at
pen 0 {
color {
formlabel caption$, f$, sz, 2
}
}
}
}
Module SetSceenPixels (pixelsX, pixelsY) {
Layer {
font "courier new"
window 4, pixelsX*twipsX, pixelsY*twipsY;
mode 12
}
}


' here we set the current output the background layer,
' which is behind the console layer. The background layer is the form, the console window.
' Console window by default exgtend to full screen.
'window 12, 18000, 12000;
' window 12, window
oldfont$=fontname$
oldmode=mode
'SetSceenPixels 1280, 1024
SetSceenPixels 1024, 768
'SetSceenPixels 640, 480


' so now we make some loambda functions (they hide some state inside as closures)
(cX, cY, cfx, cfy, cfo)=lambda ->{
Back {
cls 0,0
MaxX=scale.x/1920/twipsX
MaxY=scale.y/1080/twipsY
OverAll=sqrt(scale.x*scale.y/1920/1080/twipsX/twipsY)
}
Data lambda MaxX (i)->{
=(8000+i*10)*MaxX
}
Data lambda MaxY (i)->{
=(5000+i*4)*MaxY
}
Data lambda MaxX (i)->{
=i*MaxX
}
Data lambda MaxY (i)->{
=i*MaxY
}
Data lambda OverAll (i)->{
=i*OverAll
}
=array([])
}() ' auto execute the lambda


' first we hide the console layer - not the form
Hide
Back {
' PREPARE BACKGROUND AND OBJECTS
' we make an emf file in a buffer (in memory only)

drawing 1920*twipsX, 1080*twipsY/2 {
title.display "ORGUSS", 0, ? ,?
title.display "Super Dimension Century Orguss", 3000, ?, 72

} as titles
' rendering emf file to a DIB in a string
t$="" : image titles to t$ (15), cfx(100) ' (15) make the not used pixel as white.
' some useful numbers
IY=Image.Y(t$)*2/3
M=100
a$=""
' now we read image orgus.png to a DIB in a string
' dib is the RGB only representation
' for this example we use Sprite statement with the simple DIB image.
' Although Sprite handle Orguss as is : Sprite Orguss, ...
image "Orguss.png" to a$
' we save the output to a temporary in memory place
' later we use Release to get it back
' so the backgroud can be restrored using Release, one statement only
' Now the background
cls 1, 0
move 0,0
fill scale.x, scale.y*3/5, 0,1,,1
move scale.x, scale.y*3/5
' we use a pen with 120/255 opacity, only for the circle
' we don't use FILL in Circle, but we use Color 14 { }
' so we get a filled circle file without outline
pen 14, 120 {color 14 {circle scale.y/1.5}}
move 0, scale.y*3/5
fill scale.x, scale.y*2/5, #aaccff, 4
hold
' now we move to standard user folder for M2000.
module SetFolder "export"
delay=1000/60
' delay 1 second for the next refresh or the next refresh statement, any came first.
refresh 1000
counter=0
i=1
' best use for animation is the every statement
every delay {
release
move scale.x/2, IY
if i>500 then sprite t$,15,,,M,1: M*=.98
move cx(i), cy(i)
sprite a$, #FF00FF, 30-i/30, cfo(i/5),,1
if i<500 then move scale.x/2, IY:Sprite t$, 15
saveBMP "frame"+str$(counter,"000")
counter++
refresh 1000
i+=8: if i>1000 then exit
}
move cx(i)-cfx(180), cy(i)+cfy(90)
' we can use a for next and wait inside.
for k=1 to 100
release
sprite a$, #FF00FF, 30-i/30, cfo((i-k*12)/5),100-k,1
saveBMP "frame"+str$(counter,"000")
counter++
step cfx(-180), cfy(90)
refresh 1000
wait delay
next
}
window 4, window
back {cls 0}
font oldfont$
Mode oldmode
dir user







orguss.zip

Τετάρτη 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