; TxtView v1.52 by Kouri (Kouri@ucdavis.edu)
; http://kouri.cjb.net
; http://void.calc.org
; 2000.08.12

; Program Information:
; TxtView allows you to view ASCII text files created by CalcText.exe on your calculator.
; For information on how to use CalcText, please read CalcText.c.txt.

; Archive contents:
; TxtView.asm.txt - The file that you're reading right now!
; TxtView.82p     - The program that you send to your calc to view the text files (for CrASH).
; ASCII.82P       - An example text file.
; README.82P      - Another example text file.
; CalcText.c.txt  - Contains information on how to use CalcText.
; CalcText.exe    - Converts computer files to calculator files.
; 4x6font.inc     - Default font file used by TxtView.
; DOSfont.inc     - DOS font file used by TxtView.

; Usage:
; First install the appropriate shell on your calculator.
; Then send TxtView to your calc, follwed by as many text files as you want.
; Run TxtView from your shell. A list of all the valid text files on your calc will be shown. If you can't run TxtView, then
; you don't have any valid text files on your calculator.

; Controls (when selecting a file to view):
; Up		- Scroll up
; Down		- Scroll down
; Right		- Change page (if more than 9 files on calc)
; [MODE]	- Return to TI-OS
; [CLEAR]	- Return to the shell

; Controls (when viewing a file):
; Arrows	- Scroll
; [DEL]		- PageUp
; [STAT]	- PageDown
; [2nd]+[LEFT]	- PageLeft
; [2nd]+[RIGHT]	- PageRight
; [TRACE]	- Home
; [GRAPH]	- End
; [MODE]	- Toggle word wrap
; [X,T,0]	- Toggle font
; [Y=]		- Invert the screen
; [CLEAR]	- Return to the Main Menu

; Notes:
; You can also change the contrast and turn off the calc while in TxtView.
; If you want to make your own custom font for TxtView, look in 4x6font.inc or DOSfont.inc and follow the example.

; Version History:
; 1.52- Sixth release. Minor optimizations. Improved documentation. 2291 bytes.
; 1.51- Fifth release. (Hopefully) fixed last of program detection bugs. Small optimizations. 2298 bytes.
; 1.5 - Fourth release. Fixed program detection routine to work better. Cleaned up source code a little too. 2300 bytes.
; 1.4 - Third release. Expanded font to 3 bytes/char (4x6 pixel) to better support ASCII extended characters. DOS font and
;	font selector added. Left and right now move 8 spaces instead of 1. Tabs are now handled correctly. 2301 bytes.
; 1.3 - Second release. Font now supports 255 characters at 2 bytes/char (3x5 pixel). PageLeft and PageRight added. Screen
;	invert added. Fixed coordinates used in C_CHARPUT routine. 1226 bytes.
; 1.2 - First release. Compressed font to 2 bytes/char (3x5 pixel). Word wrap added. Left, right, PageUp, and PageDown
;	added. ~915 bytes.
; 1.1 - Not released. Switched to custom font (96 characters supported at 5 bytes/char (8x5 pixel)). ~1200 bytes.
; 1.0 - Not released. Used built-in menu font. Up, down, Home, and End supported. ~575 bytes.

; Credits:
; The CrASH team: Jeff Mears and Hideaki Omuro, for ideas and optimizations.
; Jimmy Mrdell and Florent Dhordain for SQRXZ 82, and Andreas Ess and Sam Heald for Plain Jump 82, whose programs showed me
; how to search the VAT and make cool little menus. :)
; Mark May, for major help with just about everything.
; Eric Piel, for the tab routine.
; Thomas Hruska, for helping out too.
; Joel (GuruMSD) for finding bugs.
; All of the A82 list for their various contributions.
; THANKS GUYS!

#include CrASH82.inc

#define text(x,y,string) ld bc,256*x+y \ ld hl,string \ call D_ZC_STR
VAT_END_ADDR = $8D12
MUL_HL       = $1F18-$1A
TABSIZE      = 8
CHARWIDTH    = 4
CHARHEIGHT   = 6
X_MAX        = 96
Y_MAX        = 64
NUMCOLS      = X_MAX/CHARWIDTH

dataptr   = OP1		; Pointer to the current string
offset    = OP1+2	; Offset used for left and right
num_files = OP1+4	; Number of text files on the calc
top       = OP1+5	; Pointer offset used when selecting files
files     = TEXT_MEM	; See comment below

; Available RAM limits the number of TxtView files on the calc to 64
; (128/2 == 64). Bad things will probably happen if you try to run
; TxtView with more than 64 TxtView files on the calc, so don't do it!

Title:
.db "TxtView v1.52 by Kouri",0

 ld hl,VAT_START			; FE6Eh
 ld ix,files				; ix -> area to store addresses

