lowlevel.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. {
  2. This file is part of the Free Pascal run time library.
  3. A file in Amiga system run time library.
  4. Copyright (c) 1998-2003 by Nils Sjoholm
  5. member of the Amiga RTL development team.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {
  13. History:
  14. Added functions and procedures with array of const.
  15. For use with fpc 1.0.7. Thay are in systemvartags.
  16. 11 Nov 2002.
  17. Added the defines use_amiga_smartlink and
  18. use_auto_openlib.
  19. 13 Jan 2003.
  20. Update for AmigaOS 3.9.
  21. Changed startcode for unit.
  22. 09 Feb 2003.
  23. [email protected]
  24. }
  25. {$I useamigasmartlink.inc}
  26. {$ifdef use_amiga_smartlink}
  27. {$smartlink on}
  28. {$endif use_amiga_smartlink}
  29. UNIT lowlevel;
  30. INTERFACE
  31. USES exec, utility, timer;
  32. Type
  33. { structure for use with QueryKeys() }
  34. pKeyQuery = ^tKeyQuery;
  35. tKeyQuery = record
  36. kq_KeyCode : WORD;
  37. kq_Pressed : Boolean;
  38. end;
  39. {***************************************************************************}
  40. Const
  41. LOWLEVELNAME : PChar = 'lowlevel.library';
  42. { bits in the return value of GetKey() }
  43. LLKB_LSHIFT = 16;
  44. LLKB_RSHIFT = 17;
  45. LLKB_CAPSLOCK = 18;
  46. LLKB_CONTROL = 19;
  47. LLKB_LALT = 20;
  48. LLKB_RALT = 21;
  49. LLKB_LAMIGA = 22;
  50. LLKB_RAMIGA = 23;
  51. LLKF_LSHIFT = 65536;
  52. LLKF_RSHIFT = 131072;
  53. LLKF_CAPSLOCK = 262144;
  54. LLKF_CONTROL = 524288;
  55. LLKF_LALT = 1048576;
  56. LLKF_RALT = 2097152;
  57. LLKF_LAMIGA = 4194304;
  58. LLKF_RAMIGA = 8388608;
  59. {***************************************************************************}
  60. { Tags for SetJoyPortAttrs() }
  61. SJA_Dummy = (TAG_USER+$c00100);
  62. SJA_Type = (SJA_Dummy+1); { force type to mouse, joy, game cntrlr }
  63. SJA_Reinitialize = (SJA_Dummy+2); { free potgo bits, reset to autosense }
  64. { Controller types for SJA_Type tag }
  65. SJA_TYPE_AUTOSENSE = 0;
  66. SJA_TYPE_GAMECTLR = 1;
  67. SJA_TYPE_MOUSE = 2;
  68. SJA_TYPE_JOYSTK = 3;
  69. {***************************************************************************}
  70. { ReadJoyPort() return value definitions }
  71. { Port types }
  72. JP_TYPE_NOTAVAIL = 0; { port data unavailable }
  73. JP_TYPE_GAMECTLR = 268435456; { port has game controller }
  74. JP_TYPE_MOUSE = 536870912; { port has mouse }
  75. JP_TYPE_JOYSTK = 805306368; { port has joystick }
  76. JP_TYPE_UNKNOWN = 1073741824; { port has unknown device }
  77. JP_TYPE_MASK = -268435456; { controller type }
  78. { Button types, valid for all types except JP_TYPE_NOTAVAIL }
  79. JPB_BUTTON_BLUE = 23; { Blue - Stop; Right Mouse }
  80. JPB_BUTTON_RED = 22; { Red - Select; Left Mouse; Joystick Fire }
  81. JPB_BUTTON_YELLOW = 21; { Yellow - Repeat }
  82. JPB_BUTTON_GREEN = 20; { Green - Shuffle }
  83. JPB_BUTTON_FORWARD = 19; { Charcoal - Forward }
  84. JPB_BUTTON_REVERSE = 18; { Charcoal - Reverse }
  85. JPB_BUTTON_PLAY = 17; { Grey - Play/Pause; Middle Mouse }
  86. JPF_BUTTON_BLUE = 8388608;
  87. JPF_BUTTON_RED = 4194304;
  88. JPF_BUTTON_YELLOW = 2097152;
  89. JPF_BUTTON_GREEN = 1048576;
  90. JPF_BUTTON_FORWARD = 524288;
  91. JPF_BUTTON_REVERSE = 262144;
  92. JPF_BUTTON_PLAY = 131072;
  93. JP_BUTTON_MASK = JPF_BUTTON_BLUE OR JPF_BUTTON_RED OR JPF_BUTTON_YELLOW OR JPF_BUTTON_GREEN OR JPF_BUTTON_FORWARD OR JPF_BUTTON_REVERSE OR JPF_BUTTON_PLAY;
  94. { Direction types, valid for JP_TYPE_GAMECTLR and JP_TYPE_JOYSTK }
  95. JPB_JOY_UP = 3;
  96. JPB_JOY_DOWN = 2;
  97. JPB_JOY_LEFT = 1;
  98. JPB_JOY_RIGHT = 0;
  99. JPF_JOY_UP = 8;
  100. JPF_JOY_DOWN = 4;
  101. JPF_JOY_LEFT = 2;
  102. JPF_JOY_RIGHT = 1;
  103. JP_DIRECTION_MASK = JPF_JOY_UP OR JPF_JOY_DOWN OR JPF_JOY_LEFT OR JPF_JOY_RIGHT;
  104. { Mouse position reports, valid for JP_TYPE_MOUSE }
  105. JP_MHORZ_MASK = 255; { horzizontal position }
  106. JP_MVERT_MASK = 65280; { vertical position }
  107. JP_MOUSE_MASK = JP_MHORZ_MASK OR JP_MVERT_MASK;
  108. { Obsolete ReadJoyPort() definitions, here for source code compatibility only.
  109. * Please do NOT use in new code.
  110. }
  111. JPB_BTN1 = JPB_BUTTON_BLUE ;
  112. JPF_BTN1 = JPF_BUTTON_BLUE ;
  113. JPB_BTN2 = JPB_BUTTON_RED ;
  114. JPF_BTN2 = JPF_BUTTON_RED ;
  115. JPB_BTN3 = JPB_BUTTON_YELLOW ;
  116. JPF_BTN3 = JPF_BUTTON_YELLOW ;
  117. JPB_BTN4 = JPB_BUTTON_GREEN ;
  118. JPF_BTN4 = JPF_BUTTON_GREEN ;
  119. JPB_BTN5 = JPB_BUTTON_FORWARD;
  120. JPF_BTN5 = JPF_BUTTON_FORWARD;
  121. JPB_BTN6 = JPB_BUTTON_REVERSE;
  122. JPF_BTN6 = JPF_BUTTON_REVERSE;
  123. JPB_BTN7 = JPB_BUTTON_PLAY ;
  124. JPF_BTN7 = JPF_BUTTON_PLAY ;
  125. JPB_UP = JPB_JOY_UP ;
  126. JPF_UP = JPF_JOY_UP ;
  127. JPB_DOWN = JPB_JOY_DOWN ;
  128. JPF_DOWN = JPF_JOY_DOWN ;
  129. JPB_LEFT = JPB_JOY_LEFT ;
  130. JPF_LEFT = JPF_JOY_LEFT ;
  131. JPB_RIGHT = JPB_JOY_RIGHT ;
  132. JPF_RIGHT = JPF_JOY_RIGHT ;
  133. {***************************************************************************}
  134. { Tags for SystemControl() }
  135. SCON_Dummy = (TAG_USER+$00C00000);
  136. SCON_TakeOverSys = (SCON_Dummy+0);
  137. SCON_KillReq = (SCON_Dummy+1);
  138. SCON_CDReboot = (SCON_Dummy+2);
  139. SCON_StopInput = (SCON_Dummy+3);
  140. SCON_AddCreateKeys = (SCON_Dummy+4);
  141. SCON_RemCreateKeys = (SCON_Dummy+5);
  142. { Reboot control values for use with SCON_CDReboot tag }
  143. CDReboot_On = 1;
  144. CDReboot_Off = 0;
  145. CDReboot_Default = 2;
  146. {***************************************************************************}
  147. { Rawkey codes returned when using SCON_AddCreateKeys with SystemControl() }
  148. RAWKEY_PORT0_BUTTON_BLUE = $72;
  149. RAWKEY_PORT0_BUTTON_RED = $78;
  150. RAWKEY_PORT0_BUTTON_YELLOW = $77;
  151. RAWKEY_PORT0_BUTTON_GREEN = $76;
  152. RAWKEY_PORT0_BUTTON_FORWARD = $75;
  153. RAWKEY_PORT0_BUTTON_REVERSE = $74;
  154. RAWKEY_PORT0_BUTTON_PLAY = $73;
  155. RAWKEY_PORT0_JOY_UP = $79;
  156. RAWKEY_PORT0_JOY_DOWN = $7A;
  157. RAWKEY_PORT0_JOY_LEFT = $7C;
  158. RAWKEY_PORT0_JOY_RIGHT = $7B;
  159. RAWKEY_PORT1_BUTTON_BLUE = $172;
  160. RAWKEY_PORT1_BUTTON_RED = $178;
  161. RAWKEY_PORT1_BUTTON_YELLOW = $177;
  162. RAWKEY_PORT1_BUTTON_GREEN = $176;
  163. RAWKEY_PORT1_BUTTON_FORWARD = $175;
  164. RAWKEY_PORT1_BUTTON_REVERSE = $174;
  165. RAWKEY_PORT1_BUTTON_PLAY = $173;
  166. RAWKEY_PORT1_JOY_UP = $179;
  167. RAWKEY_PORT1_JOY_DOWN = $17A;
  168. RAWKEY_PORT1_JOY_LEFT = $17C;
  169. RAWKEY_PORT1_JOY_RIGHT = $17B;
  170. RAWKEY_PORT2_BUTTON_BLUE = $272;
  171. RAWKEY_PORT2_BUTTON_RED = $278;
  172. RAWKEY_PORT2_BUTTON_YELLOW = $277;
  173. RAWKEY_PORT2_BUTTON_GREEN = $276;
  174. RAWKEY_PORT2_BUTTON_FORWARD = $275;
  175. RAWKEY_PORT2_BUTTON_REVERSE = $274;
  176. RAWKEY_PORT2_BUTTON_PLAY = $273;
  177. RAWKEY_PORT2_JOY_UP = $279;
  178. RAWKEY_PORT2_JOY_DOWN = $27A;
  179. RAWKEY_PORT2_JOY_LEFT = $27C;
  180. RAWKEY_PORT2_JOY_RIGHT = $27B;
  181. RAWKEY_PORT3_BUTTON_BLUE = $372;
  182. RAWKEY_PORT3_BUTTON_RED = $378;
  183. RAWKEY_PORT3_BUTTON_YELLOW = $377;
  184. RAWKEY_PORT3_BUTTON_GREEN = $376;
  185. RAWKEY_PORT3_BUTTON_FORWARD = $375;
  186. RAWKEY_PORT3_BUTTON_REVERSE = $374;
  187. RAWKEY_PORT3_BUTTON_PLAY = $373;
  188. RAWKEY_PORT3_JOY_UP = $379;
  189. RAWKEY_PORT3_JOY_DOWN = $37A;
  190. RAWKEY_PORT3_JOY_LEFT = $37C;
  191. RAWKEY_PORT3_JOY_RIGHT = $37B;
  192. {***************************************************************************}
  193. { Return values for GetLanguageSelection() }
  194. LANG_UNKNOWN = 0 ;
  195. LANG_AMERICAN = 1 ; { American English }
  196. LANG_ENGLISH = 2 ; { British English }
  197. LANG_GERMAN = 3 ;
  198. LANG_FRENCH = 4 ;
  199. LANG_SPANISH = 5 ;
  200. LANG_ITALIAN = 6 ;
  201. LANG_PORTUGUESE = 7 ;
  202. LANG_DANISH = 8 ;
  203. LANG_DUTCH = 9 ;
  204. LANG_NORWEGIAN = 10;
  205. LANG_FINNISH = 11;
  206. LANG_SWEDISH = 12;
  207. LANG_JAPANESE = 13;
  208. LANG_CHINESE = 14;
  209. LANG_ARABIC = 15;
  210. LANG_GREEK = 16;
  211. LANG_HEBREW = 17;
  212. LANG_KOREAN = 18;
  213. {***************************************************************************}
  214. { --- functions in V40 or higher (Release 3.1) --- }
  215. VAR LowLevelBase : pLibrary;
  216. FUNCTION AddKBInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  217. FUNCTION AddTimerInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  218. FUNCTION AddVBlankInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  219. FUNCTION ElapsedTime(context : pEClockVal) : ULONG;
  220. FUNCTION GetKey : ULONG;
  221. FUNCTION GetLanguageSelection : BYTE;
  222. PROCEDURE QueryKeys(queryArray : pKeyQuery; arraySize : ULONG);
  223. FUNCTION ReadJoyPort(port : ULONG) : ULONG;
  224. PROCEDURE RemKBInt(intHandle : POINTER);
  225. PROCEDURE RemTimerInt(intHandle : POINTER);
  226. PROCEDURE RemVBlankInt(intHandle : POINTER);
  227. FUNCTION SetJoyPortAttrsA(portNumber : ULONG;const tagList : pTagItem) : BOOLEAN;
  228. PROCEDURE StartTimerInt(intHandle : POINTER; timeInterval : ULONG; continuous : LONGINT);
  229. PROCEDURE StopTimerInt(intHandle : POINTER);
  230. FUNCTION SystemControlA(const tagList : pTagItem) : ULONG;
  231. {Here we read how to compile this unit}
  232. {You can remove this include and use a define instead}
  233. {$I useautoopenlib.inc}
  234. {$ifdef use_init_openlib}
  235. procedure InitLOWLEVELLibrary;
  236. {$endif use_init_openlib}
  237. {This is a variable that knows how the unit is compiled}
  238. var
  239. LOWLEVELIsCompiledHow : longint;
  240. IMPLEMENTATION
  241. {$ifndef dont_use_openlib}
  242. uses msgbox;
  243. {$endif dont_use_openlib}
  244. FUNCTION AddKBInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  245. BEGIN
  246. ASM
  247. MOVE.L A6,-(A7)
  248. MOVEA.L intRoutine,A0
  249. MOVEA.L intData,A1
  250. MOVEA.L LowLevelBase,A6
  251. JSR -060(A6)
  252. MOVEA.L (A7)+,A6
  253. MOVE.L D0,@RESULT
  254. END;
  255. END;
  256. FUNCTION AddTimerInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  257. BEGIN
  258. ASM
  259. MOVE.L A6,-(A7)
  260. MOVEA.L intRoutine,A0
  261. MOVEA.L intData,A1
  262. MOVEA.L LowLevelBase,A6
  263. JSR -078(A6)
  264. MOVEA.L (A7)+,A6
  265. MOVE.L D0,@RESULT
  266. END;
  267. END;
  268. FUNCTION AddVBlankInt(const intRoutine : POINTER;const intData : POINTER) : POINTER;
  269. BEGIN
  270. ASM
  271. MOVE.L A6,-(A7)
  272. MOVEA.L intRoutine,A0
  273. MOVEA.L intData,A1
  274. MOVEA.L LowLevelBase,A6
  275. JSR -108(A6)
  276. MOVEA.L (A7)+,A6
  277. MOVE.L D0,@RESULT
  278. END;
  279. END;
  280. FUNCTION ElapsedTime(context : pEClockVal) : ULONG;
  281. BEGIN
  282. ASM
  283. MOVE.L A6,-(A7)
  284. MOVEA.L context,A0
  285. MOVEA.L LowLevelBase,A6
  286. JSR -102(A6)
  287. MOVEA.L (A7)+,A6
  288. MOVE.L D0,@RESULT
  289. END;
  290. END;
  291. FUNCTION GetKey : ULONG;
  292. BEGIN
  293. ASM
  294. MOVE.L A6,-(A7)
  295. MOVEA.L LowLevelBase,A6
  296. JSR -048(A6)
  297. MOVEA.L (A7)+,A6
  298. MOVE.L D0,@RESULT
  299. END;
  300. END;
  301. FUNCTION GetLanguageSelection : BYTE;
  302. BEGIN
  303. ASM
  304. MOVE.L A6,-(A7)
  305. MOVEA.L LowLevelBase,A6
  306. JSR -036(A6)
  307. MOVEA.L (A7)+,A6
  308. MOVE.L D0,@RESULT
  309. END;
  310. END;
  311. PROCEDURE QueryKeys(queryArray : pKeyQuery; arraySize : ULONG);
  312. BEGIN
  313. ASM
  314. MOVE.L A6,-(A7)
  315. MOVEA.L queryArray,A0
  316. MOVE.L arraySize,D1
  317. MOVEA.L LowLevelBase,A6
  318. JSR -054(A6)
  319. MOVEA.L (A7)+,A6
  320. END;
  321. END;
  322. FUNCTION ReadJoyPort(port : ULONG) : ULONG;
  323. BEGIN
  324. ASM
  325. MOVE.L A6,-(A7)
  326. MOVE.L port,D0
  327. MOVEA.L LowLevelBase,A6
  328. JSR -030(A6)
  329. MOVEA.L (A7)+,A6
  330. MOVE.L D0,@RESULT
  331. END;
  332. END;
  333. PROCEDURE RemKBInt(intHandle : POINTER);
  334. BEGIN
  335. ASM
  336. MOVE.L A6,-(A7)
  337. MOVEA.L intHandle,A1
  338. MOVEA.L LowLevelBase,A6
  339. JSR -066(A6)
  340. MOVEA.L (A7)+,A6
  341. END;
  342. END;
  343. PROCEDURE RemTimerInt(intHandle : POINTER);
  344. BEGIN
  345. ASM
  346. MOVE.L A6,-(A7)
  347. MOVEA.L intHandle,A1
  348. MOVEA.L LowLevelBase,A6
  349. JSR -084(A6)
  350. MOVEA.L (A7)+,A6
  351. END;
  352. END;
  353. PROCEDURE RemVBlankInt(intHandle : POINTER);
  354. BEGIN
  355. ASM
  356. MOVE.L A6,-(A7)
  357. MOVEA.L intHandle,A1
  358. MOVEA.L LowLevelBase,A6
  359. JSR -114(A6)
  360. MOVEA.L (A7)+,A6
  361. END;
  362. END;
  363. FUNCTION SetJoyPortAttrsA(portNumber : ULONG;const tagList : pTagItem) : BOOLEAN;
  364. BEGIN
  365. ASM
  366. MOVE.L A6,-(A7)
  367. MOVE.L portNumber,D0
  368. MOVEA.L tagList,A1
  369. MOVEA.L LowLevelBase,A6
  370. JSR -132(A6)
  371. MOVEA.L (A7)+,A6
  372. TST.W D0
  373. BEQ.B @end
  374. MOVEQ #1,D0
  375. @end: MOVE.B D0,@RESULT
  376. END;
  377. END;
  378. PROCEDURE StartTimerInt(intHandle : POINTER; timeInterval : ULONG; continuous : LONGINT);
  379. BEGIN
  380. ASM
  381. MOVE.L A6,-(A7)
  382. MOVEA.L intHandle,A1
  383. MOVE.L timeInterval,D0
  384. MOVE.L continuous,D1
  385. MOVEA.L LowLevelBase,A6
  386. JSR -096(A6)
  387. MOVEA.L (A7)+,A6
  388. END;
  389. END;
  390. PROCEDURE StopTimerInt(intHandle : POINTER);
  391. BEGIN
  392. ASM
  393. MOVE.L A6,-(A7)
  394. MOVEA.L intHandle,A1
  395. MOVEA.L LowLevelBase,A6
  396. JSR -090(A6)
  397. MOVEA.L (A7)+,A6
  398. END;
  399. END;
  400. FUNCTION SystemControlA(const tagList : pTagItem) : ULONG;
  401. BEGIN
  402. ASM
  403. MOVE.L A6,-(A7)
  404. MOVEA.L tagList,A1
  405. MOVEA.L LowLevelBase,A6
  406. JSR -072(A6)
  407. MOVEA.L (A7)+,A6
  408. MOVE.L D0,@RESULT
  409. END;
  410. END;
  411. const
  412. { Change VERSION and LIBVERSION to proper values }
  413. VERSION : string[2] = '0';
  414. LIBVERSION : longword = 0;
  415. {$ifdef use_init_openlib}
  416. {$Info Compiling initopening of lowlevel.library}
  417. {$Info don't forget to use InitLOWLEVELLibrary in the beginning of your program}
  418. var
  419. lowlevel_exit : Pointer;
  420. procedure CloselowlevelLibrary;
  421. begin
  422. ExitProc := lowlevel_exit;
  423. if LowLevelBase <> nil then begin
  424. CloseLibrary(LowLevelBase);
  425. LowLevelBase := nil;
  426. end;
  427. end;
  428. procedure InitLOWLEVELLibrary;
  429. begin
  430. LowLevelBase := nil;
  431. LowLevelBase := OpenLibrary(LOWLEVELNAME,LIBVERSION);
  432. if LowLevelBase <> nil then begin
  433. lowlevel_exit := ExitProc;
  434. ExitProc := @CloselowlevelLibrary;
  435. end else begin
  436. MessageBox('FPC Pascal Error',
  437. 'Can''t open lowlevel.library version ' + VERSION + #10 +
  438. 'Deallocating resources and closing down',
  439. 'Oops');
  440. halt(20);
  441. end;
  442. end;
  443. begin
  444. LOWLEVELIsCompiledHow := 2;
  445. {$endif use_init_openlib}
  446. {$ifdef use_auto_openlib}
  447. {$Info Compiling autoopening of lowlevel.library}
  448. var
  449. lowlevel_exit : Pointer;
  450. procedure CloselowlevelLibrary;
  451. begin
  452. ExitProc := lowlevel_exit;
  453. if LowLevelBase <> nil then begin
  454. CloseLibrary(LowLevelBase);
  455. LowLevelBase := nil;
  456. end;
  457. end;
  458. begin
  459. LowLevelBase := nil;
  460. LowLevelBase := OpenLibrary(LOWLEVELNAME,LIBVERSION);
  461. if LowLevelBase <> nil then begin
  462. lowlevel_exit := ExitProc;
  463. ExitProc := @CloselowlevelLibrary;
  464. LOWLEVELIsCompiledHow := 1;
  465. end else begin
  466. MessageBox('FPC Pascal Error',
  467. 'Can''t open lowlevel.library version ' + VERSION + #10 +
  468. 'Deallocating resources and closing down',
  469. 'Oops');
  470. halt(20);
  471. end;
  472. {$endif use_auto_openlib}
  473. {$ifdef dont_use_openlib}
  474. begin
  475. LOWLEVELIsCompiledHow := 3;
  476. {$Warning No autoopening of lowlevel.library compiled}
  477. {$Warning Make sure you open lowlevel.library yourself}
  478. {$endif dont_use_openlib}
  479. END. (* UNIT LOWLEVEL *)