1(* 2 Copyright (c) 2001, 2015 3 David C.J. Matthews 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License version 2.1 as published by the Free Software Foundation. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with this library; if not, write to the Free Software 16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17*) 18(* This contains various types and other values which are needed in various 19 modules. All the exported types are contained in other structures. *) 20structure Base: 21sig 22 val winCall0: Foreign.symbol -> unit -> 'a Foreign.conversion -> unit -> 'a 23 val winCall1: Foreign.symbol -> 'a Foreign.conversion -> 'b Foreign.conversion -> 'a -> 'b 24 val winCall2: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion -> 'c Foreign.conversion -> 'a * 'b -> 'c 25 val winCall3: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion -> 'd Foreign.conversion -> 'a * 'b * 'c -> 'd 26 val winCall4: Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion -> 'e Foreign.conversion -> 27 'a * 'b * 'c * 'd -> 'e 28 val winCall5: 29 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion -> 30 'f Foreign.conversion -> 'a * 'b * 'c * 'd * 'e -> 'f 31 val winCall6: 32 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 33 'f Foreign.conversion -> 'g Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f -> 'g 34 val winCall7: 35 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 36 'f Foreign.conversion * 'g Foreign.conversion -> 'h Foreign.conversion -> 37 'a * 'b * 'c * 'd * 'e * 'f * 'g -> 'h 38 val winCall8: 39 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 40 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion -> 'i Foreign.conversion -> 41 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h -> 'i 42 val winCall9: 43 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 44 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion -> 45 'j Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i -> 'j 46 val winCall10: 47 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 48 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion -> 49 'k Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j -> 'k 50 val winCall11: 51 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 52 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion -> 53 'l Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k -> 'l 54 val winCall12: 55 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 56 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion * 57 'l Foreign.conversion -> 'm Foreign.conversion -> 58 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l -> 'm 59 val winCall13: 60 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 61 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion * 62 'l Foreign.conversion * 'm Foreign.conversion -> 'n Foreign.conversion -> 63 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm -> 'n 64 val winCall14: 65 Foreign.symbol -> 'a Foreign.conversion * 'b Foreign.conversion * 'c Foreign.conversion * 'd Foreign.conversion * 'e Foreign.conversion * 66 'f Foreign.conversion * 'g Foreign.conversion * 'h Foreign.conversion * 'i Foreign.conversion * 'j Foreign.conversion * 'k Foreign.conversion * 67 'l Foreign.conversion * 'm Foreign.conversion * 'n Foreign.conversion -> 68 'o Foreign.conversion -> 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j * 'k * 'l * 'm * 'n -> 'o 69 70 val winAbi: Foreign.LibFFI.abi 71 72 val kernel: string -> Foreign.symbol 73 and user: string -> Foreign.symbol 74 and commdlg: string -> Foreign.symbol 75 and gdi: string -> Foreign.symbol 76 and shell: string -> Foreign.symbol 77 and comctl: string -> Foreign.symbol 78 79 val cSIZE_T: int Foreign.conversion 80 and cLPARAM: int Foreign.conversion 81 and cLONG_PTR: int Foreign.conversion 82 and cULONG_PTR: int Foreign.conversion 83 and cINT_PTR: int Foreign.conversion 84 and cUINT_PTR: int Foreign.conversion 85 and cDWORD: int Foreign.conversion 86 and cWORD: int Foreign.conversion 87 and cDWORD_PTR: int Foreign.conversion 88 and cUINT_PTRw: SysWord.word Foreign.conversion 89 90 val cUint8w: Word8.word Foreign.conversion 91 and cUint16w: Word.word Foreign.conversion 92 and cUint32w: Word32.word Foreign.conversion 93 and cUintw: Word32.word Foreign.conversion 94 and cUlongw: Word32.word Foreign.conversion 95 96 val cDWORDw: Word32.word Foreign.conversion 97 and cWORDw: Word.word Foreign.conversion 98 99 val cBool: bool Foreign.conversion 100 101 val successState: string -> unit Foreign.conversion 102 val cPOSINT: string -> int Foreign.conversion 103 104 type POINT = { x: int, y: int } 105 val cPoint: POINT Foreign.conversion 106 type RECT = { left: int, top: int, right: int, bottom: int } 107 val cRect: RECT Foreign.conversion 108 type SIZE = { cx: int, cy: int } 109 val cSize: SIZE Foreign.conversion 110 111 eqtype 'a HANDLE 112 val hNull: 'a HANDLE 113 val isHNull: 'a HANDLE -> bool 114 val handleOfVoidStar: Foreign.Memory.voidStar -> 'a HANDLE 115 and voidStarOfHandle: 'a HANDLE -> Foreign.Memory.voidStar 116 117 eqtype HMENU and HDC and HWND and HINSTANCE and HGDIOBJ 118 and HDROP and HRSRC and HUPDATE 119 120 val cHGDIOBJ: HGDIOBJ Foreign.conversion 121 and cHDROP: HDROP Foreign.conversion 122 and cHMENU: HMENU Foreign.conversion 123 and cHINSTANCE: HINSTANCE Foreign.conversion 124 and cHDC: HDC Foreign.conversion 125 and cHWND: HWND Foreign.conversion 126 val cHMENUOPT: HMENU option Foreign.conversion 127 and cHGDIOBJOPT: HGDIOBJ option Foreign.conversion 128 and cHWNDOPT: HWND option Foreign.conversion 129 and cHRSRC: HRSRC Foreign.conversion 130 and cHUPDATE: HUPDATE Foreign.conversion 131 132 val hgdiObjNull:HGDIOBJ 133 and isHgdiObjNull: HGDIOBJ -> bool 134 and hdcNull: HDC 135 and isHdcNull: HDC -> bool 136 and hmenuNull: HMENU 137 and isHmenuNull: HMENU -> bool 138 and hinstanceNull: HINSTANCE 139 and isHinstanceNull: HINSTANCE -> bool 140 and hwndNull: HWND 141 142 type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ 143 and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ 144 and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ 145 146 val cHPALETTE: HPALETTE Foreign.conversion 147 and cHFONT: HFONT Foreign.conversion 148 and cHPEN: HPEN Foreign.conversion 149 and cHBITMAP: HBITMAP Foreign.conversion 150 and cHRGN: HRGN Foreign.conversion 151 and cHBRUSH: HBRUSH Foreign.conversion 152 and cHENHMETAFILE: HENHMETAFILE Foreign.conversion 153 and cHMETAFILE: HMETAFILE Foreign.conversion 154 155 156 type HICON = HGDIOBJ and HCURSOR = HGDIOBJ 157 val cHICON: HICON Foreign.conversion 158 and cHCURSOR: HCURSOR Foreign.conversion 159 160 val absConversion: 161 {abs: 'a -> 'b, rep: 'b -> 'a} -> 'a Foreign.conversion -> 'b Foreign.conversion 162 163 val tableLookup: 164 (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option -> (''a -> ''b) * (''b -> ''a) 165 and tableSetLookup: 166 (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option -> 167 (''a list -> Word32.word) * (Word32.word -> ''a list) 168 169 val tableConversion: 170 (''a * ''b) list * ((''b -> ''a) * (''a -> ''b)) option -> 171 ''b Foreign.conversion -> ''a Foreign.conversion 172 (* tableSetConversion is always a cUint *) 173 and tableSetConversion: 174 (''a * Word32.word) list * ((Word32.word -> ''a) * (''a -> Word32.word)) option -> 175 ''a list Foreign.conversion 176 177 val list2Vector: 'a Foreign.conversion -> 'a list -> Foreign.Memory.voidStar * int 178 179 datatype ClassType = NamedClass of string | ClassAtom of int 180 val cCLASS: ClassType Foreign.conversion 181 182 datatype ClipboardFormat = 183 CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF | 184 CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT | 185 CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT | 186 CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int | 187 CF_HDROP | CF_LOCALE 188 val clipLookup: (ClipboardFormat -> int) * (int -> ClipboardFormat) 189 190 datatype RESID = IdAsInt of int | IdAsString of string 191 val cRESID: RESID Foreign.conversion 192 193 val STRINGOPT: string option Foreign.conversion 194 val cCHARARRAY: int -> string Foreign.conversion 195 val fromCstring: Foreign.Memory.voidStar -> string 196 val toCstring: string -> Foreign.Memory.voidStar (* Memory must be freed *) 197 val copyStringToMem: Foreign.Memory.voidStar * int * string -> unit 198 val fromCWord8vec: Foreign.Memory.voidStar * int -> Word8Vector.vector 199 val toCWord8vec: Word8Vector.vector -> Foreign.Memory.voidStar (* Memory must be freed *) 200 201 val getStringCall: (Foreign.Memory.voidStar * int -> int) -> string 202 val getStringWithNullIsLength: (Foreign.Memory.voidStar * int -> int) -> string 203 val getVectorResult: 204 'a Foreign.conversion -> (Foreign.Memory.voidStar * int -> int) -> int -> 'a vector 205 206 eqtype HGLOBAL 207 val cHGLOBAL: HGLOBAL Foreign.conversion 208 val GlobalAlloc: int * int -> HGLOBAL 209 val GlobalLock: HGLOBAL -> Foreign.Memory.voidStar 210 val GlobalFree: HGLOBAL -> HGLOBAL 211 val GlobalSize: HGLOBAL -> int 212 val GlobalUnlock: HGLOBAL -> bool 213 214 val HIWORD: Word32.word -> Word.word 215 val LOWORD: Word32.word -> Word.word 216 val MAKELONG: Word.word * Word.word -> Word32.word 217 val HIBYTE: Word.word -> Word8.word 218 val LOBYTE: Word.word -> Word8.word 219 220 val unicodeToString: Word8Vector.vector -> string 221 val stringToUnicode: string -> Word8Vector.vector 222 223 val GetLastError: unit -> OS.syserror 224 225 val checkResult: bool -> unit 226 val raiseSysErr: unit -> 'a 227 228 structure FindReplaceFlags: 229 sig 230 include BIT_FLAGS 231 val FR_DIALOGTERM : flags 232 val FR_DOWN : flags 233 val FR_FINDNEXT : flags 234 val FR_HIDEMATCHCASE : flags 235 val FR_HIDEUPDOWN : flags 236 val FR_HIDEWHOLEWORD : flags 237 val FR_MATCHCASE : flags 238 val FR_NOMATCHCASE : flags 239 val FR_NOUPDOWN : flags 240 val FR_NOWHOLEWORD : flags 241 val FR_REPLACE : flags 242 val FR_REPLACEALL : flags 243 val FR_SHOWHELP : flags 244 val FR_WHOLEWORD : flags 245 val cFindReplaceFlags: flags Foreign.conversion 246 end 247 248end = 249struct 250 open Foreign 251(* val System_isShort : vol -> bool = 252 RunCall.run_call1 RuntimeCalls.POLY_SYS_is_short*) 253 254 fun absConversion {abs: 'a -> 'b, rep: 'b -> 'a} (c: 'a conversion) : 'b conversion = 255 let 256 val { load=loadI, store=storeI, ctype } = breakConversion c 257 fun load m = abs(loadI m) 258 fun store(m, v) = storeI(m, rep v) 259 in 260 makeConversion { load = load, store = store, ctype = ctype } 261 end 262 263 (* In many cases we can pass a set of options as a bit set. *) 264 (* 265 fun bitsetConversion {abs, rep} = 266 let 267 val (fromC, toC, Ctype) = breakConversion INT 268 val fromList = List.foldl (fn(i, n) => IntInf.orb(rep i, n)) 0 269 fun toList n = [abs n] (* This is a bit of a mess. *) 270 in 271 mkConversion (toList o fromCuint) (toCuint o fromList) Cuint 272 end*) 273 274 (* Conversions between Word/Word32/LargeWord etc. *) 275 local 276 open Memory LowLevel 277 fun noFree () = () 278 in 279 local 280 fun load(m: voidStar): Word8.word = get8(m, 0w0) 281 fun store(m: voidStar, i: Word8.word) = (set8(m, 0w0, i); noFree) 282 in 283 val cUint8w: Word8.word conversion = 284 makeConversion{ load=load, store=store, ctype = cTypeUint8 } 285 end 286 local 287 fun load(m: voidStar): Word.word = get16(m, 0w0) 288 fun store(m: voidStar, i: Word.word) = (set16(m, 0w0, i); noFree) 289 in 290 val cUint16w: Word.word conversion = 291 makeConversion{ load=load, store=store, ctype = cTypeInt16 } 292 end 293 local 294 fun load(m: voidStar): Word32.word = get32(m, 0w0) 295 fun store(m: voidStar, i: Word32.word) = (set32(m, 0w0, i); noFree) 296 in 297 val cUint32w: Word32.word conversion = 298 makeConversion{ load=load, store=store, ctype = cTypeUint32 } 299 300 end 301 val cUintw = cUint32w 302 (* Int should be 32-bits on Windows. *) 303 val _ = #size LowLevel.cTypeUint = #size LowLevel.cTypeUint32 304 orelse raise Fail "unsigned int is not 32-bits" 305 val cUlongw = cUint32w 306 val _ = #size LowLevel.cTypeUlong = #size LowLevel.cTypeUint32 307 orelse raise Fail "unsigned long is not 32-bits" 308 end 309 310 val cDWORD = cUint32 (* Defined to be 32-bit unsigned *) 311 and cWORD = cUint16 (* Defined to be 16-bit unsigned *) 312 313 val cDWORDw = cUint32w 314 and cWORDw = cUint16w 315 316 (* For some reason Windows has both INT_PTR and LONG_PTR and they 317 are slightly different. *) 318 val cLONG_PTR = 319 if #size LowLevel.cTypePointer = 0w4 320 then cLong 321 else cInt64 322 323 val cINT_PTR = 324 if #size LowLevel.cTypePointer = 0w4 325 then cInt 326 else cInt64 327 328 val cULONG_PTR = 329 if #size LowLevel.cTypePointer = 0w4 330 then cUlong 331 else cUint64 332 333 val cUINT_PTR = 334 if #size LowLevel.cTypePointer = 0w4 335 then cUint 336 else cUint64 337 338 val cLPARAM = cLONG_PTR 339 val cSIZE_T = cULONG_PTR (* Probably. *) 340 val cDWORD_PTR = cULONG_PTR (* Defined to be the same so I'm not sure why it's there .*) 341 342 val cUINT_PTRw = absConversion{abs=Memory.voidStar2Sysword, rep=Memory.sysWord2VoidStar} cPointer 343 344 (* These are called XXX32.DLL on both 32-bit and 64-bit. *) 345 fun kernel name = getSymbol(loadLibrary "kernel32.dll") name 346 and user sym = getSymbol(loadLibrary "user32.DLL") sym 347 and commdlg sym = getSymbol(loadLibrary "comdlg32.DLL") sym 348 and gdi sym = getSymbol(loadLibrary "gdi32.DLL") sym 349 and shell sym = getSymbol(loadLibrary "shell32.DLL") sym 350 and comctl sym = getSymbol(loadLibrary "comctl32.DLL") sym 351 352 (* We need to use the Pascal calling convention on 32-bit Windows. *) 353 val winAbi = 354 case List.find (fn ("stdcall", _) => true | _ => false) LibFFI.abiList of 355 SOME(_, abi) => abi 356 | NONE => LibFFI.abiDefault 357 358 (* As well as setting the abi we can also use the old argument order. *) 359 fun winCall0 sym argConv resConv = buildCall0withAbi(winAbi, sym, argConv, resConv) 360 and winCall1 sym argConv resConv = buildCall1withAbi(winAbi, sym, argConv, resConv) 361 and winCall2 sym argConv resConv = buildCall2withAbi(winAbi, sym, argConv, resConv) 362 and winCall3 sym argConv resConv = buildCall3withAbi(winAbi, sym, argConv, resConv) 363 and winCall4 sym argConv resConv = buildCall4withAbi(winAbi, sym, argConv, resConv) 364 and winCall5 sym argConv resConv = buildCall5withAbi(winAbi, sym, argConv, resConv) 365 and winCall6 sym argConv resConv = buildCall6withAbi(winAbi, sym, argConv, resConv) 366 and winCall7 sym argConv resConv = buildCall7withAbi(winAbi, sym, argConv, resConv) 367 and winCall8 sym argConv resConv = buildCall8withAbi(winAbi, sym, argConv, resConv) 368 and winCall9 sym argConv resConv = buildCall9withAbi(winAbi, sym, argConv, resConv) 369 and winCall10 sym argConv resConv = buildCall10withAbi(winAbi, sym, argConv, resConv) 370 and winCall11 sym argConv resConv = buildCall11withAbi(winAbi, sym, argConv, resConv) 371 and winCall12 sym argConv resConv = buildCall12withAbi(winAbi, sym, argConv, resConv) 372 and winCall13 sym argConv resConv = buildCall13withAbi(winAbi, sym, argConv, resConv) 373 and winCall14 sym argConv resConv = buildCall14withAbi(winAbi, sym, argConv, resConv) 374 375 (* Previously we had a specific call to do this. The error state is 376 no longer set by the new FFI. *) 377(* 378 fun GetLastError(): OS.syserror = 379 RunCall.run_call2 RuntimeCalls.POLY_SYS_os_specific (1100, ()) 380*) 381 local 382 val getLastError = winCall0 (kernel "GetLastError") () cDWORD 383 in 384 fun GetLastError(): OS.syserror = 385 (* Windows error codes are negative values in OS.syserror. *) 386 RunCall.unsafeCast (~ (getLastError())) 387 end 388 389 (* The string argument of the SysErr exception is supposed to match the result of OS.errMsg. *) 390 fun raiseSysErr () = let val err = GetLastError() in raise OS.SysErr(OS.errorMsg err, SOME err) end 391 392 (* Many system calls return bool. If the result is false we raise an exception. *) 393 fun checkResult true = () | checkResult false = raiseSysErr () 394 395 val cBool: bool conversion = 396 absConversion{abs = fn 0 => false | _ => true, rep = fn false => 0 | true => 1} cInt 397 398 fun successState name: unit conversion = 399 absConversion { abs = checkResult, rep = fn _ => raise Fail ("successState:" ^ name) } cBool 400 401 402 type POINT = { x: int, y: int } 403 404 local 405 fun breakPoint ({x,y}: POINT) = (x,y) 406 fun mkPoint (x,y): POINT = {x=x, y=y} 407 in 408 val cPoint = absConversion {abs=mkPoint, rep=breakPoint} (cStruct2 (cLong, cLong)) 409 end 410 411 type RECT = { left: int, top: int, right: int, bottom: int } 412 413 local 414 fun breakRect ({left,top,right,bottom}: RECT) = (left,top,right,bottom) 415 fun mkRect (left,top,right,bottom): RECT = 416 {left=left,top=top,right=right,bottom=bottom} 417 in 418 val cRect = absConversion {abs=mkRect, rep=breakRect} (cStruct4 (cLong,cLong,cLong,cLong)) 419 end 420 421 type SIZE = { cx: int, cy: int } 422 local 423 fun breakSize ({cx,cy}: SIZE) = (cx,cy) 424 fun mkSize (cx,cy): SIZE = {cx=cx, cy=cy} 425 in 426 val cSize = absConversion {abs=mkSize, rep=breakSize} (cStruct2 (cLong,cLong)) 427 end 428 429 (* Handles are generally opaque values. We want them to be eqtypes, though. *) 430 local 431 structure HandStruct :> 432 sig 433 eqtype 'a HANDLE 434 val hNull: 'a HANDLE 435 val isHNull: 'a HANDLE -> bool 436 val handleOfVoidStar: Memory.voidStar -> 'a HANDLE 437 and voidStarOfHandle: 'a HANDLE -> Memory.voidStar 438 end = 439 struct 440 type 'a HANDLE = Memory.voidStar 441 val hNull = Memory.null 442 fun isHNull h = h = hNull 443 444 (* We sometimes need the next two functions internally. 445 They're needed externally unless we change the result type 446 of SendMessage to allow us to return a handle for certain 447 messages. *) 448 fun handleOfVoidStar h = h 449 and voidStarOfHandle h = h 450 end 451 in 452 open HandStruct 453 end 454 455 (* We just need these as placeholders. We never create values of 456 these types. They are used simply as a way of creating different 457 handle types. *) 458 (* Don't use abstype - we want them to eqtypes *) 459 datatype GdiObj = GdiObj 460 and Instance = Instance 461 and Drop = Drop 462 and DeviceContext = DeviceContext 463 and Menu = Menu 464 and Window = Window 465 and Global = Global 466 and Src = Src 467 and Update = Update 468 469 (* HINSTANCE is used as an instance of a module. *) 470 type HINSTANCE = Instance HANDLE 471 and HDROP = Drop HANDLE 472 and HGDIOBJ = GdiObj HANDLE 473 and HDC = DeviceContext HANDLE 474 and HMENU = Menu HANDLE 475 and HWND = Window HANDLE 476 and HGLOBAL = Global HANDLE 477 and HRSRC = Src HANDLE 478 and HUPDATE = Update HANDLE 479 480 local 481 fun cHANDLE() = 482 absConversion {abs=handleOfVoidStar, rep=voidStarOfHandle} cPointer 483 fun hoptOfvs n = 484 if Memory.voidStar2Sysword n = 0w0 then NONE else SOME(handleOfVoidStar n) 485 486 fun cHANDLEOPT() = 487 absConversion {abs=hoptOfvs, rep=fn v => voidStarOfHandle(getOpt(v, hNull)) } cPointer 488 in 489 val cHGDIOBJ: HGDIOBJ conversion = cHANDLE() 490 and cHDROP: HDROP conversion = cHANDLE() 491 and cHMENU: HMENU conversion = cHANDLE() 492 and cHINSTANCE: HINSTANCE conversion = cHANDLE() 493 and cHDC: HDC conversion = cHANDLE() 494 and cHWND: HWND conversion = cHANDLE() 495 496 val cHMENUOPT: HMENU option conversion = cHANDLEOPT() 497 and cHGDIOBJOPT: HGDIOBJ option conversion = cHANDLEOPT() 498 and cHWNDOPT: HWND option conversion = cHANDLEOPT() 499 500 val cHGLOBAL: HGLOBAL conversion = cHANDLE() 501 and cHRSRC: HRSRC conversion = cHANDLE() 502 and cHUPDATE: HUPDATE conversion = cHANDLE() 503 end 504 505 (* Temporary declarations. *) 506 val hgdiObjNull:HGDIOBJ = hNull 507 and isHgdiObjNull: HGDIOBJ -> bool = isHNull 508 and hdcNull: HDC = hNull 509 and isHdcNull: HDC -> bool = isHNull 510 and hmenuNull: HMENU = hNull 511 and isHmenuNull: HMENU -> bool = isHNull 512 and hinstanceNull: HINSTANCE = hNull 513 and isHinstanceNull: HINSTANCE -> bool = isHNull 514 and hwndNull: HWND = hNull 515 516 (* All these are various kinds of HGDIOBJ. It's too complicated to try 517 to use different types for them. *) 518 type HPALETTE = HGDIOBJ and HFONT = HGDIOBJ and HPEN = HGDIOBJ 519 and HBITMAP = HGDIOBJ and HRGN = HGDIOBJ and HBRUSH = HGDIOBJ 520 and HENHMETAFILE = HGDIOBJ and HMETAFILE = HGDIOBJ 521 522 val cHPALETTE: HPALETTE conversion = cHGDIOBJ 523 and cHFONT: HFONT conversion = cHGDIOBJ 524 and cHPEN: HPEN conversion = cHGDIOBJ 525 and cHBITMAP: HBITMAP conversion = cHGDIOBJ 526 and cHRGN: HRGN conversion = cHGDIOBJ 527 and cHBRUSH: HBRUSH conversion = cHGDIOBJ 528 and cHENHMETAFILE: HENHMETAFILE conversion = cHGDIOBJ 529 and cHMETAFILE: HMETAFILE conversion = cHGDIOBJ 530 531 (* I'm not so happy about treating these as HGDIOBJ but it makes the 532 types of messages such as BM_SETIMAGE simpler. *) 533 type HICON = HGDIOBJ and HCURSOR = HGDIOBJ 534 val cHICON = cHGDIOBJ and cHCURSOR = cHGDIOBJ 535 536 (* The easiest way to deal with datatypes is often by way of a table. *) 537 fun tableLookup (table: (''a * ''b) list, default) = 538 let 539 fun toInt [] x = 540 (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x) 541 | toInt ((y, i) :: tl) x = if x = y then i else toInt tl x 542 543 fun fromInt [] x = 544 (case default of 545 NONE => raise Fail ("tableLookup: not found") 546 | SOME (d, _) => d x) 547 | fromInt ((y, i) :: tl) x = if x = i then y else fromInt tl x 548 in 549 (toInt table, fromInt table) 550 end 551 552 fun tableConversion (table: (''a * ''b) list, default) (conv: ''b conversion): ''a conversion = 553 let 554 val (toInt, fromInt) = tableLookup(table, default) 555 in 556 absConversion {abs = fromInt, rep = toInt} conv 557 end 558 559 (* In other cases we have sets of options. We represent them by a list. 560 The order of the elements in the table is significant if we are to be 561 able to handle multiple bits. Patterns with more than one bit set 562 MUST be placed later than those with a subset of those bits. *) 563 fun tableSetLookup (table: (''a * Word32.word) list, default) = 564 let 565 open Word32 566 (* Conversion to integer - just fold the values. *) 567 fun toInt' [] x = 568 (case default of NONE => raise Fail "tableLookup: not found" | SOME (_, d) => d x) 569 | toInt' ((y, i) :: tl) x = if x = y then i else toInt' tl x 570 571 val toInt = List.foldl (fn (a, b) => orb(toInt' table a, b)) 0w0 572 573 (* It would speed up the searches if we ordered the list so that multiple 574 bit entries preceded those with fewer bits but it's much easier to lay 575 out the tables if we do it this way. *) 576 fun fromInt _ _ 0w0 = [] (* Zero is an empty list. *) 577 578 | fromInt [] NONE x = (* Not found *) 579 (case default of 580 NONE => raise Fail ("tableLookup: not found" ^ Word32.toString x) 581 | SOME (d, _) => [d x]) 582 583 | fromInt [] (SOME(res, bits)) x = (* Found something - remove it from the set. *) 584 (res :: fromInt table NONE (andb(x, notb bits))) 585 586 | fromInt ((res, bits)::tl) sofar x = 587 if bits <> 0w0 andalso andb(x, bits) = bits 588 then (* Matches *) fromInt tl (SOME(res, bits)) x 589 else (* Doesn't match *) fromInt tl sofar x 590 in 591 (toInt, fromInt table NONE) 592 end 593 594 fun tableSetConversion (table: (''a * Word32.word) list, default): ''a list conversion = 595 let 596 val (toInt, fromInt) = tableSetLookup(table, default) 597 in 598 absConversion {abs = fromInt, rep = toInt} cUintw 599 end 600 601 602 structure FindReplaceFlags:> 603 sig 604 include BIT_FLAGS 605 val FR_DIALOGTERM : flags 606 val FR_DOWN : flags 607 val FR_FINDNEXT : flags 608 val FR_HIDEMATCHCASE : flags 609 val FR_HIDEUPDOWN : flags 610 val FR_HIDEWHOLEWORD : flags 611 val FR_MATCHCASE : flags 612 val FR_NOMATCHCASE : flags 613 val FR_NOUPDOWN : flags 614 val FR_NOWHOLEWORD : flags 615 val FR_REPLACE : flags 616 val FR_REPLACEALL : flags 617 val FR_SHOWHELP : flags 618 val FR_WHOLEWORD : flags 619 val cFindReplaceFlags: flags conversion 620 end = 621 struct 622 open Word32 623 type flags = word 624 val toWord = toLargeWord 625 and fromWord = fromLargeWord 626 val flags = List.foldl (fn (a, b) => orb(a,b)) 0w0 627 fun allSet (fl1, fl2) = andb(fl1, fl2) = fl1 628 fun anySet (fl1, fl2) = andb(fl1, fl2) <> 0w0 629 fun clear (fl1, fl2) = andb(notb fl1, fl2) 630 631 val FR_DOWN = 0wx00000001 632 val FR_WHOLEWORD = 0wx00000002 633 val FR_MATCHCASE = 0wx00000004 634 val FR_FINDNEXT = 0wx00000008 635 val FR_REPLACE = 0wx00000010 636 val FR_REPLACEALL = 0wx00000020 637 val FR_DIALOGTERM = 0wx00000040 638 val FR_SHOWHELP = 0wx00000080 639 val FR_NOUPDOWN = 0wx00000400 640 val FR_NOMATCHCASE = 0wx00000800 641 val FR_NOWHOLEWORD = 0wx00001000 642 val FR_HIDEUPDOWN = 0wx00004000 643 val FR_HIDEMATCHCASE = 0wx00008000 644 val FR_HIDEWHOLEWORD = 0wx00010000 645 646 val all = flags[FR_DOWN, FR_WHOLEWORD, FR_MATCHCASE, FR_FINDNEXT, FR_REPLACE, 647 FR_REPLACEALL, FR_DIALOGTERM, FR_NOUPDOWN, FR_NOMATCHCASE, 648 FR_NOWHOLEWORD, FR_HIDEUPDOWN, FR_HIDEMATCHCASE, FR_HIDEWHOLEWORD] 649 650 val intersect = List.foldl (fn (a, b) => andb(a,b)) all 651 652 val cFindReplaceFlags = cDWORDw 653 end 654 655 (* The class "string" may be a name or an atom. *) 656 datatype ClassType = NamedClass of string | ClassAtom of int 657 658 local 659 open Memory 660 val {store=storeS, load=loadS, ctype} = breakConversion cString 661 662 fun storeClass(m, ClassAtom i) = 663 if i >= 0 andalso i < 0xC000 664 then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ()) 665 else raise Fail "atom out of range" 666 | storeClass(m, NamedClass s) = storeS(m, s) 667 668 fun loadClass m = 669 let 670 val v = getAddress(m, 0w0) 671 in 672 if voidStar2Sysword v < 0wxC000 673 then ClassAtom(SysWord.toInt(voidStar2Sysword v)) 674 else NamedClass(loadS m) 675 end 676 677 in 678 val cCLASS = makeConversion { load = loadClass, store = storeClass, ctype = ctype } 679 end 680 681 (* Clipboard formats. I've added CF_NONE, CF_PRIVATE, CF_GDIOBJ and CF_REGISTERED. 682 This is here because it is used in both Clipboard and Message (WM_RENDERFORMAT) *) 683 datatype ClipboardFormat = 684 CF_NONE | CF_TEXT | CF_BITMAP | CF_METAFILEPICT | CF_SYLK | CF_DIF | CF_TIFF | 685 CF_OEMTEXT | CF_DIB | CF_PALETTE | CF_PENDATA | CF_RIFF | CF_WAVE | CF_UNICODETEXT | 686 CF_ENHMETAFILE | CF_OWNERDISPLAY | CF_DSPTEXT | CF_DSPBITMAP | CF_DSPMETAFILEPICT | 687 CF_DSPENHMETAFILE | CF_PRIVATE of int | CF_GDIOBJ of int | CF_REGISTERED of int | 688 CF_HDROP | CF_LOCALE 689 690 local 691 val tab = [ 692 (CF_NONE, 0), 693 (CF_TEXT, 1), 694 (CF_BITMAP, 2), 695 (CF_METAFILEPICT, 3), 696 (CF_SYLK, 4), 697 (CF_DIF, 5), 698 (CF_TIFF, 6), 699 (CF_OEMTEXT, 7), 700 (CF_DIB, 8), 701 (CF_PALETTE, 9), 702 (CF_PENDATA, 10), 703 (CF_RIFF, 11), 704 (CF_WAVE, 12), 705 (CF_UNICODETEXT, 13), 706 (CF_ENHMETAFILE, 14), 707 (CF_HDROP, 15), 708 (CF_LOCALE, 16), 709 (CF_OWNERDISPLAY, 0x0080), 710 (CF_DSPTEXT, 0x0081), 711 (CF_DSPBITMAP, 0x0082), 712 (CF_DSPMETAFILEPICT, 0x0083), 713 (CF_DSPENHMETAFILE, 0x008E) 714 ] 715 fun toInt (CF_PRIVATE i) = 716 if i >= 0 andalso i < 0xff then 0x0200 + i else raise Size 717 | toInt (CF_GDIOBJ i) = 718 if i >= 0 andalso i < 0xff then 0x0300 + i else raise Size 719 | toInt (CF_REGISTERED i) = i 720 | toInt _ = raise Match 721 722 fun fromInt i = 723 if i >= 0x0200 andalso i <= 0x02ff then CF_PRIVATE(i-0x0200) 724 else if i >= 0x0300 andalso i <= 0x03ff then CF_GDIOBJ(i-0x0300) 725 else if i >= 0xC000 andalso i < 0xFFFF then CF_REGISTERED i 726 else raise Match 727 in 728 val clipLookup = tableLookup (tab, SOME(fromInt, toInt)) 729 end 730 731 (* Resources may be specified by strings or by ints. *) 732 datatype RESID = IdAsInt of int | IdAsString of string 733 734 local 735 open Memory 736 val {store=storeS, load=loadS, ctype} = breakConversion cString 737 738 fun storeResid(m, IdAsInt i) = 739 if i >= 0 andalso i < 65536 740 then (setAddress(m, 0w0, sysWord2VoidStar(SysWord.fromInt i)); fn () => ()) 741 else raise Fail "resource id out of range" 742 | storeResid(m, IdAsString s) = storeS(m, s) 743 744 fun loadResid m = 745 let 746 val v = getAddress(m, 0w0) 747 in 748 if voidStar2Sysword v < 0w65536 749 then IdAsInt(SysWord.toInt(voidStar2Sysword v)) 750 else IdAsString(loadS m) 751 end 752 in 753 val cRESID = 754 makeConversion { load = loadResid, store = storeResid, ctype = ctype } 755 end 756 757 (*datatype HelpContext = 758 HelpInfo_MenuItem of 759 | HelpInfo_Window of 760 761 type HELPINFO = { 762 }*) 763 764 765 (* Useful conversions. *) 766 (* Various functions return zero if error. This conversion checks for that. *) 767 fun cPOSINT _ = 768 absConversion {abs = fn 0 => raiseSysErr() | n => n, rep = fn i => i} cInt 769 770 (* Conversion between string option and C strings. NONE is converted to NULL. *) 771 val STRINGOPT = cOptionPtr cString 772 773 (* Convert a C string to ML. *) 774 fun fromCstring buff = 775 let 776 open Memory 777 (* We can't use #load cString because the argument is the address of 778 the address of the string. *) 779 fun sLen i = if get8(buff, i) = 0w0 then i else sLen(i+0w1) 780 val length = sLen 0w0 781 fun loadChar i = 782 Char.chr(Word8.toInt(get8(buff, Word.fromInt i))) 783 in 784 CharVector.tabulate(Word.toInt length, loadChar) 785 end 786 787 (* Copy a string to a particular offset in a buffer and 788 add a null terminator. *) 789 fun copyStringToMem (buf, n, s) = 790 let 791 open Memory 792 infix 6 ++ 793 fun copyToBuf (i, v) = set8(buf, Word.fromInt(i+n), Byte.charToByte v) 794 in 795 CharVector.appi copyToBuf s; 796 set8(buf, Word.fromInt(n + size s), 0w0) 797 end 798 799 fun toCstring s = 800 let 801 open Memory 802 val sLen = Word.fromInt(String.size s) 803 val sMem = malloc(sLen + 0w1) 804 val () = copyStringToMem(sMem, 0, s) 805 in 806 sMem 807 end 808 809 (* When getting a string it is often the case that passing NULL returns the 810 length required. Then a second call will actually retrieve the string. *) 811 fun getStringWithNullIsLength(f: Memory.voidStar*int -> int): string = 812 let 813 open Memory 814 val realLength = f(null, 0) 815 val buff = malloc (Word.fromInt(realLength+1)) 816 val _ = f(buff, realLength) handle ex => (free buff; raise ex) 817 in 818 fromCstring buff before free buff 819 end 820 821 (* In several cases when extracting a string it is not possible in advance 822 to know how big to make the buffer. This function loops until all the 823 string has been extracted. *) 824 (* This is at least needed for GetClassName *) 825 fun getStringCall(f: Memory.voidStar*int -> int): string = 826 let 827 open Memory 828 829 fun doCall initialSize = 830 let 831 (* Allocate a buffer to receive the result. For safety we make it 832 one character longer than we actually say because it's not always 833 clear whether the length we pass is the size including the NULL. 834 Equally we are only certain we have read the whole string if 835 the return value is less than initialSize-1 because the return 836 value could be the number of real characters copied to the buffer. *) 837 val buff = malloc (Word.fromInt(initialSize+1)) 838 val resultSize = 839 f(buff, initialSize) handle ex => (free buff; raise ex) 840 in 841 if resultSize < initialSize-1 842 then (* We've got it all. *) 843 fromCstring buff before free buff 844 else ( free buff; doCall(initialSize + initialSize div 2) ) 845 end 846 in 847 doCall (*1024*) 3 (* Use a small size initially for testing. *) 848 end 849 850 (* We have a number of calls that extract a vector of results. They 851 are called with an initial size, set the vector to the results and 852 return a count of the number actually assigned. *) 853 fun getVectorResult(element: 'a conversion) = 854 let 855 val { load=loadElem, ctype={size=sizeElem, ...}, ...} = breakConversion element 856 fun run f initialCount = 857 let 858 open Memory 859 infix 6 ++ -- 860 val vec = malloc(Word.fromInt initialCount * sizeElem) 861 fun getElement i = loadElem(vec ++ Word.fromInt i * sizeElem) 862 val resultCount = 863 f (vec, initialCount) handle ex => (free vec; raise ex) 864 in 865 Vector.tabulate(resultCount, getElement) before free vec 866 end 867 in 868 run 869 end 870 871 (* Some C functions take a vector of values to allow a variable number of 872 elements to be passed. We use a list for this in ML. *) 873 (* TODO: This discards the result of any store function so if we 874 store strings we'll leak store. *) 875 fun list2Vector (conv: 'a conversion) (l:'a list): Memory.voidStar * int = 876 let 877 val count = List.length l 878 val {store=storea, ctype={size=sizea, ...}, ...} = breakConversion conv 879 open Memory 880 infix 6 ++ 881 val vec = malloc(Word.fromInt count * sizea) 882 fun setItem(item, v) = (ignore(storea(v, item)); v ++ sizea) 883 val _ = List.foldl setItem vec l 884 in 885 (vec, count) 886 end 887 888 val GlobalAlloc = winCall2 (kernel "GlobalAlloc") (cInt, cSIZE_T) cHGLOBAL 889 val GlobalLock = winCall1 (kernel "GlobalLock") (cHGLOBAL) cPointer 890 val GlobalFree = winCall1 (kernel "GlobalFree") (cHGLOBAL) cHGLOBAL 891 val GlobalSize = winCall1 (kernel "GlobalSize") (cHGLOBAL) cSIZE_T 892 val GlobalUnlock = winCall1 (kernel "GlobalUnlock") (cHGLOBAL) cBool 893 894 (* Conversion for Word8Vector. We can't do this as a general conversion because 895 we can't find out how big the C vector is. *) 896 fun fromCWord8vec (buff, length) = 897 Word8Vector.tabulate(length, fn i => Memory.get8(buff, Word.fromInt i)) 898 899 fun toCWord8vec(s: Word8Vector.vector): Memory.voidStar = 900 let 901 open Memory Word8Vector 902 val sLen = Word.fromInt(length s) 903 val sMem = malloc sLen 904 val () = appi(fn(i, b) => set8(sMem, Word.fromInt i, b)) s 905 in 906 sMem 907 end 908 909(* 910 (* Conversion for a fixed size byte array. *) 911 fun BYTEARRAY n = 912 let 913 val base = Cstruct (List.tabulate (n, fn _ => Cchar)) 914 fun from v = toWord8vec(address v, n) 915 fun to w = 916 if Word8Vector.length w <> n then raise Size else deref(fromWord8vec w) 917 in 918 mkConversion from to base 919 end *) 920 921 (* Conversion for a fixed size char array. *) 922 fun cCHARARRAY n : string conversion = 923 let 924 (* Make it a struct of chars *) 925 val { size=sizeC, align=alignC, ffiType=ffiTypeC } = LowLevel.cTypeChar 926 val arraySize = sizeC * Word.fromInt n 927 fun ffiType () = 928 LibFFI.createFFItype { 929 size = arraySize, align = alignC, typeCode=LibFFI.ffiTypeCodeStruct, 930 elements = List.tabulate (n, fn _ => ffiTypeC()) } 931 val arrayType: LowLevel.ctype = 932 { size = arraySize, align = alignC, ffiType = ffiType } 933 934 open Memory 935 936 fun load(v: voidStar): string = 937 let 938 (* It should be null-terminated but just in case... *) 939 fun sLen i = if i = Word.fromInt n orelse get8(v, i) = 0w0 then i else sLen(i+0w1) 940 val length = sLen 0w0 941 fun loadChar i = 942 Char.chr(Word8.toInt(get8(v, Word.fromInt i))) 943 in 944 CharVector.tabulate(Word.toInt length, loadChar) 945 end 946 947 fun store(v: voidStar, s: string) = 948 let 949 (* The length must be less than the size to allow for the null *) 950 val sLen = size s 951 val _ = sLen < n orelse raise Fail "string too long" 952 in 953 CharVector.appi(fn(i, ch) => set8(v, Word.fromInt i, Word8.fromInt(Char.ord ch))) s; 954 set8(v, Word.fromInt sLen, 0w0); 955 fn () => () 956 end 957 in 958 makeConversion { load = load, store = store, ctype = arrayType } 959 end 960 961 (* These should always be UNSIGNED values. *) 962 local 963 open Word32 964 infix << >> orb andb 965 val w32ToW = Word.fromLargeWord o Word32.toLargeWord 966 and wTow32 = Word32.fromLargeWord o Word.toLargeWord 967 in 968 fun LOWORD(l) = w32ToW(l andb 0wxFFFF) 969 fun HIWORD(l) = w32ToW((l >> 0w16) andb 0wxFFFF) 970 971 fun MAKELONG(a, b) = (wTow32 b << 0w16) orb (wTow32 a andb 0wxFFFF) 972 end 973 974 local 975 open Word 976 infix << >> orb andb 977 val wToW8 = Word8.fromLargeWord o Word.toLargeWord 978 in 979 fun HIBYTE(w) = wToW8((w >> 0w8) andb 0wxFF) 980 fun LOBYTE(w) = wToW8(w andb 0wxFF) 981 end 982 983 (* Convert between strings and vectors containing Unicode characters. 984 N.B. These are not null terminated. *) 985 local 986 val CP_ACP = 0 (* Default *) 987 val WideCharToMultiByte = winCall8 (kernel "WideCharToMultiByte") 988 (cUint, cDWORD, cByteArray, cInt, cPointer, cInt, cPointer, cPointer) cInt 989 val MultiByteToWideChar = 990 winCall6 (kernel "MultiByteToWideChar") (cUint, cDWORD, cString, cInt, cPointer, cInt) cInt 991 in 992 fun unicodeToString(w: Word8Vector.vector): string = 993 let 994 open Memory 995 val inputLength = Word8Vector.length w div 2 (* Number of unicode chars *) 996 val outputLength = 997 WideCharToMultiByte(CP_ACP, 0, w, inputLength, null, 0, null, null) 998 val outputBuf = malloc(Word.fromInt outputLength) 999 1000 val conv = WideCharToMultiByte(CP_ACP, 0, w, inputLength, outputBuf, outputLength, null, null) 1001 1002 fun loadChar i = 1003 Char.chr(Word8.toInt(get8(outputBuf, Word.fromInt i))) 1004 in 1005 (* We can't use fromCstring here because it's not necessarily null terminated. *) 1006 CharVector.tabulate(conv, loadChar) before free outputBuf 1007 end 1008 1009 fun stringToUnicode(s: string): Word8Vector.vector = 1010 let 1011 open Memory 1012 val inputLength = size s (* This does not include a terminating NULL *) 1013 (* The lengths returned by MultiByteToWideChar are the number of Unicode chars *) 1014 val outputLength = MultiByteToWideChar(CP_ACP, 0, s, inputLength, null, 0) 1015 val outputBuf = malloc(Word.fromInt outputLength * 0w2) 1016 val conv = MultiByteToWideChar(CP_ACP, 0, s, inputLength, outputBuf, outputLength) 1017 fun loadByte i = get8(outputBuf, Word.fromInt i) 1018 in 1019 Word8Vector.tabulate(conv*2, loadByte) before free outputBuf 1020 end 1021 end 1022 1023end; 1024