diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..52a676a --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +TARGETS = hgr.byte view.dhgr dhgr.byte +all: $(TARGETS) + +.PHONY: clean +clean: + $(RM) $(TARGETS) + +hgr.byte: hgr.byte.s + merlin32 $< + +view.dhgr: view.dhgr.s + merlin32 $< + +dhgr.byte: dhgr.byte.s + merlin32 $< + diff --git a/README.md b/README.md index 0930e22..8f06af6 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,21 @@ -#Apple 2 HGR BYTE Inspector +# Apple 2 DHGR/HGR BYTE Inspector ![hgr byte inspector screenshot](hgrbyte.png?raw=true) +![dhgr byte inspector screenshot](dhgrbyte.png?raw=true) +![view screenshot](viewdhgr.png?raw=true) Keys: ``` ESC Quit + g Toggle fullscreen G Toggle fullscreen + i Move cursor up + j Move cursor left + k Move cursor right + l Move cursor down + I Move cursor up J Move cursor left K Move cursor right @@ -47,3 +55,77 @@ Keys: = Paste byte (Set cursor byte from temporary) ``` +# DHGR Colors + +```Basic +0 DIM M(16) +1 FOR I=0 TO 7:READ M(I*2):M(I*2+1)=M(I*2)+8:NEXT:GOTO 9 +2 POKE A,X:RETURN +3 B3=C *2:IF B3 > 15 THEN B3 = B3 - 15 +4 B2=B3*2:IF B2 > 15 THEN B2 = B2 - 15 +5 B1=B2*2:IF B1 > 15 THEN B1 = B1 - 15 +6 B0=B1*2:IF B0 > 15 THEN B0 = B0 - 15 +7 RETURN +9 P=8192:PRINT CHR$(12);CHR$(21):REM 40-COL +10 TEXT:HOME:HGR2:HGR +20 FOR Y=0 TO 7 +30 Z=P + Y*1024 +40 A=Z+P+1:GOSUB 110 +50 A=Z+128:GOSUB 210 +60 A=Z+P+256:GOSUB 110 +70 A=Z+384+1:GOSUB 210 +80 NEXT +90 POKE 3*P-2,0:POKE 3*P-1,32:REM SQUIRT PREVIEW:DHGR +100 VTAB 22:PRINT "BSAVE PAL.DHGR,A$2000,L$4000":END +110 FOR C=0 TO 15:REM Aux/Main/Aux/Main +120 GOSUB 3:IF C=0 THEN 190 +130 A = A-P: X=M(B0):GOSUB 2 +140 A = A+P: X=M(B1):GOSUB 2 +150 A=A+1 +160 A = A-P: X=M(B2):GOSUB 2 +170 A = A+P: X=M(B3):GOSUB 2 +180 A=A+1:PRINT +190 NEXT:RETURN +210 FOR C=0 TO 15:REM Main/Aux/Main/Aux +220 GOSUB 3:IF C=0 THEN 290 +230 A = A+P: X=M(B0):GOSUB 2 +240 A=A+1 +250 A = A-P: X=M(B1):GOSUB 2 +260 A = A+P: X=M(B2):GOSUB 2 +270 A=A+1 +280 A = A-P: X=M(B3):GOSUB 2 +290 NEXT:RETURN +900 REM DHGR 16-colors +900 REM $00,$04,$44,$4C,$22,$2A,$66,$6E +901 REM $11,$19,$55,$5D,$33,$3B,$77,$7F +902 DATA 0,68 +903 DATA 34,102 +904 DATA 17,85 +905 DATA 51,119 +``` + +Memory order is: + +* $2000 -> AUX $2000 +* $4000 -> MAIN $2000 + +To view, you'll need a [DHGR Viewer](dhgr.view.s) + +# Tutankhamun + +Convertedt to dhgr via Sheldon's [tohgr](http://wsxyz.net/tohgr.html) + +``` +tohgr.mac -dhgr tut.png +``` + + +# Assembler + +* [Merlin32](https://www.brutaldeluxe.fr/products/crossdevtools/merlin/) + + +# License + +* [WTFPL](http://www.wtfpl.net/) + diff --git a/dhgr.byte b/dhgr.byte new file mode 100644 index 0000000..d2fd342 Binary files /dev/null and b/dhgr.byte differ diff --git a/dhgr.byte.s b/dhgr.byte.s new file mode 100644 index 0000000..c5dd3ae --- /dev/null +++ b/dhgr.byte.s @@ -0,0 +1,873 @@ +; Merlin32 + +CONFIG_DHGR = 1 + +; DHGR Byte Inspector +; https://github.com/Michaelangel007/apple2_hgrbyte/ +; +; Michael Pohoreski +; Version 21 +; +; TL:DR; +; IJKL to move +; Ctrl-IJKL to move to edges +; 0..9, A..F enter nibble into cursor byte +; +; Keys: +; +; ESC Quit +; G Toggle fullscreen +; +; i Move cursor up +; j Move cursor left +; k Move cursor right +; l Move cursor down +; +; I Move cursor up +; J Move cursor left +; K Move cursor right +; L Move cursor down +; +; ^I Move cursor to col 0 +; ^J Move cursor to col 39 +; ^K Move cursor to row 0 +; ^L Move cursor to row 191 +; RET Center cursor +; +; 0..9 "Append" hex nibble to cursor byte +; A..F +; +; ! Toggle bit 0 +; @ Toggle bit 1 +; # Toggle bit 2 +; $ Toggle bit 3 +; % Toggle bit 4 +; ^ Toggle bit 5 +; & Toggle bit 6 +; * Toggle bit 7 (high bit) +; SPC Toggle high bit of byte (bit 7) +; ( Set byte to $FF (Shift-9) +; ) Clear byte to $00 (Shift-0) +; ` Flip all bits +; ~ Flip all bits +; +; , Shift byte left (with zero) +; . Shift byte right (with one ) +; < Shift byte left (with zero) +; > Shift byte right (with one ) +; [ Rotate byte left +; ] Rotate byte right +; +; - Save cursor byte to temporary +; = Set cursor byte from temporary +; +; TODO: +; Z = clear aux mem +; X = swap aux/main mem +; M = mark begin/end -- show width,height,x,y +; +; These aren't needed +; S = save +; L = load + +CH = $24 ; 40-col text cursor column +CV = $25 ; 40-col text cursor row +CH80 = $57B ; 80-col text cursor column + +GBASL = $26 ; 16-bit pointer to start of D/HGR scanline +GBASH = $27 + + DO CONFIG_DHGR +WIDTH = 80 ; + ELSE +WIDTH = 40 ; HGR + FIN + +HPOSN = $F411 ; A=row, Y,X=col update GBASL GBASH +TABV = $FB5B ; A=row, ALL STA $25, JMP $FC22 + +HOME = $FC58 +SETTXT = $FB39 + +; In 80-col mode +; CWSL = $36 -> C307 Output +; KSWL = $38 -> C305 Input +;COUT80 = $C307 +;CR = $FC62 +;CROUT = $FD8E ; Output CR + +COUT = $FDED ; Char Output aka putch() +PR_HEX = $FDD3 +PRBYTE = $FDDA + +; When Store80 ON, Page 2=ON, read/writes touch aux mem +SW_STORE80 = $C000 ; STA $+0 OFF, $+1 ON +SW_STORE81 = $C001 ; $C055 = page 2 + +SW_SET40COL = $C00C ; 40-col mode +SW_SET80COL = $C00D ; 80-col mode + +SW_AUXRDOFF = $C002 ; $+0 = Read Main $0200..$BFFF, $+1 = Read Aux $0200..$BFFF +SW_AUXWROFF = $C004 ; $+0 = Write Main $0200..$BFFF, $+1 = Write Aux $0200..$BFFF + +SW_ALTCHAR0 = $C00E ; Mouse text off + +KEYBOARD = $C000 ; LDA +KEYSTROBE = $C010 +TXTCLR = $C050 ; Mode Graphics +MIXCLR = $C052 ; Full screen +MIXSET = $C053 ; Split screen +PAGE1 = $C054 +HIRES = $C057 ; Mode HGR +SW_DHGR = $C05E ; Mode DHGR +SW_SHGR = $C05F ; Mode SHGR + +cursor_row = $E2 ; used by Applesoft HGR.row +aux_ptr = $F7 ; copy of GBAS but points to HGR2 $40xx..$5Fxx +lastkey = $F9 +cursor_org = $FA ; When cursor is saved +cursor_tmp = $FB ; Flashing cursor byte +cursor_val = $FC ; Current byte under cursor + +FLAG_FULL = $01 ; == $1 -> $C052 MIXCLR +; ; == $0 -> $C053 MIXSET +flags = $FD ; + +temp = $FE +cursor_col = $FF ; x2 for DHGR +HGRPAGE = $E6 ; used by Applesoft HGR.page + ; $20=MAIN, $40=AUX + +MOV_SRC = $003C ; A1L +MOV_END = $003E ; A2L +MOV_DST = $0042 ; A4L +AUXMOVE = $C311 ; C=0 Aux->Main, C=1 Main->Aux +MOVE = $FE2C ; Main<->Main, *MUST* set Y=0 prior! + +__MAIN = $900 + + ORG __MAIN + +DhgrByte + LDA #21 ; Version - copy HGR1 to aux, HGR2 to HGR1 + JSR Init_Exit ; FEATURE: Set to 00 if you don't want to copy AUX $2000 to MAIN $4000 + + BIT PAGE1 ; Page 1 + BIT TXTCLR ; not text, but graphics + BIT MIXSET ; Split screen text/graphics + BIT HIRES ; HGR, no GR + + DO CONFIG_DHGR + STA SW_DHGR ; $C05E DHGR, not HGR + STA SW_SET80COL ; $C00D 80-col on + STA SW_STORE80 ; Turn off for app + FIN + + BRA _Center + +; Funcs that JMP to GetByte before GotKey() +; Funcs that JMP to PutByte after GotKey() +_Screen ; Toggle mixed/full screen + LDA flags ; A = %????_???? + AND #FLAG_FULL ; A = %0000_000f + TAX ; f=0 f=1 + STA MIXCLR,X ; C052 C053 + LDA flags ; FULL MIX + EOR #FLAG_FULL ; mode is based on old leading edge + STA flags ; not new trailing edge + BRA FlashByte +_HighBit + EOR #$80 + BRA PutByte + +_MoveL DEC cursor_col + BPL GetByte +_MoveR INC cursor_col + LDA cursor_col + CMP #WIDTH + BCC GetByte ; intentional fall into _EdgeR +_EdgeR LDA #WIDTH-1 + DB $2C ; BIT $ABS skip next instruction +_EdgeL LDA #0 + STA cursor_col + BPL GetByte ; always + +_MoveU DEC cursor_row + LDA cursor_row + CMP #$FF + BNE GetByte ; INTENTIONAL fall into if Y < 0 +_MoveD INC cursor_row + LDA cursor_row + CMP #192 + BCC GetByte ; intentional fall into +_EdgeD LDA #$BF + DB $2C ; BIT $ABS skip next instruction +_EdgeU LDA #00 + STA cursor_row + ADC #1 + BNE GetByte ; always + + +_Center + LDA #WIDTH/2 + STA cursor_col + LDA #192/2 + STA cursor_row ; intentional fall into GetByte + +GetByte ; Cursor position changed + LDY #0 ; Update pointer to screen + LDX #0 + LDA cursor_row + JSR HPOSN ; A=row, Y,X=col X->E0 Y->E1 + + DO CONFIG_DHGR +; NOTE $4000 is our shadow copy of AUX ram + LDA GBASL + STA aux_ptr+0 + LDA GBASH + AND #$1F + ORA #$40 + STA aux_ptr+1 + FIN + + JSR GetCursorByte +PutByte + STA cursor_val ; current value + STA cursor_tmp ; flashing cursor + JSR DrawStatus +FlashByte + JSR FlashCursorByte +GetKey + LDA KEYBOARD + BPL FlashByte + STA KEYSTROBE + AND #$7F ; Force ASCII + STA lastkey + CMP #'0' ; key < '0' + BCC TestHex + CMP #'9'+1 ; key < '9'+1 + BCC Nibble +TestHex + CMP #'A' + BCC LoadKeys + CMP #'F'+1 + BCC Nibble +LoadKeys + LDX #nKeys +FindKey + DEX + BMI FlashByte + CMP aKeys, X + BNE FindKey +GotKey + ; If code doesn't fit within +/-127 bytes + ; LDA aFuncHi, X + LDA #>__MAIN ; FuncAddressHi + PHA + LDA aFuncLo, X ; FuncAddressLo + PHA + LDA cursor_val ; Last displayed byte may be cursor_tmp + JSR SetCursorByte ; restore current val in case cursor moves + ; Also pre-load for ROL/R SHL/R + LDX #0 ; for Toggle Bit # + RTS ; And call function assigned to key + ; To BRUN under DOS.3 change above RTS to + ; RTS + ;_Exit JMP $3D0 ; DOS 3.3 Warmstart vector + + ; Functions starting with _ are invoked via keys +_Exit + LDA #$80 ; "Reverse" current DHGR mem config so it can be saved + JMP Init_Exit ; Exit to TEXT80 +_ByteFF + LDA #$FF + BNE PutByte ; always +_Byte00 + LDA #$00 + BEQ PutByte ; always +_FlipByte + LDX #$FF + BNE PutBits +_Rol CMP #$80 ; C=a A=%abcd_efgh +_SHL1 ROL ; C=b A=%bcde_fgha C<-bit7, bit0<-C + BRA PutByte ; force branch always + +_SHR1 ORA #$01 ; Force C=1 via ROR (SHR, OR #$80) + ; Intentional fall into _Ror + ; Using LSR instead of ROR to save a byte + ; 8 Byte version + ; CLC + ; ROR + ; BCC PutByte + ; ORA #$80 + ; BCS PutByte +_Ror LSR ; C=h A=%0abc_defg 0->bit7, bit0->C + BCC PutByte + ORA #$80 + BCS PutByte ; always + +_ShiftL ASL ; C=a A=%bcde_fgh0 + DB $A2 ; skip next LSR instruction; LDX dummy imm val +_ShiftR LSR ; C=h A=%0abc_defg + BRA PutByte + +_Bit7 INX ; intentional fall into +_Bit6 INX +_Bit5 INX +_Bit4 INX +_Bit3 INX +_Bit2 INX +_Bit1 INX +_Bit0 INX + TAY ; save cursor_val + SEC ; + LDA #0 ; X=? A=0 C=1 +TogBit ROL + DEX + BNE TogBit + TAX + TYA ; load cursor_val +; common _Bit# code +PutBits STX temp + EOR temp +GotoPutByte + BRA PutByte ; code is too far to directly branch + +_SaveByte + STA cursor_org ; intentional fall into +_LoadByte + +; ******************* +; NOTE: Above code must fit on 1 page! +; IF _LoadByte overflows the initial page then +; LDA aFuncLo PHA RTS +; will have unpredictable results! +; ******************** + ERR *-1/$9FF ; Merlin: Error if PC > $9FF + + LDA cursor_org + CLC + BCC GotoPutByte ; always +Nibble + LDY #3 +CursorSHL + ASL cursor_val + DEY + BPL CursorSHL + + LDA lastkey + CMP #$41 + BCC Digit + SBC #7 ; $41 - 6 - (C=1) = $3A +Digit + AND #$0F ; Key to Hex + CLC + ADC cursor_val + BCC GotoPutByte ; always + +; ------------------------------------------------------------------------ +DrawStatus + LDA #20 ; Cursor.Y = Top of 4 line split TEXT/HGR window + JSR VTAB_COL0 ; Cursor.X = 0 + + LDA #'X'+$80 ; X=## Y=## $=####:## + JSR COUT + + DO CONFIG_DHGR + LDY #0 ; src = &char[0][8] + LDA cursor_col + CLC + ROR + BCS HaveMainMem +HaveAuxMem + LDY #8 ; src = &char[1][8] +HaveMainMem + + LDX #0 ; dst = 0 +CopyMemType + LDA sMemType,Y + STA sTextFooter,X + INY ; src++ + INX ; dst++ + CPX #8 + BNE CopyMemType + FIN + + LDA cursor_col + JSR PR_HEX + LDA #' '+$80 + JSR COUT + LDA #'Y'+$80 + JSR COUT + LDA cursor_row + JSR PR_HEX + LDA #' '+$80 + JSR COUT + LDA #'$'+$80 + JSR COUT + LDA GBASH + JSR PR_HEX + LDA cursor_col + DO CONFIG_DHGR + LSR ; Aux/Main is interleaved + FIN + CLC + ADC GBASL + JSR PRBYTE + LDA #':'+$80 + JSR COUT + LDA cursor_val + JSR PRBYTE + LDA #' '+$80 + JSR COUT + +ReverseByte + LDX #8 + LDA cursor_val + STA temp +ReverseBit + ASL temp + ROR + DEX + BNE ReverseBit + STA temp + + PHA ; save reverse byte + + LDX #8 +PrintBitsNormal + LDA temp + JSR Bit2Asc + ROR temp + DEX + BNE PrintBitsNormal + + LDA #'~'+$80 + JSR COUT + + LDX #8 + LDA cursor_val +PrintBitsReverse + TAY + JSR Bit2Asc + TYA + LSR + DEX + BNE PrintBitsReverse + + LDA #'$'+$80 + JSR COUT + PLA ; restore reverse byte + JSR PRBYTE + + LDA cursor_org ; X=0 + PHA + LSR + LSR + LSR + LSR + JSR NibToInvTxt + PLA + JSR NibToInvTxt + + LDA #21 ; Cursor.Y = 21 + JSR VTAB_COL0 + + LDX #0 +PrintFooter1 + LDA sTextFooter, X + BEQ _DoneStatus + JSR COUT ; 4 line text window, 2nd row + INX + BNE PrintFooter1 ; (almost) always +_DoneStatus + +; Draw pixel grouping + LDA #22 + JSR VTAB_COL0 + LDA #29 + JSR HTAB + +; (x & 4) * 8 + LDA cursor_col ; src = sPixelFooter + AND #3 + ASL + ASL + ASL + TAX ; src += 8*col + LDY #8 ; char [4][8] +PrintFooter2 + LDA sPixelFooter,X + JSR COUT + INX + DEY ; len-- + BNE PrintFooter2 + RTS + +; Display nibble as inverse text +; 0 -> '0' $30 +; 9 -> '9' $39 +; A -> ^A $01 +; F -> ^F $06 +NibToInvTxt + AND #$F + ORA #'0'+$00 ; ASCII: +$80 + CMP #'9'+$01 ; ASCII: +$81 + BCC PrintSave + SBC #$39 ; C=1, $3A ':' -> $01 'A', $3F '?' -> $06 +PrintSave + STA sTextFooter+17,X ; VTAB 21:HTAB 17 and 18, Update Saved Byte + INX + RTS + +Bit2Asc + AND #$01 ; 0 -> B0 + BEQ FlipBit + LDA #$81 ; 1 -> 31 +FlipBit + EOR #$B0 + JMP COUT + +; OUT: +; A = byte +; USES: +; GBAS = HGR Page 1 ptr +; aux_ptr = HGR Page 2 AUX shadow copy +GetCursorByte ; Main Aux + + ; Col & 1 + ; Even = Read Aux + ; Odd = Read Main + DO CONFIG_DHGR + LDA cursor_col + CLC + ROR ; Aux/Main is interleaved + TAY ; y = byte column + BCS _get_main +_get_aux + STA SW_STORE80 + LDA (aux_ptr),Y + DB $2C ; OPTIMIZAITON: BRA _get_val -> BIT $abs - BIT $26B1 +_get_main + ELSE + LDY cursor_col + FIN + LDA (GBASL),Y +_get_val + STA cursor_val + RTS + +FlashCursorByte + LDA cursor_tmp + EOR #$FF + STA cursor_tmp +; OPTIMIZATION: Intentionally fall-into SetCursorByte to save 3-byte JMP + +; In: +; A = byte +SetCursorByte + ; Col & 1 + ; Even = Read Aux + ; Odd = Read Main + DO CONFIG_DHGR + TAX ; push byte + + LDA cursor_col + CLC + ROR + TAY + TXA ; pop byte + BCS _set_main + + STA SW_STORE80 + STA (aux_ptr),Y ; Shadow copy to HGR2 + STA SW_AUXWROFF+1 ; Write AUX +_set_main + ELSE + LDY cursor_col + FIN + STA (GBASL),Y ; Write to AUX or MAIN + STA SW_AUXWROFF ; Write MAIN + RTS + + + +; ======================================================================== +; Common code -- called by Init, Exit +Init_Exit + STA SW_SET80COL + + DO CONFIG_DHGR + STA SW_AUXRDOFF + STA SW_AUXWROFF + + STA SW_STORE80 + STA SW_ALTCHAR0 ; Turn off mouse-text + + LDX #$20 + LDY #$40 + +; Q. Why is this needed? +; A. After the program has initially run memory is in "reverse" order +; MAIN = 2000 +; AUX = 2000 +; In order to allow the user to BSAVE linear memory we need to end up: +; MAIN 2000 -> MAIN 4000 +; AUX 2000 -> MAIN 2000 +; +; A=pos Convert DHGR format: MAIN 2000->AUX 2000, MAIN 4000->MAIN 2000 +; A=neg Reverse DHGR format: MAIN 2000->MAIN 4000, AUX 2000->MAIN 2000 + PHA + PLA ; Force flags to be updated + BMI OnExit + +; HGR12 to AUX/MAIN +OnInit + ; Copy MAIN $2000 -> AUX $2000 + STX MOV_SRC+1 ; Src Hi + STY MOV_END+1 ; End Hi + STX MOV_DST+1 ; Dst Hi + CLC ; C=0 don't do MOVE + JSR SetDst00 + SEC ; C=1 Main to Aux + JSR AUXMOVE + + ; Copy MAIN $4000 -> MAIN $2000 + LDA #$60 + STY MOV_SRC+1 ; Src Hi + STA MOV_END+1 ; End Hi + STX MOV_DST+1 ; Dst Hi + SEC ; C=1 also do MOVE + JSR SetDst00 + + ; Copy AUX $2000 -> MAIN $4000 + LDY #$40 + STX MOV_SRC+1 ; Src Hi + STY MOV_END+1 ; End Hi + STY MOV_DST+1 ; Dst Hi + CLC ; C=0 Aux to Main + JSR SetDst00 ; C=0 don't do MOVE + JSR AUXMOVE + BRA DoneCopy + +; "Unlinearize" interleaved AUX/MAIN memory +; so it can be saved in a linear format +OnExit + + ; Copy MAIN $2000 -> MAIN $4000 + STX MOV_SRC+1 ; Src Hi + STY MOV_END+1 ; End Hi + STY MOV_DST+1 ; Dst Hi + SEC ; C=1 also do MOVE + JSR SetDst00 + + ; Copy AUX $2000 -> MAIN $2000 + LDY #$40 + STX MOV_SRC+1 ; Src Hi + STY MOV_END+1 ; End Hi + STX MOV_DST+1 ; Dst Hi + CLC ; C=0 Aux to Main + JSR SetDst00 + JSR AUXMOVE + + STA SW_SET40COL ; $C00C + STA SW_DHGR+1 ; $C05F DHGR off + STA SW_STORE81 ; Enable Page 2 switching +DoneCopy + FIN + + JSR SETTXT + JSR HOME + + LDX #$20 + STX HGRPAGE + + LDA #0 ; also used by PrintFooter + STA flags + STA cursor_col + STA cursor_row + STA cursor_org + +; OPTIMIZATION: Intentionally fall into HTAB + +; ------------------------------------------------------------------------ +; Move cursor to row=A,col=0 +; A = Row +VTAB_COL0 + JSR TABV ; update CV +HTAB00 + LDA #0 +HTAB + STA CH + STA CH80 + + RTS + +; ------------------------------------------------------------------------ +; Set src,dst,end low byte pointers to zero +; C=1 Also do MOVE +SetDst00 + STZ MOV_SRC+0 ; Src Lo + STZ MOV_DST+0 ; Dst Hi + STZ MOV_END+0 ; End Lo + BCC DoneDst00 + LDY #0 + JMP MOVE +DoneDst00 + RTS + + +; ======================================================================== +sTextFooter + ; 1 2 3 + ; 0123456789012345678901234567890123456789 + ; "X=## Y=## $=####:## %%%%%%%%~%%%%%%%%$00" + ASC "---/---- SAVE:?? 76543210 " + INV "12345678" ;1-8 INVERSE +; DB '1' & $3F +; DB '2' & $3F +; DB '3' & $3F +; DB '4' & $3F +; DB '5' & $3F +; DB '6' & $3F +; DB '7' & $3F +; DB '8' & $3F + DB $00 + +; char [2][8] +sMemType + ASC "AUX/----" + ASC "---/MAIN" + +; These alternative text groupings +; don't look as good in DHGR 80-col text mode +; "()" +; "<>" +; "{}" +; "[] +; The brackets look the best +; +; char [4][8] +; 12345678123456781234567812345678 +; Show which pixel group the bits belong to +sPixelFooter + ASC "[11]222 " + ASC "2[33]44 " + ASC "44[55]6 " + ASC "666[77] " + + +; ------------------------------------------------------------------------ +; Keys are searched in reverse order +; Sorted by least used to most used +aKeys + DB '[' & $1F ; _Exit ESC Quit + ASC 'g' ; _Screen G Toggle fullscreen + ASC 'G' ; _Screen G Toggle fullscreen + + DB 'H' & $1F ; _MoveL <- Ctrl-H $08 + DB 'U' & $1F ; _MoveR -> Ctrl-U $15 + + DB 'I' & $1F ; _EdgeU ^I Ctrl-I $09 Move cursor to row 0 + DB 'J' & $1F ; _EdgeL ^J Ctrl-J $0A Move cursor to col 0 + DB 'K' & $1F ; _EdgeD ^K Ctrl-K $0B Move cursor to row 191 + DB "L" & $1F ; _EdgeR ^L Ctrl-L $0C Move cursor to col 39 + DB "M" & $1F ; _Center Center cursor + + ASC 'i' ; _moveU Move cursor Up + ASC 'j' ; _moveL Move cursor Left + ASC 'k' ; _MoveD Move cursor Down + ASC 'l' ; _MoveR Move cursor Right + + ASC 'I' ; _moveU Move cursor Up + ASC 'J' ; _moveL Move cursor Left + ASC 'K' ; _MoveD Move cursor Down + ASC 'L' ; _MoveR Move cursor Right + + ASC '!' ; _Bit0 Toggle bit 0 + ASC '@' ; _Bit1 Toggle bit 1 + ASC '#' ; _Bit2 Toggle bit 2 + ASC '$' ; _Bit3 Toggle bit 3 + ASC '%' ; _Bit4 Toggle bit 4 + ASC '^' ; _Bit5 Toggle bit 5 + ASC '&' ; _Bit6 Toggle bit 6 + ASC '*' ; _Bit7 Toggle bit 7 + + DO CONFIG_DHGR + ASC ' ' ; _HighBit Toggle high bit of byte (bit 7) is useless in DHGR mode + ELSE + ASC ' ' ; _HighBit Toggle high bit of byte (bit 7) + FIN + ASC '(' ; _ByteFF Set byte to FF (Shift-9) + ASC ')' ; _Byte00 Set byte to 00 (Shift-0) + ASC '`' ; _FlipByte Toggle all bits + ASC '~' ; _FlipByte + + ASC ',' ; _ShiftL (with zero) + ASC '<' ; _ShiftL (with one ) + ASC '.' ; _ShiftR (with zero) + ASC '>' ; _ShiftR (with one ) + ASC '[' ; _Rol + ASC ']' ; _Ror + + ASC '-' ; _SaveByte Save cursor byte + ASC '=' ; _LoadByte Load cursor byte +eKeys +nKeys = eKeys - aKeys ; + +; Table of function pointers +; *Note*: Must match aKeys order! +aFuncLo + DB <_Exit -1 ; ESC + DB <_Screen -1 ; g + DB <_Screen -1 ; G + + DB <_MoveL -1 ; <- + DB <_MoveR -1 ; -> + + DB <_EdgeU -1 ; ^I + DB <_EdgeL -1 ; ^J + DB <_EdgeD -1 ; ^K + DB <_EdgeR -1 ; ^L + DB <_Center -1 ; RET + + DB <_MoveU -1 ; 'i' + DB <_MoveL -1 ; 'j' + DB <_MoveD -1 ; 'k' + DB <_MoveR -1 ; 'l' + + DB <_MoveU -1 ; 'I' + DB <_MoveL -1 ; 'J' + DB <_MoveD -1 ; 'K' + DB <_MoveR -1 ; 'L' + + DB <_Bit0 -1 ; '1' + DB <_Bit1 -1 ; '2' + DB <_Bit2 -1 ; '3' + DB <_Bit3 -1 ; '4' + DB <_Bit4 -1 ; '5' + DB <_Bit5 -1 ; '6' + DB <_Bit6 -1 ; '7' + DB <_Bit7 -1 ; '8' + + DO CONFIG_DHGR + DB <_HighBit -1 ; SPC + ELSE + DB <_HighBit -1 ; SPC + FIN + DB <_ByteFF -1 ; '(' + DB <_Byte00 -1 ; ')' + DB <_FlipByte-1 ; '`' + DB <_FlipByte-1 ; '~' not a dup mistake + + DB <_ShiftL -1 ; `,` + DB <_SHL1 -1 ; `<` + DB <_ShiftR -1 ; `.` + DB <_SHR1 -1 ; `>` + DB <_Rol -1 ; '[' + DB <_Ror -1 ; '] + + DB <_SaveByte-1 ; '=' + DB <_LoadByte-1 ; '-' +__END ; Needed for DOS 3.3 + diff --git a/dhgrbyte.png b/dhgrbyte.png new file mode 100644 index 0000000..b59842d Binary files /dev/null and b/dhgrbyte.png differ diff --git a/hgr.byte b/hgr.byte new file mode 100644 index 0000000..f600d24 Binary files /dev/null and b/hgr.byte differ diff --git a/hgr.byte.s b/hgr.byte.s new file mode 100644 index 0000000..73af082 --- /dev/null +++ b/hgr.byte.s @@ -0,0 +1,556 @@ +; Merlin32 + +CONFIG_PRODOS = 1 + +; HGR Byte Inspector +; https://github.com/Michaelangel007/apple2_hgrbyte/ +; +; Michael Pohoreski +; Version 17 +; +; TL:DR; +; IJKL to move +; Ctrl-IJKL to move to edges +; 0..9, A..F enter nibble into cursor byte +; +; Keys: +; +; ESC Quit +; G Toggle fullscreen +; +; i Move cursor up +; j Move cursor left +; k Move cursor right +; l Move cursor down +; +; I Move cursor up +; J Move cursor left +; K Move cursor right +; L Move cursor down +; +; ^I Move cursor to col 0 +; ^J Move cursor to col 39 +; ^K Move cursor to row 0 +; ^L Move cursor to row 191 +; RET Center cursor +; +; 0..9 "Append" hex nibble to cursor byte +; A..F +; +; ! Toggle bit 0 +; @ Toggle bit 1 +; # Toggle bit 2 +; $ Toggle bit 3 +; % Toggle bit 4 +; ^ Toggle bit 5 +; & Toggle bit 6 +; * Toggle bit 7 (high bit) +; SPC Toggle high bit of byte (bit 7) +; ( Set byte to $FF (Shift-9) +; ) Clear byte to $00 (Shift-0) +; ` Flip all bits +; ~ Flip all bits +; +; , Shift byte left (with zero) +; . Shift byte right (with one ) +; < Shift byte left (with zero) +; > Shift byte right (with one ) +; [ Rotate byte left +; ] Rotate byte right +; +; - Save cursor byte to temporary +; = Set cursor byte from temporary + + +GBASL = $26 ; 16-bit pointer to start of D/HGR scanline +GBASH = $27 + +WIDTH = 40 ; HGR + +CH = $24 ; text cursor column +CV = $25 ; text cursor row +ROW_20 = $0550 ; VTAB 21 TEXT address +ROW_21 = $06D0 ; VTAB 22 TEXT address +ROW_22 = $0750 ; VTAB 23 TEXT address +ROW_23 = $07D0 ; VTAB 24 TEXT address + +HPOSN = $F411 ; A=row, Y,X=col update GBASL GBASH +TABV = $FB5B ; A=row, ALL STA $25, JMP $FC22 +VTAB = $FC22 ; $25=row //e LDA $25, STA$28 +HOME = $FC58 +COUT = $FDED +PR_HEX = $FDD3 +PRBYTE = $FDDA + +KEYBOARD = $C000 +SW_SET40COL = $C00C ; 40-col mode +KEYSTROBE = $C010 +TXTCLR = $C050 ; Mode Graphics +MIXCLR = $C052 ; Full screen +MIXSET = $C053 ; Split screen +PAGE1 = $C054 +HIRES = $C057 ; Mode HGR + +lastkey = $F9 +cursor_row = $E2 ; used by Applesoft HGR.row +cursor_org = $FA ; When cursor is saved +cursor_tmp = $FB ; Flashing cursor byte +cursor_val = $FC ; Current byte under cursor +flags = $FD ; +temp = $FE +cursor_col = $FF +FLAG_FULL = $01 ; == $1 -> $C052 MIXCLR +; ; == $0 -> $C053 MIXSET +HGRPAGE = $E6 ; used by Applesoft HGR.page + +__MAIN = $0900 + + DO CONFIG_PRODOS + ORG __MAIN + ELSE + ORG __MAIN - 4 + DW __MAIN + DW __END-*-2 + FIN + +HgrByte + LDA #17 ; Version + STA SW_SET40COL ; Version 17 force 40-col mode + BIT PAGE1 ; Page 1 + BIT TXTCLR ; not text, but graphics + BIT MIXSET ; Split screen text/graphics + BIT HIRES ; HGR, no GR + + LDX #0 ; also used by PrintFooter + STX flags + STX cursor_col + STX cursor_row + STX cursor_org + STX GBASL + LDA #$20 + STA GBASH + STA HGRPAGE +PrintFooter + LDA TextFooter, X + BEQ _Center ; Default to center of screen + STA ROW_21,X ; 4 line text window, 2nd row + INX + BNE PrintFooter ; (almost) always + +; Funcs that JMP to GetByte before GotKey() +; Funcs that JMP to PutByte after GotKey() +_Screen ; Toggle mixed/full screen + LDA flags ; A = %????_???? + AND #FLAG_FULL ; A = %0000_000f + TAX ; f=0 f=1 + STA MIXCLR,X ; C052 C053 + LDA flags ; FULL MIX + EOR #FLAG_FULL ; mode is based on old leading edge + STA flags ; not new trailing edge + BRA FlashByte +_HighBit + EOR #$80 + BRA PutByte + +_MoveL DEC cursor_col + BPL GetByte +_MoveR INC cursor_col + LDA cursor_col + CMP #WIDTH + BCC GetByte ; intentional fall into _EdgeR +_EdgeR LDA #WIDTH-1 + DB $2C ; BIT $ABS skip next instruction +_EdgeL LDA #0 + STA cursor_col + BPL GetByte ; always + +_MoveU DEC cursor_row + LDA cursor_row + CMP #$FF + BNE GetByte ; INTENTIONAL fall into if Y < 0 +_MoveD INC cursor_row + LDA cursor_row + CMP #192 + BCC GetByte ; intentional fall into +_EdgeD LDA #$BF + DB $2C ; BIT $ABS skip next instruction +_EdgeU LDA #00 + STA cursor_row + ADC #1 + BNE GetByte ; always + + +_Center + LDA #WIDTH/2 + STA cursor_col + LDA #192/2 + STA cursor_row ; intentional fall into GetByte + +GetByte ; Cursor position changed + LDY #0 ; Update pointer to screen + LDX #0 + LDA cursor_row + JSR HPOSN ; A=row, Y,X=col X->E0 Y->E1 + JSR GetCursorByte +PutByte + STA cursor_val ; current value + STA cursor_tmp ; flashing cursor + JSR DrawStatus +FlashByte + JSR FlashCursorByte +GetKey + LDA KEYBOARD + BPL FlashByte + STA KEYSTROBE + AND #$7F ; Force ASCII + STA lastkey + CMP #'0' ; key < '0' + BCC TestHex + CMP #'9'+1 ; key < '9'+1 + BCC Nibble +TestHex + CMP #'A' + BCC LoadKeys + CMP #'F'+1 + BCC Nibble +LoadKeys + LDX #nKeys +FindKey + DEX + BMI FlashByte + CMP aKeys, X + BNE FindKey +GotKey + ; If code doesn't fit within +/-127 bytes + ; LDA aFuncHi, X + LDA #>__MAIN ; FuncAddressHi + PHA + LDA aFuncLo, X ; FuncAddressLo + PHA + LDA cursor_val ; Last displayed byte may be cursor_tmp + JSR SetCursorByte ; restore current val in case cursor moves + ; Also pre-load for ROL/R SHL/R + LDX #0 ; for Toggle Bit # +_Exit RTS ; And call function assigned to key + ; To BRUN under DOS.3 change above RTS to + ; RTS + ;_Exit JMP $3D0 ; DOS 3.3 Warmstart vector + + ; Functions starting with _ are invoked via keys +_ByteFF + LDA #$FF + BNE PutByte ; always +_Byte00 + LDA #$00 + BEQ PutByte ; always +_FlipByte + LDX #$FF + BNE PutBits +_Rol CMP #$80 ; C=a A=%abcd_efgh +_SHL1 ROL ; C=b A=%bcde_fgha C<-bit7, bit0<-C + BRA PutByte ; force branch always + +_SHR1 ORA #$01 ; Force C=1 via ROR (SHR, OR #$80) + ; Intentional fall into _Ror + ; Using LSR instead of ROR to save a byte + ; 8 Byte version + ; CLC + ; ROR + ; BCC PutByte + ; ORA #$80 + ; BCS PutByte +_Ror LSR ; C=h A=%0abc_defg 0->bit7, bit0->C + BCC PutByte + ORA #$80 + BCS PutByte ; always + +_ShiftL ASL ; C=a A=%bcde_fgh0 + DB $A2 ; skip next LSR instruction; LDX dummy imm val +_ShiftR LSR ; C=h A=%0abc_defg + BRA PutByte + +_Bit7 INX ; intentional fall into +_Bit6 INX +_Bit5 INX +_Bit4 INX +_Bit3 INX +_Bit2 INX +_Bit1 INX +_Bit0 INX + TAY ; save cursor_val + SEC ; + LDA #0 ; X=? A=0 C=1 +TogBit ROL + DEX + BNE TogBit + TAX + TYA ; load cursor_val +; common _Bit# code +PutBits STX temp + EOR temp +GotoPutByte + BRA PutByte ; code is too far to directly branch +_SaveByte + STA cursor_org ; intentional fall into +_LoadByte + +; ******************* +; NOTE: Above code must fit on 1 page! +; IF _LoadByte overflows the initial page then +; LDA aFuncLo PHA RTS +; will have unpredictable results! +; ******************** + ERR *-1/$9FF ; Merlin: Error if PC > $9FF + + LDA cursor_org + CLC + BCC GotoPutByte ; always +Nibble + LDY #3 +CursorSHL + ASL cursor_val + DEY + BPL CursorSHL + + LDA lastkey + CMP #$41 + BCC Digit + SBC #7 ; $41 - 6 - (C=1) = $3A +Digit + AND #$0F ; Key to Hex + CLC + ADC cursor_val + BCC GotoPutByte ; always + +TextFooter + ; "X=## Y=## $=####:## %%%%%%%%~%%%%%%%%" + ASC " SAVE:?? 76543210 " + INV "12345678" ;1-8 INVERSE + DB $00 + +; ------------------------------------------------------------------------ +DrawStatus + LDA #0 ; Cursor.X = 0 + STA CH + LDA #20 ; Cursor.Y = Top of 4 line split TEXT/HGR window + JSR TABV ; update CV +PrintStatus + LDA #'X'+$80 ; X=## Y=## $=####:## + JSR COUT + LDA cursor_col + JSR PR_HEX + LDA #' '+$80 + JSR COUT + LDA #'Y'+$80 + JSR COUT + LDA cursor_row + JSR PR_HEX + LDA #' '+$80 + JSR COUT + LDA #'$'+$80 + JSR COUT + LDA GBASH + JSR PR_HEX + LDA GBASL + CLC + ADC cursor_col + JSR PRBYTE + LDA #':'+$80 + JSR COUT + LDA cursor_val + JSR PRBYTE + LDA #' '+$80 + JSR COUT + +ReverseByte + LDX #8 + LDA cursor_val + STA temp +ReverseBit + ASL temp + ROR + DEX + BNE ReverseBit + STA temp + + PHA ; save reverse byte + + LDX #8 +PrintBitsNormal + LDA temp + JSR Bit2Asc + ROR temp + DEX + BNE PrintBitsNormal + + LDA #'~'+$80 + JSR COUT + + LDX #8 + LDA cursor_val +PrintBitsReverse + TAY + JSR Bit2Asc + TYA + LSR + DEX + BNE PrintBitsReverse + + LDA #'$'+$80 + JSR COUT + PLA ; restore reverse byte + JSR PRBYTE + + LDA cursor_org ; X=0 + PHA + LSR + LSR + LSR + LSR + JSR NibToInvTxt + PLA +; Display nibble as inverse text +; 0 -> '0' $30 +; 9 -> '9' $39 +; A -> ^A $01 +; F -> ^F $06 +NibToInvTxt + AND #$F + ORA #'0'+$00 ; ASCII: +$80 + CMP #'9'+$01 ; ASCII: +$81 + BCC PrintSave + SBC #$39 ; C=1, $3A ':' -> $01 'A', $3F '?' -> $06 +PrintSave + STA ROW_21+17,X + INX + RTS + +Bit2Asc + AND #$01 ; 0 -> B0 + BEQ FlipBit + LDA #$81 ; 1 -> 31 +FlipBit + EOR #$B0 + JMP COUT + +; A = byte +GetCursorByte + LDY cursor_col + LDA (GBASL),Y + STA cursor_val +SetCursorByte + LDY cursor_col + STA (GBASL),Y + RTS + +FlashCursorByte + LDA cursor_tmp + EOR #$FF + STA cursor_tmp + JMP SetCursorByte + +; Keys are searched in reverse order +; Sorted by least used to most used +aKeys + DB '[' & $1F ; _Exit ESC Quit + ASC 'g' ; _Screen G Toggle fullscreen + ASC 'G' ; _Screen G Toggle fullscreen + + DB 'H' & $1F ; _MoveL <- Ctrl-H $08 + DB 'U' & $1F ; _MoveR -> Ctrl-U $15 + + DB 'I' & $1F ; _EdgeU ^I Ctrl-I $09 Move cursor to row 0 + DB 'J' & $1F ; _EdgeL ^J Ctrl-J $0A Move cursor to col 0 + DB 'K' & $1F ; _EdgeD ^K Ctrl-K $0B Move cursor to row 191 + DB "L" & $1F ; _EdgeR ^L Ctrl-L $0C Move cursor to col 39 + DB "M" & $1F ; _Center Center cursor + + ASC 'i' ; _moveU Move cursor Up + ASC 'j' ; _moveL Move cursor Left + ASC 'k' ; _MoveD Move cursor Down + ASC 'l' ; _MoveR Move cursor Right + + ASC 'I' ; _moveU Move cursor Up + ASC 'J' ; _moveL Move cursor Left + ASC 'K' ; _MoveD Move cursor Down + ASC 'L' ; _MoveR Move cursor Right + + ASC '!' ; _Bit0 Toggle bit 0 + ASC '@' ; _Bit1 Toggle bit 1 + ASC '#' ; _Bit2 Toggle bit 2 + ASC '$' ; _Bit3 Toggle bit 3 + ASC '%' ; _Bit4 Toggle bit 4 + ASC '^' ; _Bit5 Toggle bit 5 + ASC '&' ; _Bit6 Toggle bit 6 + ASC '*' ; _Bit7 Toggle bit 7 + + ASC ' ' ; _HighBit Toggle high bit of byte (bit 7) + ASC '(' ; _ByteFF Set byte to FF (Shift-9) + ASC ')' ; _Byte00 Set byte to 00 (Shift-0) + ASC '`' ; _FlipByte Toggle all bits + ASC '~' ; _FlipByte + + ASC ',' ; _ShiftL (with zero) + ASC '<' ; _ShiftL (with one ) + ASC '.' ; _ShiftR (with zero) + ASC '>' ; _ShiftR (with one ) + ASC '[' ; _Rol + ASC ']' ; _Ror + + ASC '-' ; _SaveByte Save cursor byte + ASC '=' ; _LoadByte Load cursor byte +eKeys +nKeys = eKeys - aKeys ; + +; Table of function pointers +; *Note*: Must match aKeys order! +aFuncLo + DB <_Exit -1 ; ESC + DB <_Screen -1 ; g + DB <_Screen -1 ; G + + DB <_MoveL -1 ; <- + DB <_MoveR -1 ; -> + + DB <_EdgeU -1 ; ^I + DB <_EdgeL -1 ; ^J + DB <_EdgeD -1 ; ^K + DB <_EdgeR -1 ; ^L + DB <_Center -1 ; RET + + DB <_MoveU -1 ; 'i' + DB <_MoveL -1 ; 'j' + DB <_MoveD -1 ; 'k' + DB <_MoveR -1 ; 'l' + + DB <_MoveU -1 ; 'I' + DB <_MoveL -1 ; 'J' + DB <_MoveD -1 ; 'K' + DB <_MoveR -1 ; 'L' + + DB <_Bit0 -1 ; '1' + DB <_Bit1 -1 ; '2' + DB <_Bit2 -1 ; '3' + DB <_Bit3 -1 ; '4' + DB <_Bit4 -1 ; '5' + DB <_Bit5 -1 ; '6' + DB <_Bit6 -1 ; '7' + DB <_Bit7 -1 ; '8' + + DB <_HighBit -1 ; SPC + DB <_ByteFF -1 ; '(' + DB <_Byte00 -1 ; ')' + DB <_FlipByte-1 ; '`' + DB <_FlipByte-1 ; '~' not a dup mistake + + DB <_ShiftL -1 ; `,` + DB <_SHL1 -1 ; `<` + DB <_ShiftR -1 ; `.` + DB <_SHR1 -1 ; `>` + DB <_Rol -1 ; '[' + DB <_Ror -1 ; '] + + DB <_SaveByte-1 ; '=' + DB <_LoadByte-1 ; '-' +__END ; Needed for DOS 3.3 + diff --git a/hgrbyte.dsk b/hgrbyte.dsk index e789e14..dc741b4 100644 Binary files a/hgrbyte.dsk and b/hgrbyte.dsk differ diff --git a/hgrbyte.png b/hgrbyte.png index 01aeac9..9d8f5cd 100644 Binary files a/hgrbyte.png and b/hgrbyte.png differ diff --git a/view.dhgr b/view.dhgr new file mode 100644 index 0000000..843d93b Binary files /dev/null and b/view.dhgr differ diff --git a/view.dhgr.s b/view.dhgr.s new file mode 100644 index 0000000..445a02e --- /dev/null +++ b/view.dhgr.s @@ -0,0 +1,93 @@ +; Show DHGR image in AUX,MAIN format +; +; References: +; Apple IIe #3 +; Double High-Resolution Graphics +; * http://www.1000bit.it/support/manuali/apple/technotes/aiie/tn.aiie.03.html + +MOV_SRC = $003C ; A1L +MOV_END = $003E ; A2L +MOV_DST = $0042 ; A4L + +KEY = $C000 +KEYSTROBE = $C010 + +STORE80 = $C000 +RDMAINRAM = $C002 ; 2=OFF, 3=ON +WRMAINRAM = $C004 + +SET40COL = $C00C +SET80COL = $C00D + +CLRALTCHAR = $C00E + +AUXMOVE = $C311 ; Main<->Aux +MOVE = $FE2C ; Main<->Main + +GR = $C050 +FULL = $C052 +MIXED = $C053 +HGR = $C057 +AN3 = $C05E + +SETTXT = $FB39 +HOME = $FC58 + +; ======================================== + ORG $1F80 + +; Main + STA STORE80 + STA RDMAINRAM + STA WRMAINRAM + + STA SET40COL + STA CLRALTCHAR + +; Copy MAIN $2000..$3FFF to AUX $2000 + + LDA #$20 ; + STA MOV_SRC+1 ; Src Hi + STA MOV_DST+1 ; Dst Hi + LDA #$40 + STA MOV_END+1 ; End Hi + + LDA #$00 ; + STA MOV_SRC+0 ; Src Lo + STA MOV_DST+0 ; Dst Hi + STA MOV_END+0 ; End Lo + + SEC ; C=1 Main to Aux + JSR AUXMOVE + +; Copy MAIN $4000..$5FFF to MAIN $2000 + LDA #$40 + STA MOV_SRC+1 ; Src Hi + LDA #$20 + STA MOV_DST+1 ; Dst Hi + LDA #$60 + STA MOV_END+1 ; End Hi + + LDA #$00 ; + STA MOV_SRC+0 ; Src Lo + STA MOV_DST+0 ; Dst Hi + STA MOV_END+0 ; End Lo + + JSR MOVE + +; Showtime! + STA HGR ; $C057 + STA GR ; $C050 + STA AN3 ; $C05E + STA FULL ; $C052 + STA SET80COL ; $C00D + +WaitForKey + LDA KEY + BPL WaitForKey + STA KEYSTROBE + + STA SET40COL ; $C00C + STA AN3+1 ; $C05F + JSR SETTXT + JMP HOME diff --git a/viewdhgr.png b/viewdhgr.png new file mode 100644 index 0000000..d381909 Binary files /dev/null and b/viewdhgr.png differ