Subversion Repositories SvarDOS

Compare Revisions

Ignore whitespace Rev 1478 → Rev 1479

/more/build.bat
0,0 → 1,0
A72 MORE.A72 MORE.COM
/more/more.a72
0,0 → 1,342
;
; MORE ver 2023.0
;
; 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 "MORE 2023.0", 13, 10
db 10
db "MORE < README.TXT", 13, 10
db "DIR | 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 "Press any key..."
PRESSANYKEY:
mov dx, di
mov ah, 9h ; disp $-termin. string pointed by DX
int 21h
mov dx, DOTS
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="
DOTS 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" (CP866)
TEXT_TR DB "DAHA FAZLA$"
 
; uninitialized buffer area (must be defined last)
BUFFER: