source: xtideuniversalbios/trunk/XTIDE_Universal_BIOS/Src/Libraries/print.asm@ 87

Last change on this file since 87 was 77, checked in by krille_n_@…, 14 years ago

Minor size optimizations plus a bug fix in print.asm in Print_IntSW (DI was left hanging on the stack if the parameter in AX was positive). Also a very minor speed optimization in keys.asm in Keys_Backspace.

File size: 19.0 KB
Line 
1; File name : print.asm
2; Project name : Print library
3; Created date : 6.10.2009
4; Last update : 4.1.2011
5; Author : Tomi Tilli,
6; : Krister Nordvall (optimizations)
7; Description : ASM library for character and string
8; printing related functions.
9
10;--------------- Equates -----------------------------
11
12; String library function to include
13%define USE_PRINT_FORMAT ; Print_Format
14%define USE_PRINT_NEWLINE ; Print_Newline
15;%define USE_PRINT_CHARBUFFER ; Print_CharBuffer
16%define USE_PRINT_REPEAT ; Print_Repeat
17;%define USE_PRINT_BOOL ; Print_Bool
18;%define USE_PRINT_INTSW ; Print_IntSW
19%define USE_PRINT_INTUW ; Print_IntUW
20;%define USE_PRINT_INTUDW ; Print_IntUDW
21%define USE_PRINT_INTHEXB ; Print_IntHexB
22%define USE_PRINT_INTHEXW ; Print_IntHexW
23;%define USE_PRINT_INTHEXDW ; Print_IntHexDW
24
25
26; Text mode character attribute byte bits for CGA+
27FLG_CGA_FG_B EQU (1<<0)
28FLG_CGA_FG_G EQU (1<<1)
29FLG_CGA_FG_R EQU (1<<2)
30FLG_CGA_FG_I EQU (1<<3)
31FLG_CGA_BG_B EQU (1<<4)
32FLG_CGA_BG_G EQU (1<<5)
33FLG_CGA_BG_R EQU (1<<6)
34FLG_CGA_BG_I EQU (1<<7)
35FLG_CGA_BG_BLNK EQU (1<<7) ; Blinking is default
36
37; Text mode character attribute bytes for MDA/Hercules (mono)
38; *Not displayed on some monitors
39ATTR_MDA_HIDDEN EQU 00h ; Not displayed
40ATTR_MDA_ULINE EQU 01h ; Underlined
41ATTR_MDA_NORMAL EQU 07h ; Normal (white on black)
42ATTR_MDA_INT_U EQU 09h ; High intensity, underlined
43ATTR_MDA_INT EQU 0Fh ; High intensity
44ATTR_MDA_REVERSE EQU 70h ; Reverse video (black on white)
45ATTR_MDA_BLNK EQU 87h ; Blinking white on black*
46ATTR_MDA_BLNK_INT EQU 8Fh ; Blinking high intensity*
47ATTR_MDA_BLNK_REV EQU 0F0h; Blinking reverse video
48
49
50;-------------- Private global variables -------------
51; Section containing initialized data
52;SECTION .data
53
54g_rgbHex: db "0123456789ABCDEF" ; Hexadecimal printing
55g_rgbFormat: ; Placeholders for Print_Format
56%ifdef USE_PRINT_INTSW
57 db 'd' ; Prints signed 16-bit integer (WORD)
58%endif
59%ifdef USE_PRINT_INTUW
60 db 'u' ; Prints unsigned 16-bit integer (WORD)
61%endif
62%ifdef USE_PRINT_INTUDW
63 db 'U' ; Prints unsigned 32-bit integer (DWORD)
64%endif
65%ifdef USE_PRINT_INTHEXW
66 db 'x' ; Prints 8- or 16-bit hexadecimal (BYTE, WORD)
67%endif
68%ifdef USE_PRINT_INTHEXDW
69 db 'X' ; Prints 32-bit hexadecimal (DWORD)
70%endif
71 db 's' ; Prints string (DS segment)
72 db 'S' ; Prints string (far pointer)
73%ifdef USE_PRINT_BOOL
74 db 'b' ; Prints boolean value 0 or 1
75%endif
76 db 'c' ; Prints character
77 db 'C' ; Prints character number of times
78_g_rgbFormatEnd:
79
80; Number of different placeholders for Print_Format
81CNT_PLCEHLDRS EQU (_g_rgbFormatEnd-g_rgbFormat)
82
83
84
85;-------------- Public functions ---------------------
86; Section containing code
87SECTION .text
88
89; Include one file to use DOS, BIOS or VRAM macros and functions
90;%include "prntdos.asm" ; Include to use DOS printing functions
91%include "prntbios.asm" ; Include to use BIOS printing functions
92;%include "prntvram.asm" ; Include to access VRAM directly
93
94
95;--------------------------------------------------------------------
96; Debugging macro that prints wanted character and newline.
97;
98; PRINT_DBG_CH
99; Parameters:
100; %1: Character to print
101; Returns:
102; Nothing
103; Corrupts registers:
104; Nothing
105;--------------------------------------------------------------------
106%macro PRINT_DBG_CH 1
107 pushf
108 push dx
109 push ax
110 mov dl, %1
111 PRINT_CHAR
112 ;mov dl, CR
113 ;PRINT_CHAR
114 ;mov dl, LF
115 ;PRINT_CHAR
116 pop ax
117 pop dx
118 popf
119%endmacro
120
121
122;--------------------------------------------------------------------
123; Simplified printf-style string formatting function.
124; Strings must end to STOP character.
125; Supports following formatting types:
126; %d Prints signed 16-bit integer (WORD)
127; %u Prints unsigned 16-bit integer (WORD)
128; %U Prints unsigned 32-bit integer (DWORD)
129; %x Prints 8- or 16-bit hexadecimal (BYTE, WORD)
130; %X Prints 32-bit hexadecimal (DWORD)
131; %s Prints string (DS segment)
132; %S Prints string (far pointer)
133; %b Prints boolean value 0 or 1
134; %c Prints character
135; %[num]C Prints character number of times (up to 256, 0=256)
136; %% Prints '%' character (no parameter pushed)
137;
138; Any placeholder can be set to minimum length by specifying
139; minimum number of characters. For example %8d would append spaces
140; after integer so that at least 8 characters would be printed.
141;
142; NOTE! Caller must clean the stack variables!
143;
144; Print_Format
145; Parameters:
146; DL: Min length character (usually space)
147; DS:SI: Pointer to string to format
148; Stack: Parameters for formatting placeholders.
149; Parameter for first placeholder must be pushed last
150; (to top of stack).
151; High word must be pushed first for 32-bit parameters.
152; Returns:
153; Nothing
154; Corrupts registers:
155; AX, SI
156;--------------------------------------------------------------------
157%ifdef USE_PRINT_FORMAT
158ALIGN JUMP_ALIGN
159Print_Format:
160 push es
161 push bp
162 push di
163 push cx
164 push dx ; Push min length character
165 push cs ; Copy CS...
166 pop es ; ...to ES
167 mov bp, sp ; Copy SP to BP
168 add bp, 2*6 ; Set BP to point first stack parameter
169 cld ; Set LODSB to increment SI
170ALIGN JUMP_ALIGN
171.PrintLoop: ; Load characters from string
172 lodsb ; Load char from DS:SI to AL, increment SI
173 xor cx, cx ; Zero minimum length
174 cmp al, STOP ; End of string?
175 je .Return ; If so, return
176 cmp al, '%' ; Format placeholder?
177 je .Format ; If so, jump to format
178ALIGN JUMP_ALIGN
179.PrintChar: ; Prints single character from AL
180 mov dl, al ; Copy character to DL
181 PRINT_CHAR ; Print character
182 mov dx, 1 ; One char printed
183ALIGN JUMP_ALIGN
184.PrintXtraSoMin: ; Checks how many spaces to append
185 sub cx, dx ; Number of spaces needed
186 jle .PrintLoop ; Read next character if no spaces needed
187 pop dx ; Pop min length character to DL
188ALIGN JUMP_ALIGN
189.PrintSpaceLoop: ; Loop to print the required spaces
190 PRINT_CHAR
191 loop .PrintSpaceLoop
192 push dx ; Store min length character
193 jmp .PrintLoop ; Jump to read next character
194ALIGN JUMP_ALIGN
195.Return: ; The only return point for this function
196 pop dx
197 pop cx
198 pop di
199 pop bp
200 pop es
201 ret
202ALIGN JUMP_ALIGN
203.UpdateMinLen: ; Updates placeholder minimum length
204 mov al, ah ; Copy digit to AL
205 mov ah, cl ; Copy previous length to AH
206 aad ; AL=10*AH+AL, AH=0
207 mov cx, ax ; Copy new length to CX
208ALIGN JUMP_ALIGN
209.Format:
210 lodsb ; Load char from DS:SI to AL, increment SI
211 cmp al, STOP
212 je .Return ; Invalid string
213 mov ah, al ; Copy char to AH
214 sub ah, '0' ; Possible digit char to digit
215 cmp ah, 9 ; Was valid digit?
216 jbe .UpdateMinLen ; If so, jump to update minimum length
217 push cx ; Store number of spaces needed
218 mov cx, CNT_PLCEHLDRS ; Load number of placeholders to CX
219 mov di, g_rgbFormat ; Load offset to format placeholder table
220 repne scasb ; Compare AL to [ES:DI] until match
221 pop cx ; Restore spaces needed
222 jne .PrintChar ; If no match, jump to print character
223 sub di, g_rgbFormat+1 ; To placeholder index
224 shl di, 1 ; Shift for word lookup
225 jmp [cs:di+.g_rgwFormJump] ; Jump to format
226
227%ifdef USE_PRINT_INTSW
228ALIGN JUMP_ALIGN
229.Print_d: ; Print signed 16-bit integer
230 mov ax, [bp] ; Load word to print
231 times 2 inc bp
232 call Print_IntSW
233 jmp .PrintXtraSoMin
234%endif
235%ifdef USE_PRINT_INTUW
236ALIGN JUMP_ALIGN
237.Print_u: ; Print unsigned 16-bit integer
238 mov ax, [bp] ; Load word to print
239 times 2 inc bp
240 call Print_IntUW
241 jmp .PrintXtraSoMin
242%endif
243%ifdef USE_PRINT_INTUDW
244ALIGN JUMP_ALIGN
245.Print_U: ; Print unsigned 32-bit integer
246 mov ax, [bp] ; Load loword
247 mov dx, [bp+2] ; Load hiword
248 add bp, 4
249 call Print_IntUDW
250 jmp .PrintXtraSoMin
251%endif
252%ifdef USE_PRINT_INTHEXW
253ALIGN JUMP_ALIGN
254.Print_x: ; Prints 8- or 16-bit hexadecimal
255 mov ax, [bp] ; Load word to print
256 times 2 inc bp
257%ifdef USE_PRINT_INTHEXB
258 test ah, ah ; 16-bit hexadecimal?
259 jnz .Print_x16 ; If so, jump to print it
260 call Print_IntHexB
261 jmp .PrintXtraSoMin
262.Print_x16:
263%endif
264 call Print_IntHexW
265 jmp .PrintXtraSoMin
266%endif
267%ifdef USE_PRINT_INTHEXDW
268ALIGN JUMP_ALIGN
269.Print_X: ; Prints 32-bit hexadecimal
270 mov ax, [bp] ; Load loword
271 mov dx, [bp+2] ; Load hiword
272 add bp, 4
273 call Print_IntHexDW
274 jmp .PrintXtraSoMin
275%endif
276ALIGN JUMP_ALIGN
277.Print_s: ; Prints string from DS segment
278 mov dx, [bp] ; Load offset to string
279 times 2 inc bp
280 PRINT_STR_LEN ; Print string
281 jmp .PrintXtraSoMin
282ALIGN JUMP_ALIGN
283.Print_S: ; Prints string using far pointer
284 push ds ; Store DS
285 mov dx, [bp] ; Load offset to string
286 mov ds, [bp+2] ; Load segment to string
287 add bp, 4
288 PRINT_STR_LEN ; Print string
289 pop ds ; Restore DS
290 jmp .PrintXtraSoMin
291%ifdef USE_PRINT_BOOL
292ALIGN JUMP_ALIGN
293.Print_b: ; Prints boolean value
294 mov ax, [bp] ; Load boolean value to print
295 times 2 inc bp
296 call Print_Bool
297 jmp .PrintXtraSoMin
298%endif
299ALIGN JUMP_ALIGN
300.Print_c: ; Prints character
301 mov ax, [bp] ; Load character to print
302 times 2 inc bp
303 jmp .PrintChar ; Jump to print character
304ALIGN JUMP_ALIGN
305.Print_C:
306 mov dx, [bp] ; Load character to print
307 times 2 inc bp
308ALIGN JUMP_ALIGN
309.CharLoop:
310 PRINT_CHAR
311 loop .CharLoop
312 jmp .PrintLoop
313
314ALIGN JUMP_ALIGN
315.g_rgwFormJump: ; Jump table for Print_Format
316%ifdef USE_PRINT_INTSW
317 dw .Print_d
318%endif
319%ifdef USE_PRINT_INTUW
320 dw .Print_u
321%endif
322%ifdef USE_PRINT_INTUDW
323 dw .Print_U
324%endif
325%ifdef USE_PRINT_INTHEXW
326 dw .Print_x
327%endif
328%ifdef USE_PRINT_INTHEXDW
329 dw .Print_X
330%endif
331 dw .Print_s
332 dw .Print_S
333%ifdef USE_PRINT_BOOL
334 dw .Print_b
335%endif
336 dw .Print_c
337 dw .Print_C
338%endif
339
340
341;--------------------------------------------------------------------
342; Prints newline character to change line.
343;
344; Print_Newline
345; Parameters:
346; Nothing
347; Returns:
348; Nothing
349; Corrupts registers:
350; Nothing
351;--------------------------------------------------------------------
352%ifdef USE_PRINT_NEWLINE
353ALIGN JUMP_ALIGN
354Print_Newline:
355 push ax
356 push dx
357 mov dl, LF
358 PRINT_CHAR
359 mov dl, CR
360 PRINT_CHAR
361 pop dx
362 pop ax
363 ret
364%endif
365
366
367;--------------------------------------------------------------------
368; Prints wanted number of characters.
369;
370; Print_CharBuffer
371; Parameters:
372; CX: Number of characters to print
373; ES:DI: Ptr to character buffer
374; Returns:
375; Nothing
376; Corrupts registers:
377; AX, CX
378;--------------------------------------------------------------------
379%ifdef USE_PRINT_CHARBUFFER
380ALIGN JUMP_ALIGN
381Print_CharBuffer:
382 jcxz .Return
383 push ds
384 push si
385 push dx
386 push es
387 pop ds
388 mov si, di
389 cld ; LODSB to increment SI
390ALIGN JUMP_ALIGN
391.Rep:
392 lodsb ; Load from [DS:SI] to AL
393 mov dl, al ; Copy to DL for printing
394 PRINT_CHAR
395 loop .Rep
396 pop dx
397 pop si
398 pop ds
399.Return:
400 ret
401%endif
402
403
404;--------------------------------------------------------------------
405; Repeats wanted character.
406;
407; Print_Repeat
408; Parameters:
409; CX: Number of times to repeat character
410; DL: Character to repeat
411; Returns:
412; Nothing
413; Corrupts registers:
414; AX, CX
415;--------------------------------------------------------------------
416%ifdef USE_PRINT_REPEAT
417ALIGN JUMP_ALIGN
418Print_Repeat:
419 jcxz .Return
420ALIGN JUMP_ALIGN
421.Rep:
422 PRINT_CHAR
423 loop .Rep
424.Return:
425 ret
426%endif
427
428
429;--------------------------------------------------------------------
430; Prints boolean value.
431;
432; Print_Bool
433; Parameters:
434; AX: Boolean value (0=FALSE, non-zero=TRUE)
435; Returns:
436; DX: Number of characters printed
437; Corrupts registers:
438; AX
439;--------------------------------------------------------------------
440%ifdef USE_PRINT_BOOL
441ALIGN JUMP_ALIGN
442Print_Bool:
443 mov dl, '0' ; Assume FALSE
444 test ax, ax ; Is FALSE?
445 jz .Print ; If so, jump to print
446 inc dx ; Increment to '1'
447ALIGN JUMP_ALIGN
448.Print:
449 PRINT_CHAR
450 mov dx, 1 ; One character printed
451 ret
452%endif
453
454
455;--------------------------------------------------------------------
456; Prints signed or unsigned 16-bit integer.
457;
458; Print_IntSW Prints signed 16-bit word
459; Print_IntUW Prints unsigned 16-bit word
460; Parameters:
461; AX: Word to print
462; Returns:
463; DX: Number of characters printed
464; Corrupts registers:
465; AX
466;--------------------------------------------------------------------
467%ifdef USE_PRINT_INTSW
468ALIGN JUMP_ALIGN
469Print_IntSW:
470 test ax, ax ; Positive integer?
471 jns Print_IntUW ; If so, jump to print it
472 push di ; Store DI
473 push ax ; Store word
474 mov dl, '-' ; Print '-'
475 PRINT_CHAR
476 mov di, 1 ; One character printed
477 pop ax ; Restore word
478 neg ax ; Negative to positive
479 jmp Print_ContinueFromIntSW
480%endif
481
482%ifdef USE_PRINT_INTUW or USE_PRINT_INTSW
483ALIGN JUMP_ALIGN
484Print_IntUW:
485 push di ; Store DI
486 xor di, di ; Zero character counter
487Print_ContinueFromIntSW:
488 push bx ; Store BX
489 push cx ; Store CX
490 mov bx, 10 ; Load divisor to BX
491 mov cx, 5 ; Load number of divisions to CX
492ALIGN JUMP_ALIGN
493.DivLoop:
494 xor dx, dx ; Zero DX for division
495 div bx ; DX:AX / 10 => AX=quot, DX=rem
496 push dx ; Push digit
497 loop .DivLoop ; Loop to separate all characters
498 xor bx, bx ; First char printed flag
499 mov cl, 5 ; Load number of characters to CL
500ALIGN JUMP_ALIGN
501.PrintLoop:
502 pop dx ; Pop character to DX
503 or bx, dx ; Still skipping zeroes?
504 loopz .PrintLoop ; If so, loop
505 add dx, '0' ; Digit to character
506 PRINT_CHAR ; Print character
507 inc di ; Increment chars printed
508 test cx, cx ; Characters left
509 jnz .PrintLoop ; If so, loop
510 mov dx, di ; Copy chars printed to DX
511 pop cx
512 pop bx
513 pop di
514 ret
515%endif
516
517
518;--------------------------------------------------------------------
519; Prints unsigned 32-bit integer.
520;
521; Print_IntUDW
522; Parameters:
523; DX:AX: 32-bit unsigned integer to print
524; Returns:
525; DX: Number of characters printed
526; Corrupts registers:
527; AX
528;--------------------------------------------------------------------
529%ifdef USE_PRINT_INTUDW
530ALIGN JUMP_ALIGN
531Print_IntUDW:
532 push di ; Store DI
533 push si ; Store SI
534 push bx ; Store BX
535 push cx ; Store CX
536 mov cx, 10 ; Load divider to CX
537 mov si, cx ; Load number of divisions (10) to SI
538 xor di, di ; Zero DI (char counter)
539ALIGN JUMP_ALIGN
540.DivLoop:
541 call Math_DivDWbyW ; DX:AX / 10 => DX:AX=quot, BX=rem
542 push bx ; Push digit
543 dec si ; Decrement number of divisions
544 jnz .DivLoop ; Loop while divisions left
545 xor bx, bx ; First char printed flag
546ALIGN JUMP_ALIGN
547.PrintLoop:
548 pop dx ; Pop character to DX
549 or bx, dx ; Still skipping zeroes?
550 loopz .PrintLoop ; If so, loop
551 add dx, '0' ; Digit to character
552 PRINT_CHAR ; Print character
553 inc di ; Increment chars printed
554 test cx, cx ; Characters left
555 jnz .PrintLoop ; If so, loop
556 mov dx, di ; Copy characters printed to DX
557 pop cx
558 pop bx
559 pop si
560 pop di
561 ret
562%endif
563
564
565;--------------------------------------------------------------------
566; Prints 8-bit byte as hexadecimal string.
567;
568; Print_IntHexB
569; Parameters:
570; AL: 8-bit BYTE to print
571; Returns:
572; DX: Number of characters printed
573; Corrupts registers:
574; AX
575;--------------------------------------------------------------------
576%ifdef USE_PRINT_INTHEXB
577ALIGN JUMP_ALIGN
578Print_IntHexB:
579 push bx
580 xor ah, ah ; Zero AH, AX=word to print
581 mov bx, 0FFh ; Set to print two digits
582 call Print_HexString ; Print hex string
583 push dx ; Store number of chars printed
584 mov dl, 'h' ; Print 'h' at the end
585 PRINT_CHAR
586 pop dx ; Restore number of chars printed
587 inc dx ; Increment for 'h'
588 pop bx
589 ret
590%endif
591
592
593;--------------------------------------------------------------------
594; Prints 16-bit word as hexadecimal string.
595;
596; Print_IntHexW
597; Parameters:
598; AX: 16-bit WORD to print
599; Returns:
600; DX: Number of characters printed
601; Corrupts registers:
602; AX
603;--------------------------------------------------------------------
604%ifdef USE_PRINT_INTHEXW
605ALIGN JUMP_ALIGN
606Print_IntHexW:
607 push bx
608 xor bx, bx ; Set to print all zeroes...
609 dec bx ; ...BX = FFFFh
610 call Print_HexString ; Print hex string
611 push dx ; Store number of chars printed
612 mov dl, 'h' ; Print 'h' at the end
613 PRINT_CHAR
614 pop dx ; Restore number of chars printed
615 inc dx ; Increment for 'h'
616 pop bx
617 ret
618%endif
619
620
621;--------------------------------------------------------------------
622; Prints 32-bit dword as hexadecimal string.
623;
624; Print_IntHexDW
625; Parameters:
626; DX:AX: 32-bit DWORD to print
627; Returns:
628; DX: Number of characters printed
629; Corrupts registers:
630; AX
631;--------------------------------------------------------------------
632%ifdef USE_PRINT_INTHEXDW
633ALIGN JUMP_ALIGN
634Print_IntHexDW:
635 push cx
636 push bx
637 push ax ; Store loword
638 mov ax, dx ; Copy hiword to AX
639 xor bx, bx ; Set to print all zeroes...
640 dec bx ; ...BX = FFFFh
641 call Print_HexString ; Print hex string
642 mov cx, dx ; Copy number of chars printed
643 pop ax ; Pop loword
644 call Print_HexString ; Print hex string
645 add cx, dx ; Add number of chars printed
646 mov dl, 'h' ; Print 'h' at the end
647 PRINT_CHAR
648 inc cx ; Increment number of chars printed
649 mov dx, cx ; Copy number of chars printed to DX
650 pop bx
651 pop cx
652 ret
653%endif
654
655
656;-------------- Private functions --------------------
657
658;--------------------------------------------------------------------
659; Prints hexadecimal character for every nybble for WORD.
660;
661; Print_HexString
662; Parameters:
663; AX: 16-bit WORD to print
664; BX: Mask for zero nybbles to print:
665; FFFFh = prints all digits
666; 0FFFh = does not print digit 3 if it is zero
667; 00FFh = does not print digits 3 and 2 if both are zeroes
668; 000Fh = prints only digit 0 if other are zeroes
669; 0000h = prints nothing if value is zero
670; Returns:
671; DX: Number of characters printed
672; Corrupts registers:
673; AX
674;--------------------------------------------------------------------
675%ifdef USE_PRINT_INTHEXB or USE_PRINT_INTHEXW or USE_PRINT_INTHEXDW
676ALIGN JUMP_ALIGN
677Print_HexString:
678 push bp
679 push di
680 push si
681 push cx
682
683 mov di, ax ; Backup WORD to DI
684 mov cl, 12 ; Prepare to shift nybbles
685 xor dx, dx ; Zero DX
686ALIGN JUMP_ALIGN
687.NybbleLoop:
688 mov bp, bx ; Copy mask to BP
689 mov si, di ; Copy WORD to SI
690 shr bp, cl ; Shift wanted mask nybble to BP
691 shr si, cl ; Shift wanted nybble to SI
692 and bp, 0Fh ; Clear unwanted mask bits
693 and si, 0Fh ; Clear unwanted bits
694 or bp, si ; Skip zeroes in front?
695 jz .SkipPrint ; If so, skip printing
696 mov dl, [cs:si+g_rgbHex]; Load char to DL
697 PRINT_CHAR ; Print character
698 inc dh ; Increment characters printed
699.SkipPrint:
700 sub cl, 4 ; Prepare to shift next nybble
701 jnc .NybbleLoop ; Loop while nybbles left
702 mov dl, dh ; Copy chars printed to DL
703 xor dh, dh ; Zero DH
704
705 pop cx
706 pop si
707 pop di
708 pop bp
709 ret
710%endif
Note: See TracBrowser for help on using the repository browser.