SearchVAT:
 ld a,(hl)				; Save type
 ld c,6
 sub c \ jr z,FoundProgram		; Type 6 == edit-locked program
 inc a \ jr nz,SkipEntry		; Type 5 == regular program
FoundProgram:
 dec hl \ ld e,(hl)			; Get past type byte
 dec hl \ ld d,(hl)			; de -> program data
 dec hl \ ld c,(hl)			; Save namelength
 inc c					; Inc so we skip that byte too
 ex de,hl				; Save hl in de, hl -> program data
 inc hl \ inc hl			; Skip program length
 ld a,(hl)
 inc a \ jr nz,KeepSearching		; Is first byte FFh?
 inc hl
 cp (hl) \ jr nz,KeepSearching		; Is second byte 0?
 inc hl					; This one's a keeper!
 ld (ix),l \ inc ix			; Store address of program data
 ld (ix),h \ inc ix			; Point to address for next entry
 ld hl,num_files
 inc (hl)				; Increase number of files found
KeepSearching:
 ex de,hl				; Restore pointer
SkipEntry:
 or a					; Clear carry flag
 sbc hl,bc				; Skip c bytes
 ld de,(VAT_END_ADDR)			; End of program table - 1
 inc de
 call CP_HL_DE \ jr nc,SearchVAT
 ld a,(num_files)
 or a \ ret z				; No files found, return to shell

SelectFileMenu:
 call StartGraphmemFill
 ld (hl),0
 push hl			; Save hl -> GRAPH_MEM
 ldir				; Fill GRAPH_MEM with 0
 text(1,1,Title)		; Display Title
 pop hl
 ld ixh,a			; Number of files in current list = 0
 call Invert_NoRowCalc		; Invert part of the GRAPH_MEM
 ld a,(top)
 add a,a
 ld d,b				; d = 0
 ld e,a				; de = 2*top
 ld hl,files
 add hl,de			; hl -> pointer to files
 ld c,8				; Go to line after title
DisplayFile:
 ld e,(hl) \ inc hl		; de -> program data
 ld d,(hl) \ inc hl		; Point to next address
 ld a,d
 or a \ jr z,UpdateChoice	; If d == 0, end of list reached
 push hl			; Save pointer
 ex de,hl			; hl -> program data
 ld b,1				; x-coord = 1
 push ix
 call D_ZC_STR			; Show file description
 pop ix
 pop hl				; Restore pointer
 inc ixh			; Increase number of files in list
 ld a,c \ add a,CHARHEIGHT \ ld c,a	; y-coord += CHARHEIGHT
 sub Y_MAX-2 \ jr nz,DisplayFile	; Continue until bottom of screen

UpdateChoice:
 ld ixl,a
 call InvertChoice
Choose:
 call CR_KHAND			; bc = 0
NextPage:
 dec a  \ jr nz,Choose2		; cp K_RIGHT
 ld a,(top)
 add a,9			; Add top of list with 9
 ld hl,num_files
 cp (hl) \ jr c,ChangePage	; Check if we can go to next page
 xor a				; If not, start at page 0
ChangePage:
 ld (top),a
 jr SelectFileMenu
Choose2:
 sub K_UP-K_RIGHT   \ jr z,ChoiceUp	; cp K_UP
 dec a              \ jr z,ChoiceDown	; cp K_DOWN
 dec a              \ jr z,SelectFile	; cp K_ENTER
 dec a              \ ret z		; cp K_CLEAR
 sub K_MODE-K_CLEAR \ jp z,EXIT_2_TIOS	; cp K_MODE
 jr Choose
ChoiceUp:
 call InvertChoice
 or a \ jr nz,_ChoiceUp		; Check if at top of list
 ld a,ixh			; a = bottom of list + 1
_ChoiceUp:
 dec a
 jr UpdateChoice
ChoiceDown:
 call InvertChoice
 inc a
 cp ixh \ jr nz,UpdateChoice	; Check if bottom of list passed
 xor a				; If so, current file is 0
 jr UpdateChoice
SelectFile:
 ld (offset),bc			; Clear offset
 ld d,a				; d = a = 0
 ld a,(top)
 add a,ixl			; Add current choice
 add a,a			; Because addresses are 2 bytes
 ld e,a
 ld hl,files
 add hl,de			; hl -> pointer to current file
 ld e,(hl)
 inc hl
 ld d,(hl)			; de = file to open
 ex de,hl			; hl -> first string in file

UpdateDisplay:
 ld (dataptr),hl		; Save the pointer
_UpdateDisplay:
 call StartGraphmemFill
FillValue:
 ld (hl),0
 ldir
 ld hl,(dataptr)
 ld c,2				; Cursor starts at (0,2)
DisplayLoop:
 jr DisplayLoop+2		; Change to 24 to enable word wrap
 ld de,(offset)			; 4
