Subversion Repositories SvarDOS

Rev

Rev 1483 | Rev 1485 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1483 Rev 1484
1
;
1
;
2
; SvarDOS MORE
2
; SvarDOS MORE
3
;
3
;
4
; Displays output one screen at a time
4
; Displays output one screen at a time
5
;
5
;
6
;  - multilingual (looks up the LANG env variable)
6
;  - multilingual (looks up the LANG env variable)
7
;  - tiny (fits in a single disk sector)
7
;  - tiny (fits in a single disk sector)
8
;
8
;
9
; This program is part of the SvarDOS project <http://svardos.org>
9
; This program is part of the SvarDOS project <http://svardos.org>
10
;
10
;
11
; ****************************************************************************
11
; ****************************************************************************
12
; *** Distributed under the terms of the MIT LICENSE *************************
12
; *** Distributed under the terms of the MIT LICENSE *************************
13
; ****************************************************************************
13
; ****************************************************************************
14
;
14
;
15
; Copyright (C) 2023 Mateusz Viste
15
; Copyright (C) 2023 Mateusz Viste
16
;
16
;
17
; Permission is hereby granted, free of charge, to any person obtaining a copy
17
; Permission is hereby granted, free of charge, to any person obtaining a copy
18
; of this software and associated documentation files (the "Software"), to
18
; of this software and associated documentation files (the "Software"), to
19
; deal in the Software without restriction, including without limitation the
19
; deal in the Software without restriction, including without limitation the
20
; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
20
; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
21
; sell copies of the Software, and to permit persons to whom the Software is
21
; sell copies of the Software, and to permit persons to whom the Software is
22
; furnished to do so, subject to the following conditions:
22
; furnished to do so, subject to the following conditions:
23
;
23
;
24
; The above copyright notice and this permission notice shall be included in
24
; The above copyright notice and this permission notice shall be included in
25
; all copies or substantial portions of the Software.
25
; all copies or substantial portions of the Software.
26
;
26
;
27
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
32
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33
; IN THE SOFTWARE.
33
; IN THE SOFTWARE.
34
; ****************************************************************************
34
; ****************************************************************************
35
;
35
;
36
; To be compiled with the A72 assembler:
36
; To be compiled with the A72 assembler:
37
; A72 MORE.ASM MORE.COM
37
; A72 MORE.ASM MORE.COM
38
;
38
;
39
 
39
 
40
 
40
 
41
; COM file always has a 100h origin
41
; COM file always has a 100h origin
42
ORG     100h
42
ORG     100h
43
 
43
 
44
 
44
 
45
; ****************************************************************************
45
; ****************************************************************************
46
; * Display a short help screen if any (non-empty) argument is provided.     *
46
; * Display a short help screen if any (non-empty) argument is provided.     *
47
; *                                                                          *
47
; *                                                                          *
48
; * detect presence of arguments in the command's tail (PSP 81h), looking    *
48
; * detect presence of arguments in the command's tail (PSP 81h), looking    *
49
; * at the first non-space character. If it's a CR then no argument (CR is   *
49
; * at the first non-space character. If it's a CR then no argument (CR is   *
50
; * the tail's terminator).                                                  *
50
; * the tail's terminator).                                                  *
51
; ****************************************************************************
51
; ****************************************************************************
52
mov     ax, ds
52
mov     ax, ds
53
mov     es, ax
53
mov     es, ax
54
mov     al, ' '
54
mov     al, ' '
55
mov     cx, 0ffh
55
mov     cx, 0ffh
56
mov     di, 81h
56
mov     di, 81h
57
repe    scasb                          ; compare AL with [ES:DI++]
57
repe    scasb                          ; compare AL with [ES:DI++]
58
mov     al, [di-1]
58
mov     al, [di-1]
59
 
59
 
60
cmp     al, 0Dh
60
cmp     al, 0Dh
61
je      NO_ARGS
61
je      NO_ARGS
62
 
62
 
