; $$$$$$$$$$$$$$$$$$$ ABAKIS $$$$$$$$$$$$$$$$$$$$$ ; *************** STAR^2 SOFTWARE **************** ; ????????????????? SYSTEM.INC ??????????????????? ;;;;;;;;;;;;;;;;;;;;; IMPORT ;;;;;;;;;;;;;;;;;;;;; ; RVAs of dll names and tables, ending with 20 ; zero bytes macro library [names] { forward dd 0,0,0,\ RVA names#_name, RVA names#_table common dd 0,0,0,0,0 } ; DLL name + import RVA table. each table ends with 0. ; finally, import names. dw 0 is "ordinal" (N/A) macro import name, [names] { common name#_name \ ; text DLL_name='DLL.DLL' db `name#'.DLL', 0 name#_table: ; DLL_table: forward IF used !#names !#names dd RVA _#names ; import name RVAs macro names [p] \{ ; call with no \common ; invoke prefix pushr p call [!#names] \} END IF common dd 0 ; end forward IF used !#names _#names dw 0 ; import names db `names, 0 ; 'import' END IF } ;;;;;;;;;;;;;;;;;;;; IMPORTS ;;;;;;;;;;;;;;;;;;;;; data import library MSVCRT, KERNEL32, USER32, SHELL32,\ GDI32, COMDLG32 import MSVCRT, sprintf import KERNEL32,\ ExitProcess, GetCommandLineA,\ HeapCreate, HeapAlloc, HeapReAlloc, HeapSize,\ HeapFree, HeapDestroy, VirtualAlloc, VirtualFree,\ GetModuleHandleA, GetModuleFileNameA,\ CreateFileA, GetFileSize, ReadFile, WriteFile,\ SetFilePointer, CloseHandle, CopyFileA,\ MoveFileA, DeleteFileA, GetTickCount,\ GetSystemTime, GetLocalTime, GetFileTime,\ FileTimeToSystemTime, SystemTimeToFileTime,\ FileTimeToLocalFileTime,\ SystemTimeToTzSpecificLocalTime,\ GetFileAttributesExA, CompareFileTimeA,\ GetCurrentDirectoryA, SetCurrentDirectoryA,\ CreateDirectoryA, LoadLibraryA, FreeLibrary,\ GetProcAddress, FindFirstFileA, FindNextFileA,\ FindClose, WaitForSingleObject, Sleep import USER32,\ GetDC, ReleaseDC,\ MessageBoxA, RegisterClassExA, CreateWindowExA,\ DestroyWindow, ShowWindow, MoveWindow,\ UpdateWindow, GetMessageA, PeekMessageA,\ TranslateMessage, DispatchMessageA,\ SendMessageA, DefWindowProcA, PostQuitMessage,\ WaitMessage, GetAsyncKeyState, LoadImageA,\ LoadIconA, LoadCursorA, SetCursor, ShowCursor,\ SetCursorPos, OpenClipboard, SetClipboardData,\ IsClipboardFormatAvailable, GetClipboardData,\ CloseClipboard, EmptyClipboard,\ GetSystemMetrics, BeginPaint, EndPaint,\ FillRect, InvalidateRect, SetTimer import SHELL32, ShellExecuteA, ShellExecuteExA import GDI32, SelectObject, DeleteObject,\ GetObjectA, DeleteDC, TextOutA, CreateFontA,\ CreateFontIndirectA, SetDIBits, BitBlt, StretchBlt,\ CreateBitmap, CreateCompatibleDC import COMDLG32, GetOpenFileNameA,\ GetSaveFileNameA, ChooseColorA, ChooseFontA END data ;;;;;;;;;;;;;;;;;;;;; SYSTEM ;;;;;;;;;;;;;;;;;;;;; align void @module, @heap void directory, file.name, command.line ?t equ directory ;;;;;;;;;;;;;;;;;;;; MINIMAL ;;;;;;;;;;;;;;;;;;;;; ; say t - display message ; say t, m - title and message ; say.n n - number ; examples: ; say 'Hi' ; say name ; say.n 123 macro os.say t, m { MessageBoxA 0, m, t, 0 } macro os.ask q, t { MessageBoxA, 0, q, t, 3 } function _say, t, m os.say t, m endf macro say a, b { pusha IF a eqtype '' make.txt r0, a ELSE . r0=a END IF IF b eq _say r0, r0 ELSE IF b eqtype '' make.txt r2, b ELSE . r2=b END IF _say r2, r0 END IF popa } function say.n, n locale t(32) pusha . r1=&t u2t n, r1 . r1=&t _say r1, r1 popa endf function say.h, n locale t(32) pusha . r1=&t h2t n, r1 . r1=&t _say r1, r1 popa endf function say.b, n locale t(32) pusha . r1=&t b2t n, r1 . r1=&t _say r1, r1 popa endf macro sayz t { say ?LITERALS+?literals.i ?literal t } macro ask q, t { os.ask q, t } macro cinvoke proc,[arg] { common size@ccall = 0 IF ~ arg eq reverse pushd arg size@ccall = size@ccall+4 common END IF call [proc] IF size@ccall add esp,size@ccall END IF } macro sprintf t, f, [p] { common cinvoke !sprintf, t, f, p } ;;;;;;;;;;;;;;;;;;;;; DEBUG ;;;;;;;;;;;;;;;;;;;;;; bug.t db 'BUG', 0 macro bug { say bug.t } macro bug.x t { log t execute log.file exit } macro BUG { db 0CCh } ; int3 breakpoint ;;;;;;;;;;;;;;;;;;;; MEMORY ;;;;;;;;;;;;;;;;;;;;;; macro os.memory { get @heap=HeapCreate 0, 0, 0 } macro os.allocate n { HeapAlloc @heap, 0, n } macro os.reallocate p, n { HeapReAlloc @heap, 0, p, n } macro os.destroy p { HeapFree @heap, 0, p } ;;;;;;;;;;;;;;; ALLOCATE, DESTROY ;;;;;;;;;;;;;;;; ; allocate n ; allocate.p &p, n ; destroy &p ; example: try p=allocate 4*KB function allocate, n os.allocate n endf function allocate.p, p, n if p=0 allocate n return end os.reallocate p, n endf function destroy, p if p os.destroy p end endf macro destroy [p] { forward destroy p } ;;;;;;;;;;;;;;;;;;;;; TIME ;;;;;;;;;;;;;;;;;;;;;;; FILE.TIME fix u64 macro os.get.time { GetLocalTime local.time update.time local.time } function os.delay, ms locals start get start=GetTickCount @@: GetTickCount . r1=start, r1+ms cmp r0, r1 jb @b endf get.clock fix GetTickCount ;;;;;;;;;;;;;;;;;;;;; RANDOM ;;;;;;;;;;;;;;;;;;;;; align integer @seed ; generate unique random number: 0-n ; seed=(seed*343FDh)+269EC3h ; seed=((seed>>16)&7FFFh)/(n+1) function random, n . r0=@seed if false ; initialize seed rdtsc ; read date/time stamp counter . @seed=r0 end . r0*343FDh, r0+269EC3h,\ @seed=r0, r0>>16, r0&7FFFh,\ r1=n, r1+1, r0/r1, r0=r2 endf ; random(from-to-2)+from function random.x, from, to . r0=from, r0-to, r0-2 random r0 . r0+from endf ;;;;;;;;;;;;;;;;;;; FILE I/O ;;;;;;;;;;;;;;;;;;;;; numeric EOF=-1,\ CREATE_NEW=1, CREATE_ALWAYS, OPEN_EXISTING,\ OPEN_ALWAYS, TRUNCATE_EXISTING,\ GENERIC_READ=80000000h, GENERIC_WRITE=40000000h,\ FILE_SHARE_READ=1, FILE_SHARE_WRITE,\ FILE_ATTRIBUTE_NORMAL=80h,\ SEEK.BEGIN=0, SEEK.SET, SEEK.END ;;;;;;;;;;;;;;;;; CURRENT FILE ;;;;;;;;;;;;;;;;;;; align void file.p ; pointer for load/save integer file.h,\ ; handle file.n64, file.n ; size 64:32 integer tmp.rw macro flush { destroy file.p } ; return handle or -1 if error function os.create.file, file, access, share,\ security, action, attributes, template call !text.copy, file.name, file CreateFileA file.name, access, share,\ security, action, attributes, template . file.h=r0 endf macro os.open file { os.create.file file, GENERIC_READ \ or GENERIC_WRITE, FILE_SHARE_READ,\ 0, OPEN_EXISTING, 0, 0 } macro os.create file { os.create.file file, GENERIC_WRITE,\ 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 } macro os.seek n, r { SetFilePointer file.h, n, 0, r } macro os.read p, n { . r0=&tmp.rw ReadFile file.h, p, n, r0, 0 } macro os.write p, n { . r0=&tmp.rw WriteFile file.h, p, n, r0, 0 } macro os.get.file.size { GetFileSize file.h, 0 } macro os.close { CloseHandle file.h } ;;;;;;;;;;;;;;; COPY, MOVE, DELETE ;;;;;;;;;;;;;;; macro copy.file a, b, r { CopyFileA a, b, r } macro move.file a, b { MoveFileA a, b } macro delete.file f { DeleteFileA f } macro rename.file a, b { os.move.file a, b } ;;;;;;;;;;;;;;;;;; DIRECTORIES ;;;;;;;;;;;;;;;;;;; macro os.get.directory { GetCurrentDirectoryA 256, directory } macro os.set.directory f { SetCurrentDirectoryA f } macro os.create.directory f { CreateDirectoryA f, 0 } macro os.get.file.name { GetModuleFileNameA 0, directory, 1*KB } macro os.get.command.line { GetCommandLineA } ;;;;;;;;;;;;;;;;;;;; EXECUTE ;;;;;;;;;;;;;;;;;;;;; macro execute file { ShellExecuteA 0, 0, file, 0, 0, 3 } ;;;;;;;;;;;;;;;;;; FIND FILES ;;;;;;;;;;;;;;;;;;;; macro os.find.data { BLOCK find.data(338) ; WIN32_FIND_DATA os.found.file equ find.data+44 } macro os.find.first file { FindFirstFileA file, find.data } macro os.find.next { FindNextFileA find.data.h, find.data } macro os.find.end { FindClose find.data.h } ;;;;;;;;;;;;;;;;;;;;; REDRAW ;;;;;;;;;;;;;;;;;;;;; macro create.blank.screen w, h { create.vga os.w, os.h, WHITE ; 0 create.blank.window w, h } macro redraw { calle draw } macro render b { ; call !clear.screen IF b eq redraw END IF IF used cursor call !draw.cursor, cursor END IF IF b eq call !show.vga ELSE copy.box box, b call !show.vga.box END IF } ;;;;;;;;;;;;;;;;;;;; WINDOZE ;;;;;;;;;;;;;;;;;;;;; macro POINT [a] { a: integer a#.x, a#.y } macro RECT [a] { a: integer a#.left,\ a#.top, a#.right, a#.bottom } macro MSG [a] { a: integer a#.hwnd, a#.message,\ a#.wParam, a#.lParam, a#.time POINT a#.pt } macro WNDCLASSEX [a] { a: integer a#.cbSize=48,\ a#.style, a#.lpfnWndProc, a#.cbClsExtra,\ a#.cbWndExtra, a#.hInstance, a#.hIcon,\ a#.hCursor, a#.hbrBackground,\ a#.lpszMenuName, a#.lpszClassName, a#.hIconSm WNDCLASSEX.$=$-a } macro PAINTSTRUCT [a] { a: integer a#.hdc, a#.fErase RECT a#.rcPaint integer a#.fRestore, a#.fIncUpdate text a#.rgbReserved(32) } macro BITMAP [a] { a: integer a#.bmType,\ a#.bmWidth, a#.bmHeight, a#.bmWidthBytes short a#.bmPlanes, a#.bmBitsPixel void a#.bmBits BITMAP.$=$-a } macro BITMAPINFOHEADER [a] { a: integer a#.biSize, a#.biWidth, a#.biHeight short a#.biPlanes, a#.biBitCount integer a#.biCompression, a#.biSizeImage,\ a#.biXPelsPerMeter, a#.biYPelsPerMeter,\ a#.biClrUsed, a#.biClrImportant BITMAPINFOHEADER.$=$-a } macro BITMAPINFO [a] { BITMAPINFOHEADER a integer bmiColors BITMAPINFO.$=BITMAPINFOHEADER.$+4 } ; window messages numeric WM_*, \ CREATE=1, DESTROY=2, MOVE=3, SIZE=5,\ SETFOCUS=7, KILLFOCUS=8, GETTEXT=0Dh,\ SETTEXT=0Ch, GETTEXTLENGTH=0Eh,\ PAINT=0Fh, CLOSE=10h, QUIT=12h, CUT=300h,\ COPY=301h, PASTE=302h, CLEAR=303h,\ SETFONT=30h, COMMAND=111h, TIMER=0113h ; window styles numeric WS_*, \ POPUP=80000000h, MINIMIZE=20000000h,\ VISIBLE=10000000h, MAXIMIZE=1000000h,\ CAPTION=0C00000h, BORDER=800000h,\ DLGFRAME=400000h, VSCROLL=200000h,\ HSCROLL=100000h, SYSMENU=80000h,\ THICKFRAME=40000h, MINIMIZEBOX=20000h,\ MAXIMIZEBOX=10000h WS_BLANK = WS_VISIBLE+WS_POPUP WS_DEFAULT = WS_VISIBLE+WS_CAPTION+\ WS_MINIMIZEBOX+WS_SYSMENU CS_DBLCLKS=8 ; keyboard+mouse messages numeric WM_*,\ KEYDOWN=100h, KEYUP, CHAR, DEADCHAR,\ SYSKEYDOWN, SYSKEYUP, SYSCHAR numeric WM_*,\ MOUSEMOVE=200h, LBUTTONDOWN, LBUTTONUP,\ LBUTTONDBLCLK, RBUTTONDOWN, RBUTTONUP,\ RBUTTONDBLCLK, MBUTTONDOWN, MBUTTONUP,\ MBUTTONDBLCLK, MOUSEWHEEL ; virtual key codes. function keys=(6Fh+N). ; example: F1=70h (6Fh+1) numeric K.*,\ FUNCTION=6Fh, LEFT=25h, UP=26h, RIGHT=27h,\ DOWN=28h, ESCAPE=1Bh, SPACE=20h, DELETE=2Eh,\ CONTROL=11h, LCONTROL=0A2h, RCONTROL=0A3h,\ LALT=0A4h, RALT=0A5h, BACK=8, TAB=9,\ RETURN=0Dh, END=23h, HOME=24h,\ A='A', S='S', D='D', W='W' SRCCOPY=00CC0020h macro os.show.cursor { ShowCursor 1 } ;;;;;;;;;;;;;;;;;;;;; SYSTEM ;;;;;;;;;;;;;;;;;;;;; align integer os.w, os.h, os.bpp void _hwnd ; handle void _dc ; device context void _mdc ; memory dc WNDCLASSEX _wc ; window MSG _wm ; message PAINTSTRUCT _ps ; for PAINT BITMAP _bm ; for draw.bitmap.w void vga.hbm BITMAPINFO vga.bmi RECT vga.rect text _cn='WC', _wt='' ; classname, title ;;;;;;;;;;;;;;;;; CREATE WINDOW ;;;;;;;;;;;;;;;;;; function create.window.x, style, procedure,\ w, h, title, class.name locals x, y . _wc.cbSize=WNDCLASSEX.$ . _wc.hInstance=@module . _wc.lpfnWndProc=procedure . _wc.lpszClassName=class.name . _wc.style=CS_DBLCLKS, _wc.hbrBackground=8 get _wc.hIcon=LoadIconA 0, 7F00h get _wc.hCursor=LoadCursorA 0, 7F00h try RegisterClassExA _wc . r0=os.w, r0>>1, r2=w, r2>>1, r0-r2, x=r0 . r0=os.h, r0>>1, r2=h, r2>>1, r0-r2, y=r0 try _hwnd=CreateWindowExA 0, class.name,\ title, style, x, y, w, h, 0, 0, @module, 0 endf 1 macro create.blank.window w, h { create.window.x WS_BLANK, !_window.procedure,\ w, h, _wt, _wc } macro create.default.window title { create.window.x WS_DEFAULT, !_window.procedure,\ os.w, os.h, title, _wc } ;;;;;;;;;;;;;;;;;; MESSAGE LOOP ;;;;;;;;;;;;;;;;;; macro begin.message.loop { .begin.ml: GetMessageA _wm, 0, 0, 0 fail .end.ml TranslateMessage _wm DispatchMessageA _wm } macro end.message.loop { go .begin.ml .end.ml: . r0=_wm.wParam } macro message.loop { begin.message.loop end.message.loop } macro process.messages { .begin.ml: PeekMessageA _wm, 0, 0, 0, 0 fail .no.message GetMessageA _wm, 0, 0, 0 fail .end.ml TranslateMessage _wm DispatchMessageA _wm go .begin.ml .no.message: } macro end.messages { go .begin.ml .end.ml: ExitProcess _wm.wParam } macro minimize.window { ShowWindow _hwnd, 6 } ;;;;;;;;;;;;;;;;;;;;;; INPUT ;;;;;;;;;;;;;;;;;;;;; align 4 integer event.id, key.event, mouse.event,\ key, any.key, key.c, exit.if.esc=YES,\ mouse.1, mouse.2, mouse.x, mouse.y,\ mouse.px, mouse.py, mouse.double, mouse.wheel,\ mouse.drag, mouse.drag.x, mouse.drag.y,\ mouse.drop, mouse.drop.x, mouse.drop.y macro os.key.state k { GetAsyncKeyState k } macro os.set.cursor.xy x, y { SetCursorPos x, y } function key.state, k os.key.state k endf ; if key state macro if.key k { !if key.state K.#k } macro if.not.keys k { !if.n key.state K.#k } function select.box, box . r0=mouse.x, r1=mouse.y IF defined cursor . r0+cursor.spot.x, r1+cursor.spot.y END IF call !point.inside, box, r0, r1 endf macro if.select box { !if select.box, box } macro else.if.select box { !else.if select.box, box } macro if.not.select box { !if.n select.box, box } macro if.click box { select.box box and r0, mouse.1 if true } ;;;;;;;;;;;;;;;;;;;;; EVENTS ;;;;;;;;;;;;;;;;;;;;; align void !_on.event, !on.main,\ !_on.create, !_on.destroy, !_on.close,\ !_on.draw, !_on.game, !_on.command,\ !_on.key, !_on.mouse, !_on.timer, !_on.exit macro define.events [e] { mov [!!_on.#e], !on.#e } macro calle e { if dword [!!_on.#e] call dword [!!_on.#e] end } !call fix calle macro !on name { function on.#name } macro !end { endf 1 } ;;;;;;;;;;;;;;;;;;;;; TIMER ;;;;;;;;;;;;;;;;;;;;;; macro os.set.timer f, ms { SetTimer _hwnd, 1, ms, f } macro set.timer a, b { IF b eq os.set.timer !on.timer, a ELSE os.set.timer a, b END IF } ;;;;;;;;;;;;;;;; WINDOW PROCEDURE ;;;;;;;;;;;;;;;; function _window.procedure, window, message, wp, lp alias m=r0 . m=message, event.id=0, mouse.double=0 if m=WM_PAINT get _dc=BeginPaint _hwnd, _ps render EndPaint _hwnd, _ps go .default else.if m=WM_COMMAND calle command else.if m=WM_KEYDOWN . key=wp, event.id='k', key.event='k' if exit.if.esc if wp=K.ESCAPE SendMessageA window, WM_DESTROY, 0, 0 end end .key: calle key return 0 else.if m=WM_KEYUP . key=NO, event.id='k', key.event='r' go .key else.if m=WM_CHAR . key=wp, event.id='k', key.event='c' go .key else.if m=WM_MOUSEMOVE . mouse.event='m' if mouse.1 if not mouse.drag . mouse.drag=YES,\ mouse.drag.x=mouse.x,\ mouse.drag.y=mouse.y end end .mouse: . event.id='m', r0=lp, r1=r0,\ r0&0FFFFh, mouse.x=r0,\ r1>>16, r1&0FFFFh, mouse.y=r1 calle mouse if mouse.event='m' . mouse.px=mouse.x,\ mouse.py=mouse.y end return 0 else.if m=WM_LBUTTONDOWN . mouse.event='c', mouse.1=YES,\ mouse.drop=NO go .mouse else.if m=WM_LBUTTONUP . mouse.event='r', mouse.1=NO if mouse.drag . mouse.drop=YES,\ mouse.drop.x=mouse.x,\ mouse.drop.y=mouse.y,\ mouse.drag=NO end go .mouse else.if m=WM_LBUTTONDBLCLK . mouse.double=YES go .mouse else.if m=WM_RBUTTONDOWN . mouse.event='rc', mouse.2=YES go .mouse else.if m=WM_RBUTTONUP . mouse.event='rr', mouse.2=NO go .mouse else.if m=WM_MOUSEWHEEL . mouse.event='w', r1=wp,\ r1>>16, mouse.wheel=r1 go .mouse else.if m=WM_CREATE calle create go .default else.if m=WM_DESTROY .destroy: calle destroy PostQuitMessage 0 end .default: DefWindowProcA \ window, message, wp, lp endf ;;;;;;;;;;;;;;; LOAD/DRAW H/BITMAP ;;;;;;;;;;;;;;; function load.bitmap.w, file locals p try p=LoadImageA @module, file, 0, 0, 0, 10h GetObjectA p, BITMAP.$, _bm endf p function draw.bitmap.w, hbmp, x, y, w, h locals bmw, bmh GetObjectA hbmp, BITMAP.$, _bm . bmw=_bm.bmWidth, bmh=_bm.bmHeight get _mdc=CreateCompatibleDC _dc SelectObject _mdc, hbmp StretchBlt _dc, x, y, w, h,\ _mdc, 0, 0, bmw, bmh, SRCCOPY DeleteDC _mdc endf ;;;;;;;;;;;;;;;;;;;;;; VGA ;;;;;;;;;;;;;;;;;;;;;;; macro os.get.screen.w { get os.w=GetSystemMetrics 0 } macro os.get.screen.h { get os.h=GetSystemMetrics 1 } function os.create.vga, w, h alias p=r0, x=r1 ; set.screen screen.w, screen.h, screen.bpp try vga.hbm=CreateBitmap \ screen.w, screen.h, 32, 1, vga.p memory.zero vga.bmi, BITMAPINFOHEADER.$ . vga.bmi.biSize=BITMAPINFOHEADER.$ . vga.bmi.biWidth=screen.w . x=screen.h, neg x, vga.bmi.biHeight=x . vga.bmi.biPlanes=1, vga.bmi.biBitCount=32 endf function os.show.vga SetDIBits _dc, vga.hbm, 0, screen.h,\ vga.p, vga.bmi, 0 draw.bitmap.w vga.hbm, 0, 0, screen.w, screen.h . vga.rect.left=0, vga.rect.top=0,\ vga.rect.right=screen.w,\ vga.rect.bottom=screen.h InvalidateRect _hwnd, vga.rect, 0 endf function show.vga.box SetDIBits _dc, vga.hbm, 0, screen.h,\ vga.p, vga.bmi, 0 draw.bitmap.w vga.hbm,\ box.x, box.y, box.w, box.y . r0=box.x, r1=box.y,\ vga.rect.left=r0, vga.rect.top=r1,\ r0+box.w, vga.rect.right=r0,\ r1+box.h, vga.rect.bottom=r1 InvalidateRect _hwnd, vga.rect, 0 endf macro show.vga.box b { IF ~b eq copy.box box, b END IF show.vga.box } macro define.vga { os.define.vga } ; create vga/buffer for drawing function create.vga, w, h, c if vga.p=0, r0=w, r0*h, r0<<2 try vga.p=allocate r0 end call !clear.screen, c os.create.vga w, h endf 1 function show.vga os.show.vga endf function set.vga, w, h os.set.vga endf function end.vga destroy vga os.end.vga endf ;;;;;;;;;;;;;;;;;; ENTER+EXIT ;;;;;;;;;;;;;;;;;;;; ; user-defined enter/exit routines will be called ; if defined/nonzero function os.enter try @module=GetModuleHandleA 0 try @heap=HeapCreate 0, 0, 0 try directory=allocate 1*KB . r0=directory, *r0=0 try file.name=allocate 1*KB . r0=file.name, *r0=0 os.get.directory os.get.command.line . command.line=r0 endf 1 function exit ExitProcess 0 endf ;;;;;;;;;;;;; EXECUTABLE STRUCTURE ;;;;;;;;;;;;;;; align section '.one' \ code readable writable executable !main: os.enter if false say 'System error' exit end call !main! exit ret function main! os.get.screen.w os.get.screen.h set.screen WINDOW.W, WINDOW.H, 32 try create.vga screen.w, screen.h, BLACK define.events create, draw, key, mouse create.blank.window screen.w, screen.h os.show.cursor message.loop endf align