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

; Ported to TI-83(+) by Sam Heald (evil_sam@hotmail.com)

; Program Information:
; TxtView allows you to view ASCII text files created by CalcText.exe on your TI-83(+).
; 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.83p     - The program that you send to your calc to view the text files (for ION 83).
; TxtView.8xp     - The program that you send to your calc to view the text files (for ION 83+).
; 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)
; [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:
; *** DO NOT TURN OFF THE CALC WHILE IN TXTVIEW!!! IT WILL CRASH!!! ***
; Allowing the calc to auto-power-down is okay though. You can also change the contrast 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.

; Credits:
; Jason Kovacs and Dan Englender, for tremendous help with the TI-83(+) version.
; Sam Heald for the initial TI-83 port.
; 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!

; Version History:
; 1.53- Modified ION version to use built-in ionDetect routine for more reliable program detection. ION 83: 2287 bytes.
;	ION 83+: 2292 bytes.
; 1.52- Fixed APD bug in 83(+) version. 2nd+ON still crashes though. ION 83: 2321 bytes. ION 83+: 2326 bytes.
; 1.51- Ported to TI-83(+) for SOS and ION by Sam Heald. SOS: 2325 bytes. ION 83: 2322 bytes. ION 83+: 2327 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.

#include ion.inc

#ifdef TI83P
.org progstart-2
.db $BB,$6D

#else
.org progstart
#endif

 xor a \ jr nc,Start

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

#define text(x,y,string) ld bc,256*x+y \ ld hl,string \ call D_ZC_STR
TABSIZE      = 8
CHARWIDTH    = 4
CHARHEIGHT   = 6
X_MAX        = 96
Y_MAX        = 64
NUMCOLS      = X_MAX/CHARWIDTH

#ifdef TI83
TEXT_MEM  = statram
#else
TEXT_MEM  = appbackupscreen
#endif
dataptr   = pencol			; Pointer to the current string
offset    = TEXT_MEM			; Offset used for left and right
num_files = TEXT_MEM+2			; Number of text files on the calc
top       = TEXT_MEM+3			; Pointer offset used when selecting files
files     = TEXT_MEM+4			; Storage space for program addresses

Start:
 ld hl,TEXT_MEM				; Clear the TEXT_MEM
 ld de,TEXT_MEM+1
 ld (hl),0
#ifdef TI83
 ld bc,530
#else
 ld bc,767
#endif
 ldir

 ld hl,(progptr)			; $FE6E
 ld bc,files				; bc -> area to store addresses

SearchLoop:
 ld ix,ID				; ix -> TxtView file ID string
 push bc
 call ionDetect
 pop bc
 jr nz,Done
 xor a
 cp (hl) \ jr nz,KeepSearching
 inc hl
 ld a,l \ ld (bc),a \ inc bc
 ld a,h \ ld (bc),a \ inc bc
 ld hl,num_files
 inc (hl)
KeepSearching:
 ex de,hl
 jr SearchLoop
Done:
 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 the screen

UpdateChoice:
 ld ixl,a
 call InvertChoice
Choose:
 bcall(_getkey)				; bc = 0
 ld bc,$0000
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 2 \ jr z,ChoiceUp			; cp K_UP
 dec a \ jr z,ChoiceDown		; cp K_DOWN
 dec a \ jr z,SelectFile		; cp K_ENTER
 sub 4 \ ret z				; cp K_CLEAR
 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 FastCopy				; Display the screen

Main:
 bcall(_getkey)				; bc = 0
 ld 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 5  \ 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 34 \ jr z,PageDown			; cp K_STAT
 sub 19 \ jr z,End			; cp K_GRAPH
 dec a  \ jr z,ToggleWordWrap		; cp K_MODE
 sub 4  \ jr z,InvertScreen		; cp K_YEDIT
 sub 17 \ jr z,Home			; cp K_TRACE
 sub 90 \ jr nz,Main			; cp K_XTO

ChangeFont:
 ld de,(WhichFont+1)
 ld hl,DefaultFont
 bcall(_cphlde) \ 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)		 \ ld (ScrollUpWrap+1),a
			\ xor 13 \ ld (WordWrap+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
 ld d,b					; 1
 ld e,c					; 1
 bcall(_cphlde)				; 3
 pop de					; 1
 jr c,_ScrollUpWrap			; 2
 ex de,hl				; 1
ScrollUpNoWrap:
 pop bc
 djnz _ScrollUp
Update:
 jp UpdateDisplay

Scroll:
 ld de,0				; 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
 bcall(_htimesl)			; 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
FastCopy:
 push af
 push bc
 push de
 push hl
 push ix
 call ionFastCopy
 pop ix
 pop hl
 pop de
 pop bc
 pop af
 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
 rrca \ rr c
 rrca \ rr c
 rrca \ rr c
 rrca \ rr c
 rrca \ rr c
 rrca \ rr c
 rrca \ 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	; Mask out left half and shift
 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

ID: .db $FF, 0

.end