DoOffset:
 ld a,d				; 1
 or e \ jr z,NoOffset		; 3
 dec de				; 1
 ld a,(hl)			; 1
 inc hl				; 1
 or a \ jr z,MakeNewLine	; 3
 cp $09 \ call z,SkipTab	; 5
 inc a \ jr nz,DoOffset		; 3
 jr DisplayScreen		; 2
NoOffset:
 call D_ZC_STR
 ld a,(hl)
 inc a \ jr z,DisplayScreen	; Check for $FF (EOF)
WordWrap:
 jr WordWrap+2			; Change to 13 to enable word wrap
 ld a,b				; 1
 sub X_MAX \ jr nz,MakeNewLine	; 4
NoWrap:
 cp (hl)			; 1
 inc hl				; 1
 jr nz,NoWrap			; 2
 dec a				; 1
 cp (hl) \ jr z,DisplayScreen	; 3
MakeNewLine:
 ld b,0					; x-coord = 0
 ld a,c \ add a,CHARHEIGHT \ ld c,a	; y-coord += CHARHEIGHT
 cp Y_MAX-2 \ jr nz,DisplayLoop	; Continue until bottom of screen
DisplayScreen:
 call CR_GRBCopy		; Display the screen

Main:
 call CR_KHAND			; bc = 0
 ld hl,(dataptr)
 dec a  \ jr z,Right		; cp K_RIGHT
 dec a  \ jr z,Left		; cp K_LEFT
 dec a  \ jp z,ScrollUp		; cp K_UP
 dec a  \ jr z,ScrollDown	; cp K_DOWN
 sub 2  \ jp z,SelectFileMenu	; cp K_CLEAR
 dec a  \ jr z,PageUp		; cp K_DEL
 sub 4  \ jr z,PageLeft		; cp K_BOL
 dec a  \ jr z,PageRight	; cp K_EOL
 sub 43 \ jr z,PageDown		; cp K_STAT
 sub 14 \ jr z,End		; cp K_GRAPH
 dec a  \ jr z,ToggleWordWrap	; cp K_MODE
 sub 4  \ jr z,InvertScreen	; cp K_YEDIT
 sub 19 \ jr z,Home		; cp K_TRACE
 sub 88 \ jr nz,Main		; cp K_XTO

ChangeFont:
 ld de,(WhichFont+1)
 ld hl,DefaultFont
 call CP_HL_DE \ jr nz,_ChangeFont
 ld hl,DOSfont
_ChangeFont:
 ld (WhichFont+1),hl
 jr _Update

PageLeft:	ld de,-NUMCOLS \ jr TestNewOffset2
Left:		ld de,-TABSIZE \ jr TestNewOffset2
PageRight:	ld  e, NUMCOLS \ jr TestNewOffset1
Right:		ld  e, TABSIZE
TestNewOffset1:
 ld d,b				; d = 0
TestNewOffset2:
 ld hl,(offset)
 add hl,de			; Get new offset
 bit 7,h \ jr z,ChangeOffset	; If h is negative, offset = 0
 ld h,b
 ld l,c
ChangeOffset:
 ld (offset),hl
_Update:
 jp _UpdateDisplay

End:
 dec a				; a = $FF
 ld (offset),bc			; offset = 0
 cpir				; Go to EOF
 dec hl				; hl -> EOF

PageUp:
 ld b,10			; Do 10 lines
 jr _ScrollUp

Home:
 dec a				; a = $FF
 ld (offset),bc			; offset = 0
 cpdr				; Go to BOF
 inc hl				; hl -> BOF

ScrollDown:
 ld b,1				; Do 1 line
_ScrollDown:
 call Scroll
 ld a,(hl)
 inc a \ jr z,ScrollUp		; Check for EOF
 djnz _ScrollDown
 jr Update

PageDown:
 ld b,10			; Do 10 lines
 jr _ScrollDown

InvertScreen:
 ld a,(FillValue+1) \ cpl \ ld (FillValue+1),a
 jr _Update

ToggleWordWrap:
 ld (offset),bc			; offset = 0
 ld a,(DisplayLoop+1)	\ xor 24 \ ld (DisplayLoop+1),a
				 \ ld (Scroll+1),a
 ld a,(WordWrap+1)	\ xor 13 \ ld (WordWrap+1),a
 ld a,(ScrollUpWrap+1)	\ xor 11 \ ld (ScrollUpWrap+1),a
 dec hl
 ld a,(hl)
 inc hl
 or a \ jr z,Update

ScrollUp:
 ld b,1				; Do 1 line
_ScrollUp:
 dec hl \ dec hl		; Get past previous zero
 ld a,(hl)
 inc hl \ inc hl
 inc a \ jr z,Update		; Check for BOF
 xor a
 push bc
 ld b,h
 ld c,l				; Save dataptr
 dec hl
