Subversion Repositories SvarDOS

Rev

Rev 1480 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

;
; SvarDOS MORE
;
; Displays output one screen at a time
;
;  - multilingual (looks up the LANG env variable)
;  - tiny (fits in a single disk sector)
;
; This program is part of the SvarDOS project <http://svardos.org>
;
; ****************************************************************************
; *** Distributed under the terms of the MIT LICENSE *************************
; ****************************************************************************
;
; Copyright (C) 2023 Mateusz Viste
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to
; deal in the Software without restriction, including without limitation the
; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
; sell copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
; IN THE SOFTWARE.
; ****************************************************************************
;
; To be compiled with the A72 assembler:
; A72 MORE.ASM MORE.COM
;


; COM file always has a 100h origin
ORG     100h


; ****************************************************************************
; * Display a short help screen if any (non-empty) argument is provided.     *
; *                                                                          *
; * detect presence of arguments in the command's tail (PSP 81h), looking    *
; * at the first non-space character. If it's a CR then no argument (CR is   *
; * the tail's terminator).                                                  *
; ****************************************************************************
mov     ax, ds
mov     es, ax
mov     al, ' '
mov     cx, 0ffh
mov     di, 81h
repe    scasb                          ; compare AL with [ES:DI++]
mov     al, [di-1]

cmp     al, 0Dh
je      NO_ARGS

; some arg found: display help and quit
mov     ah, 9h
mov     dx, offset HELP
int     21h
int     20h

HELP db "SvarDOS MORE 2023.0", 13, 10
     db 10
     db "MORE < README.TXT", 13, 10
     db "TYPE README.TXT | MORE", 13, 10, '$'

NO_ARGS:


; ****************************************************************************
; * detect screen dimensions (max usable row and column)                     *
; ****************************************************************************
mov     ah, 0Fh                        ; GET CURRENT VIDEO MODE
int     10h
mov     byte ptr [MAXCOL], ah
mov     ax, 40h
mov     es, ax
mov     ah, [es:84h]                   ; 0040:0084
test    ah, ah                         ; ancient PCes do not know this
jz      SKIP_ROWS_DETECTION
mov     byte ptr [MAXROW], ah

SKIP_ROWS_DETECTION:


; ****************************************************************************
; * Scan the environement block looking for the LANG variable                *
; ****************************************************************************

