source: xtideuniversalbios/trunk/XTIDE_Universal_BIOS/Src/Menus/HotkeyBar.asm @ 601

Last change on this file since 601 was 601, checked in by krille_n_, 5 years ago

Changes:

  • Building the BIOS now works again.
  • Added a new IDE device type/transfer mode for use only with XT-IDE rev 2+ (or Chuck(G)-modded rev 1) cards installed in any of the following machines: Olivetti M24, AT&T PC6300, Xerox 6060 and Logabax Persona 1600. This new transfer mode is slightly faster than the regular XT-IDE rev 1 device type and requires that the card is configured for High Speed mode (or, in case of the card being a rev 1 card, has the Chuck(G) mod done). The new device type is called "XTIDE rev 2 (Olivetti M24)" in XTIDECFG.
  • Made some minor improvements to the library code that handles 'Drive Not Ready' errors in XTIDECFG.
  • Optimizations.
File size: 17.9 KB
Line 
1; Project name  :   XTIDE Universal BIOS
2; Description   :   Hotkey Bar related functions.
3
4;
5; XTIDE Universal BIOS and Associated Tools
6; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
7;
8; This program is free software; you can redistribute it and/or modify
9; it under the terms of the GNU General Public License as published by
10; the Free Software Foundation; either version 2 of the License, or
11; (at your option) any later version.
12;
13; This program is distributed in the hope that it will be useful,
14; but WITHOUT ANY WARRANTY; without even the implied warranty of
15; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16; GNU General Public License for more details.
17; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18;
19
20; Section containing code
21SECTION .text
22
23
24;--------------------------------------------------------------------
25; Handler for INT 1Ch System Timer Tick.
26; Reads key presses and draws hotkey bar.
27;
28; HotkeyBar_TimerTickHandler
29;   Parameters:
30;       DS:     RAMVARS segment
31;       ES:     BDA segment (zero)
32;   Returns:
33;       Nothing
34;   Corrupts registers:
35;       Nothing
36;--------------------------------------------------------------------
37ALIGN JUMP_ALIGN
38HotkeyBar_TimerTickHandler:
39    push    es
40    push    ds
41%ifdef USE_186
42    ePUSHA
43%else
44    push    di
45    push    si
46    push    dx
47    push    cx
48    push    ax
49%endif
50
51    ;!!! Keep interrupts disabled so there won't be another
52    ; timer tick call before we are ready
53
54    LOAD_BDA_SEGMENT_TO es, ax
55    call    RamVars_GetSegmentToDS
56
57    ; Call previous handler
58    pushf
59    call    FAR [es:BOOTVARS.hotkeyVars+HOTKEYVARS.fpPrevTimerHandler]
60
61    ; Update Hotkeybar (process key input and draw) every fourth tick
62    test    BYTE [es:BDA.dwTimerTicks], 11b
63    jnz     SHORT .ReturnFromHandler
64    call    UpdateDuringDriveDetection
65
66.ReturnFromHandler:
67%ifdef USE_186
68    ePOPA
69%else
70    pop     ax
71    pop     cx
72    pop     dx
73    pop     si
74    pop     di
75%endif
76    pop     ds
77    pop     es
78    iret
79
80
81;--------------------------------------------------------------------
82; Scans key presses and draws any hotkey changes.
83;
84; HotkeyBar_UpdateDuringDriveDetection
85;   Parameters:
86;       DS:     RAMVARS segment
87;       ES:     BDA segment (zero)
88;   Returns:
89;       Nothing
90;   Corrupts registers:
91;       AX, CX, DX, SI, DI
92;--------------------------------------------------------------------
93UpdateDuringDriveDetection:
94    call    ScanHotkeysFromKeyBufferAndStoreToBootvars
95
96    ; If ESC pressed, abort detection by forcing timeout
97    cmp     al, ESC_SCANCODE
98    jne     SHORT .ContinueDrawing
99    mov     BYTE [RAMVARS.bTimeoutTicksLeft], 0
100.ContinueDrawing:
101    ; Fall to HotkeyBar_DrawToTopOfScreen
102
103
104;--------------------------------------------------------------------
105; HotkeyBar_DrawToTopOfScreen
106;   Parameters:
107;       DS:     RAMVARS segment
108;       ES:     BDA segment (zero)
109;   Returns:
110;       Nothing
111;   Corrupts registers:
112;       AX, CX, DX, SI, DI
113;--------------------------------------------------------------------
114HotkeyBar_DrawToTopOfScreen:
115    ; Store current screen coordinates to be restored
116    ; when Hotkey Bar is rendered
117    call    DetectPrint_GetSoftwareCoordinatesToAX
118    push    ax
119
120    call    MoveCursorToScreenTopLeftCorner
121    ; Fall to .PrintFloppyDriveHotkeys
122
123;--------------------------------------------------------------------
124; .PrintFloppyDriveHotkeys
125;   Parameters:
126;       DS:     RAMVARS segment
127;       ES:     BDA segment (zero)
128;   Returns:
129;       Nothing
130;   Corrupts registers:
131;       AX, CX, DX, SI, DI
132;--------------------------------------------------------------------
133.PrintFloppyDriveHotkeys:
134    call    FloppyDrive_GetCountToAX
135    xchg    cx, ax      ; Any Floppy Drives?
136    jcxz    .SkipFloppyDriveHotkeys
137
138    mov     ax, (ANGLE_QUOTE_RIGHT << 8) | DEFAULT_FLOPPY_DRIVE_LETTER
139    mov     cl, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bFddLetter]
140    mov     di, g_szFDD
141
142    ; Clear CH if floppy drive is selected for boot
143%if 0 ; Not needed until more flags added
144    mov     ch, FLG_HOTKEY_HD_FIRST
145    and     ch, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bFlags]
146%else
147    mov     ch, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bFlags]
148%endif
149    call    FormatDriveHotkeyString
150
151.SkipFloppyDriveHotkeys:
152    ; Fall to .PrintHardDriveHotkeys
153
154;--------------------------------------------------------------------
155; .PrintHardDriveHotkeys
156;   Parameters:
157;       DS:     RAMVARS segment
158;       ES:     BDA segment (zero)
159;   Returns:
160;       Nothing
161;   Corrupts registers:
162;       AX, CX, DX, SI, DI
163;--------------------------------------------------------------------
164    call    BootVars_GetLetterForFirstHardDriveToAX
165    mov     ah, ANGLE_QUOTE_RIGHT
166    mov     cx, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.wHddLetterAndFlags]  ; Letter to CL, flags to CH
167    ;and        ch, FLG_HOTKEY_HD_FIRST ; Not needed until more flags added
168    xor     ch, FLG_HOTKEY_HD_FIRST     ; Clear CH if HD is selected for boot, set otherwise
169    mov     di, g_szHDD
170    call    FormatDriveHotkeyString
171    ; Fall to .PrintBootMenuHotkey
172
173;--------------------------------------------------------------------
174; .PrintBootMenuHotkey
175;   Parameters:
176;       ES:     BDA segment (zero)
177;   Returns:
178;       Nothing
179;   Corrupts registers:
180;       AX, CX, DX, SI, DI
181;--------------------------------------------------------------------
182.PrintBootMenuHotkey:
183%ifdef MODULE_BOOT_MENU
184    mov     ax, BOOT_MENU_HOTKEY_SCANCODE | ('2' << 8)
185    mov     di, g_szBootMenu
186    call    FormatFunctionHotkeyString
187%endif
188    ; Fall to .PrintComDetectHotkey
189
190;--------------------------------------------------------------------
191; .PrintComDetectHotkey
192;   Parameters:
193;       ES:     BDA segment (zero)
194;   Returns:
195;       Nothing
196;   Corrupts registers:
197;       AX, CX, DX, SI, DI
198;--------------------------------------------------------------------
199.PrintComDetectHotkey:
200%ifdef MODULE_SERIAL
201    mov     ax, COM_DETECT_HOTKEY_SCANCODE | ('6' << 8)
202    mov     di, g_szHotComDetect
203    call    FormatFunctionHotkeyString
204%endif
205    ; Fall to .PrintRomBootHotkey
206
207;--------------------------------------------------------------------
208; .PrintRomBootHotkey
209;   Parameters:
210;       ES:     BDA segment (zero)
211;   Returns:
212;       Nothing
213;   Corrupts registers:
214;       AX, CX, DX, SI, DI
215;--------------------------------------------------------------------
216.PrintRomBootHotkey:
217    mov     ax, ROM_BOOT_HOTKEY_SCANCODE | ('8' << 8)
218    mov     di, g_szRomBoot
219    call    FormatFunctionHotkeyString
220    ; Fall to .EndHotkeyBarRendering
221
222;--------------------------------------------------------------------
223; .EndHotkeyBarRendering
224;   Parameters:
225;       Stack:  Screen coordinates before drawing Hotkey Bar
226;   Returns:
227;       Nothing
228;   Corrupts registers:
229;       AX, CX, DI
230;--------------------------------------------------------------------
231.EndHotkeyBarRendering:
232    call    HotkeyBar_ClearRestOfTopRow
233    pop     ax
234    jmp     SHORT HotkeyBar_RestoreCursorCoordinatesFromAX
235
236
237;--------------------------------------------------------------------
238; HotkeyBar_ClearRestOfTopRow
239;   Parameters:
240;       Nothing
241;   Returns:
242;       Nothing
243;   Corrupts registers:
244;       AX, CX, DI
245;--------------------------------------------------------------------
246HotkeyBar_ClearRestOfTopRow:
247    CALL_DISPLAY_LIBRARY GetColumnsToALandRowsToAH
248    eMOVZX  cx, al
249    CALL_DISPLAY_LIBRARY GetSoftwareCoordinatesToAX
250    sub     cl, al
251    mov     al, ' '
252    JMP_DISPLAY_LIBRARY PrintRepeatedCharacterFromALwithCountInCX
253
254
255;--------------------------------------------------------------------
256; FormatDriveHotkeyString
257;   Parameters:
258;       CH:         Zero if letter in CL is selected for boot
259;       CL:         Drive letter hotkey from BOOTVARS
260;       AL:         First character for drive key string
261;       AH:         Second character for drive key string (ANGLE_QUOTE_RIGHT)
262;       SI:         Offset to hotkey description string
263;       ES:         BDA segment (zero)
264;   Returns:
265;       Nothing
266;   Corrupts registers:
267;       AX, CX, DX, SI, DI
268;--------------------------------------------------------------------
269FormatDriveHotkeyString:
270    ; Invalid scancodes are filtered on HotkeyBar_StoreHotkeyToBootvarsIfValidKeystrokeInAX
271    ; so here we have either drive letter or function key pressed. If latter, draw
272    ; drive letters as unselected
273    cmp     BYTE [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bScancode], FIRST_FUNCTION_KEY_SCANCODE
274    jae     SHORT GetNonSelectedHotkeyDescriptionAttributeToDX
275
276    ; Drive selected to boot from?
277    test    ch, ch
278    jnz     SHORT GetNonSelectedHotkeyDescriptionAttributeToDX
279    jmp     SHORT GetSelectedHotkeyDescriptionAttributeToDX
280
281
282;--------------------------------------------------------------------
283; FormatFunctionHotkeyString
284;   Parameters:
285;       AL:         Scancode of function key, to know which if any to show as selected
286;                   Later replaced with an 'F' for the call to the output routine
287;       AH:         Second character for drive key string
288;       SI:         Offset to hotkey description string
289;       ES:         BDA segment (zero)
290;   Returns:
291;       Nothing
292;   Corrupts registers:
293;       AX, CX, DX, SI, DI
294;--------------------------------------------------------------------
295FormatFunctionHotkeyString:
296    xor     cx, cx      ; Null character, eaten in output routines
297
298    cmp     [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bScancode], al
299    mov     al, 'F'     ; Replace scancode with character for output
300
301%ifdef MODULE_BOOT_MENU
302
303GetSelectedHotkeyDescriptionAttributeToDX:
304    mov     si, ATTRIBUTE_CHARS.cHighlightedItem    ; Selected hotkey
305    je      SHORT GetDescriptionAttributeToDX       ; From compare with bScancode above and from FormatDriveHotkeyString
306
307GetNonSelectedHotkeyDescriptionAttributeToDX:
308    mov     si, ATTRIBUTE_CHARS.cItem               ; Unselected hotkey
309
310    ; Display Library should not be called like this
311GetDescriptionAttributeToDX:
312    xchg    dx, ax
313    call    MenuAttribute_GetToAXfromTypeInSI
314    xchg    dx, ax                  ; DX = Description attribute
315    ;;  fall through to PushHotkeyParamsAndFormat
316
317
318%else ; if no MODULE_BOOT_MENU - No boot menu so use simpler attributes
319
320GetSelectedHotkeyDescriptionAttributeToDX:
321    mov     dx, (COLOR_ATTRIBUTE(COLOR_YELLOW, COLOR_CYAN) << 8) | MONO_REVERSE_BLINK
322    je      SHORT SelectAttributeFromDHorDLbasedOnVideoMode     ; From compare with bScancode above and from FormatDriveHotkeyString
323
324GetNonSelectedHotkeyDescriptionAttributeToDX:
325    mov     dx, (COLOR_ATTRIBUTE(COLOR_BLACK, COLOR_CYAN) << 8) | MONO_REVERSE
326
327SelectAttributeFromDHorDLbasedOnVideoMode:
328    mov     ch, [es:BDA.bVidMode]       ; We only need to preserve CL
329    shr     ch, 1
330    jnc     SHORT .AttributeLoadedToDL  ; Black & White modes
331    shr     ch, 1
332    jnz     SHORT .AttributeLoadedToDL  ; MDA
333    mov     dl, dh
334.AttributeLoadedToDL:
335    ;;  fall through to PushHotkeyParamsAndFormat
336
337%endif ; MODULE_BOOT_MENU
338
339
340;--------------------------------------------------------------------
341; PushHotkeyParamsAndFormat
342;   Parameters:
343;       AL:         First character
344;       AH:         Second character
345;       DX:         Description Attribute
346;       CX:         Description string parameter
347;       CS:DI:      Description string
348;   Returns:
349;       Nothing
350;   Corrupts registers:
351;       AX, SI, DI
352;--------------------------------------------------------------------
353PushHotkeyParamsAndFormat:
354    push    bp
355    mov     bp, sp
356
357    mov     si, MONO_BRIGHT
358
359    push    si              ; Key attribute
360    push    ax              ; First Character
361    mov     al, ah
362    push    ax              ; Second Character
363
364    push    dx              ; Description attribute
365    push    di              ; Description string
366    push    cx              ; Description string parameter
367
368    push    si              ; Key attribute for last space
369
370    mov     si, g_szHotkey
371    jmp     DetectPrint_FormatCSSIfromParamsInSSBP
372
373
374;--------------------------------------------------------------------
375; MoveCursorToScreenTopLeftCorner
376;   Parameters:
377;       Nothing
378;   Returns:
379;       Nothing
380;   Corrupts registers:
381;       AX, DI
382;--------------------------------------------------------------------
383MoveCursorToScreenTopLeftCorner:
384    xor     ax, ax          ; Top left corner (0, 0)
385    ; Fall to HotkeyBar_RestoreCursorCoordinatesFromAX
386
387
388;--------------------------------------------------------------------
389; HotkeyBar_RestoreCursorCoordinatesFromAX
390;   Parameters:
391;       Nothing
392;   Returns:
393;       Nothing
394;   Corrupts registers:
395;       AX, DI
396;--------------------------------------------------------------------
397HotkeyBar_RestoreCursorCoordinatesFromAX:
398    JMP_DISPLAY_LIBRARY SetCursorCoordinatesFromAX
399
400
401;--------------------------------------------------------------------
402; HotkeyBar_StoreDefaultDriveLettersToHotkeyVars
403;   Parameters:
404;       ES:     BDA Segment
405;   Returns:
406;       Nothing
407;   Corrupts registers:
408;       AX
409;--------------------------------------------------------------------
410HotkeyBar_StoreDefaultDriveLettersToHotkeyVars:
411    call    BootVars_GetLetterForFirstHardDriveToAX
412    mov     ah, DEFAULT_FLOPPY_DRIVE_LETTER
413    xchg    al, ah
414    mov     [es:BOOTVARS.hotkeyVars+HOTKEYVARS.wFddAndHddLetters], ax
415    ret
416
417
418;--------------------------------------------------------------------
419; HotkeyBar_InitializeVariables
420;   Parameters:
421;       DS:     RAMVARS Segment
422;       ES:     BDA Segment
423;   Returns:
424;       Nothing
425;   Corrupts registers:
426;       AX, CX, DX, DI
427;--------------------------------------------------------------------
428HotkeyBar_InitializeVariables:
429    push    ds
430    push    es
431    pop     ds
432
433    ; Store system 1Ch Timer Tick handler and install our hotkeybar handler
434    mov     ax, [BIOS_SYSTEM_TIMER_TICK_INTERRUPT_1Ch*4]
435    mov     [BOOTVARS.hotkeyVars+HOTKEYVARS.fpPrevTimerHandler], ax
436    mov     ax, [BIOS_SYSTEM_TIMER_TICK_INTERRUPT_1Ch*4+2]
437    mov     [BOOTVARS.hotkeyVars+HOTKEYVARS.fpPrevTimerHandler+2], ax
438    mov     al, BIOS_SYSTEM_TIMER_TICK_INTERRUPT_1Ch
439    mov     si, HotkeyBar_TimerTickHandler
440    call    Interrupts_InstallHandlerToVectorInALFromCSSI
441
442    ; Store time when hotkeybar is displayed
443    ; (it will be displayed after initialization is complete)
444    call    TimerTicks_ReadFromBdaToAX
445    mov     [BOOTVARS.hotkeyVars+HOTKEYVARS.wTimeWhenDisplayed], ax
446
447    pop     ds
448
449    ; Initialize HOTKEYVARS by storing default drives to boot from
450    call    HotkeyBar_StoreDefaultDriveLettersToHotkeyVars
451    mov     dl, [cs:ROMVARS.bBootDrv]
452    ; Fall to HotkeyBar_StoreHotkeyToBootvarsForDriveNumberInDL
453
454
455;--------------------------------------------------------------------
456; HotkeyBar_StoreHotkeyToBootvarsForDriveNumberInDL
457;   Parameters:
458;       DS:     RAMVARS segment
459;       ES:     BDA segment (zero)
460;       DL:     Drive Number
461;   Returns:
462;       Nothing
463;   Corrupts registers:
464;       AX, CX, DL, DI
465;--------------------------------------------------------------------
466HotkeyBar_StoreHotkeyToBootvarsForDriveNumberInDL:
467    call    DriveXlate_ConvertDriveNumberFromDLtoDriveLetter
468    ; Fall to StoreHotkeyToBootvarsForDriveLetterInDL
469
470
471;--------------------------------------------------------------------
472; StoreHotkeyToBootvarsForDriveLetterInDL
473;   Parameters:
474;       DS:     RAMVARS segment
475;       ES:     BDA segment (zero)
476;       DL:     Drive Letter ('A'...)
477;   Returns:
478;       Nothing
479;   Corrupts registers:
480;       AX, CX, DI
481;--------------------------------------------------------------------
482StoreHotkeyToBootvarsForDriveLetterInDL:
483    eMOVZX  ax, dl
484    or      al, 32  ; Upper case drive letter to lower case keystroke
485    jmp     SHORT HotkeyBar_StoreHotkeyToBootvarsIfValidKeystrokeInAX
486
487
488;--------------------------------------------------------------------
489; ScanHotkeysFromKeyBufferAndStoreToBootvars
490;   Parameters:
491;       DS:     RAMVARS segment
492;       ES:     BDA segment (zero)
493;   Returns:
494;       AL:     Last scancode value
495;   Corrupts registers:
496;       AH, CX
497;--------------------------------------------------------------------
498ScanHotkeysFromKeyBufferAndStoreToBootvars:
499    call    Keyboard_GetKeystrokeToAX
500    jz      SHORT NoHotkeyToProcess
501
502    ; Prepare to read another key from buffer
503    ePUSH_T cx, ScanHotkeysFromKeyBufferAndStoreToBootvars
504    ; Fall to HotkeyBar_StoreHotkeyToBootvarsIfValidKeystrokeInAX
505
506
507;--------------------------------------------------------------------
508; HotkeyBar_StoreHotkeyToBootvarsIfValidKeystrokeInAX
509;   Parameters:
510;       AL:     Hotkey ASCII code
511;       AH:     Hotkey Scancode
512;       DS:     RAMVARS segment
513;       ES:     BDA segment (zero)
514;   Returns:
515;       AL:     Last scancode seen
516;       CF:     Set if valid hotkey in AL
517;               Clear if scancode in AL is not for any hotkey
518;   Corrupts registers:
519;       AH, CX, DI
520;--------------------------------------------------------------------
521HotkeyBar_StoreHotkeyToBootvarsIfValidKeystrokeInAX:
522    mov     di, BOOTVARS.hotkeyVars+HOTKEYVARS.bScancode
523
524    ; All scancodes are saved, even if it wasn't a drive letter,
525    ; which also covers our function key case.  Invalid function keys
526    ; will not do anything (won't be printed, won't be accepted as input)
527    mov     [es:di], ah
528
529    ; Drive letter hotkeys remaining, allow 'a' to 'z'
530    call    Char_IsLowerCaseLetterInAL
531    jnc     SHORT .KeystrokeIsNotValidDriveLetter
532    and     al, ~32                 ; We want to print upper case letters
533
534    ; Clear HD First flag to assume Floppy Drive hotkey
535    dec     di
536    and     BYTE [es:di], ~FLG_HOTKEY_HD_FIRST
537
538    ; Determine if Floppy or Hard Drive hotkey
539    xchg    cx, ax
540    call    BootVars_GetLetterForFirstHardDriveToAX
541    cmp     cl, al
542    jb      SHORT .StoreDriveLetter ; Store Floppy Drive letter
543
544    ; Store Hard Drive letter
545    or      BYTE [es:di], FLG_HOTKEY_HD_FIRST
546
547.StoreDriveLetter:
548    sbb     di, BYTE 1              ; Sub CF if Floppy Drive
549    xchg    ax, cx
550    stosb
551    stc                             ; Valid hotkey scancode returned in AL
552
553.KeystrokeIsNotValidDriveLetter:
554NoHotkeyToProcess:
555    mov     al, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bScancode]
556    ret
557
558
559;--------------------------------------------------------------------
560; HotkeyBar_GetBootDriveNumbersToDX
561;   Parameters:
562;       DS:     RAMVARS segment
563;       ES:     BDA segment (zero)
564;   Returns:
565;       DX:     Drives selected as boot device, DL is primary
566;   Corrupts registers:
567;       AX
568;--------------------------------------------------------------------
569HotkeyBar_GetBootDriveNumbersToDX:
570    mov     dx, [es:BOOTVARS.hotkeyVars+HOTKEYVARS.wFddAndHddLetters]
571
572    ; HotkeyBar_GetBootDriveNumbersToDX is called when all drives are detected and
573    ; drive letters are known.
574    ; Replace unavailable hard drive letter with first hard drive.
575    ; If we have boot menu, it will be displayed instead.
576%ifndef MODULE_BOOT_MENU
577    call    BootVars_GetLetterForFirstHardDriveToAX
578    mov     ah, al
579    add     al, [es:BDA.bHDCount]   ; AL now contains first unavailable HD letter
580    cmp     dh, al                  ; Unavailable drive?
581    jb      SHORT .ValidHardDriveLetterInDH
582    mov     dh, ah                  ; Replace unavailable drive with first drive
583.ValidHardDriveLetterInDH:
584%endif
585
586    test    BYTE [es:BOOTVARS.hotkeyVars+HOTKEYVARS.bFlags], FLG_HOTKEY_HD_FIRST
587    jnz     SHORT .noflip
588    xchg    dl, dh
589.noflip:
590    call    DriveXlate_ConvertDriveLetterInDLtoDriveNumber
591    xchg    dl, dh
592    ; Fall to HotkeyBar_FallThroughTo_DriveXlate_ConvertDriveLetterInDLtoDriveNumber
593
594HotkeyBar_FallThroughTo_DriveXlate_ConvertDriveLetterInDLtoDriveNumber:
595
Note: See TracBrowser for help on using the repository browser.