editor.a65

     1:  ;  B-12. Function Name: IOBASE
     2:  ;
     3:  ;    Purpose: Define I/O memory page
     4:  ;    Call address: $FFF3 (hex) 65523 (decimal)
     5:  ;    Communication registers: X, Y
     6:  ;    Preparatory routines: None
     7:  ;    Error returns:
     8:  ;    Stack requirements: 2
     9:  ;    Registers affected: X, Y
    10:  ;
    11:  ;
    12:  ;    Description: This routine sets the X and Y registers to the address of
    13:  ;  the memory section where the memory mapped 110 devices are located. This
    14:  ;  address can then be used with an offset to access the memory mapped I/O
    15:  ;  devices in the Commodore 64. The offset is the number of locations from
    16:  ;  the beginning of the page on which the I/O register you want is located.
    17:  ;  The X register contains the low order address byte, while the Y register
    18:  ;  contains the high order address byte.
    19:  ;    This routine exists to provide compatibility between the Commodore 64,
    20:  ;  VIC-20, and future models of the Commodore 64. If the J/0 locations for
    21:  ;  a machine language program are set by a call to this routine, they should
    22:  ;  still remain compatible with future versions of the Commodore 64, the
    23:  ;  KERNAL and BASIC.
    24:  ;
    25:  ;
    26:  ;  How to Use:
    27:  ;
    28:  ;    1) Call this routine by using the JSR instruction.
    29:  ;    2) Store the X and the Y registers in consecutive locations.
    30:  ;    3) Load the Y register with the offset.
    31:  ;    4) Access that I/O location.
    32:  ;
    33:  ;  EXAMPLE:
    34:  ;
    35:  ;    ;SET THE DATA DIRECTION REGISTER OF THE USER PORT TO 0 (INPUT)
    36:  ;    JSR IOBASE
    37:  ;    STX POINT       ;SET BASE REGISTERS
    38:  ;    STY POINT+1
    39:  ;    LDY #2
    40:  ;    LDA #0          ;OFFSET FOR DDR OF THE USER PORT
    41:  ;    STA (POINT),Y   ;SET DDR TO 0
    42:  ;
    43:  ;
    44:  iIOBASE:
    45:          ; return IO base address in x/y
    46:          ; On the VIC-20 and C64, this is interpreted as
    47:          ; the base address of the first VIA/CIA.
    48:  
    49:          ldx     #<IOBASE
    50:          ldy     #>IOBASE
    51:          rts
    52:  
    53:  ;  B-26. Function Name: SCREEN
    54:  ;
    55:  ;    Purpose: Return screen format
    56:  ;    Call address: $FFED (hex) 65517 (decimal)
    57:  ;    Communication registers: X, Y
    58:  ;    Preparatory routines: None
    59:  ;    Stack requirements: 2
    60:  ;    Registers affected: X, Y
    61:  ;
    62:  ;    Description: This routine returns the format of the screen, e.g., 40
    63:  ;  columns in X and 25 lines in Y. The routine can be used to determine what
    64:  ;  machine a program is running on. This function has been implemented on
    65:  ;  the Commodore 64 to help upward compatibility of your programs.
    66:  ;
    67:  ;
    68:  ;
    69:  ;
    70:  ;  How to Use:
    71:  ;
    72:  ;    1) Call this routine.
    73:  ;
    74:  ;  EXAMPLE:
    75:  ;
    76:  ;    JSR SCREEN
    77:  ;    STX MAXCOL
    78:  ;    STY MAXROW
    79:  ;
    80:  ;
    81:  iSCREEN:
    82:          ; return screen resolution in x / y
    83:  
    84:          ldx     #EDITOR_COLS
    85:          ldy     #EDITOR_ROWS
    86:          rts
    87:  
    88:  ;  B-19. Function Name: PLOT
    89:  ;
    90:  ;    Purpose: Set cursor location
    91:  ;    Call address: $FFF0 (hex) 65520 (decimal)
    92:  ;    Communication registers: A, X, Y
    93:  ;    Preparatory routines: None
    94:  ;    Error returns: None
    95:  ;    Stack requirements: 2
    96:  ;    Registers affected: A, X, Y
    97:  ;
    98:  ;    Description: A call to this routine with the accumulator carry flag
    99:  ;  set loads the current position of the cursor on the screen (in X,Y
   100:  ;  coordinates) into the Y and X registers. Y is the column number of the
   101:  ;  cursor location (0-39), and X is the row number of the location of the
   102:  ;  cursor (0-24). A call with the carry bit clear moves the cursor to X,Y
   103:  ;  as determined by the Y and X registers.
   104:  ;
   105:  ;  How to Use:
   106:  ;
   107:  ;
   108:  ;  READING CURSOR LOCATION
   109:  ;
   110:  ;    1) Set the carry flag.
   111:  ;    2) Call this routine.
   112:  ;    3) Get the X and Y position from the Y and X registers, respectively.
   113:  ;
   114:  ;
   115:  ;  SETTING CURSOR LOCATION
   116:  ;
   117:  ;    1) Clear carry flag.
   118:  ;    2) Set the Y and X registers to the desired cursor location.
   119:  ;    3) Call this routine.
   120:  ;
   121:  ;
   122:  ;  EXAMPLE:
   123:  ;
   124:  ;    ;MOVE THE CURSOR TO ROW 10, COLUMN 5 (5,10)
   125:  ;    LDX #10
   126:  ;    LDY #5
   127:  ;    CLC
   128:  ;    JSR PLOT
   129:  ;
   130:  ;
   131:  iPLOT:
   132:          bcs     iPLOTReadOnly                   ; carry set -> read values
   133:  
   134:  JDLE50C:
   135:          stx     zTBLX                           ; save new cursor pos: y position from X
   136:          sty     zPNTR                           ; save new cursor pos: x position from Y
   137:  
   138:          jsr     SET_CURSORPOS                   ; update all other internal states to the new position
   139:  
   140:  iPLOTReadOnly:
   141:          ldx     zTBLX                           ; get cursor position in x and y
   142:          ldy     zPNTR
   143:  
   144:          rts
   145:  
   146:  ;  B-7. Function Name: CINT
   147:  ;
   148:  ;    Purpose: Initialize screen editor & 6567 video chip
   149:  ;    Call address: $FF81 (hex) 65409 (decimal)
   150:  ;    Communication registers: None
   151:  ;    Preparatory routines: None
   152:  ;    Error returns: None
   153:  ;    Stack requirements: 4
   154:  ;    Registers affected: A, X, Y
   155:  ;
   156:  ;
   157:  ;    Description: This routine sets up the 6567 video controller chip in the
   158:  ;  Commodore 64 for normal operation. The KERNAL screen editor is also
   159:  ;  initialized. This routine should be called by a Commodore 64 program
   160:  ;  cartridge.
   161:  ;
   162:  ;  How to Use:
   163:  ;
   164:  ;    1) Call this routine.
   165:  ;
   166:  ;  EXAMPLE:
   167:  ;
   168:  ;    JSR CINT
   169:  ;    JMP RUN       ;BEGIN EXECUTION
   170:  ;
   171:  ;
   172:  iCINT:  jsr     CLRCHN_AND_VIC_DEFAULTS
   173:  
   174:  .if CompileComputer < C64_GENERAL
   175:  
   176:          ; adjust the VIC-I to take the screen memory from the memory
   177:          ; area that is configured in lHIBASE.
   178:  
   179:          ; on the VIC-I,
   180:          ; bits 13-10 of the video RAM address is stored in VICI_O_MemoryLocations.7-VICI_O_MemoryLocations.4,
   181:          ; and bit 9 of the video RAM address is stored in VIC_02.7.
   182:  
   183:          ; Now, bit 13 of the VIC-I is connected to the INVERSE of A15 of the 6502.
   184:          ; Thus, the VIC-I sees another memory map than the 6502:
   185:          ;
   186:          ; VIC-I       <-> 6502
   187:          ; $0000-$1FFF <-> $8000-$9FFF
   188:          ; $2000-$3FFF <-> $0000-$1FFF (*)
   189:          ;
   190:          ; (*) Note that the VIC-I cannot see $0400-$0FFF of the 6502 memory map
   191:          ;     ($2400-$2FFF in the VIC-I memory map) due to the way the memory
   192:          ;     is connected on the external RAM cartridge
   193:  
   194:  
   195:          lda     lHIBASE                 ; get high byte of video RAM address
   196:          and     #~$02                   ; mask out bit 9 (bit 8-0 of the base address
   197:                                          ; must be 0, anyway)
   198:  
   199:          asl     a                       ; shift the address to the right position
   200:          asl     a
   201:  
   202:          ; here, A.7 - A.4 contain the address of the video RAM start, as needed in VICI_O_MemoryLocations
   203:  
   204:          ora     #$80                    ; b13 of the VIC-I is connected to the INVERSE of A15
   205:                                          ; of the 6502 (cf. comment above).
   206:                                          ; Take this into account and make the  VIC-I see
   207:                                          ; the 6502 memory area $0000-$1FFF.
   208:  
   209:          sta     VIC + VICI_O_MemoryLocations    ; store b
   210:  
   211:          lda     lHIBASE                 ; get back the high byte of the video RAM address
   212:          and     #$02                    ; is bit 9 set?
   213:          beq     @NoSetBit9              ; no, branch -> do not set VICI_O_VideoColumns.7
   214:                                          ; (it has been already reset in CLRCHN_AND_VIC_DEFAULTS)
   215:  
   216:          lda     #VICI_B_VideoColumns_ScreenMemoryB9     ; set bit 9 of video RAM address
   217:          ora     VIC + VICI_O_VideoColumns               ; in VICI_O_02.7
   218:          sta     VIC + VICI_O_VideoColumns
   219:  
   220:  @NoSetBit9:
   221:  
   222:  .endif
   223:  
   224:          lda     #$00                    ; set editor mode
   225:          sta     lMODE
   226:  
   227:          sta     zBLNON                  ; blink mode: Currently, the cursor is not on
   228:  
   229:          lda     #<CHECK_SHIFT_CTRL_CBM
   230:          sta     lKEYLOG
   231:          lda     #>CHECK_SHIFT_CTRL_CBM
   232:          sta     lKEYLOG + 1
   233:  
   234:          lda     #10
   235:          sta     lXMAX                   ; maximum number of characters in the keyboard buffer is 10
   236:          sta     lDELAY                  ; set delay for delay of the start of key repetition to default (10)
   237:  
   238:          lda     #DEFAULT_COLOR          ; set default color
   239:          sta     lCOLOR
   240:  
   241:          lda     #$04                    ; set delay counter for key repetitions
   242:          sta     lKOUNT
   243:  
   244:          lda     #$0C
   245:          sta     zBLNCT                  ; set the blink counter
   246:          sta     zBLNSW                  ; disable cursor
   247:  
   248:          ; update the table of low bytes and link bits for the screen row
   249:  
   250:  ClearScreen:
   251:          lda     lHIBASE                 ; get the high byte of the start of the video RAM
   252:          ora     #$80                    ; set link bit -> this row is not connected to the previous one
   253:          tay
   254:          lda     #$00                    ; low byte of the start of the video RAM (A) := 0
   255:          tax                             ; row counter (X) := 0, start in row zero
   256:  
   257:  @Next:  sty     zLDTB1,x                ; store the high byte and the link bit of this line
   258:  
   259:          ; proceed to next row by adding the number of columns in one row (EDITOR_COLS)
   260:  
   261:          clc
   262:          adc     #EDITOR_COLS            ; add the number of columns in a line to the low byte
   263:          bcc     @NoHighByte             ; no carry -> we do not need to increment the high byte
   264:          iny                             ; increment the high byte
   265:  
   266:  @NoHighByte:
   267:          inx                             ; increment row counter
   268:          cpx     #EDITOR_ROWS + 1        ; did we reach the last row?
   269:          bne     @Next                   ; not yet, store the next high byte and link bit
   270:  
   271:          lda     #$FF                    ; write the "end marker"
   272:          sta     zLDTB1,x                ; into the location for the row past the last one
   273:  
   274:          ; Erase the screen rows (overwrite with spaces)
   275:  
   276:          ldx     #EDITOR_ROWS - 1        ; start in the last row
   277:  @NextLine:
   278:          jsr     EraseScreenRow          ; erase the row
   279:          dex                             ; go to previous line
   280:          bpl     @NextLine               ; not all rows are processed, branch -> process previous one
   281:  
   282:  CURSOR_HOME:
   283:          ; set cursor position to 0/0
   284:  
   285:          ldy     #0
   286:          sty     zPNTR                   ; column
   287:          sty     zTBLX                   ; row
   288:  
   289:  SET_CURSORPOS:
   290:  
   291:          ; this routine updates the internal pointer to conform to the cursor
   292:          ; position set in zPNTR/zTBLX. As this function is used from other
   293:          ; places, for example from iPLOT, it is completely implemented, and it
   294:          ; does not use any hard-coded constants for CURSOR_HOME only.
   295:  
   296:          ldx     zTBLX                   ; get row into X
   297:          lda     zPNTR                   ; get column into A
   298:  
   299:  @AddLine:
   300:          ldy     zLDTB1,x                ; is this row combined with the previous one?
   301:          bmi     @StandaloneLine         ; no, branch
   302:  
   303:          ; otherwise, add the number of columns to the col position
   304:          clc
   305:          adc     #EDITOR_COLS
   306:          sta     zPNTR                   ; store (updated) column position
   307:          dex                             ; ... and the row is one less
   308:          bpl     @AddLine                ; unconditional jump (row 0 should never be combined with the previous row!)
   309:          ; --------------
   310:  
   311:  @StandaloneLine:
   312:  
   313:  .if CompileComputer >= C64_03 .or .defined(C64JAPAN)
   314:          jsr     CalculateScreenPointerFromRowNumber                     ; This is essentially the same as the VIC20 (and C64-02 and earlier) implementation. Only the order in which high and low byte are calculated is changed.
   315:                                          ; This subroutine is used to gain memory for the later patch
   316:  .else
   317:          lda     zLDTB1,x                ; get high byte of the starting address of this row
   318:          and     #>lVIDEORAM_SIZE        ; mask out additional bits used as flags
   319:          ora     lHIBASE                 ; add the video RAM base
   320:          sta     zPNT + 1                ; remember high byte
   321:  
   322:          lda     SCREEN_LOWBYTE,x        ; get low byte of the starting address of this row
   323:          sta     zPNT                    ; remember low byte
   324:  
   325:  .endif
   326:  
   327:          ; calculate the number of columns (more precisely: The last column number)
   328:          ; in this logical line
   329:  
   330:          lda     #EDITOR_COLS - 1        ; start with one physical line
   331:  
   332:          inx                             ; Here, X points to the first line *before* the extended long line. Thus, go back into the long line area
   333:  
   334:  @Loop:  ldy     zLDTB1,x                ; is this line combined with the previous one?
   335:          bmi     @StoreLineLength        ; no, branch -> quit loop
   336:  
   337:          clc                             ; add a complete line length
   338:          adc     #EDITOR_COLS
   339:  
   340:          inx                             ; proceed to the next line
   341:          bpl     @Loop                   ; (unconditional branch)
   342:          ; -----------------------------
   343:  
   344:  @StoreLineLength:
   345:          sta     zLNMX                   ; store line length
   346:  
   347:  .if CompileComputer >= C64_03 .or .defined(C64JAPAN)
   348:          ; this fixes a bug in the C64 ROMs:
   349:          ; If the last row is combined to build a long line (80 chars),
   350:          ; and the last character is deleted afterwards with backspace,
   351:          ; the C64 will start a LOAD command and will not react
   352:          ; anymore unless there is an attached tape recorder.
   353:  
   354:          ; This patch fixes this:
   355:  
   356:          jmp     UpdateColorRAMPointerToVideoramPointer  ; also update the color RAM pointer (zUSER)
   357:  
   358:          ; this is another patch on the C64-03 ROMs:
   359:          ; TODO: why?
   360:          ;
   361:          ; It is called after LDX zTBLX (get current cursor row)
   362:  
   363:  Patch_CursorOneRowUp:
   364:          cpx     zLXSP                                   ; did the cursor row change while we were in the routine?
   365:          beq     @Ret                                    ; no -> branch, we're done
   366:          jmp     CursorOneRowUp
   367:  @Ret:   rts
   368:  
   369:          nop
   370:  
   371:  .else
   372:          rts
   373:  .endif
   374:  
   375:  
   376:          ; unused in VIC20 and C64 ROM!
   377:  
   378:          jsr     CLRCHN_AND_VIC_DEFAULTS
   379:          jmp     CURSOR_HOME
   380:  
   381:  
   382:  CLRCHN_AND_VIC_DEFAULTS:
   383:          ; This routine restores input and output to the terminal
   384:          ; (screen and keyboard).
   385:          ; Afterwards, it initialises the VIC (or VIC-II) registers
   386:  
   387:          lda     #FILE_SCREEN                    ; default output to screen
   388:          sta     zDFLTO
   389:          lda     #FILE_KEYBOARD                  ; default input to keyboard
   390:          sta     zDFLTN
   391:  
   392:  SET_VIC_DEFAULTS:
   393:  
   394:          ; This routine initialises the VIC (or VIC-II) registers
   395:  
   396:          ; Loop throught the table and overwrite the VIC (-II) registers
   397:          ; with the table contents
   398:  
   399:          ldx     #END_VIC_DEFAULTS - VIC_DEFAULTS + 1    ; number of register values in the table
   400:  @Loop:  lda     VIC_DEFAULTS - 1,x
   401:          sta     VIC - 1,x
   402:          dex
   403:          bne     @Loop
   404:          rts
   405:  
   406:  GETIN_KEYB:
   407:  
   408:          ; Get a character from the keyboard buffer
   409:          ; this function returns a character from the keyboard buffer
   410:          ; It also deletes it from there.
   411:          ;
   412:          ; Prerequisites:
   413:          ;
   414:          ; - Before calling this function, the I flag must be set (SEI)
   415:          ;   It will be cleared on exit
   416:          ;
   417:          ; - Make sure the keyboard buffer is not empty before calling this function!
   418:  
   419:          ldy     lKEYD                                   ; remember first key press in the keyboard buffer
   420:  
   421:          ; move all key presses in the keyboard buffer one step to the front
   422:  
   423:          ldx     #0
   424:  @Loop:  lda     lKEYD + 1,x                             ; move entry one step ahead
   425:          sta     lKEYD,x
   426:          inx                                             ; proceed to next one
   427:          cpx     zNDX                                    ; did we already process all keys?
   428:          bne     @Loop                                   ; no, branch -> process the next key
   429:  
   430:          dec     zNDX                                    ; we just removed and key, thus, decrement the number of keys in the buffer
   431:  
   432:          tya                                             ; get back the first key press in the keyboard buffer
   433:  
   434:          cli
   435:          clc                                             ; quit with success
   436:          rts
   437:  
   438:  OutputCharacterAndWaitForKeyPress:
   439:          jsr     CHROUT_SCREEN
   440:  
   441:  WaitForKeyPress:
   442:          lda     zNDX                                    ; number of key presses in keyboard buffer
   443:          sta     zBLNSW                                  ; if > 0: disable cursor blinking, otherwise: Enable cursor blinking
   444:          sta     lAUTODN                                 ; If there was some key press, mark that any output that will combine two rows will scroll down the screen contents
   445:                                                          ; (this loop cannot be quit with lAUTODN = 0)
   446:          beq     WaitForKeyPress                         ; no key presses -> wait until some key has been pressed
   447:  
   448:          ; now that one or more key has been pressed, output it/them
   449:  
   450:          ; first, restore the character under the cursor (if the cursor has been visible)
   451:  
   452:          sei
   453:          lda     zBLNON                                  ; cursor currently visible?
   454:          beq     @CursorNotVisible                       ; no, skip restoring it
   455:  
   456:          ; restore character under the cursor
   457:  
   458:          lda     zGDBLN                                  ; get character code
   459:          ldx     lGDCOL                                  ; and color of character under cursor
   460:  
   461:          ldy     #0
   462:          sty     zBLNON                                  ; mark: Cursor is currently invisible
   463:  
   464:          jsr     StoreCharacterOnScreenAndDisableBlinking        ; store the character under the cursor on the screen
   465:  
   466:  @CursorNotVisible:
   467:  
   468:  .ifdef JIFFY
   469:          jsr     JDLF9E5
   470:  .else
   471:          jsr     GETIN_KEYB                              ; get the next character
   472:  .endif
   473:          cmp     #KEY_SHIFTRUN                           ; was is Shift + Run/Stop?
   474:          bne     NoShiftRunStop                          ; no, branch -> jump special processing
   475:  
   476:          ; If we reach here, the user pressed Shift + Run/Stop
   477:          ; Then, store the special text into the keyboard buffer and
   478:          ; process the characters, one after the other.
   479:          ;
   480:          ; Note that any other key presses that might have been
   481:          ; in the keyboard buffer are removed.
   482:  
   483:          ldx     #END_TEXT_SHIFTRUNSTOP - TEXT_SHIFTRUNSTOP
   484:          sei
   485:          stx     zNDX                                    ; set the count of characters
   486:  
   487:  @ShiftRunStop:
   488:          lda     TEXT_SHIFTRUNSTOP - 1,x                 ; copy text
   489:          sta     lKEYD - 1,x                             ; into the keyboard buffer
   490:          dex
   491:          bne     @ShiftRunStop                           ; until all characters have been processed
   492:          beq     WaitForKeyPress                         ; now, process the key presses (uncond. branch)
   493:          ; -------------------------------
   494:  
   495:  NoShiftRunStop:
   496:          cmp     #ASC_CR                                 ; was the key a CR?
   497:          bne     OutputCharacterAndWaitForKeyPress       ; no, output the character and wait for the next key press
   498:  
   499:          ; When we reach here, the user has entered anything and pressed CR.
   500:          ; Now, we process the input
   501:  
   502:          ldy     zLNMX                                   ; get the (logical) line length of the current line
   503:          sty     zCRSW                                   ; store it as number of characters to read
   504:  
   505:  @CheckSpaceNext:
   506:          lda     (zPNT),y                                ; read the next character at the end of the line
   507:          cmp     #' '                                    ; is it a space?
   508:          bne     @NoSpace                                ; No -> branch,
   509:          dey                                             ; Yes, it was a space: Test the previous character
   510:          bne     @CheckSpaceNext                         ; until we have checked all characters in this line, branch
   511:  
   512:  @NoSpace:
   513:          iny
   514:          sty     zINDX                                   ; remember the number of characters in the current line
   515:          ldy     #0
   516:          sty     lAUTODN                                 ; Mark: No key press yet, thus, any output will scroll down the screen contents if some rows will be combined
   517:          sty     zPNTR                                   ; start reading at the beginning of the line
   518:          sty     zQTSW                                   ; We are not in quotation mark mode
   519:          lda     zLXSP                                   ; value of zTBLX before calling BASIN (zTBLX is not changed when calling GETIN
   520:          bmi     BASIN_KEYB_PROCESS_KEY                  ; TODO what?
   521:  
   522:          ldx     zTBLX                                   ; current cursor row on screen
   523:  
   524:          ; set the cursor one (virtual) row up
   525:  
   526:  .if CompileComputer >= C64_03 .or .defined(C64JAPAN)
   527:          ; for -03 ROMs, it was decided that this the cursor row is only moved up
   528:          ; if it has already changed since this function began
   529:          ; TODO why?
   530:          ;
   531:          jsr     Patch_CursorOneRowUp
   532:  .else
   533:          jsr     CursorOneRowUp
   534:  .endif
   535:  
   536:          ; here: with X := current row (modified in CursorOneRowUp / Patch_CursorOneRowUp)
   537:  
   538:          cpx     zLXSP                                   ; has the cursor row on screen changed?
   539:          bne     BASIN_KEYB_PROCESS_KEY                  ; yes -> branch, get keyboard input
   540:  
   541:  .if CompileComputer < C64_GENERAL
   542:          bne     BASIN_KEYB_PROCESS_KEY                  ; some superfluous leftover
   543:  .endif
   544:  
   545:          lda     zTEMP_zPNTR                             ; restore current cursor column (zPNTR) from zTEMP_zPNTR
   546:          sta     zPNTR
   547:          cmp     zINDX                                   ; did the column change while we were in the routine?
   548:          bcc     BASIN_KEYB_PROCESS_KEY                  ; we are now to the left of the column at the beginning -> branch, get new key input
   549:          bcs     BASIN_KEYB_END_LINE                     ; we are at the same column or right from it -> branch, done
   550:          ; -------------------------
   551:  
   552:  BASIN_KEYB:
   553:  
   554:          ; remember Y and X on the stack
   555:  
   556:          tya
   557:          pha
   558:          txa
   559:          pha
   560:  
   561:          lda     zCRSW                                   ; has CR been pressed already?
   562:                                                          ; that is, are there already keys to
   563:                                                          ; process on the screen?
   564:          beq     WaitForKeyPress                         ; No, wait for input of a complete line
   565:  
   566:  BASIN_KEYB_PROCESS_KEY:
   567:          ldy     zPNTR                                   ; get pointer into current line
   568:          lda     (zPNT),y                                ; get current character at that position
   569:  
   570:  .if CompileComputer >= C64_GENERAL
   571:  .elseif CompileComputer >= VIC20_06
   572:  
   573:  ;       FillUntil $E672,$EA
   574:          FillNOP 23
   575:  
   576:  .else
   577:  
   578:          ; It seems the VIC20_0ß2 ROM has some kind of "cooked" screen codes.
   579:          ; This routine converts some characters into others, bypassing the
   580:          ; screen code to PETSCII conversion later
   581:  
   582:          ldx     lMODE                                   ; if lMODE == 0 then we do not use the "cooked" mode
   583:          beq     @End                                    ; Thus, in  this case, skip the conversion
   584:  
   585:          ldx     #SpecialScreenCodeHandleTable_END - SpecialScreenCodeHandleTable - 2
   586:  @FindChar:
   587:          cmp     SpecialScreenCodeHandleTable,x          ; is the current character a special one?
   588:          beq     @FoundCharacter                         ; yes, branch -> convert it
   589:          dex                                             ; no, proceed to previous special character
   590:          dex
   591:          bpl     @FindChar                               ; test the next char
   592:          bmi     @End                                    ; table has completed -> branch, quit
   593:          ; ---------------
   594:  
   595:  @FoundCharacter:
   596:          lda     SpecialScreenCodeHandleTable + 1,x      ; convert the screen code to the replacement
   597:          bne     @ProceedToNextScreenLocation            ; (uncond. branch)
   598:          ; --------------------------------------
   599:  
   600:  @End:
   601:  
   602:  .endif
   603:  
   604:          ; convert the character (in A) into PETSCII TODO
   605:          ;
   606:          ; Here, we convert the codes as follows:
   607:          ;
   608:          ; SCREEN CODE -> PETSCII
   609:          ; $00-$1F     -> $40-$5F
   610:          ; $20-$3F     -> $20-$3F
   611:          ; $40-$5F     -> $60-$7F
   612:          ; TODO ???
   613:          ;
   614:  
   615:          sta     zSCHAR                                  ; store the character
   616:  
   617:          and     #$3F                                    ; mask out the upper 2 bits (7, 6)
   618:          asl     zSCHAR                                  ; put bit 7 into C
   619:          bit     zSCHAR                                  ; test the remaining part
   620:  
   621:          ; now, we have the following status of the flags:
   622:          ; C = bit 7 of A on input
   623:          ; N = bit 6 of A on input
   624:          ; V = bit 5 of A on input
   625:  
   626:          bpl     @DoNotSetBit7                           ; N=0 -> bit 6 was 0, that is, we have $00-$3F or $80-$BF
   627:  
   628:          ora     #$80                                    ; otherwise, set bit 7
   629:  
   630:  @DoNotSetBit7:
   631:          bcc     @Process0x00_To_0x7F                    ; was bit 7 == 0? --> branch
   632:  
   633:          ldx     zQTSW                                   ; Check quotation mark mode
   634:          bne     @ProceedToNextScreenLocation            ; branch if we are in quotation mark mode
   635:  
   636:  @Process0x00_To_0x7F:
   637:          bvs     @ProceedToNextScreenLocation            ; was bit 5 == 1? --> branch
   638:          ora     #$40                                    ; otherwise, set bit 6
   639:  
   640:          ; here, we converted: (TODO: check again!)
   641:          ; $00-$1F -> $40-$5F
   642:          ; $20-$3F -> $20-$3F
   643:          ; $40-$5F -> $80-$9F
   644:          ; $60-$7F -> $C0-$DF
   645:  
   646:  @ProceedToNextScreenLocation:
   647:          inc     zPNTR                                   ; proceed to next screen location
   648:  
   649:          jsr     CheckQuote                              ; update the quotation mark mode flag
   650:  
   651:          cpy     zINDX                                   ; have we reached the end of the line?
   652:          bne     BASIN_KEYB_QUIT                         ; no, return the current character
   653:  
   654:          ; if we reach here, then we have read the complete line
   655:          ; Thus, clear all states and return the CR as marker for end-of-line
   656:  
   657:  BASIN_KEYB_END_LINE:
   658:          lda     #0
   659:          sta     zCRSW                                   ; remember: We do not have any characters anymore
   660:          lda     #ASC_CR                                 ; return a CR Value
   661:  
   662:          ldx     zDFLTN
   663:          cpx     #FILE_SCREEN                            ; default input file = screen?
   664:          beq     @OutputCharacter                        ; yes, output the CR
   665:  
   666:          ldx     zDFLTO
   667:          cpx     #FILE_SCREEN                            ; default output file = screen?
   668:          beq     @QuitWithCR                             ; yes, quit
   669:  
   670:          ; if we reach here, the input was from the keyboard, and the output was not the screen.
   671:          ; Thus, output the CR we got from the keyboard
   672:  
   673:  @OutputCharacter:
   674:          jsr     CHROUT_SCREEN                           ; output the character on the screen
   675:  
   676:  @QuitWithCR:
   677:          lda     #ASC_CR                                 ; return a CR value
   678:  
   679:  BASIN_KEYB_QUIT:
   680:          sta     zSCHAR                                  ; remember read char
   681:  
   682:          ; restore X and Y from stack
   683:          pla
   684:          tax
   685:          pla
   686:          tay
   687:  
   688:          lda     zSCHAR                                  ; get back remembered read char
   689:          cmp     #ASC_PI                                 ; is it the PETSCII code for PI?
   690:          bne     @ClcRts                                 ; no, branch -> we are done
   691:          lda     #TokPi                                  ; yes, replace it by the BASIC token for PI (TODO why did CBM choose this route?)
   692:  @ClcRts:
   693:          clc                                             ; we successfully ended the routine
   694:          rts
   695:          ; --------------
   696:  
   697:  CheckQuote:
   698:          cmp     #'"'                                    ; Is the current char a quotation mark?
   699:          bne     @Rts                                    ; no, quit
   700:  
   701:          ; invert the state of the quotation mark
   702:          lda     zQTSW
   703:          eor     #$01
   704:          sta     zQTSW
   705:  
   706:          lda     #'"'                                    ; restore the character
   707:  @Rts:   rts
   708:  
   709:  
   710:  ; @@@@@
   711:  
   712:  LE691:  ora     #$40
   713:  
   714:  CHROUT_SCREEN_OUTPUT_WITH_TEST_RVS:
   715:          ldx     zRVS                                    ; Is the flag "output in reverse" set?
   716:          beq     CHROUT_OUTPUT_SCREEN_IN_NORMAL          ; no -> branch, output in normal
   717:  
   718:  CHROUT_SCREEN_OUTPUT_IN_RVS:
   719:          ora     #$80                                    ; setting bit 7 of the char to output: reverse the char
   720:  
   721:  CHROUT_OUTPUT_SCREEN_IN_NORMAL:
   722:          ldx     zINSRT                                  ; Number of characters to output in "insert mode"
   723:          beq     @NoInsertMode                           ; none -> we are not in insert mode -> branch
   724:          dec     zINSRT                                  ; decrement number of characters to output in revers mode
   725:  
   726:  @NoInsertMode:
   727:          ldx     lCOLOR                                  ; get the current color
   728:          jsr     StoreCharacterOnScreenAndDisableBlinking        ; output character in A, color in X
   729:          jsr     MoveCursorRightAfterOutput              ; move the cursor to the next output position
   730:  
   731:  CHROUT_SCREEN_END:
   732:          pla                                             ; restore Y from stack
   733:          tay
   734:  
   735:          lda     zINSRT                                  ; insert mode?
   736:          beq     @DontStopQuotationMode                  ; no, branch
   737:          lsr     zQTSW                                   ; end quotation mark mode
   738:  @DontStopQuotationMode:
   739:          pla                                             ; restore X from stack
   740:          tax
   741:  
   742:          pla                                             ; restore A from stack
   743:          clc                                             ; we ended successfully
   744:          cli
   745:          rts
   746:          ; --------------
   747:  
   748:  MoveCursorRightAfterOutput:
   749:          jsr     AdjustCursorRowBeforeMovingRight        ; if we will move to the next row, increment row number
   750:          inc     zPNTR                                   ; increment column into current row -> move cursor to the right
   751:          lda     zLNMX                                   ; get number of column in current row
   752:          cmp     zPNTR                                   ; did we go past the last column?
   753:          bcs     EditorRts                               ; no -> branch, we do not need to adjust column
   754:          cmp     #(EDITOR_MAX_COMBINED_ROWS * EDITOR_COLS) - 1   ; did we reach the maximum length of a virtual row?
   755:          beq     SetCursorToTheBeginningOfTheNextLine    ; yes -> branch, set cursor to the beginning of the next line
   756:  
   757:          lda     lAUTODN                                 ; do we have to scroll down the screen contents?
   758:          beq     @CombineRows                            ; no, skip the scrolling
   759:          jmp     LE967                                   ; (will return to LogicallyCombineTwoRows)
   760:          ; ------------------
   761:  
   762:  @CombineRows:
   763:          ldx     zTBLX
   764:          cpx     #EDITOR_ROWS
   765:          bcc     LogicallyCombineTwoRows
   766:          jsr     LE8EA
   767:          dec     zTBLX
   768:          ldx     zTBLX
   769:  
   770:  LogicallyCombineTwoRows:
   771:          asl     zLDTB1,x                                ; clear bit 7 -> combine this phyiscal row with the previous one
   772:          lsr     zLDTB1,x
   773:  
   774:    .macro EDITOR_PATCH_LogicallyCombineTwoRows_FIX
   775:  
   776:          ; only present on VIC20-06 ROMs and above, and C64 ROMs.
   777:  
   778:          ; mark the next row as being stand-alone
   779:  
   780:          ; TODO what exactly does this patch fix?
   781:  
   782:          inx                                             ; go to the next row
   783:          lda     zLDTB1,x
   784:          ora     #$80                                    ; set bit 7 --> this row is not combined with the previous one
   785:          sta     zLDTB1,x
   786:          dex                                             ; go back to the previous row
   787:    .endmacro
   788:  
   789:    .macro EDITOR_PATCH_LogicallyCombineTwoRows_COMMON
   790:          ; from here on, this is done for all variants, including the VIC20-2
   791:  
   792:          lda     zLNMX                                   ; maximum number of columns on the current (virtual) row
   793:          clc
   794:    .endmacro
   795:  
   796:          ; depending on the firmware built,
   797:  .if CompileComputer >= C64_GENERAL
   798:          EDITOR_PATCH_LogicallyCombineTwoRows_FIX
   799:          EDITOR_PATCH_LogicallyCombineTwoRows_COMMON
   800:  .elseif CompileComputer >= VIC20_06
   801:          ; on the VIC20-06 and -07, this patch is really a patch.
   802:          ; We come back with a JMP
   803:          jmp     EditorPatchLogicallyCombineTwoRows
   804:  EditorPatchLogicallyCombineTwoRows_Return:
   805:  
   806:  .else
   807:          ; old implementation for VIC20-02
   808:          EDITOR_PATCH_LogicallyCombineTwoRows_COMMON
   809:  .endif
   810:  
   811:          adc     #EDITOR_COLS                            ; add the number of column of one (physical) row
   812:          sta     zLNMX                                   ; and set it as the new maximum number of columns on the current (virtual) row
   813:  
   814:  CursorOneRowUp:
   815:          ; input: X := Cursor row
   816:          ;
   817:          ; set the cursor row to point to the (virtual) row above us.
   818:  
   819:          lda     zLDTB1,x                                        ; is the current row combined with the previous one?
   820:          bmi     @NotCombined                                    ; no, we're done
   821:          dex                                                     ; cursor on (physical) row up
   822:          bne     CursorOneRowUp                                  ; not 0 -> not at top of screen -> branch, test the next (physical) row
   823:  
   824:  @NotCombined:
   825:          jmp     CalculateScreenPointerFromRowNumber             ; adjust screen pointer
   826:          ; -----------------------------------------
   827:  
   828:  SetCursorToTheBeginningOfTheNextLine:
   829:          dec     zTBLX                                           ; go up one row (will be undone in the next routine)
   830:          jsr     GoDownOneVirtualRow                             ; go down one (virtual) row
   831:          lda     #0
   832:          sta     zPNTR                                           ; set column to the beginning of the row
   833:  EditorRts:
   834:          rts
   835:          ; -----------------------------------------
   836:  
   837:  
   838:  ; Perform the wrap-around to the previous row of
   839:  ; INS/DEL or CRSR LEFT is pressed on the leftmost column.
   840:  ;
   841:  ; If the cursor is not at the home position, it
   842:  ; puts the cursor one row to the top, and on the last
   843:  ; column if that row.
   844:  ;
   845:  ; NOTE:
   846:  ; If the cursor is already at the home position,
   847:  ; this function removes the return address from the stack!
   848:  ; Instead, it jumps to CHROUT_SCREEN_END.
   849:  ;
   850:  CHROUT_SCREEN_WrapAroundToPreviousRow:
   851:          ldx     zTBLX                                   ; get row of current cursor position
   852:          bne     @CanGoBack                                      ; not zero -> branch
   853:  
   854:          ; if we reach here, then we are already on the first ("0th") row, and we are in the first ("0th") column (as we were called in the first place).
   855:          ; Thus, we do not have an option to go more to the left.
   856:  
   857:          stx     zPNTR                                   ; set column to 0 (TODO: should not be necessary, as it is already set to 0!)
   858:  
   859:          ; Remove the return address from the stack:
   860:          ; we do not want to return to the caller;
   861:          ; instead, we will abort the output!
   862:          ;
   863:          pla
   864:          pla
   865:  
   866:          bne     CHROUT_SCREEN_END                       ; end the output (uncond. branch as long as the caller of the caller does not reside on the memory area $00xx.)
   867:          ; -----------------------
   868:  
   869:  @CanGoBack:
   870:          dex
   871:          stx     zTBLX                                   ; set the cursor one row to the top
   872:  
   873:          jsr     SET_CURSORPOS                           ; set the cursor position (and calculate the line length of the current line, in zLNMX)
   874:  
   875:          ; set the cursor to the last column of the line
   876:          ldy     zLNMX                                   ; get current (virtual) line length
   877:          sty     zPNTR                                   ; and set the cursor to that column
   878:          rts
   879:  
   880:  ; CHROUT onto screen
   881:  ;
   882:  ; Output the character in A to the current cursor position on the screen
   883:  ;
   884:  CHROUT_SCREEN:
   885:          pha                                             ; remember the character to output on stack
   886:          sta     zSCHAR                                  ; and in memory
   887:  
   888:          ; remember X and Y on the stack
   889:          txa
   890:          pha
   891:          tya
   892:          pha
   893:  
   894:          lda     #$00                                    ; no CR has been pressed yet
   895:          sta     zCRSW                                   ; That is, on next BASIN, the routine will wait for an input again, regardless if the input has been completely used yet.
   896:  
   897:          ldy     zPNTR                                   ; get pointer into current (logical) line
   898:          lda     zSCHAR                                  ; character to be output
   899:          bpl     @PositiveChar                           ; is it positive (<= $7F) -> branch
   900:          jmp     @NegativeChar
   901:          ; ------------
   902:  
   903:  @PositiveChar:
   904:          cmp     #ASC_CR                                 ; is the character a CR?
   905:          bne     @NoCR                                   ; No -> branch, next test
   906:          jmp     CHROUT_SCREEN_CR                        ; Output a CR
   907:  
   908:  @NoCR:
   909:          ; Here, we convert the codes as follows:
   910:          ;
   911:          ; PETSCII -> SCREEN CODE
   912:          ; $20-$3F -> $20-$3F
   913:          ; $40-$5F -> $00-$1F
   914:          ; $60-$7F -> $40-$5F
   915:  
   916:          cmp     #$20                                    ; is the character a control code (< $20)?
   917:          bcc     @TestControlCode                        ; yes, process the control code
   918:  
   919:          cmp     #$60                                    ; is the character small than $60 (i.e., $20..$5F)?
   920:          bcc     @Convert0x20_0x60                       ; yes, branch -> convert char
   921:          and     #~$20                                   ; convert $60-$7F --> $40-$5F
   922:          bne     @CheckQuoteAndOutput                    ; (uncond. branch)
   923:          ; -----------------
   924:  
   925:  @Convert0x20_0x60:
   926:          and     #$3F                                    ; converts $20-$3F --> $20-$3F, but $40-$5F -> $00-$1F
   927:  
   928:  @CheckQuoteAndOutput:
   929:          jsr     CheckQuote                              ; update quote state, if necessary
   930:          jmp     CHROUT_SCREEN_OUTPUT_WITH_TEST_RVS
   931:          ; -----------------
   932:  
   933:  @TestControlCode:
   934:          ldx     zINSRT                                  ; are we in insert mode?
   935:          beq     @ProcessControlCode                     ; no, branch -> process control codes
   936:          jmp     CHROUT_SCREEN_OUTPUT_IN_RVS             ; output the control codes in reverse (and quit), do not process them
   937:          ; -----------------
   938:  
   939:  @ProcessControlCode:
   940:          cmp     #ASC_INSDEL                             ; is the character an INS/DEL?
   941:          bne     @NoINSDEL                               ; no -> branch, skip special handling of INS/DEL
   942:  
   943:          tya                                             ; A := Y (zPNTR), offset of current column into current screen line
   944:          bne     @NotFirstColumn                         ; not the first column -> branch
   945:  
   946:          jsr     CHROUT_SCREEN_WrapAroundToPreviousRow   ; Perform the wrap around to the previous row, putting the cursor on the rightmost column of the previous line.
   947:                                                          ; If we are at the home position already, this function does NOT return, but goes to CHROUT_SCREEN_END instead.
   948:          jmp     @AddBlankAtCurrentPosition
   949:          ; -----------------
   950:  
   951:  @NotFirstColumn:
   952:          jsr     AdjustCursorRowBeforeMovingLeft         ; we want to move the cursor to the left. If we will cross a row this way, decrement the row number.
   953:  
   954:          ; move cursor one to the left
   955:  
   956:          dey
   957:          sty     zPNTR
   958:          jsr     UpdateColorRAMPointerToVideoramPointer  ; update color RAM pointer
   959:  
   960:          ; move the screen parts to the right of the cursor one to the left
   961:  
   962:  @MoveLoop:
   963:          iny                                             ; get the char to the right
   964:          lda     (zPNT),y
   965:          dey                                             ; and copy it one to the left
   966:          sta     (zPNT),y
   967:  
   968:          iny                                             ; get the color to the right
   969:          lda     (zUSER),y
   970:          dey                                             ; and copy it one to the left
   971:          sta     (zUSER),y
   972:  
   973:          iny                                             ; proceed to the next position (to the right)
   974:          cpy     zLNMX                                   ; did we reach the end of the (logical) line?
   975:          bne     @MoveLoop                               ; no, move the next char
   976:  
   977:          ; if we "fall through", then Y points to the last location on the current (logical) screen line
   978:  
   979:  @AddBlankAtCurrentPosition:
   980:          lda     #' '                                    ; put a space char (blank)
   981:          sta     (zPNT),y                                ; into the current screen location
   982:          lda     lCOLOR                                  ; put the default color
   983:          sta     (zUSER),y                               ; into the current color location
   984:          bpl     @End2                                   ; BUG: This is meant as an uncond. branch. It is one as long as no-one has the idea to put a negative color into lCOLOR!
   985:          ; ------------------
   986:  
   987:  @NoINSDEL:
   988:          ldx     zQTSW                                   ; are we in quotation mark mode?
   989:          beq     @NoQuotationMode                        ; no -> branch
   990:          jmp     CHROUT_SCREEN_OUTPUT_IN_RVS             ; output the control codes in reverse
   991:          ; ---------------------------------
   992:  
   993:  @NoQuotationMode:
   994:          cmp     #ASC_RVS                                ; character code for reverse (RVS) mode?
   995:          bne     @NotReverse                             ; no -> branch, next test
   996:          sta     zRVS                                    ; remember the reverse mode
   997:  
   998:  @NotReverse:
   999:          cmp     #ASC_HOME                               ; character code for cursor home?
  1000:          bne     @NoCrsrHome                             ; no -> branch, next test
  1001:          jsr     CURSOR_HOME                             ; put the cursor at the home position
  1002:  
  1003:  @NoCrsrHome:
  1004:          cmp     #ASC_CURSORLEFTRIGHT                    ; character code for cursor left/right?
  1005:          bne     @NoCrsrLeftRight                        ; no -> branch, next test
  1006:  
  1007:          iny                                             ; move cursor to the right
  1008:          jsr     AdjustCursorRowBeforeMovingRight        ; if we will move to the next row, increment row number
  1009:          sty     zPNTR                                   ; store cursor column
  1010:  
  1011:          dey                                             ; get old cursor position back
  1012:          cpy     zLNMX                                   ; was the cursor before the end of the (virtual) row?
  1013:          bcc     @End                                    ; yes -> branch
  1014:  
  1015:          ; If we reach here, we moved from the end of the previous row to the current row
  1016:  
  1017:          dec     zTBLX                                   ; decrement the row number (go up one row)
  1018:                                                          ; the first operation GoDownOneVirtualRow does is increment
  1019:                                                          ; the row number. This dec is a countermeasure
  1020:                                                          ; for this incrementing.
  1021:          jsr     GoDownOneVirtualRow                     ; go down one (virtual) row
  1022:          ldy     #0                                      ; set cursor to the beginning of the row
  1023:  @StoreColAndEnd:
  1024:          sty     zPNTR
  1025:  
  1026:  @End:   jmp     CHROUT_SCREEN_END
  1027:          ; -------------------------
  1028:  
  1029:  @NoCrsrLeftRight:
  1030:          cmp     #ASC_CURSORUPDOWN                       ; character code for cursor up/down?
  1031:          bne     @NoCrsrUpDown                           ; no -> branch, next test
  1032:  
  1033:          ; In case we moved down one phyiscal row, but we are still in the
  1034:          ; same (virtual) row, we calculate the new column we would be at
  1035:          ; If this case is not true, the calculation will be thrown away.
  1036:          ; Otherwise, we will use this value.
  1037:  
  1038:          clc
  1039:          tya                                             ; A := Y (current column number)
  1040:          adc     #EDITOR_COLS                            ; add the number of columns in a physical row
  1041:          tay                                             ; Y := A (column number if we are still in the same virtual row)
  1042:  
  1043:          ;
  1044:          inc     zTBLX                                   ; go down one row
  1045:          cmp     zLNMX                                   ; compare just calculated column number with maximum number of column in the current row
  1046:          bcc     @StoreColAndEnd                         ; calculated row number is smaller -> we are still in the same virtual row -> branch, store column
  1047:          beq     @StoreColAndEnd                         ; calculated row number is equal -> we are still in the same virtual row -> branch, store column
  1048:          dec     zTBLX                                   ; go up one row (again to where we started)
  1049:                                                          ; this is a preparation to the JSR GoDownOneVirtualRow below
  1050:  
  1051:          ; "Normalise" the column (in zPNTR)
  1052:          ; That is, calculate zPNTR MOD EDITOR_COLS with a loop
  1053:          ; TODO why?
  1054:  
  1055:  @Normalise:
  1056:          ; sec, but we already have C=1: If we come from "above", then we would have branched in the bcc
  1057:          ; if C=0
  1058:          ; if we looped, then we would have branched in the other bcc from below
  1059:  
  1060:          sbc     #EDITOR_COLS                            ; subtract the number of columns in a physical row
  1061:          bcc     @GoDown                                 ; if we reached < 0, end the loop
  1062:          sta     zPNTR                                   ; store the column
  1063:          bne     @Normalise                              ; if we did not reach 0 yet, loop again
  1064:  
  1065:  @GoDown:
  1066:          jsr     GoDownOneVirtualRow                     ; go down one (virtual) row
  1067:  @End2:
  1068:          jmp     CHROUT_SCREEN_END
  1069:  
  1070:  @NoCrsrUpDown:
  1071:          jsr     EditorCheckColorCodeAndSetColor         ; check if the current PETSCII code is a color. IF yes, set lCOLOR. Return anyway
  1072:  
  1073:  .if CompileComputer >= VIC20_06
  1074:          jmp     EditorCheckForAscLowercase              ; check for additional codes (change uppercase, change lowercase, allow changing uppercase/lowercase, disallow it)
  1075:  .else
  1076:          jmp     CHROUT_SCREEN_END                       ; we're done
  1077:  .endif
  1078:  
  1079:  
  1080:  @NegativeChar:
  1081:  
  1082:  .if CompileComputer >= C64_GENERAL
  1083:  
  1084:  .elseif CompileComputer < VIC20_06
  1085:          ; depending on lMODE, the old VIC20 KERNAL does some translation of
  1086:          ; character codes to be output.
  1087:          ; All of these codes being replaced have in common that their
  1088:          ; 7th bit ($80) is set.
  1089:          ;
  1090:          ; TODO Why is this done?
  1091:  
  1092:          ldx     lMODE                                   ; get lMODE
  1093:          beq     @LE815                                  ; is it 0? Then do NOT do any conversion
  1094:  
  1095:          ldx     #$31
  1096:  @LE807: cmp     CHROUT_REPLACEMENT_TABLE,x
  1097:          beq     @LE812
  1098:          dex
  1099:          dex
  1100:          bpl     @LE807
  1101:          bmi     @LE815
  1102:  @LE812: lda     CHROUT_REPLACEMENT_TABLE - 1,x
  1103:  .else
  1104:  
  1105:  ;       FillUntil $E815, $EA
  1106:          FillNOP 21
  1107:  
  1108:  .endif
  1109:  
  1110:  @LE815:
  1111:  
  1112:          and     #$7F
  1113:          cmp     #TokPi  - $80
  1114:          bne     @LE7DC
  1115:  .if .defined(C64JAPAN)
  1116:          lda     #ASC_PI - $40                                   ; @@@???
  1117:  .else
  1118:          lda     #ASC_PI - $80
  1119:  .endif
  1120:  @LE7DC:
  1121:  .if CompileComputer >= C64_GENERAL
  1122:  
  1123:  .elseif CompileComputer >= VIC20_06
  1124:      .repeat 6
  1125:          nop
  1126:      .endrep
  1127:  .else
  1128:          cmp     #$04
  1129:          bne     @LE823
  1130:          lda     #$7F
  1131:  .endif
  1132:  
  1133:  @LE823:
  1134:          cmp     #' '
  1135:          bcc     @LE7E3
  1136:          jmp     LE691
  1137:          ; ----------------
  1138:  
  1139:  @LE7E3: cmp     #ASC_CR
  1140:          bne     @LE7EA
  1141:          jmp     CHROUT_SCREEN_CR
  1142:  
  1143:  @LE7EA: ldx     zQTSW                                           ; are we in quotation mark mode?
  1144:          bne     @LE82D                                          ; yes -> branch
  1145:          cmp     #ASC_INSDEL
  1146:          bne     @LE829
  1147:          ldy     zLNMX
  1148:          lda     (zPNT),y
  1149:          cmp     #' '
  1150:          bne     @LE7FE
  1151:          cpy     zPNTR
  1152:          bne     @LE805
  1153:  @LE7FE: cpy     #(EDITOR_MAX_COMBINED_ROWS * EDITOR_COLS) - 1
  1154:          beq     @LE826
  1155:          jsr     LE965
  1156:  @LE805: ldy     zLNMX
  1157:          jsr     UpdateColorRAMPointerToVideoramPointer
  1158:  @LE80A: dey
  1159:          lda     (zPNT),y
  1160:          iny
  1161:          sta     (zPNT),y
  1162:          dey
  1163:          lda     (zUSER),y
  1164:          iny
  1165:          sta     (zUSER),y
  1166:          dey
  1167:          cpy     zPNTR
  1168:          bne     @LE80A
  1169:          lda     #' '
  1170:          sta     (zPNT),y
  1171:          lda     lCOLOR
  1172:          sta     (zUSER),y
  1173:          inc     zINSRT                                          ; increment number of characters to output in insert mode
  1174:  @LE826: jmp     CHROUT_SCREEN_END
  1175:          ; -----------------------
  1176:  
  1177:  @LE829: ldx     zINSRT                                          ; number of characters to output in insert mode
  1178:          beq     @LE832
  1179:  @LE82D: ora     #$40
  1180:          jmp     CHROUT_SCREEN_OUTPUT_IN_RVS
  1181:  
  1182:  @LE832: cmp     #ASC_CURSORUPDOWN
  1183:          bne     @LE84C
  1184:          ldx     zTBLX
  1185:          beq     @LE871
  1186:          dec     zTBLX
  1187:          lda     zPNTR
  1188:          sec
  1189:          sbc     #EDITOR_COLS
  1190:          bcc     @LE847
  1191:          sta     zPNTR
  1192:          bpl     @LE871
  1193:  @LE847: jsr     SET_CURSORPOS
  1194:          bne     @LE871
  1195:  @LE84C: cmp     #ASC_RVS
  1196:          bne     @LE854
  1197:          lda     #$00
  1198:          sta     zRVS
  1199:  @LE854: cmp     #ASC_CURSORLEFTRIGHT
  1200:          bne     @LE86A
  1201:          tya
  1202:          beq     @LE864
  1203:          jsr     AdjustCursorRowBeforeMovingLeft         ; we want to move the cursor to the left. If we will cross a row this way, decrement the row number.
  1204:          dey
  1205:          sty     zPNTR
  1206:          jmp     CHROUT_SCREEN_END
  1207:  
  1208:  @LE864: jsr     CHROUT_SCREEN_WrapAroundToPreviousRow
  1209:          jmp     CHROUT_SCREEN_END
  1210:  @LE86A: cmp     #ASC_HOME
  1211:          bne     @LE874
  1212:          jsr     ClearScreen
  1213:  @LE871: jmp     CHROUT_SCREEN_END
  1214:  @LE874: ora     #$80
  1215:          jsr     EditorCheckColorCodeAndSetColor
  1216:  .if CompileComputer >= VIC20_06
  1217:          jmp     EditorCheckForAscUppercase
  1218:  .else
  1219:          jmp     CHROUT_SCREEN_END
  1220:  .endif
  1221:          ; -----------------------
  1222:  
  1223:  GoDownOneVirtualRow:
  1224:          lsr     zLXSP
  1225:          ldx     zTBLX
  1226:  @LE880: inx
  1227:          cpx     #EDITOR_ROWS
  1228:          bne     @LE888
  1229:          jsr     LE8EA
  1230:  @LE888: lda     zLDTB1,x
  1231:          bpl     @LE880
  1232:          stx     zTBLX
  1233:          jmp     SET_CURSORPOS
  1234:  
  1235:  CHROUT_SCREEN_CR:
  1236:          ; output a CR onto the screen at the current cursor position
  1237:  
  1238:          ldx     #$00
  1239:          stx     zINSRT                                  ; end INSERT mode
  1240:          stx     zRVS                                    ; end REVERSE (RVS) mode
  1241:          stx     zQTSW                                   ; end quotation mark mode
  1242:          stx     zPNTR                                   ; put cursor to the beginning of the current line (that is, CR w/o NL, so to speak)
  1243:  
  1244:          jsr     GoDownOneVirtualRow                     ; go down one (virtual) row
  1245:          jmp     CHROUT_SCREEN_END
  1246:          ; --------------
  1247:  
  1248:  ; If the cursor will be part of the previous row after being moved to the left
  1249:  ; (that is, the cursor is at the beginning of the current row now), move
  1250:  ; the cursor one row to the top.
  1251:  
  1252:  AdjustCursorRowBeforeMovingLeft:
  1253:          ldx     #EDITOR_MAX_COMBINED_ROWS               ; maximum number of rows that can be combined in one virtual row
  1254:          lda     #$00                                    ; start counter at column 0
  1255:  @Loop:
  1256:          cmp     zPNTR                                   ; is current cursor column the same as our counter?
  1257:          beq     @DecrementAndExit                       ; yes -> branch, decrement row and exit
  1258:          clc
  1259:          adc     #EDITOR_COLS                            ; calculate next multiple of EDITOR_COLS to test against
  1260:          dex                                             ; still a row to handle?
  1261:          bne     @Loop                                   ; yes -> branch, process next row
  1262:          rts
  1263:  
  1264:  @DecrementAndExit:
  1265:          dec     zTBLX                                   ; decrement current cursor row
  1266:          rts
  1267:  
  1268:  
  1269:  ; If the cursor will be part of the next row after being moved to the right
  1270:  ; (that is, the cursor is at the end of the current row now), move
  1271:  ; the cursor one row to the bottom.
  1272:  
  1273:  AdjustCursorRowBeforeMovingRight:
  1274:          ldx     #EDITOR_MAX_COMBINED_ROWS               ; maximum number of rows that can be combined in one virtual row
  1275:          lda     #EDITOR_COLS - 1                        ; start counter at last column of a physical row
  1276:  @Loop:
  1277:          cmp     zPNTR                                   ; is current cursor column the same as our counter?
  1278:          beq     @IncrementAndExit                       ; yes -> branch, increment row and exit
  1279:          clc
  1280:          adc     #EDITOR_COLS                            ; calculate next column to test against
  1281:          dex                                             ; still a row to handle?
  1282:          bne     @Loop                                   ; yes -> branch, process next row
  1283:          rts
  1284:  
  1285:  @IncrementAndExit:
  1286:          ldx     zTBLX                                   ; is current cursor row
  1287:          cpx     #EDITOR_ROWS                            ; less than the maximum?
  1288:          beq     @Rts                                    ; no, we cannot increment as we are already at the last row -> branch, skip increment
  1289:          inc     zTBLX                                   ; increment cursor row
  1290:  @Rts:   rts
  1291:  
  1292:  ; Check if the current PETSCII code is a color code
  1293:  ; If it is, set lCOLOR accordingly.
  1294:  ; Input:  A := PETSCII code
  1295:  ; Output: if A is a color code:
  1296:  ;            lCOLOR := X := color code
  1297:  ;         else
  1298:  ;            X := $FF, lCOLOR unchanged
  1299:  ; Uses:   X
  1300:  ;
  1301:  EditorCheckColorCodeAndSetColor:
  1302:          ldx     #END_ColorCodes - ColorCodes - 1        ; get number of color codes
  1303:  @CheckColor:
  1304:          cmp     ColorCodes,x                            ; is the current char a color code?
  1305:          beq     @ColorFound                             ; yes -> branch, we found a color
  1306:          dex                                             ; test the next color
  1307:          bpl     @CheckColor                             ; until there is not one left
  1308:          rts
  1309:  @ColorFound:
  1310:          stx     lCOLOR                                  ; store the color code in lCOLOR
  1311:          rts
  1312:  
  1313:  ColorCodes:
  1314:  
  1315:          ; These are the PETSCII values of the color codes
  1316:  
  1317:          .byte   $90,$05,$1C,$9F,$9C,$1E,$1F,$9E         ; colors no. 0-7
  1318:  
  1319:  .if CompileComputer >= C64_GENERAL
  1320:          .byte   $81,$95,$96,$97,$98,$99,$9A,$9B         ; The C64 has 8 additional colors defined here: colors no. 8-15
  1321:  
  1322:  .endif
  1323:  
  1324:  END_ColorCodes:
  1325:  
  1326:  .if CompileComputer < C64_GENERAL
  1327:  
  1328:          ; depending on lMODE, the old VIC20 KERNAL does some translation of
  1329:          ; character codes to be output.
  1330:          ; All of these codes being replaced have in common that their
  1331:          ; 7th bit ($80) is set.
  1332:          ;
  1333:          ; TODO Why is this done?
  1334:  
  1335:          ; this table is organised as follows: Each entry consists of a byte pair.
  1336:          ; the byte at offset 1 is the character that is to be replaced, and
  1337:          ; the byte at offset 0 is the character with which to replace.
  1338:  
  1339:          ; This is only used in VIC20_02 ROMs, although the
  1340:          ; table is also present in later ROMs.
  1341:  
  1342:  CHROUT_REPLACEMENT_TABLE:
  1343:          .byte   $EF,$A1
  1344:          .byte   $DF,$A6
  1345:          .byte   $E1,$B1
  1346:          .byte   $E2,$B2
  1347:          .byte   $E3,$B3
  1348:          .byte   $E4,$B4
  1349:          .byte   $E5,$B5
  1350:          .byte   $E6,$B6
  1351:          .byte   $E7,$B7
  1352:          .byte   $E8,$B8
  1353:          .byte   $E9,$B9
  1354:          .byte   $FA,$BA
  1355:          .byte   $FB,$BB
  1356:          .byte   $FC,$BC
  1357:          .byte   $EC,$BD
  1358:          .byte   $FE,$BE
  1359:          .byte   $84,$BF
  1360:          .byte   $F7,$C0
  1361:          .byte   $F8,$DB
  1362:          .byte   $F9,$DD
  1363:          .byte   $EA,$DE
  1364:  
  1365:  SpecialScreenCodeHandleTable:
  1366:          ; special screen code to PETSCII conversion table
  1367:          ; the first character is the screen code to convert,
  1368:          ; the second character is the PETSCII code to convert in
  1369:          ;
  1370:          ; This is only used in VIC20_02 ROMs, although the
  1371:          ; table is also present in later ROMs.
  1372:  
  1373:          .byte   $5E,$E0
  1374:          .byte   $5B,$E1
  1375:          .byte   $5D,$E2
  1376:          .byte   $40,$B0
  1377:          .byte   $61,$B1
  1378:          .byte   $78,$DB
  1379:          .byte   $79,$DD
  1380:          .byte   $66,$B6
  1381:          .byte   $77,$C0
  1382:          .byte   $70,$F0
  1383:          .byte   $71,$F1
  1384:          .byte   $72,$F2
  1385:          .byte   $73,$F3
  1386:          .byte   $74,$F4
  1387:          .byte   $75,$F5
  1388:          .byte   $76,$F6
  1389:          .byte   $7D,$FD
  1390:  
  1391:  SpecialScreenCodeHandleTable_END:
  1392:  
  1393:  .endif
  1394:  
  1395:  LE8EA:  lda     zSAL
  1396:          pha
  1397:          lda     zSAL + 1
  1398:          pha
  1399:          lda     zEAL
  1400:          pha
  1401:          lda     zEAL + 1
  1402:          pha
  1403:  @LE8F6: ldx     #$FF
  1404:          dec     zTBLX
  1405:          dec     zLXSP
  1406:          dec     lTLNIDX
  1407:  
  1408:  @LE8FF: inx
  1409:          jsr     CalculateScreenPointerFromRowNumber
  1410:          cpx     #EDITOR_ROWS - 1
  1411:          bcs     @LE913
  1412:          lda     SCREEN_LOWBYTE + 1,x
  1413:          sta     zSAL
  1414:          lda     zLDTB1 + 1,x
  1415:          jsr     CopyPhysicalScreenRow
  1416:          bmi     @LE8FF                                  ; => jmp, as CopyPhysicalScreenRow will not return with N=0 ("bpl loop")
  1417:          ; -----------------
  1418:  
  1419:  @LE913:
  1420:          jsr     EraseScreenRow
  1421:          ldx     #0
  1422:  @LE918: lda     zLDTB1,x
  1423:          and     #$7F
  1424:          ldy     zLDTB1 + 1,x
  1425:          bpl     @LE922
  1426:          ora     #$80
  1427:  @LE922: sta     zLDTB1,x
  1428:          inx
  1429:          cpx     #EDITOR_ROWS - 1
  1430:          bne     @LE918
  1431:          lda     zLDTB1 + EDITOR_ROWS - 1
  1432:          ora     #$80
  1433:          sta     zLDTB1 + EDITOR_ROWS - 1
  1434:          lda     zLDTB1
  1435:          bpl     @LE8F6
  1436:          inc     zTBLX
  1437:          inc     lTLNIDX
  1438:  
  1439:          ; check for a pressed CTRL key:
  1440:          ; If it is pressed, incorporate an additional delay
  1441:  
  1442:  .ifdef JIFFY
  1443:  
  1444:  JDLE938:
  1445:          jsr     RestoreKeyboardRowAndRet
  1446:  
  1447:  .else
  1448:          lda     #KEYB_ROW_CTRL                          ; set the keyboard row to the row that has the CTRL key
  1449:          sta     KEYB_ROW
  1450:  .endif
  1451:          lda     KEYB_COL                                ; test the keyboard columns
  1452:          cmp     #KEYB_COL_CTRL                          ; check the CTRL key specifically
  1453:  
  1454:  .ifdef JIFFY
  1455:          bne     @SkipDelay
  1456:          ldx     zNDX
  1457:          beq     JDLE938
  1458:          lda     $0276,x
  1459:          sbc     #$13
  1460:          bne     @SkipDelay
  1461:          sta     zNDX
  1462:  @JDLE94F:       cli
  1463:          cmp     zNDX
  1464:          beq     @JDLE94F
  1465:          sta     zNDX
  1466:  
  1467:  .else
  1468:          php                                             ; remember status
  1469:          lda     #KEYB_ROW_STANDARD              ; restore the keyboard row
  1470:          sta     KEYB_ROW
  1471:          plp                                             ; get back the status
  1472:          bne     @SkipDelay                                      ; Z=1 --> CTRL key not pressed --> branch, skip delay
  1473:  
  1474:          ; create a delay of TODO clock cycles
  1475:          ldy     #0
  1476:  @Delay:
  1477:          nop
  1478:          dex
  1479:          bne     @Delay
  1480:          dey
  1481:          bne     @Delay
  1482:          sty     zNDX
  1483:  
  1484:  .endif
  1485:  
  1486:  @SkipDelay:
  1487:          ldx     zTBLX
  1488:  
  1489:  Restore_zEAL_and_zSAL:
  1490:          pla
  1491:          sta     zEAL + 1
  1492:          pla
  1493:          sta     zEAL
  1494:          pla
  1495:          sta     zSAL + 1
  1496:          pla
  1497:          sta     zSAL
  1498:          rts
  1499:  
  1500:  LE965:
  1501:          ldx     zTBLX
  1502:  LE967:
  1503:          ; find next (virtual) row
  1504:          inx                                     ; proceed to next (physical) row
  1505:          lda     zLDTB1,x                        ; is it combined with the previous one (bit 7 = 0)?
  1506:          bpl     LE967                           ; yes -> branch, loop to test the next row
  1507:  
  1508:          stx     lTLNIDX                         ; remember the row number of the next (virtual) row
  1509:          cpx     #EDITOR_ROWS - 1                ; is this the last (phyiscal) row?
  1510:          beq     @LE981                          ; yes -> branch
  1511:          bcc     @LE981                          ; row number is less than last row -> also branch
  1512:  
  1513:          jsr     LE8EA
  1514:          ldx     lTLNIDX
  1515:          dex
  1516:          dec     zTBLX
  1517:          jmp     LogicallyCombineTwoRows
  1518:          ; --------------------
  1519:  
  1520:          ; Make room on the screen for the extension of a logical screen row to comprise another
  1521:          ; physical screen row. This involves scrolling every row below lTLNIDX down (to make
  1522:          ; room), erasing the new row, and adjusting the pointers in zLDTB1.
  1523:  
  1524:  @LE981:
  1525:          ; save zEAL/zEAL+1 and zSAL/zSAL+1 on the stack as they will be used
  1526:          ; as temporary storage for pointers.
  1527:          ; These will be restored before leaving.
  1528:  
  1529:          lda     zSAL
  1530:          pha
  1531:          lda     zSAL + 1
  1532:          pha
  1533:          lda     zEAL
  1534:          pha
  1535:          lda     zEAL + 1
  1536:          pha
  1537:  
  1538:          ; Move screen contents below the current cursor position downwards
  1539:  
  1540:          ldx     #EDITOR_ROWS                            ; start at the last physical row
  1541:  @CopyRow:
  1542:          dex
  1543:          jsr     CalculateScreenPointerFromRowNumber     ; update the destination pointer into video RAM (zPNT/zPNT+1)
  1544:          cpx     lTLNIDX                                 ; have we already reached the current screen row?
  1545:          bcc     @EndMove                                ; we are above the current screen row -> branch, end the copy (TODO is this needed at all?)
  1546:          beq     @EndMove                                ; we are at the current screen row -> branch, end the copy
  1547:  
  1548:          ; update the source pointers
  1549:          lda     SCREEN_LOWBYTE - 1,x                    ; get low byte of the starting address of this row
  1550:          sta     zSAL                                    ; remember low byte
  1551:          lda     zLDTB1 - 1,x                            ; get high byte of the starting address of this row
  1552:          jsr     CopyPhysicalScreenRow                   ; copy the current screen row from source to destination, moving it down
  1553:          bmi     @CopyRow                                ; => jmp, as CopyPhysicalScreenRow will not return with N=0 ("bpl loop")
  1554:          ; ---------------------------
  1555:  
  1556:  @EndMove:
  1557:          jsr     EraseScreenRow                          ; erase the (physical) screen row in X
  1558:  
  1559:          ; update zLDTB1 to reflect the new situation
  1560:          ; copy the high order (7th) bit of the byte for each row that has been moved
  1561:          ; to the next row.
  1562:          ; source row is the row that is copied, destination row is the next row
  1563:  
  1564:          ldx     #EDITOR_ROWS - 2                        ; start at 2nd to last row of source row
  1565:  
  1566:  @MoveCombinationBits:
  1567:          cpx     lTLNIDX                                 ; have we already reached (<=) the current row?
  1568:          bcc     @EndMoveCombinationBits                 ; yes, quit
  1569:  
  1570:          lda     zLDTB1 + 1,x                            ; get the byte for the next row
  1571:          and     #~$80                                   ; clear bit 7 in all cases
  1572:          ldy     zLDTB1,x                                ; read 7th bit of source row
  1573:          bpl     @Positive                               ; if it is unset (positive), skip
  1574:          ora     #$80                                    ; set the 7th bit of destination row
  1575:  @Positive:
  1576:          sta     zLDTB1 + 1,x                            ; store byte for destination row
  1577:          dex                                             ; proceed with previous row
  1578:          bne     @MoveCombinationBits
  1579:  
  1580:  @EndMoveCombinationBits:
  1581:          ldx     lTLNIDX
  1582:          jsr     LogicallyCombineTwoRows
  1583:  
  1584:          ; restore zEAL/zEAL+1 and zSAL/zSAL+1
  1585:  
  1586:  .if CompileComputer >= C64_GENERAL
  1587:          jmp     Restore_zEAL_and_zSAL                           ; same implementation like VIC-20, but we save memory as it is already there
  1588:  .else
  1589:          pla
  1590:          sta     zEAL + 1
  1591:          pla
  1592:          sta     zEAL
  1593:          pla
  1594:          sta     zSAL + 1
  1595:          pla
  1596:          sta     zSAL
  1597:          rts
  1598:  .endif
  1599:  
  1600:          ; Copy one (physical) screen row on screen to another screen row
  1601:  
  1602:          ; This will copy a physical screen row in memory, including the video and the color RAM.
  1603:          ; It is used to scroll the screen up or down, but it is not limited to this usage.
  1604:  
  1605:          ; Input: A = high byte of start address of (logical TODO) screen row (cf. zLDTB1) from which to copy
  1606:          ;        zSAL = low byte of start address of (physical) screen row (cf. SCREEN_LOWBYTE) from which to copy
  1607:  
  1608:          ;        zPNT/zPNT+1   = Start address of physical screen row video RAM destination
  1609:          ;        zUSER/zUSER+1 = Start address of physical screen row color RAM destination
  1610:  
  1611:  CopyPhysicalScreenRow:
  1612:          and     #>lVIDEORAM_SIZE                        ; make sure to let the start address
  1613:          ora     lHIBASE                                 ; point into the current video RAM
  1614:          sta     zSAL + 1                                ; store the address as pointer
  1615:          jsr     @UpdateColorRamPointers
  1616:  
  1617:          ; Copy one (physical) row
  1618:  
  1619:          ldy     #EDITOR_COLS - 1                        ; index of last character in a (physical) row
  1620:  @CopyPreviousChar:
  1621:          lda     (zSAL),y                                ; get character from source
  1622:          sta     (zPNT),y                                ; and store it at the destination
  1623:          lda     (zEAL),y                                ; get color from source
  1624:          sta     (zUSER),y                               ; and store it at the destination
  1625:          dey                                             ; go to previous character
  1626:          bpl     @CopyPreviousChar                       ; non-negative -> branch, there is still a character to be processed
  1627:          rts
  1628:          ; -----------------------
  1629:  
  1630:          ; Update the color RAM pointers in zUSER/zUSER+1 and zEAL/zEAL+1, respectively,
  1631:          ; to point to the same locations as the video RAM pointers
  1632:          ; in zPNT/zPNT+1 and zSAL/zSAL+1, respectively.
  1633:  
  1634:  @UpdateColorRamPointers:
  1635:          jsr     UpdateColorRAMPointerToVideoramPointer  ; update the color RAM pointer to match the video RAM pointer
  1636:  
  1637:          ; adjust video RAM pointer in zSAL/zSAL+1 to point to the color RAM (in zEAL/zEAL+1)
  1638:          lda     zSAL
  1639:          sta     zEAL
  1640:          lda     zSAL + 1
  1641:          and     #>lVIDEORAM_SIZE
  1642:          ora     #>COLORRAM
  1643:          sta     zEAL + 1
  1644:          rts
  1645:  
  1646:  CalculateScreenPointerFromRowNumber:
  1647:          ; calculate the start of the screen row of which the number
  1648:          ; is given in X. Store it in zPNT.
  1649:  
  1650:          lda     SCREEN_LOWBYTE,x        ; get low byte of the starting address of this row
  1651:          sta     zPNT                    ; remember low byte
  1652:          lda     zLDTB1,x                ; get high byte of the starting address of this row
  1653:          and     #>lVIDEORAM_SIZE        ; mask out additional bits used as flags
  1654:          ora     lHIBASE                 ; add the video RAM base
  1655:          sta     zPNT + 1                ; remember high byte
  1656:          rts
  1657:  
  1658:  
  1659:  EraseScreenRow:
  1660:  
  1661:          ; this routine erases the screen row no. X
  1662:  
  1663:          ldy     #EDITOR_COLS - 1                ; start in the last column
  1664:          jsr     CalculateScreenPointerFromRowNumber     ; set the video RAM pointer in zPNT to the row we want to process
  1665:          jsr     UpdateColorRAMPointerToVideoramPointer  ; update the video RAM pointer in zUSER to correspond to zPNT
  1666:  
  1667:  @Loop:
  1668:  .if CompileComputer >= C64_03 .AND CompileComputer <> C64_4064
  1669:          jsr     Patch_StoreColor                        ; set the color of the location
  1670:  .endif
  1671:          lda     #' '                                    ; store a SPACE (' ') into the video RAM position
  1672:          sta     (zPNT),y
  1673:  .if CompileComputer = C64_02 .OR CompileComputer = C64_4064
  1674:          jsr     Patch_StoreColor                        ; set the color of the location
  1675:          nop
  1676:  .elseif CompileComputer <= C64_01
  1677:          lda     #COL_WHITE                              ; set the color of the location to white
  1678:          sta     (zUSER),y
  1679:  .endif
  1680:          dey                                             ; proceed to the previous column
  1681:          bpl     @Loop                                   ; still >= 0, branch -> process the next column
  1682:          rts
  1683:          ; ----------------------------------
  1684:  
  1685:  .if CompileComputer >= C64_03 .AND CompileComputer <> C64_4064
  1686:          nop
  1687:  .endif
  1688:  
  1689:  
  1690:  StoreCharacterOnScreenAndDisableBlinking:
  1691:          tay                                             ; remember character to output
  1692:  
  1693:          lda     #$02                                    ; set blink counter to $02 (TODO WHY?)
  1694:          sta     zBLNCT
  1695:  
  1696:          jsr     UpdateColorRAMPointerToVideoramPointer  ; set pointer to video RAM at cursor position
  1697:  
  1698:          tya                                             ; get back character to output
  1699:  
  1700:  ;
  1701:  ; Store character on screen at the current cursor position
  1702:  ;
  1703:  ; A = character
  1704:  ; X = color
  1705:  ;
  1706:  StoreCharacterOnScreen:
  1707:          ldy     zPNTR                                   ; get column offset in of current screen position
  1708:          sta     (zPNT),y                                ; store character in video RAM
  1709:          txa                                             ; get color
  1710:          sta     (zUSER),y                               ; store color in color RAM
  1711:          rts
  1712:  
  1713:  UpdateColorRAMPointerToVideoramPointer:
  1714:          lda     zPNT
  1715:          sta     zUSER
  1716:          lda     zPNT + 1
  1717:          and     #>lVIDEORAM_SIZE
  1718:          ora     #>COLORRAM
  1719:          sta     zUSER + 1
  1720:          rts
  1721:  
  1722:  KIRQ:
  1723:          jsr     kUDTIM
  1724:          lda     zBLNSW
  1725:          bne     @LEA61
  1726:          dec     zBLNCT
  1727:          bne     @LEA61
  1728:          lda     #$14
  1729:          sta     zBLNCT
  1730:          ldy     zPNTR
  1731:          lsr     zBLNON
  1732:          ldx     lGDCOL
  1733:          lda     (zPNT),y
  1734:          bcs     @LEA5C
  1735:          inc     zBLNON
  1736:          sta     zGDBLN
  1737:          jsr     UpdateColorRAMPointerToVideoramPointer
  1738:          lda     (zUSER),y
  1739:          sta     lGDCOL
  1740:          ldx     lCOLOR
  1741:          lda     zGDBLN
  1742:  @LEA5C: eor     #$80
  1743:          jsr     StoreCharacterOnScreen
  1744:  
  1745:  @LEA61:
  1746:  
  1747:  .ifdef JIFFY
  1748:  
  1749:  LEA61:  jmp     LEA7B
  1750:  LEA64:  pla
  1751:          pha
  1752:          cmp     #$98
  1753:          beq     JDLEA6D
  1754:  JDLEA6A:  jmp     LA57C
  1755:  JDLEA6D:  jsr     JDLF72C
  1756:          bne     JDLEA6A
  1757:          ldx     zTXTPTR
  1758:          ldy     #$04
  1759:          tya
  1760:          jmp     JDLA5E3
  1761:          .byte   $01
  1762:  
  1763:  .else
  1764:          lda     TAPE_REG_SENSE
  1765:          and     #TAPE_B_SENSE
  1766:          beq     @LEA71
  1767:          ldy     #$00
  1768:          sty     zCAS1
  1769:          lda     TAPE_REG_MOTOR
  1770:          ora     #TAPE_B_MOTOR_ON
  1771:          bne     @LEA79
  1772:          ; -------------------------
  1773:  
  1774:  @LEA71: lda     zCAS1
  1775:          bne     LEA7B
  1776:          lda     TAPE_REG_MOTOR
  1777:          and     #TAPE_B_MOTOR_OFF_AND
  1778:  @LEA79:
  1779:    .if CompileComputer < C64_GENERAL
  1780:          bit     VIA1_IEC
  1781:          bvs     LEA7B
  1782:    .endif
  1783:          sta     TAPE_REG_MOTOR
  1784:  
  1785:  .endif
  1786:  
  1787:  LEA7B:
  1788:  .if CompileComputer = C64_4064
  1789:          jsr     LE4C8
  1790:  .else
  1791:          jsr     iSCNKEY
  1792:  .endif
  1793:  
  1794:  .if CompileComputer >= C64_GENERAL
  1795:          lda     CIA1 + CIA_O_ICR
  1796:  .else
  1797:          bit     VIA2_T1CL
  1798:  .endif
  1799:          pla
  1800:          tay
  1801:          pla
  1802:          tax
  1803:          pla
  1804:          rti
  1805:  
  1806:  ;  B-25. Function Name: SCNKEY
  1807:  ;
  1808:  ;    Purpose: Scan the keyboard
  1809:  ;    Call address: $FF9F (hex) 65439 (decimal)
  1810:  ;    Communication registers: None
  1811:  ;    Preparatory routines: IOINIT
  1812:  ;    Error returns: None
  1813:  ;    Stack requirements: 5
  1814:  ;    Registers affected: A, X, Y
  1815:  ;
  1816:  ;    Description: This routine scans the Commodore 64 keyboard and checks
  1817:  ;  for pressed keys. It is the same routine called by the interrupt handler.
  1818:  ;  If a key is down, its ASCII value is placed in the keyboard queue. This
  1819:  ;  routine is called only if the normal IRQ interrupt is bypassed.
  1820:  ;
  1821:  ;  How to Use:
  1822:  ;
  1823:  ;  1) Call this routine.
  1824:  ;
  1825:  ;  EXAMPLE:
  1826:  ;
  1827:  ;    GET  JSR SCNKEY      ;SCAN KEYBOARD
  1828:  ;         JSR GETIN       ;GET CHARACTER
  1829:  ;         CMP #0          ;IS IT NULL?
  1830:  ;         BEQ GET         ;YES... SCAN AGAIN
  1831:  ;         JSR CHROUT      ;PRINT IT
  1832:  ;
  1833:  ;
  1834:  iSCNKEY:
  1835:          lda     #0              ; start with: No shift key (SHIFT, CTRL, CBM) is pressed
  1836:          sta     lSHFLAG
  1837:  
  1838:          ldy     #KEY_NONE       ; start with: No key pressed
  1839:          sty     zSFDX
  1840:  
  1841:          ; check if any key is pressed at all
  1842:          sta     KEYB_ROW        ; set all rows to 0
  1843:          ldx     KEYB_COL        ; get columns
  1844:          cpx     #$FF            ; everything set?
  1845:          beq     iSCNKEY_EndScan ; yes, no key is pressed, abort.
  1846:                                  ; Note that X = $FF is crucial here, as iSCNKEY_EndScan checked for the keycode (in X). If there were anything else in X, then this would be used as keycode and stored into the keyboard buffer
  1847:  
  1848:  .if CompileComputer >= C64_GENERAL
  1849:          tay                     ; Place of the key pressed in the KEYTAB (stored in Y) = 0
  1850:  .else
  1851:          lda     #~$01           ; start at row 0 (2^0)
  1852:          sta     KEYB_ROW
  1853:          ldy     #$00            ; Place of the key pressed in the KEYTAB (stored in Y) = 0
  1854:  .endif
  1855:          lda     #<KEYTAB_UNSHIFTED      ; start with the unshifted keytab
  1856:          sta     zKEYTAB
  1857:          lda     #>KEYTAB_UNSHIFTED
  1858:          sta     zKEYTAB + 1
  1859:  
  1860:  .if CompileComputer >= C64_GENERAL
  1861:          lda     #~$01           ; start at row 0 (2^0)
  1862:          sta     KEYB_ROW
  1863:  .endif
  1864:  
  1865:  @CheckAllRows:
  1866:          ldx     #8              ; process every of the 8 keyboard columns
  1867:  
  1868:  .if CompileComputer >= C64_GENERAL
  1869:          pha                     ; remember the mask we put into KEYB_ROW for later processing
  1870:  .endif
  1871:  
  1872:  @UnbounceColumns:
  1873:          lda     KEYB_COL        ; get column
  1874:          cmp     KEYB_COL        ; unbounce it
  1875:  .if CompileComputer >= C64_GENERAL
  1876:          bne     @UnbounceColumns        ; if it changed between reading, re-read it
  1877:  .else
  1878:          bne     @CheckAllRows   ; if colum changed between reading, re-read it. The LDX #8 does not do any harm here; however, for the additional PHA on the C64, we would corrupt the stack. Thus, the target of the branch was changed on the C64.
  1879:  
  1880:  .endif
  1881:  
  1882:          ; the following loop tests each column one after one if the bit was 0
  1883:          ; if it was, the key on the row/column was pressed.
  1884:          ; This is not completely right if more than one key was pressed,
  1885:          ; but this is a hardware limitation we cannot handle.
  1886:  
  1887:  @ProcessColumn:
  1888:          lsr     a                       ; get bit from column into C
  1889:          bcs     @KeyNotPressed          ; C set -> jump, key was not pressed
  1890:  
  1891:          pha                             ; remember current column mask
  1892:  
  1893:          lda     (zKEYTAB),y             ; get the key code that corresponds to the current row/column
  1894:          cmp     #5                      ; is it >= 5?
  1895:          bcs     @StoreKey               ; yes, it is a printable char, branch in order to store it
  1896:          cmp     #KEY_STOP               ; is it the Run/Stop key?
  1897:          beq     @StoreKey               ; yes, store it
  1898:  
  1899:          ; if we reached here, the character code is 1, 2, or 4: One of the keys shift or CBM
  1900:          ; thus, remember the shift flag
  1901:  
  1902:          ora     lSHFLAG                 ; set the corresponding flag
  1903:          sta     lSHFLAG                 ;
  1904:  
  1905:          bpl     @DoNotStoreKey          ; unconditional jump, as lSHFLAG.7 is never set.
  1906:          ; ----------------------------
  1907:  
  1908:  @StoreKey:
  1909:          sty     zSFDX                   ; remember key code
  1910:  
  1911:  @DoNotStoreKey:
  1912:          pla                             ; get back column mask
  1913:  
  1914:  @KeyNotPressed:
  1915:          iny                             ; this keytable entry was processed, go to the next one
  1916:          cpy     #$41                    ; did we already process all $40 entries?
  1917:          bcs     @EndScanning            ; yes, we are done (for now)
  1918:  
  1919:          dex                             ; decrement the column counter
  1920:          bne     @ProcessColumn          ; and repeat scanning, if the counter did not reach 0.
  1921:  
  1922:          ; The following code rotates the mask at KEYB_ROW to the left.
  1923:          ; This moves the "0" bit from right to left.
  1924:          ; Thus, every row is processed, one after the other.
  1925:          ; The implementation was changed between VIC20 and C64, though:
  1926:          ; While the VIC20 uses a ROL on the KEYB_ROW address directly,
  1927:          ; the C64 performs the ROL in a register and puts the value into
  1928:          ; KEYB_ROW afterwards.
  1929:          ; This change most likely occurred since ROL is a read-modify-write
  1930:          ; instruction. Thus, it will write to the location two times, in two
  1931:          ; consecutives cycles: First, it will write the old value, and after-
  1932:          ; wards, it will write the new one. This might generate some "spike"
  1933:          ; which could inadvertedly affect the reading.
  1934:  
  1935:          sec                             ; make sure to ROL in a "1" bit
  1936:  
  1937:  .if CompileComputer >= C64_GENERAL
  1938:          pla                             ; get back the mask we put at KEYB_ROW last time
  1939:          rol     a                       ; rotate it to the left
  1940:          sta     KEYB_ROW                ; and set the new mask
  1941:  .else
  1942:          rol     KEYB_ROW                ; rotate to mask to the left
  1943:  .endif
  1944:          bne     @CheckAllRows
  1945:  
  1946:  @EndScanning:
  1947:  
  1948:  .if CompileComputer >= C64_GENERAL
  1949:          pla                             ; we do not need the mask we put at KEYB_ROW last time anymore, remove it
  1950:  .endif
  1951:  
  1952:          ; Essentially, we are done with scanning here. However, we have to determine
  1953:          ; if a shift key (SHIFT, C=, CTRL) was pressed, which changes the meaning of
  1954:          ; some keys. Thus, process the shift keys now.
  1955:          jmp     (lKEYLOG)               ; points to CHECK_SHIFT_CTRL_CBM
  1956:  
  1957:  ConvertRawKeycodeToInterpretedKeycode:
  1958:          ldy     zSFDX                           ; get the character code
  1959:          lda     (zKEYTAB),y                     ; and read in the right ASCII value of it according to the right KEYTAB
  1960:          tax
  1961:  
  1962:          cpy     zLSTX
  1963:          beq     @CheckRepeat
  1964:          ldy     #$10
  1965:          sty     lDELAY
  1966:          bne     StoreKeyCodeIntoKeyBuffer
  1967:  
  1968:  @CheckRepeat:
  1969:          and     #$7F                            ; ignore bit 7 of key (TODO why?)
  1970:  
  1971:          ; determine if the key press is to be repeated
  1972:  
  1973:          bit     lRPTFLG                         ; check repeat flag
  1974:          bmi     RepeatKey                       ; bit 7 set, repeat all keys --> branch
  1975:          bvs     RestoreKeyboardRowAndRet        ; bit 6 set, do not repeat any key --> branch
  1976:  
  1977:          cmp     #$7F                            ; Is this the key ... (TODO Which key is this?)
  1978:  
  1979:  iSCNKEY_EndScan:
  1980:          beq     StoreKeyCodeIntoKeyBuffer       ; Yes, branch -> Store the key into the keyboard buffer
  1981:                                                  ; TODO Why this extra handling?
  1982:  
  1983:          cmp     #ASC_INSDEL                     ; did the user press INS/DEL?
  1984:          beq     RepeatKey                       ; yes, branch -> process repetition
  1985:  
  1986:          cmp     #' '                            ; did the user press SPACE (" "), or shifted SPACE ($A0)?
  1987:          beq     RepeatKey                       ; yes, process the repetition
  1988:  
  1989:          cmp     #ASC_CURSORLEFTRIGHT            ; did the user press CRSR LEFT/CRSR RIGHT key?
  1990:          beq     RepeatKey                       ; yes, branch -> process the repetition
  1991:  
  1992:          cmp     #ASC_CURSORUPDOWN               ; did the user press CRSR LEFT/CRSR RIGHT key?
  1993:          bne     RestoreKeyboardRowAndRet        ; no, branch -> do not store the key at all
  1994:  
  1995:  RepeatKey:
  1996:  
  1997:          ; wait for the initial delay counter lDELAY
  1998:  
  1999:          ; For key repetitions, there are two delay: One is the initial delay, that is,
  2000:          ; how long must a key be pressed before the repetition takes place.
  2001:          ; This is counted by lDELAY.
  2002:  
  2003:          ; The other delay is the counter between repeated keys, if the key is hold
  2004:          ; long enough. This is counted by lKOUNT.
  2005:  
  2006:          ldy     lDELAY                          ; is there an initial delay?
  2007:          beq     @NoInitialDelay                 ; no, repeat immediately
  2008:  
  2009:          dec     lDELAY                          ; yes, decrement the initial delay counter
  2010:          bne     RestoreKeyboardRowAndRet        ; still not delayed enough -> branch, do nothing
  2011:  
  2012:  @NoInitialDelay:
  2013:          dec     lKOUNT                          ; decrement the delay counter
  2014:          bne     RestoreKeyboardRowAndRet        ; not yet 0, do nothing
  2015:  
  2016:          ldy     #$04                            ; restore the delay counter
  2017:          sty     lKOUNT
  2018:  
  2019:          ; test if the keyboard buffer is empty.
  2020:          ; if it is not empty, no key repetition will take place.
  2021:  
  2022:          ; this way, we prevent a full keyboard buffer with repeated keys,
  2023:          ; which would not be a good user experience (we repeat keys faster
  2024:          ; than the program can handle them)
  2025:  
  2026:          ldy     zNDX                            ; number of keys in keyboard buffer
  2027:          dey                                     ; - 1
  2028:          bpl     RestoreKeyboardRowAndRet        ; still > 0? Then, the buffer is not empty -> branch, do nothing
  2029:  
  2030:  StoreKeyCodeIntoKeyBuffer:
  2031:  
  2032:          ; Store key code in X into keybuffer
  2033:  
  2034:          ; remember key code for the next call of the keyboard routines.
  2035:          ; this is used for determining if a key was pressed for a longer time
  2036:          ; and if it has to be repeated, or not.
  2037:          ldy     zSFDX
  2038:          sty     zLSTX
  2039:  
  2040:          ; remember shift states for the next call of the keyboard routines.
  2041:          ; this way, we prevent that SHIFT-CBM is processed more than once, as
  2042:          ;it is only processed if the shift state changed.
  2043:          ldy     lSHFLAG
  2044:          sty     lLSTSHF
  2045:  
  2046:          cpx     #$FF                            ; is the key an invalid one ($FF in the keyboard tables?)
  2047:          beq     RestoreKeyboardRowAndRet        ; yes, branch -> do not store it
  2048:          txa
  2049:  
  2050:          ; here, we store the keycode that is in A into the keyboard buffer
  2051:          ; Note that this routine will generate a race in case it is called w/o
  2052:          ; interrupts disabled
  2053:  
  2054:          ldx     zNDX                    ; get the index into the keyboard buffer
  2055:          cpx     lXMAX                   ; is the buffer full?
  2056:          bcs     RestoreKeyboardRowAndRet        ; yes, branch -> do not store the key
  2057:          sta     lKEYD,x                 ; store the keycode into the buffer
  2058:          inx                             ; increment the number of keys in the buffer
  2059:          stx     zNDX                    ; and store it
  2060:  
  2061:  RestoreKeyboardRowAndRet:
  2062:          lda     #KEYB_ROW_STANDARD      ; restore the keyboard row
  2063:          sta     KEYB_ROW
  2064:          rts
  2065:  
  2066:  
  2067:  CHECK_SHIFT_CTRL_CBM:
  2068:  
  2069:          ; Determine if a shift key (SHIFT, C=, CTRL) was pressed, which changes
  2070:          ; the meaning of some keys.
  2071:  
  2072:          lda     lSHFLAG                         ; get the shift state
  2073:          cmp     #lSHFLAG_SHIFT | lSHFLAG_CBM    ; shift and commodore pressed?
  2074:          bne     @SwitchToShiftedKeyTable        ; no, branch -> process a shifted key table instead
  2075:  
  2076:          cmp     lLSTSHF                         ; yes, check if the state changed from the last scan
  2077:          beq     RestoreKeyboardRowAndRet        ; it's the same state, branch -> do nothing
  2078:  
  2079:          ; If we reach here, SHIFT and C= were pressed simultaneously.
  2080:          ; Thus, the user wants to switch between Uppercase+Graphics mode,
  2081:          ; and Lowercase + Uppercase mode.
  2082:  
  2083:          lda     lMODE                           ; are we allowed to switch modes?
  2084:          bmi     @ConvertRawKeycodeToInterpretedKeycode                          ; no, branch -> skip
  2085:  
  2086:  .if CompileComputer >= C64_GENERAL
  2087:  
  2088:          ; we change mode by changing the base address of the character ROM in the VIC-II
  2089:  
  2090:          lda     VIC + VICII_O_MemControl
  2091:          eor     #$02
  2092:          sta     VIC + VICII_O_MemControl
  2093:  .elseif CompileComputer >= VIC20_06
  2094:      .repeat 19
  2095:          nop
  2096:      .endrep
  2097:  
  2098:          ; we change mode by changing the base address of the character ROM in the VIC-II
  2099:  
  2100:          lda     VIC + VICI_O_MemoryLocations
  2101:          eor     #$02
  2102:          sta     VIC + VICI_O_MemoryLocations
  2103:  
  2104:      .repeat 4
  2105:          nop
  2106:      .endrep
  2107:  .else
  2108:          ; this is just a complicated way to EOR VICI_O_MemoryLocations with $02
  2109:          ; furthermore, it keeps track if the state in lMODE.4 ($10)
  2110:  
  2111:          and     #$18                    ; determine current mode
  2112:          beq     @SwitchToLowercase      ; it is uppercase, branch -> switch to lowercase
  2113:  
  2114:          ; if we reach here, we are in lowercase mode and want to switch to uppercase mode
  2115:  
  2116:          ; remember uppercase mode
  2117:          lda     #$00
  2118:          sta     lMODE
  2119:  
  2120:          ; switch VIC to uppercase mode
  2121:          lda     VIC + VICI_O_MemoryLocations
  2122:          and     #~$02
  2123:          sta     VIC + VICI_O_MemoryLocations
  2124:  
  2125:          bne     @ConvertRawKeycodeToInterpretedKeycode                  ; unconditional branch
  2126:          ; --------------
  2127:  
  2128:  @SwitchToLowercase:
  2129:          ; switch VIC to lowercase mode
  2130:          lda     VIC + VICI_O_MemoryLocations
  2131:          ora     #$02
  2132:          sta     VIC + VICI_O_MemoryLocations
  2133:  
  2134:          ; remember lowercase mode
  2135:          lda     #$08
  2136:          sta     lMODE
  2137:  .endif
  2138:  
  2139:  .if CompileComputer = VIC20_02
  2140:          bne     @ConvertRawKeycodeToInterpretedKeycode          ; unconditional jump
  2141:  .else
  2142:          jmp     @ConvertRawKeycodeToInterpretedKeycode
  2143:  .endif
  2144:          ; ---------------------------
  2145:  
  2146:  @SwitchToShiftedKeyTable:
  2147:  
  2148:          ; (here, we enter with A := lSHFLAG)
  2149:  
  2150:          ; Calculate the offset of the key table for the shift flags
  2151:          ; this is done by doubling the value of lSHFLAG, and special
  2152:          ; handling of lSHFLAG_CTRL which would double to 8, but 6 is
  2153:          ; the right offset
  2154:  
  2155:          asl     a                       ; double shift flag
  2156:          cmp     #2 * lSHFLAG_CTRL       ; is it CTRL?
  2157:          bcc     @UseOffset              ; no, use the offset
  2158:          lda     #$06                    ; yes, correct the offset
  2159:  
  2160:  .if CompileComputer >= C64_GENERAL
  2161:  .elseif CompileComputer >= VIC20_06
  2162:          nop
  2163:          nop
  2164:  .else
  2165:          bne     @VIC20_02_HandleOffsetDirectly  ; for VIC-20-02, use this offset. (unconditional branch)
  2166:          ; ------------------------------------
  2167:  .endif
  2168:  
  2169:  @UseOffset:
  2170:  
  2171:  .if CompileComputer >= C64_GENERAL
  2172:  
  2173:  .elseif CompileComputer >= VIC20_06
  2174:      .repeat 32
  2175:          nop
  2176:      .endrep
  2177:  .else
  2178:  
  2179:          ldx     lMODE                                   ; if lMODE = 0, use the offset
  2180:          beq     @VIC20_02_HandleOffsetDirectly
  2181:  
  2182:          ldx     lSHFLAG                                 ; if C= key is not the only shift key pressed:
  2183:          cpx     #lSHFLAG_CBM
  2184:          bne     @VIC20_02_HandleOffsetDirectly          ; branch -> handle the offset
  2185:  
  2186:          ; if we reach here, the C= key is pressed (but not SHIFT or CTRL)
  2187:  
  2188:          cpx     lLSTSHF                                 ; did the shift state change from the last time?
  2189:          beq     @ConvertRawKeycodeToInterpretedKeycode  ; no, just convert the key code.
  2190:  
  2191:          ; TODO
  2192:          ; switch uppercase mode with lowercase mode (why?)
  2193:          ; switch bit 4 (???) (why?)
  2194:          ; but do not update the VIC itself (why?)
  2195:  
  2196:          lda     lMODE
  2197:          eor     #$18
  2198:          sta     lMODE
  2199:  
  2200:          bpl     @ConvertRawKeycodeToInterpretedKeycode          ; branches if lMODE.7 is 0: switching between uppercase-mode and lowercase-mode is allowed. (TODO why?)
  2201:  
  2202:  @VIC20_02_HandleOffsetDirectly:
  2203:          ora     lMODE                   ; get the offset into KEYTABS_VEC
  2204:          and     #$7F
  2205:  .endif
  2206:  
  2207:          tax                             ; X := offset into KEYTABS_VEC
  2208:  
  2209:          ; switch to the right KEYTAB according to the shift states
  2210:  
  2211:          lda     @KEYTABS_VEC,x
  2212:          sta     zKEYTAB
  2213:          lda     @KEYTABS_VEC + 1,x
  2214:          sta     zKEYTAB + 1
  2215:  
  2216:  @ConvertRawKeycodeToInterpretedKeycode:
  2217:          jmp     ConvertRawKeycodeToInterpretedKeycode
  2218:  
  2219:  @KEYTABS_VEC:
  2220:          .addr   KEYTAB_UNSHIFTED        ; $00
  2221:          .addr   KEYTAB_SHIFT            ; $02 (lSHFLAG=$01, SHIFT)
  2222:          .addr   KEYTAB_CBM              ; $04 (lSHFLAG=$02, C=)
  2223:          .addr   KEYTAB_CTRL             ; $06 (lSHFLAG=$04, CTRL)
  2224:  
  2225:  .if CompileComputer < C64_GENERAL
  2226:  
  2227:          ; these keytabs are only used in the VIC20-02 ROM, but they
  2228:          ; are still present in the later ones.
  2229:  
  2230:          ; these 4 tables are used if we are in lowercase mode
  2231:          .addr   KEYTAB_UNSHIFTED        ; $08
  2232:          .addr   KEYTAB_SHIFT            ; $0A
  2233:          .addr   KEYTAB6                 ; $0C
  2234:          .addr   KEYTAB_CTRL             ; $0E
  2235:  
  2236:          .addr   KEYTAB5                 ; $10
  2237:          .addr   KEYTAB6                 ; $12
  2238:          .addr   KEYTAB6                 ; $14
  2239:          .addr   KEYTAB_CTRL             ; $16
  2240:  
  2241:  .endif
  2242:  
  2243:  KEYTAB_UNSHIFTED:
  2244:  
  2245:  .if CompileComputer >= C64_GENERAL
  2246:  
  2247:          .byte   $14,$0D,$1D,$88,$85,$86,$87,$11
  2248:          .byte   $33,$57,$41,$34,$5A,$53,$45,$01
  2249:          .byte   $35,$52,$44,$36,$43,$46,$54,$58
  2250:          .byte   $37,$59,$47,$38,$42,$48,$55,$56
  2251:  
  2252:          .byte   $39,$49,$4A,$30,$4D,$4B,$4F,$4E
  2253:          .byte   $2B,$50,$4C,$2D,$2E,$3A,$40,$2C
  2254:          .byte   $5C,$2A,$3B,$13,$01,$3D,$5E,$2F
  2255:          .byte   $31,$5F,$04,$32,$20,$02,$51,$03
  2256:          .byte   $FF
  2257:  
  2258:  .else
  2259:  
  2260:          .byte   $31,$33,$35,$37,$39,$2B,$5C,$14
  2261:          .byte   $5F,$57,$52,$59,$49,$50,$2A,$0D
  2262:          .byte   $04,$41,$44,$47,$4A,$4C,$3B,$1D
  2263:          .byte   $03,$01,$58,$56,$4E,$2C,$2F,$11
  2264:  
  2265:          .byte   $20,$5A,$43,$42,$4D,$2E,$01,$85
  2266:          .byte   $02,$53,$46,$48,$4B,$3A,$3D,$86
  2267:          .byte   $51,$45,$54,$55,$4F,$40,$5E,$87
  2268:          .byte   $32,$34,$36,$38,$30,$2D,$13,$88
  2269:  
  2270:          .byte   $FF
  2271:  
  2272:  .endif
  2273:  
  2274:  KEYTAB_SHIFT:
  2275:  
  2276:  .if CompileComputer >= C64_GENERAL
  2277:  
  2278:    .if .defined(C64JAPAN)
  2279:          .byte   $94,$8D,$9D,$8C,$89,$8A,$8B,$91
  2280:          .byte   $23,$A8,$AA,$24,$AD,$AB,$A9,$01
  2281:          .byte   $25,$A5,$AC,$26,$AF,$A4,$FF,$AE
  2282:          .byte   $27,$FF,$FF,$28,$FF,$FF,$FF,$FF
  2283:  
  2284:          .byte   $29,$FF,$FF,$30,$FF,$FF,$FF,$FF
  2285:          .byte   $A1,$FF,$FF,$A2,$3E,$5B,$FF,$3C
  2286:          .byte   $A3,$FF,$5D,$93,$01,$3D,$B0,$3F
  2287:          .byte   $21,$5F,$04,$22,$A0,$02,$A7,$83
  2288:          .byte   $FF
  2289:    .else
  2290:          .byte   $94,$8D,$9D,$8C,$89,$8A,$8B,$91
  2291:          .byte   $23,$D7,$C1,$24,$DA,$D3,$C5,$01
  2292:          .byte   $25,$D2,$C4,$26,$C3,$C6,$D4,$D8
  2293:          .byte   $27,$D9,$C7,$28,$C2,$C8,$D5,$D6
  2294:  
  2295:          .byte   $29,$C9,$CA,$30,$CD,$CB,$CF,$CE
  2296:          .byte   $DB,$D0,$CC,$DD,$3E,$5B,$BA,$3C
  2297:          .byte   $A9,$C0,$5D,$93,$01,$3D,$DE,$3F
  2298:          .byte   $21,$5F,$04,$22,$A0,$02,$D1,$83
  2299:          .byte   $FF
  2300:    .endif
  2301:  
  2302:  
  2303:  .else
  2304:  
  2305:          .byte   $21,$23,$25,$27,$29
  2306:    .if CompileComputer >= VIC20_06
  2307:          .byte                       $DB,$A9
  2308:    .else
  2309:          .byte                       $AB,$DC
  2310:    .endif
  2311:          .byte                               $94
  2312:  
  2313:    .if CompileComputer >= VIC20_06
  2314:          .byte   $5F
  2315:    .else
  2316:          .byte   $DF
  2317:    .endif
  2318:          .byte       $D7,$D2,$D9,$C9,$D0
  2319:    .if CompileComputer >= VIC20_06
  2320:          .byte                           $C0
  2321:    .else
  2322:          .byte                           $AA
  2323:    .endif
  2324:          .byte                               $8D
  2325:  
  2326:          .byte   $04,$C1,$C4,$C7,$CA,$CC,$5D,$9D
  2327:          .byte   $83,$01,$D8,$D6,$CE,$3C,$3F,$91
  2328:  
  2329:          .byte   $A0,$DA,$C3,$C2,$CD,$3E,$01,$89
  2330:          .byte   $02,$D3,$C6,$C8,$CB,$5B
  2331:    .if CompileComputer >= VIC20_06
  2332:          .byte   $3D
  2333:    .else
  2334:          .byte   $BD
  2335:    .endif
  2336:          .byte       $8A
  2337:  
  2338:          .byte           $D1,$C5,$D4,$D5,$CF
  2339:    .if CompileComputer >= VIC20_06
  2340:          .byte                               $BA
  2341:    .else
  2342:          .byte                               $C0
  2343:    .endif
  2344:          .byte   $DE,$8B
  2345:  
  2346:          .byte           $22,$24,$26,$28
  2347:    .if CompileComputer >= VIC20_06
  2348:          .byte                           $30,$DD
  2349:    .else
  2350:          .byte                           $B0,$AD
  2351:    .endif
  2352:          .byte   $93,$8C
  2353:  
  2354:          .byte   $FF
  2355:  
  2356:  .endif
  2357:  
  2358:  KEYTAB_CBM:
  2359:  
  2360:  .if CompileComputer >= C64_GENERAL
  2361:  
  2362:    .if .defined(C64JAPAN)
  2363:          .byte   $94,$8D,$9D,$8C,$89,$8A,$8B,$91
  2364:          .byte   $B1,$C3,$C1,$B3,$C2,$C4,$B2,$01
  2365:          .byte   $B4,$BD,$BC,$B5,$BF,$CA,$B6,$BB
  2366:          .byte   $D4,$DD,$B7,$D5,$BA,$B8,$C5,$CB
  2367:  
  2368:          .byte   $D6,$C6,$CF,$DC,$D3,$C9,$D7,$D0
  2369:          .byte   $CE,$BE,$D8,$CD,$D9,$DA,$DB,$C8
  2370:          .byte   $A6,$DE,$B9,$93,$01,$D1,$DF,$D2
  2371:          .byte   $C7,$5F,$04,$CC,$A0,$02,$C0,$83
  2372:          .byte   $FF
  2373:    .else
  2374:          .byte   $94,$8D,$9D,$8C,$89,$8A,$8B,$91
  2375:          .byte   $96,$B3,$B0,$97,$AD,$AE,$B1,$01
  2376:          .byte   $98,$B2,$AC,$99,$BC,$BB,$A3,$BD
  2377:          .byte   $9A,$B7,$A5,$9B,$BF,$B4,$B8,$BE
  2378:  
  2379:          .byte   $29,$A2,$B5,$30,$A7,$A1,$B9,$AA
  2380:          .byte   $A6,$AF,$B6,$DC,$3E,$5B,$A4,$3C
  2381:          .byte   $A8,$DF,$5D,$93,$01,$3D,$DE,$3F
  2382:          .byte   $81,$5F,$04,$95,$A0,$02,$AB,$83
  2383:          .byte   $FF
  2384:    .endif
  2385:  
  2386:  .elseif CompileComputer = VIC20_02
  2387:          .byte   $B1,$B3,$B5,$B7,$B9,$AB,$DC,$94
  2388:          .byte   $DF,$D7,$D2,$D9,$C9,$D0,$AA,$8D
  2389:          .byte   $04,$C1,$C4,$C7,$CA,$CC,$BB,$9D
  2390:          .byte   $83,$01,$D8,$D6,$CE,$AC,$AF,$91
  2391:  
  2392:          .byte   $0A,$DA,$C3,$C2,$CD,$AE,$01,$FF
  2393:          .byte   $02,$D3,$C6,$C8,$CB,$BA,$BD,$FF
  2394:          .byte   $D1,$C5,$D4,$D5,$CF,$C0,$DE,$FF
  2395:          .byte   $B2,$B4,$B6,$B8,$B0,$AD,$93,$FF
  2396:          .byte   $FF
  2397:  .else
  2398:          .byte   $21,$23,$25,$27,$29,$A6,$A8,$94
  2399:          .byte   $5F,$B3,$B2,$B7,$A2,$AF,$DF,$8D
  2400:          .byte   $04,$B0,$AC,$A5,$B5,$B6,$5D,$9D
  2401:          .byte   $83,$01,$BD,$BE,$AA,$3C,$3F,$91
  2402:  
  2403:          .byte   $A0,$AD,$BC,$BF,$A7,$3E,$01,$89
  2404:          .byte   $02,$AE,$BB,$B4,$A1,$5B,$3D,$8A
  2405:          .byte   $AB,$B1,$A3,$B8,$B9,$A4,$DE,$8B
  2406:          .byte   $22,$24,$26,$28,$30,$DC,$93,$8C
  2407:          .byte   $FF
  2408:  .endif
  2409:  
  2410:  .if CompileComputer >= VIC20_06
  2411:  
  2412:  KEYTAB5:        ; unused, but references for VIC20-06 and -07
  2413:  
  2414:  EditorCheckForAscLowercase:
  2415:          cmp     #ASC_LOWERCASE                          ; if this the code to change to lowercase chars?
  2416:          bne     EditorCheckForAscUppercase              ; no, test for the next code
  2417:  
  2418:          ; set the VIC memory control byte to point to the lowercase characters:
  2419:  
  2420:    .if CompileComputer >= C64_GENERAL
  2421:          lda     VIC + VICII_O_MemControl
  2422:          ora     #2
  2423:          bne     EditorSta_vMemControl                   ; sta VIC + VICII_O_MemControl ; this bne saves one byte
  2424:    .else
  2425:          lda     #2
  2426:          ora     VIC + VICI_O_MemoryLocations
  2427:          sta     VIC + VICI_O_MemoryLocations
  2428:          jmp     CHROUT_SCREEN_END                       ; we're done
  2429:                                                          ; this JMP is not necessary, but does not do any harm, either.
  2430:                                                          ; It has been removed from the C64 ROMs, presumably to save space.
  2431:    .endif
  2432:  
  2433:  EditorCheckForAscUppercase:
  2434:          cmp     #ASC_UPPERCASE                          ; if this the code to change to lowercase chars?
  2435:          bne     EditorCheckForDisallowLowercase         ; no, test for the next code
  2436:  
  2437:          ; set the VIC memory control byte to point to the uppercase characters:
  2438:  
  2439:    .if CompileComputer >= C64_GENERAL
  2440:          lda     VIC + VICII_O_MemControl
  2441:          and     #~2
  2442:  EditorSta_vMemControl:
  2443:          sta     VIC + VICII_O_MemControl
  2444:    .else
  2445:          lda     #~2
  2446:          and     VIC + VICI_O_MemoryLocations
  2447:          sta     VIC + VICI_O_MemoryLocations
  2448:    .endif
  2449:  
  2450:  EditorChroutScreenEnd:
  2451:          jmp     CHROUT_SCREEN_END
  2452:          ; ----------------------------
  2453:  
  2454:  EditorCheckForDisallowLowercase:
  2455:          cmp     #ASC_DISALLOW_LOWERCASE                 ; if this the code to disallow changing to lowercase mode via keyboard?
  2456:          bne     @CheckForAllowLowercase                 ; no, test for the next code
  2457:  
  2458:          ; disallow changing mode with SHIFT + C= by setting bit 7 of lMODE:
  2459:  
  2460:          lda     #$80
  2461:          ora     lMODE
  2462:    .if CompileComputer >= C64_GENERAL
  2463:          bmi     @Sta_lMODE                              ; sta lMODE (uncond. branch)
  2464:          ; ------------------
  2465:    .else
  2466:          sta     lMODE
  2467:          bmi     EditorChroutScreenEnd
  2468:    .endif
  2469:  @CheckForAllowLowercase:
  2470:          cmp     #ASC_ALLOW_LOWERCASE                    ; if this the code to allow changing to lowercase mode via keyboard?
  2471:          bne     EditorChroutScreenEnd                   ; no -> branch, this is no special code (or it has been already handled)
  2472:  
  2473:          ; (re-)allow changing mode with SHIFT + C= by clearing bit 7 of lMODE:
  2474:  
  2475:          lda     #$7F
  2476:          and     lMODE
  2477:  @Sta_lMODE:
  2478:          sta     lMODE
  2479:  
  2480:          ; end chrout to the screen.
  2481:          ; the VIC-20 and C64 do exactly the same.
  2482:  
  2483:    .if CompileComputer >= C64_GENERAL
  2484:          jmp     CHROUT_SCREEN_END
  2485:    .else
  2486:          bpl     EditorChroutScreenEnd                   ; branches to a "JMP CHROUT_SCREEN_END" (uncond. branch)
  2487:          ; ------------------------------
  2488:  
  2489:          ; a patch: cf. directly before EditorPatchLogicallyCombineTwoRows_Return
  2490:  
  2491:  EditorPatchLogicallyCombineTwoRows:
  2492:          EDITOR_PATCH_LogicallyCombineTwoRows_FIX
  2493:          EDITOR_PATCH_LogicallyCombineTwoRows_COMMON
  2494:          jmp     EditorPatchLogicallyCombineTwoRows_Return
  2495:  
  2496:    .endif
  2497:  .endif
  2498:  
  2499:  .if CompileComputer = VIC20_02
  2500:  
  2501:  KEYTAB5:
  2502:  
  2503:          .byte   $C7,$B1,$B4,$D4,$D6,$CE,$A6,$14
  2504:          .byte   $FF,$C3,$EC,$DD,$C6,$BE,$EA,$0D
  2505:          .byte   $04,$C1,$BC,$B7,$CF,$D8,$B9,$1D
  2506:          .byte   $03,$01,$BB,$CB,$D0,$C8,$D2,$11
  2507:          .byte   $20,$C2,$BF,$BA,$D3,$D9,$01,$85
  2508:          .byte   $02,$C4,$CA,$B8,$C9,$DA,$D1,$86
  2509:          .byte   $C0,$B2,$B6,$C5,$D7,$DB,$A1,$87
  2510:          .byte   $CC,$B3,$B5,$D5,$DC,$CD,$13,$88
  2511:          .byte   $FF
  2512:  .endif
  2513:  
  2514:  KEYTAB6:
  2515:  
  2516:  .if CompileComputer = VIC20_02
  2517:  
  2518:          .byte   $F1,$F3,$F5,$FF,$FF,$EB,$FF,$94
  2519:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$8D
  2520:          .byte   $04,$FF,$FF,$FF,$FF,$FF,$E2,$9D
  2521:          .byte   $83,$01,$FF,$FF,$FF,$FF,$FF,$91
  2522:          .byte   $A0,$FF,$FF,$FF,$FF,$EE,$01,$89
  2523:          .byte   $02,$FF,$FF,$FF,$FF,$E1,$FD,$8A
  2524:          .byte   $FF,$FF,$FF,$FF,$FF,$B0,$E0,$8B
  2525:          .byte   $F2,$F4,$F6,$FF,$F0,$ED,$93,$8C
  2526:          .byte   $FF
  2527:  
  2528:  .elseif CompileComputer < C64_GENERAL
  2529:  
  2530:          ; unused, but present
  2531:  
  2532:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2533:          .byte   $FF,$04,$FF,$FF,$FF,$FF,$FF,$E2
  2534:          .byte   $9D,$83,$01,$FF,$FF,$FF,$FF,$FF
  2535:          .byte   $91,$A0,$FF,$FF,$FF,$FF,$EE,$01
  2536:          .byte   $89,$02,$FF,$FF,$FF,$FF,$E1,$FD
  2537:          .byte   $8A,$FF,$FF,$FF,$FF,$FF,$B0,$E0
  2538:          .byte   $8B,$F2,$F4,$F6,$FF,$F0,$ED,$93
  2539:          .byte   $8C,$FF
  2540:  
  2541:  .endif
  2542:  
  2543:  KEYTAB_CTRL:
  2544:  
  2545:  .if CompileComputer >= C64_GENERAL
  2546:  
  2547:    .if .defined(C64JAPAN)
  2548:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2549:          .byte   $1C,$95,$01,$9F,$1A,$13,$96,$FF
  2550:          .byte   $9C,$97,$04,$1E,$03,$06,$98,$18
  2551:          .byte   $1F,$99,$07,$9E,$02,$08,$9A,$16
  2552:  
  2553:          .byte   $12,$9B,$0A,$92,$0D,$0B,$0F,$0E
  2554:          .byte   $08,$10,$0C,$09,$11,$14,$00,$09
  2555:          .byte   $FF,$05,$15,$FF,$FF,$17,$19,$12
  2556:          .byte   $90,$06,$FF,$05,$FF,$FF,$81,$FF
  2557:          .byte   $FF
  2558:    .else
  2559:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2560:          .byte   $1C,$17,$01,$9F,$1A,$13,$05,$FF
  2561:          .byte   $9C,$12,$04,$1E,$03,$06,$14,$18
  2562:          .byte   $1F,$19,$07,$9E,$02,$08,$15,$16
  2563:  
  2564:          .byte   $12,$09,$0A,$92,$0D,$0B,$0F,$0E
  2565:          .byte   $FF,$10,$0C,$FF,$FF,$1B,$00,$FF
  2566:          .byte   $1C,$FF,$1D,$FF,$FF,$1F,$1E,$FF
  2567:          .byte   $90,$06,$FF,$05,$FF,$FF,$11,$FF
  2568:          .byte   $FF
  2569:    .endif
  2570:  
  2571:  .else
  2572:  
  2573:          .byte   $90,$1C,$9C,$1F
  2574:    .if CompileComputer = VIC20_02
  2575:          .byte                   $FF
  2576:    .else
  2577:          .byte                   $12
  2578:    .endif
  2579:          .byte                       $FF,$FF,$FF
  2580:    .if CompileComputer = VIC20_02
  2581:          .byte   $FF
  2582:    .else
  2583:          .byte   $06
  2584:    .endif
  2585:          .byte       $FF,$12,$FF,$FF,$FF,$FF,$FF
  2586:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2587:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2588:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2589:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2590:          .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
  2591:          .byte   $05,$9F,$1E,$9E,$92,$FF,$FF,$FF
  2592:          .byte   $FF
  2593:  
  2594:  .endif
  2595:  
  2596:  VIC_DEFAULTS:
  2597:  
  2598:          ; the default values for the VIC or VIC-II
  2599:          ; these will be copied in a loop into the
  2600:          ; VIC or VIC-II register when initialising the VIC(-II)
  2601:          ;
  2602:  
  2603:  .if CompileComputer >= C64_GENERAL
  2604:  
  2605:          .byte   $00,$00,$00,$00,$00,$00,$00,$00
  2606:          .byte   $00,$00,$00,$00,$00,$00,$00,$00
  2607:          .byte   $00
  2608:    .if CompileComputer >= C64_02
  2609:          .byte       $1B | (>311 .SHL 7) ,<311
  2610:    .else
  2611:          .byte       $1B,$00
  2612:    .endif
  2613:          .byte               $00,$00,$00,$08,$00
  2614:          .byte   $14
  2615:    .if CompileComputer >= C64_02
  2616:          .byte       $0F
  2617:    .else
  2618:          .byte       $00
  2619:    .endif
  2620:          .byte           $00,$00,$00,$00,$00,$00
  2621:  
  2622:          .byte   SET_COLOR_FRAME,SET_COLOR_BACKGROUND
  2623:    .if CompileComputer = C64_4064
  2624:          .byte           $00,$00,$00,$00,$00
  2625:          .byte   $00,$00,$00,$00,$00,$00,$00
  2626:    .else
  2627:          .byte           $01,$02,$03,$04,$00
  2628:          .byte   $01,$02,$03,$04,$05,$06,$07
  2629:    .endif
  2630:  
  2631:  .else
  2632:  
  2633:    .if CompileComputer >= VIC20_07
  2634:          .byte   $0C,$26
  2635:    .else
  2636:          .byte   $05,$19
  2637:    .endif
  2638:          .byte           $16,$2E,$00,$C0,$00,$00
  2639:          .byte   $00,$00,$00,$00,$00,$00,$00
  2640:  
  2641:  .endif
  2642:  
  2643:  END_VIC_DEFAULTS:
  2644:  
  2645:  .if CompileComputer < C64_GENERAL
  2646:  
  2647:          .byte   $1B                     ; TODO unused?
  2648:  
  2649:  .endif
  2650:  
  2651:  TEXT_LOADRUN:
  2652:          .byte   "LOAD",ASC_CR
  2653:          .byte   "RUN",ASC_CR
  2654:  END_TEXT_LOADRUN:
  2655:  
  2656:  SCREEN_LOWBYTE:
  2657:  
  2658:          ; the low bytes of the screen addresses
  2659:  
  2660:      .repeat EDITOR_ROWS,i
  2661:          .byte   <(i*EDITOR_COLS)
  2662:      .endrep
  2663:  
Valid XHTML 1.0 Strict
editor.a65.html; generated on Fri Sep 18 21:44:54 2015 by ca65html
uz@cc65.org