; set ES to point at the environment segment (PSP's offset 2Ch)
mov     es, [2Ch]

; compare DS:[SI] with ES:[DI], up to 5 bytes ("LANG=")
cld
mov     di, 0
CMP_NEXT_VAR:

; if it points at a nul already then it's the end of the env block
mov     al, [es:di]
test    al, al
jz      ENDOFENV

mov     si, LANG
mov     cx, 5
repe    cmpsb

; if CX == 0 then found a LANG= match
jcxz    FOUND_LANG

; otherwise jump to next var (look for nearest nul terminator)
xor     al, al
mov     cx, 0ffffh
repne   scasb                          ; compare AL with [ES:DI++]
jmp     CMP_NEXT_VAR

; FOUND THE LANG VARIABLE (at ES:DI)
FOUND_LANG:

mov     ax, [es:di]                    ; load the LANG ID to AX and make
and     ax, 0DFDFh                     ; sure it is always upper case


; ****************************************************************************
; * Now I have a LANG ID in AX and I have to match it for something I know.  *
; * The LANG ID is simply the value for the two uppercase LANG letters, for  *
; * example the LANG ID for "PL" is 4C50h (50h = 'P', 4Ch = 'L').            *
; ****************************************************************************

; DE [44h 45h]
mov     di, TEXT_DE
cmp     ax, 4544h
je      LANGOK

; FR [46h 52h]
mov     di, TEXT_FR
cmp     ax, 5246h
je      LANGOK

; NL [4Eh 4Ch]
mov     di, TEXT_NL
cmp     ax, 4C4Eh
je      LANGOK

; PL [50h 4Ch]
mov     di, TEXT_PL
cmp     ax, 4C50h
je      LANGOK

; RU [52h 55h]
mov     di, TEXT_RU
cmp     ax, 5552h
je      LANGOK

; TR [54h 52h]
mov     di, TEXT_TR
cmp     ax, 5254h
je      LANGOK


; *** LANG NOT FOUND OR LANG ID MATCH FAILED: FALL BACK TO EN ****************

ENDOFENV:
mov di, TEXT_EN

LANGOK:


; ****************************************************************************
; * DUPLICATING HANDLES: here I duplicate stdin into a new handle so I can   *
; * safely close original stdin (file handle 0) and then duplicate stderr    *
; * into stdin. The purpose of these shenanigans is to be able to cope with  *
; * situations when stdin is redirected (eg. with CTTY)                      *
; ****************************************************************************

; duplicate stdin and keep the new file handle in BP
xor     bx, bx
mov     ah, 45h
int     21h
mov     bp, ax

; I can close stdin now
mov     ah, 3Eh
int     21h

; duplicate stderr to stdin
; bx = file handle / cx = file handle to become duplicate of bx
mov     ah, 46h
mov     bx, 2
xor     cx, cx
int     21h
; ****************************************************************************


; make sure cursor is on column 0
mov ah, 2h                             ; write character in DL to stdout
mov dl, 0Dh                            ; carriage return
int 21h


; consume stdin bytes by loading them into buffer
RELOADBUF:
mov     ah, 3Fh                        ; DOS 2+ - read from file or device
mov     bx, bp                         ; duplicated stdin is still in bp
mov     cx, 1024
mov     dx, offset BUFFER
int     21h

; abort on error
jc EXIT

; did I get any bytes? 0 bytes read means "EOF"
test    ax, ax
jnz     PREPLOOP

EXIT:
int     20h

; prepare the LODSB loop
PREPLOOP:
mov     cx, ax
mov     si, dx

NEXTCHAR:

; AL = DS:[SI++]
cld
lodsb

; EOF char? (check this first so a short jump to exit is still possible)
cmp     al, 1Ah
jz      EXIT

; [7h] BELL?
cmp     al, 7h
je      OUTPUT_CHAR

; [8h] BACKSPACE? moves the cursor back by one column, no effect if it is on
; first column already. it does not blank the characters it passes over.
cmp     al, 8h
jne     NOTBS
cmp     byte ptr [CURCOL], 0
je      OUTPUT_CHAR
dec     byte ptr [CURCOL]
jmp     short OUTPUT_CHAR
NOTBS:

; [9h] TAB?
cmp     al, 9h
jne     NOTTAB
mov     ah, [CURCOL]
add     ah, 7
and     ah, 11111000b
inc     ah
mov     [CURCOL], ah
jmp     short OUTPUT_CHAR
NOTTAB:

; [0Ah] LF?
cmp     al, 0Ah
jne     NOTLF
inc     byte ptr [CURROW]
jmp     short OUTPUT_CHAR
NOTLF:

; [0Dh] CR?
cmp     al, 0Dh
jnz     NOTCR
mov     byte ptr [CURCOL], 0
jmp     short OUTPUT_CHAR
NOTCR:

; otherwise: increment cur column, and then maybe cur row
inc     byte ptr [CURCOL]
mov     ah, [CURCOL]
cmp     ah, [MAXCOL]
jb      OUTPUT_CHAR
inc     byte ptr [CURROW]
mov     byte ptr [CURCOL], 0

OUTPUT_CHAR:
mov     dl, al
mov     ah, 2h
int     21h
mov     ah, [CURROW]
cmp     ah, [MAXROW]
jae     PRESSANYKEY

CHARLOOP:
loop    NEXTCHAR                       ; dec cx and jmp to NEXTCHAR if cx > 0
jmp     RELOADBUF


; display " --- More ---"
PRESSANYKEY:
mov     ah, 9h                         ; disp $-termin. string pointed by DX
mov     dx, offset SEPAR1
int     21h
mov     dx, di
int     21h
mov     dx, offset SEPAR2
int     21h

mov     ax, 0C08h                      ; flush keyb + wait for key, no echo
int     21h

; output a CR/LF pair and reset counters
mov     dx, offset CRLF
mov     ah, 9h
int     21h
mov     byte ptr [CURCOL], 0
mov     byte ptr [CURROW], 0
jmp     CHARLOOP


; ****************************************************************************
; * DATA                                                                     *
; ****************************************************************************

MAXROW  db      24                     ; maximum *addressable* row (not total)
MAXCOL  db      80                     ; total available columns
CURROW  db      0
CURCOL  db      0


CRLF DB 13, 10, '$'
LANG DB "LANG="
SEPAR1 DB "--- $"
SEPAR2 DB " ---$"

TEXT_DE DB "WEITER$"
TEXT_EN DB "MORE$"
TEXT_FR DB "PLUS$"
TEXT_NL DB "MEER$"
TEXT_PL DB "DALEJ$"
TEXT_RU DB 84h,80h,8Bh,85h,85h,'$'     ; "DALEE" (CP 866)
TEXT_TR DB "DAHA FAZLA$"

; uninitialized buffer area (must be defined last)
BUFFER: