1 \ Copyright (c) 2003 Scott Long <scottl@freebsd.org>
2 \ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
3 \ Copyright (c) 2006-2011 Devin Teske <devinteske@hotmail.com>
6 \ Redistribution and use in source and binary forms, with or without
7 \ modification, are permitted provided that the following conditions
9 \ 1. Redistributions of source code must retain the above copyright
10 \ notice, this list of conditions and the following disclaimer.
11 \ 2. Redistributions in binary form must reproduce the above copyright
12 \ notice, this list of conditions and the following disclaimer in the
13 \ documentation and/or other materials provided with the distribution.
15 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 \ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 \ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 \ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 \ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 \ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 \ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 include /boot/frames.4th
34 f_double \ Set frames to double (see frames.4th). Replace with
35 \ f_single if you want single frames.
36 46 constant dot \ ASCII definition of a period (in decimal)
38 4 constant menu_timeout_default_x \ default column position of timeout
39 23 constant menu_timeout_default_y \ default row position of timeout msg
40 10 constant menu_timeout_default \ default timeout (in seconds)
42 \ Customize the following values with care
44 1 constant menu_start \ Numerical prefix of first menu item
45 dot constant bullet \ Menu bullet (appears after numerical prefix)
46 5 constant menu_x \ Row position of the menu (from the top)
47 10 constant menu_y \ Column position of the menu (from left side)
50 variable menuidx \ Menu item stack for number prefixes
51 variable menurow \ Menu item stack for positioning
52 variable menubllt \ Menu item bullet
55 variable menuX \ Menu X offset (columns)
56 variable menuY \ Menu Y offset (rows)
58 \ Menu-item key association/detection
68 variable menurebootadded
72 \ Menu timer [count-down] variables
73 variable menu_timeout_enabled \ timeout state (internal use only)
74 variable menu_time \ variable for tracking the passage of time
75 variable menu_timeout \ determined configurable delay duration
76 variable menu_timeout_x \ column position of timeout message
77 variable menu_timeout_y \ row position of timeout message
79 \ Boolean option status variables
80 variable toggle_state1
81 variable toggle_state2
82 variable toggle_state3
83 variable toggle_state4
84 variable toggle_state5
85 variable toggle_state6
86 variable toggle_state7
87 variable toggle_state8
89 \ Array option status variables
99 \ Containers for storing the initial caption text
100 create init_text1 255 allot
101 create init_text2 255 allot
102 create init_text3 255 allot
103 create init_text4 255 allot
104 create init_text5 255 allot
105 create init_text6 255 allot
106 create init_text7 255 allot
107 create init_text8 255 allot
109 : arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
110 s" arch-i386" environment? dup if
115 \ This function prints a menu item at menuX (row) and menuY (column), returns
116 \ the incremental decimal ASCII value associated with the menu item, and
117 \ increments the cursor position to the next row for the creation of the next
118 \ menu item. This function is called by the menu-create function. You need not
121 : printmenuitem ( menu_item_str -- ascii_keycode )
123 menurow dup @ 1+ swap ! ( increment menurow )
124 menuidx dup @ 1+ swap ! ( increment menuidx )
126 \ Calculate the menuitem row position
129 \ Position the cursor at the menuitem position
130 dup menuX @ swap at-xy
132 \ Print the value of menuidx
141 \ Move the cursor forward 1 column
142 dup menuX @ 1+ swap at-xy
144 menubllt @ emit \ Print the menu bullet using the emit function
146 \ Move the cursor to the 3rd column from the current position
147 \ to allow for a space between the numerical prefix and the
149 menuX @ 3 + swap at-xy
151 \ Print the menu caption (we expect a string to be on the stack
152 \ prior to invoking this function)
155 \ Here we will add the ASCII decimal of the numerical prefix
156 \ to the stack (decimal ASCII for `1' is 49) as a "return value"
160 : toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
162 \ ASCII numeral equal to user-selected menu item must be on the stack.
163 \ We do not modify the stack, so the ASCII numeral is left on top.
165 s" init_textN" \ base name of buffer
166 -rot 2dup 9 + c! rot \ replace 'N' with ASCII num
169 \ NOTE: no need to check toggle_stateN since the first time we
170 \ are called, we will populate init_textN. Further, we don't
171 \ need to test whether menu_caption[x] (ansi_caption[x] when
172 \ loader_color=1) is available since we would not have been
173 \ called if the caption was NULL.
175 \ base name of environment variable
181 -rot 2dup 13 + c! rot \ replace 'x' with ASCII numeral
185 s" init_textN" \ base name of buffer
186 4 pick \ copy ASCII num to top
187 rot tuck 9 + c! swap \ replace 'N' with ASCII num
190 \ now we have the buffer c-addr on top
191 \ ( followed by c-addr/u of current caption )
193 \ Copy the current caption into our buffer
194 2dup c! -rot \ store strlen at first byte
196 rot 1+ \ bring alt addr to top and increment
197 -rot -rot \ bring buffer addr to top
198 2dup c@ swap c! \ copy current character
199 1+ \ increment buffer addr
200 rot 1- \ bring buffer len to top and decrement
201 dup 0= \ exit loop if buffer len is zero
203 2drop \ buffer len/addr
211 \ Now we are certain to have init_textN populated with the initial
212 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
213 \ We can now use init_textN as the untoggled caption and
214 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
215 \ toggled caption and store the appropriate value into menu_caption[x]
216 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll
217 \ negate the toggled state so that we reverse the flow on subsequent
220 s" toggle_stateN @" \ base name of toggle state var
221 -rot 2dup 12 + c! rot \ replace 'N' with ASCII numeral
224 \ state is OFF, toggle to ON
226 \ base name of toggled text var
232 -rot 2dup 13 + c! rot \ replace 'x' with ASCII num
235 \ Assign toggled text to menu caption
237 \ base name of caption var
243 4 pick \ copy ASCII num to top
244 rot tuck 13 + c! swap \ replace 'x' with ASCII num
246 setenv \ set new caption
248 \ No toggled text, keep the same caption
253 true \ new value of toggle state var (to be stored later)
255 \ state is ON, toggle to OFF
257 s" init_textN" \ base name of initial text buffer
258 -rot 2dup 9 + c! rot \ replace 'N' with ASCII numeral
259 evaluate \ convert string to c-addr
260 count \ convert c-addr to c-addr/u
262 \ base name of caption var
268 4 pick \ copy ASCII num to top
269 rot tuck 13 + c! swap \ replace 'x' with ASCII numeral
271 setenv \ set new caption
272 false \ new value of toggle state var (to be stored below)
275 \ now we'll store the new toggle state (on top of stack)
276 s" toggle_stateN" \ base name of toggle state var
277 3 pick \ copy ASCII numeral to top
278 rot tuck 12 + c! swap \ replace 'N' with ASCII numeral
279 evaluate \ convert string to addr
283 : cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
285 \ ASCII numeral equal to user-selected menu item must be on the stack.
286 \ We do not modify the stack, so the ASCII numeral is left on top.
288 s" cycle_stateN" \ base name of array state var
289 -rot 2dup 11 + c! rot \ replace 'N' with ASCII numeral
291 evaluate \ we now have a pointer to the proper variable
292 dup @ \ resolve the pointer (but leave it on the stack)
293 1+ \ increment the value
295 \ Before assigning the (incremented) value back to the pointer,
296 \ let's test for the existence of this particular array element.
297 \ If the element exists, we'll store index value and move on.
298 \ Otherwise, we'll loop around to zero and store that.
300 dup 48 + \ duplicate Array index and convert to ASCII numeral
302 \ base name of array caption text
304 s" ansi_caption[x][y]"
306 s" menu_caption[x][y]"
308 -rot tuck 16 + c! swap \ replace 'y' with Array index
309 4 pick rot tuck 13 + c! swap \ replace 'x' with menu choice
311 \ Now test for the existence of our incremented array index in the
312 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
313 \ enabled) as set in loader.rc(5), et. al.
316 \ No caption set for this array index. Loop back to zero.
318 drop ( getenv cruft )
319 drop ( incremented array index )
320 0 ( new array index that will be stored later )
322 \ base name of caption var
324 s" ansi_caption[x][0]"
326 s" menu_caption[x][0]"
328 4 pick rot tuck 13 + c! swap \ replace 'x' with menu choice
331 \ This is highly unlikely to occur, but to make
332 \ sure that things move along smoothly, allocate
333 \ a temporary NULL string
339 \ At this point, we should have the following on the stack (in order,
340 \ from bottom to top):
342 \ N - Ascii numeral representing the menu choice (inherited)
343 \ Addr - address of our internal cycle_stateN variable
344 \ N - zero-based number we intend to store to the above
345 \ C-Addr - string value we intend to store to menu_caption[x]
346 \ (or ansi_caption[x] with loader_color enabled)
348 \ Let's perform what we need to with the above.
350 \ base name of menuitem caption var
356 6 pick rot tuck 13 + c! swap \ replace 'x' with menu choice
357 setenv \ set the new caption
359 swap ! \ update array state variable
362 : acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
363 s" hint.acpi.0.rsdp" getenv
371 : acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
372 s" hint.acpi.0.disabled" getenv
383 \ This function prints the appropriate menuitem basename to the stack if an
384 \ ACPI option is to be presented to the user, otherwise returns -1. Used
385 \ internally by menu-create, you need not (nor should you) call this directly.
387 : acpimenuitem ( -- C-Addr | -1 )
405 menuidx dup @ 1+ swap ! ( increment menuidx )
413 \ This function creates the list of menu items. This function is called by the
414 \ menu-display function. You need not be call it directly.
418 \ Print the frame caption at (x,y)
419 s" loader_menu_title" getenv dup -1 = if
420 drop s" Welcome to FreeBSD"
422 24 over 2 / - 9 at-xy type
424 \ Print our menu options with respective key/variable associations.
425 \ `printmenuitem' ends by adding the decimal ASCII value for the
426 \ numerical prefix to the stack. We store the value left on the stack
427 \ to the key binding variable for later testing against a character
428 \ captured by the `getkey' function.
430 \ Note that any menu item beyond 9 will have a numerical prefix on the
431 \ screen consisting of the first digit (ie. 1 for the tenth menu item)
432 \ and the key required to activate that menu item will be the decimal
433 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
434 \ which is misleading and not desirable.
436 \ Thus, we do not allow more than 8 configurable items on the menu
437 \ (with "Reboot" as the optional ninth and highest numbered item).
440 \ Initialize the ACPI option status.
443 s" menu_acpi" getenv -1 <> if
444 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
446 arch-i386? if acpipresent? if
448 \ Set menu toggle state to active state
449 \ (required by generic toggle_menuitem)
452 s" acpienabled? toggle_stateN !"
453 -rot tuck 25 + c! swap
462 \ Initialize the menu_options visual separator.
465 s" menu_options" getenv -1 <> if
466 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
473 \ Initialize "Reboot" menu state variable (prevents double-entry)
474 false menurebootadded !
476 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
478 \ If the "Options:" separator, print it.
479 dup menuoptions @ = if
480 \ Optionally add a reboot option to the menu
481 s" menu_reboot" getenv -1 <> if
483 s" Reboot" printmenuitem menureboot !
484 true menurebootadded !
488 menurow @ 2 + menurow !
494 \ If this is the ACPI menu option, act accordingly.
496 acpimenuitem ( -- C-Addr | -1 )
507 \ replace 'x' with current iteration
508 -rot 2dup 13 + c! rot
510 \ test for environment variable
512 printmenuitem ( C-Addr -- N )
514 s" menukeyN !" \ generate cmd to store result
525 -rot 2dup 13 + c! rot ( replace 'x' )
529 1+ dup 56 > \ add 1 to iterator, continue if less than 57
533 \ Optionally add a reboot option to the menu
534 menurebootadded @ true <> if
535 s" menu_reboot" getenv -1 <> if
536 drop \ no need for the value
537 s" Reboot" \ menu caption (required by printmenuitem)
547 \ Takes a single integer on the stack and updates the timeout display. The
548 \ integer must be between 0 and 9 (we will only update a single digit in the
551 : menu-timeout-update ( N -- )
553 dup 9 > if ( N N 9 -- N )
558 dup 0 < if ( N N 0 -- N )
563 48 + ( convert single-digit numeral to ASCII: N 48 -- N )
565 s" Autoboot in N seconds. [Space] to pause" ( N -- N Addr C )
567 2 pick 48 - 0> if ( N Addr C N 48 -- N Addr C )
569 \ Modify 'N' (Addr+12) above to reflect time-left
571 -rot ( N Addr C -- C N Addr )
572 tuck ( C N Addr -- C Addr N Addr )
573 12 + ( C Addr N Addr -- C Addr N Addr2 )
574 c! ( C Addr N Addr2 -- C Addr )
575 swap ( C Addr -- Addr C )
579 at-xy ( position cursor: Addr C N N -- Addr C )
581 type ( print message: Addr C -- )
583 else ( N Addr C N -- N Addr C )
587 at-xy ( position cursor: N Addr C N N -- N Addr C )
589 spaces ( erase message: N Addr C -- N Addr )
594 0 25 at-xy ( position cursor back at bottom-left )
597 \ This function blocks program flow (loops forever) until a key is pressed.
598 \ The key that was pressed is added to the top of the stack in the form of its
599 \ decimal ASCII representation. This function is called by the menu-display
600 \ function. You need not call it directly.
602 : getkey ( -- ascii_keycode )
606 menu_timeout_enabled @ 1 = if
608 seconds ( get current time: -- N )
609 dup menu_time @ <> if ( has time elapsed?: N N N -- N )
611 \ At least 1 second has elapsed since last loop
612 \ so we will decrement our "timeout" (really a
613 \ counter, insuring that we do not proceed too
614 \ fast) and update our timeout display.
616 menu_time ! ( update time record: N -- )
617 menu_timeout @ ( "time" remaining: -- N )
618 dup 0> if ( greater than 0?: N N 0 -- N )
619 1- ( decrement counter: N -- N )
621 ( re-assign: N N Addr -- N )
625 dup 0= swap 0< or if ( N <= 0?: N N -- )
627 0 menu_timeout ! ( 0 Addr -- )
628 0 menu_timeout_enabled ! ( 0 Addr -- )
631 \ update the timer display ( N -- )
632 menu_timeout @ menu-timeout-update
635 \ We've reached the end of the timeout
636 \ (user did not cancel by pressing ANY
639 s" menu_timeout_command" getenv dup
648 \ No [detectable] time has elapsed (in seconds)
654 key? if \ Was a key pressed? (see loader(8))
656 \ An actual key was pressed (if the timeout is running,
657 \ kill it regardless of which key was pressed)
658 menu_timeout @ 0<> if
660 0 menu_timeout_enabled !
662 \ clear screen of timeout message
663 0 menu-timeout-update
666 \ get the key that was pressed and exit (if we
667 \ get a non-zero ASCII code)
674 50 ms \ sleep for 50 milliseconds (see loader(8))
679 : menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1.
681 \ Clear the screen area associated with the interactive menu
683 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
684 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
685 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
686 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
687 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
688 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces
691 \ Reset the starting index and position for the menu
692 menu_start 1- menuidx !
696 \ Erase and redraw the menu. Useful if you change a caption and want to
697 \ update the menu to reflect the new value.
704 \ This function initializes the menu. Call this from your `loader.rc' file
705 \ before calling any other menu-related functions.
709 1- menuidx ! \ Initialize the starting index for the menu
710 0 menurow ! \ Initialize the starting position for the menu
711 42 13 2 9 box \ Draw frame (w,h,x,y)
712 0 25 at-xy \ Move cursor to the bottom for output
715 \ Main function. Call this from your `loader.rc' file.
717 : menu-display ( -- )
719 0 menu_timeout_enabled ! \ start with automatic timeout disabled
721 \ check indication that automatic execution after delay is requested
722 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
723 drop ( just testing existence right now: Addr -- )
725 \ initialize state variables
726 seconds menu_time ! ( store the time we started )
727 1 menu_timeout_enabled ! ( enable automatic timeout )
729 \ read custom time-duration (if set)
730 s" autoboot_delay" getenv dup -1 = if
731 drop \ no custom duration (remove dup'd bunk -1)
732 menu_timeout_default \ use default setting
734 2dup ?number 0= if ( if not a number )
735 \ disable timeout if "NO", else use default
736 s" NO" compare-insensitive 0= if
737 0 menu_timeout_enabled !
738 0 ( assigned to menu_timeout below )
745 \ boot immediately if less than zero
754 menu_timeout ! ( store value on stack from above )
756 menu_timeout_enabled @ 1 = if
757 \ read custom column position (if set)
758 s" loader_menu_timeout_x" getenv dup -1 = if
759 drop \ no custom column position
760 menu_timeout_default_x \ use default setting
762 \ make sure custom position is a number
764 menu_timeout_default_x \ or use default
767 menu_timeout_x ! ( store value on stack from above )
769 \ read custom row position (if set)
770 s" loader_menu_timeout_y" getenv dup -1 = if
771 drop \ no custom row position
772 menu_timeout_default_y \ use default setting
774 \ make sure custom position is a number
776 menu_timeout_default_y \ or use default
779 menu_timeout_y ! ( store value on stack from above )
787 0 25 at-xy \ Move cursor to the bottom for output
788 getkey \ Block here, waiting for a key to be pressed
791 drop exit \ Caught abort (abnormal return)
794 \ Boot if the user pressed Enter/Ctrl-M (13) or
795 \ Ctrl-Enter/Ctrl-J (10)
796 dup over 13 = swap 10 = or if
797 drop ( no longer needed )
799 exit ( pedantic; never reached )
802 \ Evaluate the decimal ASCII value against known menu item
803 \ key associations and act accordingly
805 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
809 \ replace 'N' with current iteration
812 evaluate rot tuck = if
814 \ Adjust for missing ACPI menuitem on non-i386
815 arch-i386? true <> menuacpi @ 0<> and if
816 menuacpi @ over 2dup < -rot = or
818 ( key >= menuacpi && key < 58: N -- N )
823 \ base env name for the value (x is a number)
826 \ Copy ASCII number to string at offset 13
827 -rot 2dup 13 + c! rot
829 \ Test for the environment variable
831 \ Execute the stored procedure
834 \ We expect there to be a non-zero
835 \ value left on the stack after
836 \ executing the stored procedure.
837 \ If so, continue to run, else exit.
844 swap \ need iterator on top
848 \ Re-adjust for missing ACPI menuitem
849 arch-i386? true <> menuacpi @ 0<> and if
851 menuacpi @ 1+ over 2dup < -rot = or
858 swap \ need iterator on top
862 \ Check for menu keycode shortcut(s)
865 -rot 2dup 13 + c! rot
873 -rot 2dup 13 + c! rot
889 1+ dup 56 > \ increment iterator
890 \ continue if less than 57
894 menureboot @ = if 0 reboot then
896 again \ Non-operational key was pressed; repeat
899 \ This function unsets all the possible environment variables associated with
900 \ creating the interactive menu. Call this when you want to clear the menu
901 \ area in preparation for another menu.
905 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
907 \ basename for caption variable
913 -rot 2dup 13 + c! rot \ replace 'x' with current iteration
914 unsetenv \ not erroneous to unset unknown var
916 s" 0 menukeyN !" \ basename for key association var
917 -rot 2dup 9 + c! rot \ replace 'N' with current iteration
918 evaluate \ assign zero (0) to key assoc. var
920 1+ dup 56 > \ increment, continue if less than 57
924 \ clear the "Reboot" menu option flag
925 s" menu_reboot" unsetenv
928 \ clear the ACPI menu option flag
929 s" menu_acpi" unsetenv
932 \ clear the "Options" menu separator flag
933 s" menu_options" unsetenv
939 \ Assign configuration values
944 \ Initialize our boolean state variables
954 \ Initialize our array state variables
964 \ Initialize string containers