63
; some arg found: display help and quit
63
; some arg found: display help and quit
64
mov     ah, 9h
64
mov     ah, 9h
65
mov     dx, offset HELP
65
mov     dx, offset HELP
66
int     21h
66
int     21h
67
int     20h
67
int     20h
68
 
68
 
69
HELP db "SvarDOS MORE 2023.0", 13, 10
69
HELP db "SvarDOS MORE 2023.0", 13, 10
70
     db 10
70
     db 10
71
     db "MORE < README.TXT", 13, 10
71
     db "MORE < README.TXT", 13, 10
72
     db "TYPE README.TXT | MORE", 13, 10, '$'
72
     db "TYPE README.TXT | MORE", 13, 10, '$'
73
 
73
 
74
NO_ARGS:
74
NO_ARGS:
75
 
75
 
76
 
76
 
77
; ****************************************************************************
77
; ****************************************************************************
78
; * detect screen dimensions (max usable row and column)                     *
78
; * detect screen dimensions (max usable row and column)                     *
79
; ****************************************************************************
79
; ****************************************************************************
80
mov     ah, 0Fh                        ; GET CURRENT VIDEO MODE
80
mov     ah, 0Fh                        ; GET CURRENT VIDEO MODE
81
int     10h
81
int     10h
82
mov     byte ptr [MAXCOL], ah
82
mov     byte ptr [MAXCOL], ah
83
mov     ax, 40h
83
mov     ax, 40h
84
mov     es, ax
84
mov     es, ax
85
mov     ah, [es:84h]                   ; 0040:0084
85
mov     ah, [es:84h]                   ; 0040:0084
86
test    ah, ah                         ; ancient PCes do not know this
86
test    ah, ah                         ; ancient PCes do not know this
87
jz      SKIP_ROWS_DETECTION
87
jz      SKIP_ROWS_DETECTION
88
mov     byte ptr [MAXROW], ah
88
mov     byte ptr [MAXROW], ah
89
 
89
 
90
SKIP_ROWS_DETECTION:
90
SKIP_ROWS_DETECTION:
91
 
91
 
92
 
92
 
93
; ****************************************************************************
93
; ****************************************************************************
94
; * Scan the environement block looking for the LANG variable                *
94
; * Scan the environement block looking for the LANG variable                *
95
; ****************************************************************************
95
; ****************************************************************************
96
 
96
 
