; 6502bench SourceGen v1.7.3 ; ; Note I omitted the first two bytes $00 $08 (file code length $0800) from this ; file. This may not have been a great idea, as you'll need to prepend them to ; the assembly output to create a working relocatable object file. blink_delay .eq $50 {const} ;cursor blink delay MON_WNDLEFT .eq $20 ;left column of scroll window MON_WNDWDTH .eq $21 ;width of scroll window MON_WNDTOP .eq $22 ;top of scroll window MON_WNDBTM .eq $23 ;bottom of scroll window MON_CH .eq $24 ;cursor horizontal displacement MON_CV .eq $25 ;cursor vertical displacement MON_BASL .eq $28 ;base address for text output (lo) MON_BASH .eq $29 ;base address for text output (hi) MON_CSWL .eq $36 ;character output hook (lo) MON_CSWH .eq $37 ;character output hook (hi) MON_KSWL .eq $38 ;character input hook (lo) MON_KSWH .eq $39 ;character input hook (hi) MON_RNDL .eq $4e ;low byte of KEYIN "random" value MON_RNDH .eq $4f ;high byte of KEYIN "random" value DOS_CHARIO .eq $03ea ;jumps to routine that connects DOS KSW/CSW KBD .eq $c000 ;R last key pressed + 128 KBDSTRB .eq $c010 ;RW keyboard strobe TXTCLR .eq $c050 ;RW display graphics MIXCLR .eq $c052 ;RW display full screen TXTPAGE1 .eq $c054 ;RW display page 1 TXTPAGE2 .eq $c055 ;RW display page 2 (or read/write aux mem) HIRES .eq $c057 ;RW display hi-res graphics MON_BELL .eq $fbdd ;Sound bell unconditionally MON_VTAB .eq $fc22 ;tab to row specified in Acc MON_VTABZ .eq $fc24 MON_CLREOP .eq $fc42 ;clear screen from cursor to end of page MON_HOME .eq $fc58 ;clear screen and reset text output to top-left MON_SCROLL .eq $fc70 ;scroll up one line MON_CLREOL .eq $fc9c ;clear to end of line MON_COUT .eq $fded ;print Acc to output device via $36-37 .org $8e00 8e00: 4c 0f 8e hires1_entry jmp start NOTE: I'm not sure this is true -- it just seems to skip banner output. ; Per the documentation, this secondary entry point is used when the application ; does not want to use hires page 1 at all, only page 2. It skips clearing and ; enabling HIRES page 1. 8e03: 4c 20 8e hires2_entry jmp L8E20 8e06: 0a version .dd1 $0a ;version number * 10 ; Poked by the loader to point to the first alternate charset. ; As this defaults to the builtin charset, both ^A 0 and ^A 1 ; will display the builtin charset if no alternates are loaded. 8e07: 00 93 first_charset .dd2 builtin_charset ; Vectors for user subroutines A (^O ^Y) and B (^O ^Z). ; The user pokes these with the desired addresses. They are ; also initialized on startup and when defaults are restored, ; so the value here is arbitrary. 8e09: 4c 48 90 userA jmp return ;user subroutine A 8e0c: 4c 48 90 userB jmp return ;user subroutine B 8e0f: 20 20 8e start jsr L8E20 ; Output our banner to hires screen. 8e12: a0 00 ldy #$00 8e14: b9 43 8e @loop lda L8E43,y 8e17: 20 ed fd jsr MON_COUT 8e1a: c8 iny 8e1b: c0 1d cpy #$1d 8e1d: 90 f5 bcc @loop 8e1f: 60 rts 8e20: a9 75 L8E20 lda #<ksw_handler 8e22: 85 38 sta MON_KSWL 8e24: a9 8e lda #>ksw_handler 8e26: 85 39 sta MON_KSWH 8e28: a9 19 lda #<csw_handler 8e2a: 85 36 sta MON_CSWL 8e2c: a9 8f lda #>csw_handler 8e2e: 85 37 sta MON_CSWH 8e30: 20 ea 03 jsr DOS_CHARIO ;connect DOS to our handlers 8e33: 2c 52 c0 bit MIXCLR ;select full screen graphics 8e36: 2c 57 c0 bit HIRES ;select hires graphics 8e39: 20 55 91 jsr set_defaults 8e3c: 20 0e 90 jsr L900E 8e3f: 2c 50 c0 bit TXTCLR ;actually enable graphics mode 8e42: 60 rts 8e43: 90 L8E43 .dd1 $90 8e44: c8 c9 ad d2+ .str ↑“HI-RES CHAR GEN VERSION 1.0”,$8d 8e60: 00 esc_state .dd1 $00 8e61: 00 key_state .dd1 $00 ;keypress state. $80 after ^A, $40 after ^O, or $00 8e62: 20 hires_page .dd1 $20 ;$20 (hires1) or $40 (hires2) 8e63: 00 inverse_video .dd1 $00 ;$7f if inverse video active, $00 otherwise NOTE: Looks like a bitfield. If bit 7 ($80) is set, character will overstrike (i.e. the background is ignored). If bit 6 ($40) is set, character will complement (in the shape of its fg or bg). Apparently, "overstrike" and "transparent" modes ($80) are identical except sourcing from different pages; so are "complement" and "reverse" ($C0) modes. They are probably named in this odd way for mnemonic reasons, matching their control chars. 8e64: 00 transparent_mode .dd1 $00 ;$80=overstrike, $C0=complement, $00=no mask 8e65: 00 overlay_mode .dd1 $00 ;$60 to use second screen for overlay (^O ^T/^R) 8e66: 00 wrap_mode .dd1 $00 ;$FF if wrap mode, $00 if scroll 8e67: 00 lower_case .dd1 $00 ;$80=lower case, $00=upper 8e68: 00 93 active_charset .dd2 builtin_charset ;pointer to active charset 8e6a: d0 a0 a0 a0+ var_save .junk 8 ;save 8 bytes above during block mode 8e72: 00 block_mode .dd1 $00 ;$ff=block mode on, $00=off 8e73: 00 block_ch .dd1 $00 ;home col in block mode, or else $00 8e74: 00 block_cv .dd1 $00 ;home row in block mode, or else $00 ; ; KSW handler ; ; Wait for a keypress, blinking the cursor (an underscore) while waiting. The ; "random" KEYIN value is updated similarly to the monitor ROM, though at a ; different rate. Also handle ESC state and commands. ; ; On entry, A is the char that was present on the text screen (the caller may ; have overwritten it with a cursor, and standard KSW restores it). Y is the ; horizontal cursor position (MON_CH). ; ; On exit, A is the key pressed. X and Y are preserved. ; basl .var $2a {addr/1} bash .var $2b {addr/1} saveX .var $eb {addr/1} 8e75: 91 28 ksw_handler sta (MON_BASL),y ;restore char to text screen 8e77: 86 eb stx saveX ;we reuse X as a counter 8e79: a5 28 lda MON_BASL ;text screen low byte conveniently 8e7b: 85 2a sta basl ;matches low byte for hires line text*8 8e7d: a5 29 lda MON_BASH ;convert text screen hi byte to hires line 8e7f: 09 1c ora #$1c ;text 04..07 -> 1C..1F 8e81: 0d 62 8e ora hires_page ;-> 3C..3F (page 1) or 5C..5F (page 2) 8e84: 85 2b sta bash ;== 7th line of each 8-line char 8e86: a2 01 ldx #$01 ;initial countdown -- invert almost immediately 8e88: b1 2a lda (basl),y ;grab the 7th (cursor) line to invert it 8e8a: 48 pha ;also save it on the stack 8e8b: e6 4e @jiffy inc MON_RNDL ;count up "random" KEYIN value 0000..FFFF 8e8d: d0 0b bne @checkkey ;check for 8-bit wrap 8e8f: e6 4f inc MON_RNDH ;and propagate to high byte 8e91: ca dex ;did we cycle to $FFFF X times? 8e92: d0 06 bne @checkkey ;no, not yet time to invert cursor ; Blink the cursor. 8e94: 49 7f eor #$7f ;invert cursor line 8e96: 91 2a sta (basl),y ;and write it to screen 8e98: a2 50 ldx #blink_delay ;countdown until next invert 8e9a: 2c 00 c0 @checkkey bit KBD ;key pressed? 8e9d: 10 ec bpl @jiffy ;no, continue cycling 8e9f: 68 pla ;an eternity later, a key is pressed 8ea0: 91 2a sta (basl),y ;restore the original 7th line 8ea2: ba tsx NOTE: TODO -- why stack lookbehind 8ea3: bd 04 01 lda $0104,x ;?? 8ea6: c9 f8 cmp #$f8 ;?? 8ea8: ad 00 c0 lda KBD ;get keypress 8eab: 2c 10 c0 bit KBDSTRB ;clear keyboard strobe 8eae: 90 1b bcc @doneX ;if stack value < $f8 (BIT preserved carry) 8eb0: 48 pha ;save keypress 8eb1: 2c 60 8e bit esc_state ;esc flag (bit 7) set earlier? 8eb4: 30 18 bmi esc_char ;yes, this is ESC arg 8eb6: c9 9b cmp #$9b ;ESC pressed now? 8eb8: d0 07 bne @check_cr 8eba: a9 80 lda #$80 ;yes, ESC pressed 8ebc: 8d 60 8e sta esc_state ;set ESC flag for next time 8ebf: d0 09 bne @doneA ;always 8ec1: c9 8d @check_cr cmp #$8d ;return pressed? 8ec3: d0 05 bne @doneA 8ec5: 20 8e 92 jsr clear_hv_eol NOTE: Need to verify MON_CH is not updated. It's not clear it's absolutely necessary to restore Y based on the ROM. In particular, look at ESC-@ and cursor movement. 8ec8: a4 24 @doneY ldy MON_CH ;restore Y (horiz pos) 8eca: 68 @doneA pla ;restore A (the keypress) 8ecb: a6 eb @doneX ldx saveX ;restore X 8ecd: 60 rts ; Handle key pressed in escape mode (in accumulator). 8ece: c9 c9 esc_char cmp #‘I’ | $80 8ed0: 90 0c bcc not_esc_ijkm 8ed2: c9 cd cmp #‘M’ | $80 8ed4: f0 36 beq esc_c_or_m 8ed6: b0 06 bcs not_esc_ijkm 8ed8: c9 cb cmp #‘K’ | $80 8eda: f0 2b beq esc_a_or_k NOTE: How does doing nothing work for ESC J? 8edc: 90 ec bcc @doneA ;'J' -- do nothing ; Keypress is not IJKM (cursor keys). Disable ESC state going forward. 8ede: 0e 60 8e not_esc_ijkm asl esc_state ;disable escape state ($80->$00) 8ee1: c9 c0 cmp #‘@’ | $80 ;ESC-@ ? 8ee3: d0 06 bne not_esc_null 8ee5: 20 71 92 jsr clear_home 8ee8: 4c c8 8e jmp @doneY 8eeb: c9 c5 not_esc_null cmp #‘E’ | $80 8eed: d0 06 bne not_esc_e 8eef: 20 8e 92 jsr clear_hv_eol 8ef2: 4c c8 8e jmp @doneY 8ef5: c9 c6 not_esc_e cmp #‘F’ | $80 8ef7: d0 06 bne not_esc_f 8ef9: 20 7b 92 jsr clear_hv_eowin 8efc: 4c c8 8e jmp @doneY 8eff: c9 c3 not_esc_f cmp #‘C’ | $80 8f01: f0 09 beq esc_c_or_m 8f03: c9 c1 cmp #‘A’ | $80 8f05: d0 c3 bne @doneA ; After ESC, ABCD and IJKM are cursor movement synonyms, except ABCD exits ; escape mode after one keypress, and IJKM remains in escape mode. 8f07: c8 esc_a_or_k iny 8f08: c4 21 cpy MON_WNDWDTH 8f0a: 90 bc bcc @doneY 8f0c: a4 25 esc_c_or_m ldy MON_CV 8f0e: c8 iny 8f0f: c4 23 cpy MON_WNDBTM 8f11: 90 b5 bcc @doneY 8f13: 20 22 92 jsr scroll 8f16: 4c c8 8e jmp @doneY saveY .var $35 {addr/1} ;preserve Y saveX .var $eb {addr/1} ;preserve X saveA .var $ff {addr/1} ;preserve A 8f19: 85 ff csw_handler sta saveA ;preserve A/X/Y 8f1b: 86 eb stx saveX 8f1d: 84 35 sty saveY 8f1f: 20 29 8f jsr @csw1 8f22: a4 35 ldy saveY 8f24: a6 eb ldx saveX 8f26: a5 ff lda saveA 8f28: 60 rts 8f29: 29 7f @csw1 and #$7f 8f2b: 0e 61 8e asl key_state ;was set to $80 by Ctrl-A keypress 8f2e: b0 57 bcs ctrl_a_arg ;so if carry set after ASL, key is arg to Ctrl-A 8f30: c9 20 cmp #$20 ;control char 00..1f 8f32: 90 03 bcc control_char 8f34: 4c 7c 91 jmp draw_char ;draw char to screen, probably ; Control char 00..1f encountered in output string. Jump to corresponding ; function in table if 00..1a (@-Z), or do nothing (?) if 1b..1f. Note Ctrl-@ is ; not documented. 8f37: c9 1b control_char cmp #$1b 8f39: b0 0a bcs @ret ;1b..1f are not operations 8f3b: 0a asl A ;offset is 2*jump entry 8f3c: aa tax 8f3d: bd 4a 8f lda function_table+1,x ;put jump table entry on stack 8f40: 48 pha 8f41: bd 49 8f lda function_table,x ;for jump via RTS 8f44: 48 pha 8f45: 0e 61 8e @ret asl key_state ;sets carry if contained $80 8f48: 60 rts ;execute function 00..1b ; Jump table used by previous subroutine to handle control character input (with ; low byte -1 for RTS adjustment). Function names are based on the toolkit ; documentation, with the control char ("CB" for ^B) prepended. This is a state ; machine and a couple chars only have meaning after a ^O; those have e.g. "COR" ; for ^O ^R prepended. 8f49: 47 90 function_table .dd2 return-1 ;^@ -- noop 8f4b: 7e 8f .dd2 CA_select_charset-1 ;^A -- select charset 8f4d: aa 8f .dd2 CB_begin_block-1 8f4f: d2 8f .dd2 CC_carriage_return-1 8f51: ed 8f .dd2 CD_delimit_block-1 8f53: 1b 90 .dd2 CE_clear_eol-1 8f55: 23 90 .dd2 CF_clear_eop-1 8f57: dc fb .dd2 MON_BELL-1 ;^G -- sound bell 8f59: 2b 90 .dd2 CH_backspace-1 ;^H move cursor left (no rubout) 8f5b: 40 90 .dd2 CI_inverse_video-1 8f5d: 04 92 .dd2 CJ_linefeed-1 8f5f: 48 90 .dd2 CK_caps_lock-1 8f61: 50 90 .dd2 CL_lower_case-1 8f63: 58 90 .dd2 CM_unknown?-1 8f65: 73 90 .dd2 CN_normal_video-1 8f67: 7b 90 .dd2 CO_option_sel-1 8f69: 8e 90 .dd2 CP_clear_page-1 8f6b: 9f 90 .dd2 CQ_home_cursor-1 8f6d: ae 90 .dd2 COR_reverse-1 8f6f: bb 90 .dd2 CS_shift-1 8f71: c9 90 .dd2 COT_transparent-1 8f73: 47 90 .dd2 return-1 ;Ctrl-U -- noop 8f75: d6 90 .dd2 CV_textwin_ul-1 8f77: 04 91 .dd2 CW_textwin_lr-1 8f79: 47 90 .dd2 return-1 ;Ctrl-X -- noop 8f7b: 2b 91 .dd2 CY_textwin_full-1 8f7d: 52 91 .dd2 CZ_restore_default-1 ; Ctrl-A -- select charset. 8f7f: b0 24 CA_select_charset bcs COA_primary_hires1 ;^O ^A -- primary screen 1 8f81: a9 80 lda #$80 ;set flag indicating Ctrl-A was pressed 8f83: 8d 61 8e sta key_state 8f86: 60 rts ; ; Process ^A argument, the charset number to activate. Anything other than 1..9 ; selects the builtin charset. 1 selects the lowest charset in memory, 2 selects ; lowest + $0300, and so on. ; ; There is no further bounds check on the charset number, so if you select ; charset 4 when 3 are loaded, you will get garbage. (Since LOADHRCG loads ; charsets directly below HRCG, charsets 4 and 5 would be HRCG code and charset ; 6 is $100 into the builtin charset, which effectively shifts uppercase to ; lowercase!) ; temp .var $ee {addr/1} ;temp for multiply by 3 8f87: e9 31 ctrl_a_arg sbc #$31 ;convert digit 1..9 ($31..39) to $00..$08 8f89: c9 09 cmp #$09 ;check if char was < 1 or > 9 8f8b: b0 0d bcs @use_internal ;via unsigned cmp >= $09, so $00..$08 are valid 8f8d: ae 07 8e ldx first_charset ;X = low byte of lowest charset in memory 8f90: 85 ee sta temp ;now compute page offset; 3 pages per charset 8f92: 0a asl A 8f93: 65 ee adc temp ;A = A * 3 via temp 8f95: 6d 08 8e adc first_charset+1 ;A = high byte of desired charset 8f98: d0 04 bne @update_charset ;questionable -- tests for zero page? 8f9a: a2 00 @use_internal ldx #<builtin_charset ;select builtin charset, usually when 0 pressed 8f9c: a9 93 lda #>builtin_charset 8f9e: 8e 68 8e @update_charset stx active_charset 8fa1: 8d 69 8e sta active_charset+1 8fa4: 60 rts 8fa5: a9 20 COA_primary_hires1 lda #$20 ;write to hires 1 ($2000) now 8fa7: 8d 62 8e sta hires_page 8faa: 60 rts 8fab: b0 20 CB_begin_block bcs COB_primary_hires2 8fad: 2c 72 8e bit block_mode 8fb0: 30 10 bmi L8FC2 8fb2: a9 ff lda #$ff 8fb4: 8d 72 8e sta block_mode 8fb7: a0 07 ldy #$07 ;length of variable area 8fb9: b9 62 8e L8FB9 lda hires_page,y ;save variables 8fbc: 99 6a 8e sta var_save,y ;for when we delimit block 8fbf: 88 dey 8fc0: 10 f7 bpl L8FB9 8fc2: a5 24 L8FC2 lda MON_CH 8fc4: 8d 73 8e sta block_ch 8fc7: a5 25 lda MON_CV 8fc9: 8d 74 8e sta block_cv 8fcc: 60 rts 8fcd: a9 40 COB_primary_hires2 lda #$40 ;write to hires 2 ($4000) now 8fcf: 8d 62 8e sta hires_page 8fd2: 60 rts 8fd3: b0 0e CC_carriage_return bcs COC_complement ;^O ^C -- complement 8fd5: ad 73 8e lda block_ch 8fd8: c5 24 cmp MON_CH 8fda: 85 24 sta MON_CH 8fdc: f0 02 beq L8FE0 8fde: b0 68 bcs return 8fe0: 4c 05 92 L8FE0 jmp CJ_linefeed 8fe3: a9 c0 COC_complement lda #$c0 8fe5: 8d 64 8e sta transparent_mode 8fe8: a9 00 lda #$00 8fea: 8d 65 8e sta overlay_mode 8fed: 60 rts 8fee: b0 1e CD_delimit_block bcs L900E 8ff0: 2c 72 8e bit block_mode 8ff3: 10 53 bpl return 8ff5: a0 07 ldy #$07 ;length of variable area 8ff7: b9 6a 8e L8FF7 lda var_save,y ;restore original variables 8ffa: 99 62 8e sta hires_page,y ;saved during block begin 8ffd: 88 dey 8ffe: 10 f7 bpl L8FF7 9000: a9 00 lda #$00 9002: 8d 72 8e sta block_mode 9005: 8d 73 8e sta block_ch 9008: a5 22 lda MON_WNDTOP 900a: 8d 74 8e sta block_cv 900d: 60 rts 900e: 2c 54 c0 L900E bit TXTPAGE1 9011: ad 62 8e lda hires_page 9014: c9 40 cmp #$40 9016: d0 30 bne return 9018: 2c 55 c0 bit TXTPAGE2 901b: 60 rts 901c: b0 2a CE_clear_eol bcs return 901e: 20 8e 92 jsr clear_hv_eol 9021: 4c 9c fc jmp MON_CLREOL ;sync the text screen ; Ctrl-F -- clear to end of page. 9024: b0 22 CF_clear_eop bcs return 9026: 20 7b 92 jsr clear_hv_eowin 9029: 4c 42 fc jmp MON_CLREOP ;sync the text screen ; Move the cursor backward one space. 902c: c6 24 CH_backspace dec MON_CH 902e: 10 18 bpl return 9030: a5 21 lda MON_WNDWDTH 9032: 85 24 sta MON_CH 9034: c6 24 dec MON_CH 9036: a5 22 lda MON_WNDTOP 9038: c5 25 cmp MON_CV 903a: b0 0c bcs return 903c: c6 25 dec MON_CV 903e: 4c 22 fc jmp MON_VTAB 9041: b0 05 CI_inverse_video bcs return 9043: a9 7f lda #$7f 9045: 8d 63 8e sta inverse_video 9048: 60 return rts 9049: b0 fd CK_caps_lock bcs return 904b: a9 00 lda #$00 904d: 8d 67 8e sta lower_case 9050: 60 rts 9051: b0 f5 CL_lower_case bcs return 9053: a9 80 lda #$80 9055: 8d 67 8e sta lower_case 9058: 60 rts 9059: ac 00 c0 CM_unknown? ldy KBD 905c: 10 13 bpl L9071 905e: c0 93 cpy #$93 9060: d0 0f bne L9071 9062: 2c 10 c0 bit KBDSTRB 9065: ac 00 c0 L9065 ldy KBD 9068: 10 fb bpl L9065 906a: c0 83 cpy #$83 906c: f0 03 beq L9071 906e: 2c 10 c0 bit KBDSTRB 9071: 4c 01 92 L9071 jmp cr_lf 9074: b0 d2 CN_normal_video bcs return ;^O ^N -- unused 9076: a9 00 lda #$00 9078: 8d 63 8e sta inverse_video 907b: 60 rts 907c: b0 06 CO_option_sel bcs set_overstrike ;second ^O -- overstrike 907e: a9 40 lda #$40 ;first ^O -- record in state machine 9080: 8d 61 8e sta key_state 9083: 60 rts 9084: a9 80 set_overstrike lda #$80 9086: 8d 64 8e sta transparent_mode 9089: a9 00 lda #$00 908b: 8d 65 8e sta overlay_mode 908e: 60 rts 908f: b0 06 CP_clear_page bcs COP_print ;^O ^P -- print 9091: 20 71 92 jsr clear_home ;plain ^P 9094: 4c 58 fc jmp MON_HOME ; Set overwrite ("print") mode, the default mode, where character bg and fg both ; replace underlying graphics. 9097: a9 00 COP_print lda #$00 9099: 8d 64 8e sta transparent_mode 909c: 8d 65 8e sta overlay_mode 909f: 60 rts 90a0: b0 a6 CQ_home_cursor bcs return ;^O ^Q -- unused 90a2: ad 73 8e lda block_ch 90a5: 85 24 sta MON_CH 90a7: ad 74 8e lda block_cv 90aa: 85 25 sta MON_CV 90ac: 4c 24 fc jmp MON_VTABZ 90af: 90 74 COR_reverse bcc return1 ;^R -- unused 90b1: a9 c0 lda #$c0 ;^O ^R 90b3: 8d 64 8e sta transparent_mode 90b6: a9 60 lda #$60 90b8: 8d 65 8e sta overlay_mode 90bb: 60 rts 90bc: b0 06 CS_shift bcs COS_scroll 90be: a9 c0 lda #$c0 90c0: 8d 67 8e sta lower_case 90c3: 60 rts 90c4: a9 00 COS_scroll lda #$00 90c6: 8d 66 8e sta wrap_mode 90c9: 60 rts 90ca: 90 59 COT_transparent bcc return1 ;plain ^T -- unused 90cc: a9 80 lda #$80 ;^O ^T 90ce: 8d 64 8e sta transparent_mode 90d1: a9 60 lda #$60 90d3: 8d 65 8e sta overlay_mode 90d6: 60 rts 90d7: b0 4c CV_textwin_ul bcs return1 ;^O ^V -- unused 90d9: a5 24 lda MON_CH ;window-relative 90db: 65 20 adc MON_WNDLEFT 90dd: c9 28 cmp #40 ;Ensure left is < 40 90df: 90 02 bcc @setleft 90e1: a9 27 lda #39 90e3: 85 20 @setleft sta MON_WNDLEFT 90e5: 38 sec 90e6: a5 21 lda MON_WNDWDTH 90e8: e5 24 sbc MON_CH 90ea: 85 21 sta MON_WNDWDTH 90ec: a9 00 lda #$00 90ee: 85 24 sta MON_CH 90f0: a5 25 lda MON_CV 90f2: c9 18 cmp #24 ;Ensure top is < 24 90f4: 90 02 bcc @settop 90f6: a9 17 lda #23 90f8: 85 22 @settop sta MON_WNDTOP 90fa: 2c 72 8e bit block_mode 90fd: 30 03 bmi @vtab 90ff: 8d 74 8e sta block_cv ;update block top if block mode 9102: 4c 22 fc @vtab jmp MON_VTAB 9105: b0 1f CW_textwin_lr bcs COW_wrap 9107: a5 24 lda MON_CH 9109: 85 21 sta MON_WNDWDTH 910b: 65 20 adc MON_WNDLEFT 910d: c9 28 cmp #40 910f: 90 06 bcc L9117 9111: a9 27 lda #39 9113: e5 20 sbc MON_WNDLEFT 9115: 85 21 sta MON_WNDWDTH 9117: e6 21 L9117 inc MON_WNDWDTH 9119: a5 25 lda MON_CV 911b: c9 18 cmp #24 911d: 90 02 bcc L9121 911f: a9 17 lda #23 9121: 85 23 L9121 sta MON_WNDBTM 9123: e6 23 inc MON_WNDBTM 9125: 60 return1 rts 9126: a9 ff COW_wrap lda #$ff 9128: 8d 66 8e sta wrap_mode 912b: 60 rts 912c: b0 22 CY_textwin_full bcs COY_userA 912e: a5 20 textwin_full lda MON_WNDLEFT 9130: 65 24 adc MON_CH 9132: 85 24 sta MON_CH 9134: a9 00 lda #$00 9136: 85 20 sta MON_WNDLEFT 9138: 85 22 sta MON_WNDTOP 913a: 2c 72 8e bit block_mode 913d: 30 06 bmi @block 913f: 8d 73 8e sta block_ch ;set to $00 when not in block mode 9142: 8d 74 8e sta block_cv ;but should already have been $00 outside block mode 9145: a9 28 @block lda #40 9147: 85 21 sta MON_WNDWDTH 9149: a9 18 lda #24 914b: 85 23 sta MON_WNDBTM 914d: 4c 22 fc jmp MON_VTAB 9150: 4c 09 8e COY_userA jmp userA 9153: b0 24 CZ_restore_default bcs COZ_userB 9155: a9 00 set_defaults lda #$00 9157: a0 12 ldy #$12 ;zero out 13 bytes of variable data 9159: 99 62 8e @zero sta hires_page,y 915c: 88 dey 915d: 10 fa bpl @zero 915f: 20 2e 91 jsr textwin_full 9162: 20 9a 8f jsr @use_internal 9165: 20 a5 8f jsr COA_primary_hires1 ; Amusingly, this sets the user jump vectors to a different RTS (return1) ; than the one included in the file (return). 9168: a9 25 lda #<return1 916a: 8d 0a 8e sta userA+1 916d: 8d 0d 8e sta userB+1 9170: a9 91 lda #>return1 9172: 8d 0b 8e sta userA+2 9175: 8d 0e 8e sta userB+2 9178: 60 rts 9179: 4c 0c 8e COZ_userB jmp userB NOTE: Below, "caps lock" refers to the default state where uppercase and lowercase are printed without modification (we should call it "unmodified" state) -- it does not force everything to uppercase. "lowercase mode" prints both as lowercase. "shift" prints the first char as unmodified, then switches to lowercase mode. ; Draw char to hires screen and (invisibly) to text screen, scrolling or ; wrapping both as needed. screen .var $ec {addr/2} ;screen address to read from charptr .var $ee {addr/2} ;pointer to this char bitmap 917c: 0e 61 8e draw_char asl key_state ;if waiting for arg to ^O, 917f: b0 a4 bcs return1 ; cancel ^O and return 9181: 2c 67 8e bit lower_case ;enforced lowercase? 9184: 10 0f bpl @unmodified ;no, unmodified char 9186: 50 05 bvc @lower ;lowercase, but shift disengaged 9188: 0e 67 8e asl lower_case ;shift engaged; mode $c0->$80 sets followup lowercase 918b: b0 08 bcs @unmodified ;always (unmodified initial char) ; Charsets cover keys $20..$7f, so normally we align the effective char# to ; $00..$5f by subtracting $20 from the key. But if lowercase is enforced, we ; skip the adjustment for A-Z, as lowercase is uppercase+$20. 918d: c9 41 @lower cmp #‘A’ 918f: 90 04 bcc @unmodified ;key < A, do not lowercase 9191: c9 5b cmp #‘[’ ;'Z'+1 9193: 90 03 bcc @draw ;A-Z -- make it lowercase 9195: 38 @unmodified sec ;key is unmodified, so 9196: e9 20 sbc #$20 ;align $20..$7f to $00..$5f ; A now contains the index into the charset for this key, $00..$5f. We multiply ; this by 8 (bytes/char) to get the byte offset. Finally we add this offset to ; the active charset address to get the char's bitmap pointer. The bit ; manipulation below is just an 8x8=16bit multiply (really a 3-bit left shift of ; 8 bits into 16) followed by a 16-bit add. 9198: 0a @draw asl A ;Multiply A by 2 9199: 26 ef rol charptr+1 ;shift the overflow into high byte 919b: 0a asl A ;by 4 919c: 26 ef rol charptr+1 ;shift again 919e: 0a asl A ;by 8 919f: 26 ef rol charptr+1 ;shift again 91a1: 18 clc 91a2: 6d 68 8e adc active_charset ;16-bit add (low) 91a5: 85 ee sta charptr ;char bitmap (low) 91a7: a5 ef lda charptr+1 ;get high byte from 8x8 multiply 91a9: 29 07 and #$07 ;limit to the 3 shifted bits 91ab: 6d 69 8e adc active_charset+1 ;16-bit add (high) 91ae: 85 ef sta charptr+1 ;char bitmap (high) ; charptr now points to the bitmap for this char. 91b0: 20 cc 92 jsr compute_cur_base ;get hires addr of first col of this row 91b3: 65 24 adc MON_CH ;A is basl, add column position 91b5: 85 2a sta basl ;basl += col; dest addr to write to 91b7: 85 ec sta screen ;<screen = basl; source addr when combining 91b9: a5 2b lda bash 91bb: 4d 65 8e eor overlay_mode ;eor $60 flips $20 to $40 and vice versa 91be: 85 ed sta screen+1 ;>screen = bash, adjusted for source hires page 91c0: a4 24 ldy MON_CH 91c2: a5 ff lda saveA ;recall original keypress value 91c4: 91 28 sta (MON_BASL),y ;and store to text screen 91c6: a2 00 ldx #$00 ;for (zp) indexing, X always 0 91c8: a0 00 ldy #$00 ;line number in char ; Read a bitmap line, combine it with the source, and write it to the screen. 91ca: b1 ee @charline lda (charptr),y ;load 7-pixel char line 91cc: 4d 63 8e eor inverse_video ;flip all bits if inverted 91cf: 2c 64 8e bit transparent_mode ;check overstrike method 91d2: 10 11 bpl @store ;if bit 7 clear, opaque 91d4: 70 0d bvs @complement ;if bit 6 set 91d6: 2c 63 8e bit inverse_video ;is inverse_video set ($7f) 91d9: 70 04 bvs @invert ;yes, bit 6 set when $7f 91db: 01 ec ora (screen,x) ;combine (OR, blend) with source pixels 91dd: 50 06 bvc @store ;always 91df: 21 ec @invert and (screen,x) ;combine (AND) source with char mask 91e1: 70 02 bvs @store 91e3: 41 ec @complement eor (screen,x) ;flip (XOR) source with char mask (X=0) 91e5: 81 2a @store sta (basl,x) ;update screen (X=0) 91e7: c8 iny ;next char line 91e8: c0 08 cpy #$08 91ea: b0 0d bcs @chardone ;wrote 8 lines, done 91ec: a5 2b lda bash 91ee: 69 04 adc #$04 ;(carry is clear) 91f0: 85 2b sta bash ;next hires line is +$0400 91f2: 4d 65 8e eor overlay_mode ;adjust source addr for overlay mode again 91f5: 85 ed sta screen+1 ;update source addr 91f7: 90 d1 bcc @charline ;always 91f9: e6 24 @chardone inc MON_CH ;update horiz pos 91fb: a5 24 lda MON_CH 91fd: c5 21 cmp MON_WNDWDTH ;did we exceed window width? 91ff: 90 20 bcc @return ;no, we are done ; Move cursor to beginning of line (CR) and then fall through to linefeed (LF). 9201: a9 00 cr_lf lda #$00 ;reset col to beginning of line 9203: 85 24 sta MON_CH ; Move the cursor to the next row, without changing the column -- a linefeed. ; Wrap or scroll as needed if we reached the bottom. 9205: e6 25 CJ_linefeed inc MON_CV ;go to next line 9207: a5 25 lda MON_CV 9209: c5 23 cmp MON_WNDBTM ;check vertical pos 920b: 90 11 bcc @reposition ;still within window 920d: 2c 66 8e bit wrap_mode ;in wrap mode (bit 6)? 9210: 70 08 bvs @wrap ;yes; wrap to top 9212: c6 25 dec MON_CV ;scrolling; adjust pos to last line 9214: 20 22 92 jsr scroll ;scroll hires window 9217: 4c 70 fc jmp MON_SCROLL ;scroll text window and exit 921a: a5 22 @wrap lda MON_WNDTOP ;set vert pos to top of window 921c: 85 25 sta MON_CV 921e: 20 24 fc @reposition jsr MON_VTABZ ;reposition text mode cursor 9221: 60 @return rts ; Scroll hires window up one row, clearing the last row. 9222: a5 22 scroll lda MON_WNDTOP 9224: 48 pha ;save row number 9225: 20 ce 92 jsr compute_base ; ; On the last iteration through the start_eol line loop drawing hires lines 0..7 ; of row N, bash is $3c, $3d, $3e or $3f for line 7, and we wind up here again ; for line 0 of row N+1. (bash is not incremented to line 8.) AND #$E3 makes ; that $20, $21, $22, or $23, effectively taking bash back to line 0. Since bash ; always points to the next line (the copy source), we can now store this in ; screen (dest), increasing it by one row. basl/h is then recomputed after ; adding 1 to the row. ; ; Equivalently, a mask of $e3 zeros the 3 bits 4, 3, and 2. Assume these are ; zeroed in the address corresponding to line 0, the first of 8 lines. Then, ; adding $0400 to this address (to reach the next hires line) sets bit 2, and ; adding $0400 7 times to reach line 7 will set bits 4, 3 and 2 (0b111 == 7). So ; AND #$E3 is the same as subtracting $04*7==$1C (0b00011100) from the high ; byte, returning the pointer to line 0. ; 9228: a5 2a @next_row lda basl 922a: 85 ec sta screen 922c: a5 2b lda bash 922e: 29 e3 and #$e3 ;return bash line 7 to line 0 9230: 85 ed sta screen+1 ;inc dest line by 8 (row by 1) 9232: 68 pla ;restore row number 9233: 18 clc 9234: 69 01 adc #$01 ;inc row 9236: c5 23 cmp MON_WNDBTM ;did we hit bottom? 9238: b0 22 bcs @clear_last ;yes, finish up 923a: 48 pha ;save row number (for next loop, above) 923b: 20 ce 92 jsr compute_base ;set basl/bash for next line 923e: a2 07 ldx #$07 ;8 rows, char height 9240: a4 21 @start_eol ldy MON_WNDWDTH ;start at end of window line 9242: 88 dey 9243: b1 2a @copy_byte lda (basl),y ;copy byte 9245: 91 ec sta (screen),y 9247: 88 dey ;and move from right to left 9248: 10 f9 bpl @copy_byte ;until beginning of line 924a: ca dex ;copied all 8 lines? 924b: 30 db bmi @next_row ;yes, next row (basl/h not inc'd) ; Go to next hires line in 8-line group, at +$0400. By line 7 we will have added ; $1C00. 924d: 18 clc 924e: a5 2b lda bash 9250: 69 04 adc #$04 9252: 85 2b sta bash 9254: a5 ed lda screen+1 9256: 69 04 adc #$04 9258: 85 ed sta screen+1 925a: d0 e4 bne @start_eol ;start next row ; Clear the last line to complete the scroll up. 925c: ad 64 8e @clear_last lda transparent_mode 925f: 48 pha 9260: a5 23 lda MON_WNDBTM 9262: e9 01 sbc #$01 9264: a0 00 ldy #$00 9266: 8c 64 8e sty transparent_mode 9269: 20 92 92 jsr clear_eol 926c: 68 pla 926d: 8d 64 8e sta transparent_mode 9270: 60 rts 9271: a5 22 clear_home lda MON_WNDTOP 9273: 85 25 sta MON_CV 9275: a0 00 ldy #$00 9277: 84 24 sty MON_CH 9279: f0 04 beq clear_eowin ;always 927b: a4 24 clear_hv_eowin ldy MON_CH 927d: a5 25 lda MON_CV ; On entry, A/Y are the row/col to begin clearing from. Clears from this ; position to bottom of window. 927f: 48 clear_eowin pha ;preserve line # 9280: 20 92 92 jsr clear_eol 9283: 68 pla 9284: 18 clc 9285: 69 01 adc #$01 ;next line 9287: c5 23 cmp MON_WNDBTM ;until end of window 9289: a0 00 ldy #$00 ;start at col 0 928b: 90 f2 bcc clear_eowin 928d: 60 rts ; Load current H/V pos into Y/A and fall through to clear_eol. hpos .var $ee {addr/1} ;starting column 928e: a4 24 clear_hv_eol ldy MON_CH 9290: a5 25 lda MON_CV ; Clear line from column Y, row A to end of that line. 9292: 84 ee clear_eol sty hpos ;save starting column 9294: 20 ce 92 jsr compute_base 9297: a2 07 ldx #$07 ;clear 8 hires lines ; If transparent mode is disabled, we just want to clear by writing 7 black ; (normal, $00) or white (inverse, $7f) pixels. This conveniently corresponds ; to the value of inverse_video. 9299: ad 63 8e @clrline lda inverse_video ;load $7f or $00 for opaque mode 929c: 2c 64 8e bit transparent_mode 929f: 10 15 bpl @store ;if opaque, skip to storing 92a1: a5 2a lda basl 92a3: 85 ec sta screen 92a5: a5 2b lda bash 92a7: 4d 65 8e eor overlay_mode 92aa: 85 ed sta screen+1 92ac: b1 ec lda (screen),y 92ae: 2c 64 8e bit transparent_mode 92b1: 50 03 bvc @store 92b3: 4d 63 8e eor inverse_video 92b6: 91 2a @store sta (basl),y 92b8: c8 iny 92b9: c4 21 cpy MON_WNDWDTH ;end of screen? 92bb: 90 dc bcc @clrline ;nope, keep clearing line 92bd: ca dex ;one line complete 92be: 30 0b bmi @done ;if all lines complete, done 92c0: a4 ee ldy hpos ;restore starting column 92c2: a5 2b lda bash 92c4: 18 clc 92c5: 69 04 adc #$04 ;hi byte adds $04 per line 92c7: 85 2b sta bash ;update hi byte of screen address 92c9: d0 ce bne @clrline ;next line -- always 92cb: 60 @done rts ******************************************************************************** * Compute hires base address of a text line, the first line of every 8 line * * character; i.e. 0, 8, 16, ... The line# is the current text row. On exit, * * basl/bash has the address, and A has a copy of basl. * * * * The high byte is the same for even/odd pairs, so we divide the row by 2; * * then take this modulo 4 and OR in the hires page. * * * * For even/odd pairs the low byte is the same but with bit 7 clear (even) or * * set (odd). We can use a 12-byte (24/2) table lookup and set bit 7 according * * to even/odd. * * * * The trick is the LSR A preserves this even/odd information in the carry, * * which is still available when we reach the ROR A, which rotates the carry * * into the high bit, exactly as we needed. The byte values in the address * * table are doubled to account for the ROR. So, for the 24 text lines, the * * hires address base will go * * * * 2000, 2080, 2100, 2180, 2200, 2280, 2300, 2380, 0..7 * * 2028, 20a8, 2128, 21a8, 2228, 22a8, 2328, 23a8, 8..15 * * 2050, 20d0, 2150, 21d0, 2250, 22d0, 2350, 23d0 16..23 * * * * as expected for each 8 line interval on the hires screen. * ******************************************************************************** 92cc: a5 25 compute_cur_base lda MON_CV ;get current text row 92ce: 4a compute_base lsr A ;div by 2 *and* set carry if odd 92cf: aa tax ;save for indexing below 92d0: 29 03 and #$03 ;$20,$20,$21,$21,$22,$22,$23,$23, then repeat 92d2: 0d 62 8e ora hires_page ;(or $40,$40,$41... if page 2) 92d5: 85 2b sta bash ;is the high byte 92d7: bd e0 92 lda hires_addr_tbl,x ;index is row/2 ; 00, 50, a0 in table become 00, 28, 50 (even rows) or 80, a8, d0 (odd rows). 92da: 6a ror A ;/2; carry from LSR becomes high bit 92db: 65 20 adc MON_WNDLEFT ;adjust for window left (ROR cleared carry) 92dd: 85 2a sta basl ;is the low byte 92df: 60 rts 92e0: 00 00 00 00+ hires_addr_tbl .bulk 0000000050505050a0a0a0a0 ;12 bytes (24 lines) 92ec: b9 a2 a0 d2+ .align $0100 (20 bytes) ;unused area ; Standard monochrome (1bpp) 7x8 font bitmap, 1 byte per character line, 8 ; consecutive bytes (lines) per character. High bit is unused. Covers 96 high ; ASCII chars A0-FF. 9300: 00 00 00 00+ builtin_charset .bulk 0000000000000000 ; space 9308: 08 08 08 08+ .bulk 0808080808000800 ; ! 9310: 14 14 14 00+ .bulk 1414140000000000 ; " 9318: 14 14 3e 14+ .bulk 14143e143e141400 ; # 9320: 08 3c 0a 1c+ .bulk 083c0a1c281e0800 ; $ 9328: 06 26 10 08+ .bulk 0626100804323000 ; etc. 9330: 04 0a 0a 04+ .bulk 040a0a042a122c00 9338: 08 08 08 00+ .bulk 0808080000000000 9340: 08 04 02 02+ .bulk 0804020202040800 9348: 08 10 20 20+ .bulk 0810202020100800 9350: 08 2a 1c 08+ .bulk 082a1c081c2a0800 9358: 00 08 08 3e+ .bulk 0008083e08080000 9360: 00 00 00 00+ .bulk 0000000008080400 9368: 00 00 00 3e+ .bulk 0000003e00000000 9370: 00 00 00 00+ .bulk 0000000000000800 9378: 00 20 10 08+ .bulk 0020100804020000 9380: 1c 22 32 2a+ .bulk 1c22322a26221c00 9388: 08 0c 08 08+ .bulk 080c080808081c00 9390: 1c 22 20 18+ .bulk 1c22201804023e00 9398: 3e 20 10 18+ .bulk 3e20101820221c00 93a0: 10 18 14 12+ .bulk 101814123e101000 93a8: 3e 02 1e 20+ .bulk 3e021e2020221c00 93b0: 38 04 02 1e+ .bulk 3804021e22221c00 93b8: 3e 20 10 08+ .bulk 3e20100804040400 93c0: 1c 22 22 1c+ .bulk 1c22221c22221c00 93c8: 1c 22 22 3c+ .bulk 1c22223c20100e00 93d0: 00 00 08 00+ .bulk 0000080008000000 93d8: 00 00 08 00+ .bulk 0000080008080400 93e0: 10 08 04 02+ .bulk 1008040204081000 93e8: 00 00 3e 00+ .bulk 00003e003e000000 93f0: 04 08 10 20+ .bulk 0408102010080400 93f8: 1c 22 10 08+ .bulk 1c22100808000800 9400: 1c 22 2a 3a+ .bulk 1c222a3a1a023c00 9408: 08 14 22 22+ .bulk 081422223e222200 9410: 1e 22 22 1e+ .bulk 1e22221e22221e00 9418: 1c 22 02 02+ .bulk 1c22020202221c00 9420: 1e 22 22 22+ .bulk 1e22222222221e00 9428: 3e 02 02 1e+ .bulk 3e02021e02023e00 9430: 3e 02 02 1e+ .bulk 3e02021e02020200 9438: 3c 02 02 02+ .bulk 3c02020232223c00 9440: 22 22 22 3e+ .bulk 2222223e22222200 9448: 1c 08 08 08+ .bulk 1c08080808081c00 9450: 20 20 20 20+ .bulk 2020202020221c00 9458: 22 12 0a 06+ .bulk 22120a060a122200 9460: 02 02 02 02+ .bulk 0202020202023e00 9468: 22 36 2a 2a+ .bulk 22362a2a22222200 9470: 22 22 26 2a+ .bulk 2222262a32222200 9478: 1c 22 22 22+ .bulk 1c22222222221c00 9480: 1e 22 22 1e+ .bulk 1e22221e02020200 9488: 1c 22 22 22+ .bulk 1c2222222a122c00 9490: 1e 22 22 1e+ .bulk 1e22221e0a122200 9498: 1c 22 02 1c+ .bulk 1c22021c20221c00 94a0: 3e 08 08 08+ .bulk 3e08080808080800 94a8: 22 22 22 22+ .bulk 2222222222221c00 94b0: 22 22 22 22+ .bulk 2222222222140800 94b8: 22 22 22 2a+ .bulk 2222222a2a362200 94c0: 22 22 14 08+ .bulk 2222140814222200 94c8: 22 22 14 08+ .bulk 2222140808080800 94d0: 3e 20 10 08+ .bulk 3e20100804023e00 94d8: 3e 06 06 06+ .bulk 3e06060606063e00 94e0: 00 02 04 08+ .bulk 0002040810200000 94e8: 3e 30 30 30+ .bulk 3e30303030303e00 94f0: 00 00 08 14+ .bulk 0000081422000000 94f8: 00 00 00 00+ .bulk 000000000000007f 9500: 04 08 10 00+ .bulk 0408100000000000 9508: 00 00 1c 20+ .bulk 00001c203c223c00 9510: 02 02 1e 22+ .bulk 02021e2222221e00 9518: 00 00 3c 02+ .bulk 00003c0202023c00 9520: 20 20 3c 22+ .bulk 20203c2222223c00 9528: 00 00 1c 22+ .bulk 00001c223e023c00 9530: 18 24 04 1e+ .bulk 1824041e04040400 9538: 00 00 1c 22+ .bulk 00001c22223c201c 9540: 02 02 1e 22+ .bulk 02021e2222222200 9548: 08 00 0c 08+ .bulk 08000c0808081c00 9550: 10 00 18 10+ .bulk 100018101010120c 9558: 02 02 22 12+ .bulk 020222120e122200 9560: 0c 08 08 08+ .bulk 0c08080808081c00 9568: 00 00 36 2a+ .bulk 0000362a2a2a2200 9570: 00 00 1e 22+ .bulk 00001e2222222200 9578: 00 00 1c 22+ .bulk 00001c2222221c00 9580: 00 00 1e 22+ .bulk 00001e22221e0202 9588: 00 00 3c 22+ .bulk 00003c22223c2020 9590: 00 00 3a 06+ .bulk 00003a0602020200 9598: 00 00 3c 02+ .bulk 00003c021c201e00 95a0: 04 04 1e 04+ .bulk 04041e0404241800 95a8: 00 00 22 22+ .bulk 0000222222322c00 95b0: 00 00 22 22+ .bulk 0000222222140800 95b8: 00 00 22 22+ .bulk 000022222a2a3600 95c0: 00 00 22 14+ .bulk 0000221408142200 95c8: 00 00 22 22+ .bulk 00002222223c201c 95d0: 00 00 3e 10+ .bulk 00003e1008043e00 95d8: 38 0c 0c 06+ .bulk 380c0c060c0c3800 95e0: 08 08 08 08+ .bulk 0808080808080808 95e8: 0e 18 18 30+ .bulk 0e18183018180e00 95f0: 2c 1a 00 00+ .bulk 2c1a000000000000 95f8: 7f 7f 7f 7f+ .bulk 7f7f7f7f7f7f7f7f
No exported symbols found.