ScrollUpLoop:
 dec hl
 cp (hl) \ jr nz,ScrollUpLoop	; Get to previous zero
 inc hl				; Point to next byte
ScrollUpWrap:
 jr ScrollUpNoWrap		; Change to 0 to enable word wrap
_ScrollUpWrap:
 push hl				; 1
 call Scroll				; 3
 pop de					; 1
 call CP_HL_BC \ jr c,_ScrollUpWrap	; 5
 ex de,hl				; 1
ScrollUpNoWrap:
 pop bc
 djnz _ScrollUp
Update:
 jp UpdateDisplay

Scroll:
 ld de,$0000			; Change to NUMCOLS to enable word wrap
ScrollLoop:
 dec de
 ld a,(hl)
 inc hl
 or a \ ret z			; Go to next zero
 cp $09 \ call z,SkipTab
 ld a,d
 or e \ jr nz,ScrollLoop
 ret

SkipTab:			; Courtesy of Jeff Mears :)
 ld a,e
 and 11111000b
 ld e,a
 ret

InvertChoice:
 ld c,ixl			; c = current choice
 ld h,6*X_MAX/8
 ld l,c
 ROM_CALL(MUL_HL)		; hl = current choice * 72
 ld de,GRAPH_MEM+(7*X_MAX/8)
 add hl,de			; hl -> offset in GRAPH_MEM
Invert_NoRowCalc:
 ld b,7*X_MAX/8			; 7 lines to invert
InvertLoop:
 ld a,(hl) \ cpl \ ld (hl),a	; Invert the byte
 inc hl
 djnz InvertLoop
 ld a,c				; a = current choice
 jp CR_GRBCopy			; Draw screen and ret

StartGraphmemFill:
 ld hl,GRAPH_MEM
 ld de,GRAPH_MEM+1
 ld bc,X_MAX*Y_MAX/8-1
 ret

D_ZC_STR:			; Optimized (hacked) for TxtView
 xor a
 ld d,a
 ld e,(hl)			; Get char into e
 inc hl				; Point to next char
 or e \ ret z
 cp $09 \ jr z,Tab
 push bc \ push hl		; Save dataptr and coords
WhichFont:
 ld hl,DefaultFont		; Point to custom font
 add hl,de			; Because each sprite is 3 bytes
 add hl,de
 add hl,de
 push hl \ pop ix		; ix -> sprite for char
 ld a,b				; Save x-coord
 ld b,d				; b = 0
 ld h,b				; h = 0
 ld l,c				; l = y
 add hl,hl			; hl = 2y
 add hl,bc			; hl = 3y
 add hl,hl			; hl = 6y
 add hl,hl			; hl = 12y
 ld c,a				; Restore x-coord
 srl c \ srl c \ srl c		; x/8
 add hl,bc			; hl = 12y + x/8
 ld bc,GRAPH_MEM
 add hl,bc			; hl = 12y + x/8 + GRAPH_MEM
 cpl
 and 00000111b			; Get the remainder of x/8
 ld b,a
 add a,a
 add a,b			; Calculate number of jumps
 ld (C_CHARPUT_Shift+1),a	; Self-modifying code
 ld b,3				; Do 3 sets of 2 lines
 ld de,X_MAX/8-1		; de = offset to add to GRAPH_MEM
C_CHARPUT_Loop:
 push bc			; Save number of loops
 ld b,2				; Do 2 lines
 ld a,(ix)			; Get sprite data
 and 11110000b			; Mask out right half
C_CHARPUT_Loop2:
 ld c,d				; c = 0
C_CHARPUT_Shift:
 jr C_CHARPUT_Shift
 rra \ rr c
 rra \ rr c
 rra \ rr c
 rra \ rr c
 rra \ rr c
 rra \ rr c
 rra \ rr c
 xor (hl) \ ld (hl),a		; Write first byte
 inc hl
 ld a,c
 xor (hl) \ ld (hl),a		; Write second byte
 add hl,de			; Add offset
 ld a,(ix)			; Get sprite data
 add a,a \ add a,a \ add a,a \ add a,a
 djnz C_CHARPUT_Loop2
 pop bc				; Restore number of loops
 inc ix				; Point to next byte of sprite
 djnz C_CHARPUT_Loop
 pop hl \ pop bc		; Restore dataptr and coords
 ld a,b
 add a,CHARWIDTH		; x-coord += CHARWIDTH
CheckBounds:
 ld b,a
 cp X_MAX \ jr c,D_ZC_STR	; If x-coord >= X_MAX, we're done
 ret

Tab:				; Courtesy of Eric Piel :)
 ld a,b				; a = x-coord
 add a,TABSIZE*CHARWIDTH
 and 11100000b			; Round down to nearest TABSIZE*CHARWIDTH
 jr CheckBounds

DefaultFont:
#include 4x6font.inc

DOSfont:
#include DOSfont.inc