97
; set ES to point at the environment segment (PSP's offset 2Ch)
97
; set ES to point at the environment segment (PSP's offset 2Ch)
98
mov     es, [2Ch]
98
mov     es, [2Ch]
99
 
99
 
100
; compare DS:[SI] with ES:[DI], up to 5 bytes ("LANG=")
100
; compare DS:[SI] with ES:[DI], up to 5 bytes ("LANG=")
101
cld
101
cld
102
mov     di, 0
102
mov     di, 0
103
CMP_NEXT_VAR:
103
CMP_NEXT_VAR:
104
 
104
 
105
; if it points at a nul already then it's the end of the env block
105
; if it points at a nul already then it's the end of the env block
106
mov     al, [es:di]
106
mov     al, [es:di]
107
test    al, al
107
test    al, al
108
jz      ENDOFENV
108
jz      ENDOFENV
109
 
109
 
110
mov     si, LANG
110
mov     si, LANG
111
mov     cx, 5
111
mov     cx, 5
112
repe    cmpsb
112
repe    cmpsb
113
 
113
 
114
; if CX == 0 then found a LANG= match
114
; if CX == 0 then found a LANG= match
115
jcxz    FOUND_LANG
115
jcxz    FOUND_LANG
116
 
116
 
117
; otherwise jump to next var (look for nearest nul terminator)
117
; otherwise jump to next var (look for nearest nul terminator)
118
xor     al, al
118
xor     al, al
119
mov     cx, 0ffffh
119
mov     cx, 0ffffh
120
repne   scasb                          ; compare AL with [ES:DI++]
120
repne   scasb                          ; compare AL with [ES:DI++]
121
jmp     CMP_NEXT_VAR
121
jmp     CMP_NEXT_VAR
122
 
122
 
123
; FOUND THE LANG VARIABLE (at ES:DI)
123
; FOUND THE LANG VARIABLE (at ES:DI)
124
FOUND_LANG:
124
FOUND_LANG:
125
 
125
 
126
mov     ax, [es:di]                    ; load the LANG ID to AX and make
126
mov     ax, [es:di]                    ; load the LANG ID to AX and make
127
and     ax, 0DFDFh                     ; sure it is always upper case
127
and     ax, 0DFDFh                     ; sure it is always upper case
128
 
128
 
129
 
129
 
130
; ****************************************************************************
130
; ****************************************************************************
131
; * Now I have a LANG ID in AX and I have to match it for something I know.  *
131
; * Now I have a LANG ID in AX and I have to match it for something I know.  *
132
; * The LANG ID is simply the value for the two uppercase LANG letters, for  *
132
; * The LANG ID is simply the value for the two uppercase LANG letters, for  *
133
; * example the LANG ID for "PL" is 4C50h (50h = 'P', 4Ch = 'L').            *
133
; * example the LANG ID for "PL" is 4C50h (50h = 'P', 4Ch = 'L').            *
134
; ****************************************************************************
134
; ****************************************************************************
135
 
135
 
136
; DE [44h 45h]
136
; DE [44h 45h]
137
mov     di, TEXT_DE
137
mov     bp, TEXT_DE
138
cmp     ax, 4544h
138
cmp     ax, 4544h
139
je      LANGOK
139
je      LANGOK
140
 
140
 
141
; FR [46h 52h]
141
; FR [46h 52h]
142
mov     di, TEXT_FR
142
mov     bp, TEXT_FR
143
cmp     ax, 5246h
143
cmp     ax, 5246h
144
je      LANGOK
144
je      LANGOK
145
 
145
 
146
; NL [4Eh 4Ch]
146
; NL [4Eh 4Ch]
147
mov     di, TEXT_NL
147
mov     bp, TEXT_NL
148
cmp     ax, 4C4Eh
148
cmp     ax, 4C4Eh
149
je      LANGOK
149
je      LANGOK
150
 
150
 
151
; PL [50h 4Ch]
151
; PL [50h 4Ch]
152
mov     di, TEXT_PL
152
mov     bp, TEXT_PL
153
cmp     ax, 4C50h
153
cmp     ax, 4C50h
154
je      LANGOK
154
je      LANGOK
155
 
155
 
156
; RU [52h 55h]
156
; RU [52h 55h]
157
mov     di, TEXT_RU
157
mov     bp, TEXT_RU
158
cmp     ax, 5552h
158
cmp     ax, 5552h
159
je      LANGOK
159
je      LANGOK
160
 
160
 
161
; TR [54h 52h]
161
; TR [54h 52h]
162
mov     di, TEXT_TR
162
mov     bp, TEXT_TR
163
cmp     ax, 5254h
163
cmp     ax, 5254h
164
je      LANGOK
164
je      LANGOK
165
 
165
 
166
 
166
 
167
; *** LANG NOT FOUND OR LANG ID MATCH FAILED: FALL BACK TO EN ****************
167
; *** LANG NOT FOUND OR LANG ID MATCH FAILED: FALL BACK TO EN ****************
168
 
168
 
169
ENDOFENV:
169
ENDOFENV:
170
mov di, TEXT_EN
170
mov     bp, TEXT_EN
171
 
171
 
172
LANGOK:
172
LANGOK:
173
 
173
 
174
 
174
 
175
; ****************************************************************************
175
; ****************************************************************************
176
; * DUPLICATING HANDLES: here I duplicate stdin into a new handle so I can   *
176
; * DUPLICATING HANDLES: here I duplicate stdin into a new handle so I can   *
177
; * safely close original stdin (file handle 0) and then duplicate stderr    *
177
; * safely close original stdin (file handle 0) and then duplicate stderr    *
178
; * into stdin. The purpose of these shenanigans is to be able to cope with  *
178
; * into stdin. The purpose of these shenanigans is to be able to cope with  *
179
; * situations when stdin is redirected (eg. with CTTY)                      *
179
; * situations when stdin is redirected (eg. with CTTY)                      *
180
; ****************************************************************************
180
; ****************************************************************************
181
 
181
 
182
; duplicate stdin and keep the new file handle in BP
182
; duplicate stdin and keep the new file handle in FHANDLE
183
xor     bx, bx
183
xor     bx, bx
184
mov     ah, 45h
184
mov     ah, 45h
185
int     21h
185
int     21h
186
mov     bp, ax
186
mov     [FHANDLE], ax
187
 
187
 
188
; I can close stdin now
188
; I can close stdin now
189
mov     ah, 3Eh
189
mov     ah, 3Eh
190
int     21h
190
int     21h
191
 
191
 
192
; duplicate stderr to stdin
192
; duplicate stderr to stdin
193
; bx = file handle / cx = file handle to become duplicate of bx
193
; bx = file handle / cx = file handle to become duplicate of bx
194
mov     ah, 46h
194
mov     ah, 46h
195
mov     bx, 2
195
mov     bx, 2
196
xor     cx, cx
196
xor     cx, cx
197
int     21h
197
int     21h
198
; ****************************************************************************
198
; ****************************************************************************
199
 
199
 
200
 
200
 
201
; make sure cursor is on column 0
201
; make sure cursor is on column 0
202
mov ah, 2h                             ; write character in DL to stdout
202
mov ah, 2h                             ; write character in DL to stdout
203
mov dl, 0Dh                            ; carriage return
203
mov dl, 0Dh                            ; carriage return
204
int 21h
204
int 21h
205
 
205
 
-
 
206
; reset BX - from now on bh = cur row and bl = cur col
-
 
207
xor     bx, bx
206
 
208
 
207
; consume stdin bytes by loading them into buffer
209
; consume stdin bytes by loading them into buffer
208
RELOADBUF:
210
RELOADBUF:
-
 
211
xchg    di, bx                         ; save BX to DI (contains cur row+col)
209
mov     ah, 3Fh                        ; DOS 2+ - read from file or device
212
mov     ah, 3Fh                        ; DOS 2+ - read from file or device
210
mov     bx, bp                         ; duplicated stdin is still in bp
213
mov     bx, [FHANDLE]                  ; duplicated stdin was saved in FHANDLE
211
mov     cx, 1024
214
mov     cx, 1024
212
mov     dx, offset BUFFER
215
mov     dx, offset BUFFER
213
int     21h
216
int     21h
214
 
217
 
215
; abort on error
218
; abort on error
216
jc EXIT
219
jc EXIT
217
 
220
 
-
 
221
; restore bx
-
 
222
xchg    bx, di
-
 
223
 
218
; did I get any bytes? 0 bytes read means "EOF"
224
; did I get any bytes? 0 bytes read means "EOF"
219
test    ax, ax
225
test    ax, ax
220
jnz     PREPLOOP
226
jnz     PREPLOOP
221
 
227
 
222
EXIT:
228
EXIT:
223
int     20h
229
int     20h
224
 
230
 
225
; prepare the LODSB loop
231
; prepare the LODSB loop
226
PREPLOOP:
232
PREPLOOP:
227
mov     cx, ax
233
mov     cx, ax
228
mov     si, dx
234
mov     si, dx
229
 
235
 
230
NEXTCHAR:
236
NEXTCHAR:
231
 
237
 
232
; AL = DS:[SI++]
238
; AL = DS:[SI++]
233
cld
239
cld
234
lodsb
240
lodsb
235
 
241
 
236
; EOF char? (check this first so a short jump to exit is still possible)
242
; EOF char? (check this first so a short jump to exit is still possible)
237
cmp     al, 1Ah
243
cmp     al, 1Ah
238
jz      EXIT
244
jz      EXIT
239
 
245
 
240
; [7h] BELL?
246
; [7h] BELL?
241
cmp     al, 7h
247
cmp     al, 7h
242
je      OUTPUT_CHAR
248
je      OUTPUT_CHAR
243
 
249
 
244
; [8h] BACKSPACE? moves the cursor back by one column, no effect if it is on
250
; [8h] BACKSPACE? moves the cursor back by one column, no effect if it is on
245
; first column already. it does not blank the characters it passes over.
251
; first column already. it does not blank the characters it passes over.
246
cmp     al, 8h
252
cmp     al, 8h
247
jne     NOTBS
253
jne     NOTBS
248
cmp     byte ptr [CURCOL], 0
254
test    bl, bl                         ; am I on on column 0? (bl = cur col)
249
je      OUTPUT_CHAR
255
jz      OUTPUT_CHAR
250
dec     byte ptr [CURCOL]
256
dec     bl
251
jmp     short OUTPUT_CHAR
257
jmp     short OUTPUT_CHAR
252
NOTBS:
258
NOTBS:
253
 
259
 
254
; [9h] TAB?
260
; [9h] TAB?
255
cmp     al, 9h
261
cmp     al, 9h
256
jne     NOTTAB
262
jne     NOTTAB
257
mov     ah, [CURCOL]
263
add     bl, 8                          ; bl = cur col
258
add     ah, 7
264
and     bl, 248
259
and     ah, 11111000b
-
 
260
inc     ah
-
 
261
mov     [CURCOL], ah
-
 
262
jmp     short OUTPUT_CHAR
265
jmp     short OUTPUT_CHAR
263
NOTTAB:
266
NOTTAB:
264
 
267
 
265
; [0Ah] LF?
268
; [0Ah] LF?
266
cmp     al, 0Ah
269
cmp     al, 0Ah
267
jne     NOTLF
270
jne     NOTLF
268
inc     byte ptr [CURROW]
271
inc     bh                             ; bh = cur row
269
jmp     short OUTPUT_CHAR
272
jmp     short OUTPUT_CHAR
270
NOTLF:
273
NOTLF:
271
 
274
 
272
; [0Dh] CR?
275
; [0Dh] CR?
273
cmp     al, 0Dh
276
cmp     al, 0Dh
274
jnz     NOTCR
277
jnz     NOTCR
275
mov     byte ptr [CURCOL], 0
278
xor     bl, bl                         ; bl = cur col
276
jmp     short OUTPUT_CHAR
279
jmp     short OUTPUT_CHAR
277
NOTCR:
280
NOTCR:
278
 
281
 
279
; otherwise: increment cur column, and then maybe cur row
282
; otherwise: increment cur column, and then maybe cur row
280
inc     byte ptr [CURCOL]
283
inc     bl                             ; bl = cur col
281
mov     ah, [CURCOL]
-
 
282
cmp     ah, [MAXCOL]
284
cmp     bl, [MAXCOL]
283
jb      OUTPUT_CHAR
285
jb      OUTPUT_CHAR
284
inc     byte ptr [CURROW]
286
inc     bh                             ; bh = cur row
285
mov     byte ptr [CURCOL], 0
287
xor     bl, bl                         ; bl = cur col
286
 
288
 
287
OUTPUT_CHAR:
289
OUTPUT_CHAR:
288
mov     dl, al
290
mov     dl, al
289
mov     ah, 2h
291
mov     ah, 2h
290
int     21h
292
int     21h
291
mov     ah, [CURROW]
-
 
292
cmp     ah, [MAXROW]
293
cmp     bh, [MAXROW]                   ; bh = cur row
293
jae     PRESSANYKEY
294
jae     PRESSANYKEY
294
 
295
 
295
CHARLOOP:
296
CHARLOOP:
296
loop    NEXTCHAR                       ; dec cx and jmp to NEXTCHAR if cx > 0
297
loop    NEXTCHAR                       ; dec cx and jmp to NEXTCHAR if cx > 0
297
jmp     RELOADBUF
298
jmp     RELOADBUF
298
 
299
 
299
 
300
 
300
; display " --- More ---"
301
; display " --- More ---"
301
PRESSANYKEY:
302
PRESSANYKEY:
302
mov     ah, 9h                         ; disp $-termin. string pointed by DX
303
mov     ah, 9h                         ; disp $-termin. string pointed by DX
303
mov     dx, offset SEPAR1
304
mov     dx, offset SEPAR1
304
int     21h
305
int     21h
305
mov     dx, di
306
mov     dx, bp
306
int     21h
307
int     21h
307
mov     dx, offset SEPAR2
308
mov     dx, offset SEPAR2
308
int     21h
309
int     21h
309
 
310
 
310
; wait for a keypress
311
; wait for a keypress
311
mov     ah, 08h                        ; read char from stdin, no echo
312
mov     ah, 08h                        ; read char from stdin, no echo
312
int     21h
313
int     21h
313
; read again if an extended key was pressed
314
; read again if an extended key was pressed
314
test    al, al
315
test    al, al
315
jnz     GETKEY_DONE
316
jnz     GETKEY_DONE
316
int     21h
317
int     21h
317
GETKEY_DONE:
318
GETKEY_DONE:
318
 
319
 
319
; output a CR/LF pair and reset counters
320
; output a CR/LF pair and reset counters
320
mov     dx, offset CRLF
321
mov     dx, offset CRLF
321
mov     ah, 9h
322
mov     ah, 9h
322
int     21h
323
int     21h
323
mov     byte ptr [CURCOL], 0
-
 
324
mov     byte ptr [CURROW], 0
324
xor     bx, bx                         ; bh = cur row, bl = cur col
325
jmp     CHARLOOP
325
jmp     CHARLOOP
326
 
326
 
327
 
327
 
328
; ****************************************************************************
328
; ****************************************************************************
329
; * DATA                                                                     *
329
; * DATA                                                                     *
330
; ****************************************************************************
330
; ****************************************************************************
331
 
331
 
332
MAXROW  db      24                     ; maximum *addressable* row (not total)
332
MAXROW  db      24                     ; maximum *addressable* row (not total)
333
MAXCOL  db      80                     ; total available columns
333
MAXCOL  db      80                     ; total available columns
334
CURROW  db      0
-
 
335
CURCOL  db      0
-
 
336
 
-
 
337
 
334
 
338
CRLF DB 13, 10, '$'
335
CRLF DB 13, 10, '$'
339
LANG DB "LANG="
336
LANG DB "LANG="
340
SEPAR1 DB "--- $"
337
SEPAR1 DB "--- $"
341
SEPAR2 DB " ---$"
338
SEPAR2 DB " ---$"
342
 
339
 
343
TEXT_DE DB "WEITER$"
340
TEXT_DE DB "WEITER$"
344
TEXT_EN DB "MORE$"
341
TEXT_EN DB "MORE$"
345
TEXT_FR DB "PLUS$"
342
TEXT_FR DB "PLUS$"
346
TEXT_NL DB "MEER$"
343
TEXT_NL DB "MEER$"
347
TEXT_PL DB "DALEJ$"
344
TEXT_PL DB "DALEJ$"
348
TEXT_RU DB 84h,80h,8Bh,85h,85h,'$'     ; "DALEE" (CP 866)
345
TEXT_RU DB 84h,80h,8Bh,85h,85h,'$'     ; "DALEE" (CP 866)
349
TEXT_TR DB "DAHA FAZLA$"
346
TEXT_TR DB "DAHA FAZLA$"
350
 
347
 
351
; uninitialized buffer area (must be defined last)
348
; uninitialized area (must be defined last)
-
 
349
 
-
 
350
FHANDLE dw      ?                      ; file handle I read from (DUPed stdin)
-
 
351
 
352
BUFFER:
352
BUFFER:
353
 
353