Pygments highlights CBM BASIC & ca65 assembler sources

Since version 1.6 the syntax highlighter Pygments has support for CBM BASIC V2 and assembler sources in ca65 format. ca65 is the macro assembler of the cc65 C cross compiler package.

The language list does not have entries for them, but the lexer list in the documentation knows about Ca65Lexer in the lexers for assembly languages section and CbmBasicV2Lexer in the lexers for other languages section.

Examples

Let’s see the highlighter in action. Both examples solve assignment 3 of the HP Codewars 2012. It is about interpreting a sequence of given Reversi moves and printing the resulting board state.

Example of BASIC…

 10 cp$="w":op$="b":dimb$(7,7):fori=0to7:forj=0to7:b$(i,j)=".":next:next
 20 b$(3,3)="w":b$(4,3)="b":b$(3,4)="b":b$(4,4)="w":dimtx(7),ty(7):goto140
 30 inputl$:ifl$="end"then end
 40 y=asc(left$(l$,1))-asc("a"):x=val(right$(l$,1))-1:b$(y,x)=cp$
 50 forxd=-1to1:foryd=-1to1:if(xd or yd)=0then130
 60 xt=x:yt=y:tc=0
 70 xt=xt+xd:yt=yt+yd
 80 ifxt<0orxt>7oryt<0oryt>7thentc=0:goto120
 90 t$=b$(yt,xt):ift$<>op$then110
100 tx(tc)=xt:ty(tc)=yt:tc=tc+1:goto70
110 ift$<>cp$thentc=0
120 iftc>0then fori=0totc-1:b$(ty(i),tx(i))=cp$:next
130 next:next:t$=cp$:cp$=op$:op$=t$
140 fori=0to7:forj=0to7:printb$(j,i);:next:print:next:print:goto30

…and Assembler (yes I know, it is loooong)…

        chrout := $ffd2
        getchr := $ffe4

.enum
        WHITE = 'w'
        BLACK = 'b'
        EMPTY = '.'
.endenum

.zeropage
        zp0 := $02
        current_player := $b0
        other_player := $b1
        zp1 := $b2
        zp2 := $b3
        zp3 := $b4
        zp4 := $b5

.bss
board:  .res 64

.proc start
.code
        jsr init_game
        jmp print
loop:
        jsr get_coordinate
        bcs exit
        jsr move
print:
        jsr print_board
        jmp loop
exit:
        rts
.endproc

;--------------------------------------
; Init the board with empty fields and the four pieces in the middle.
; Set `current_player` & `other_player` to `WHITE` & `BLACK`.
;
; Changes: `board`, `current_player`, `other_player`
.proc init_game
        ldy #63
        lda #EMPTY
loop:   sta board,y
        dey
        bpl loop
        lda #WHITE
        sta current_player
        sta board + 3*8+3
        sta board + 4*8+4
        lda #BLACK
        sta other_player
        sta board + 3*8+4
        sta board + 4*8+3
        rts
.endproc

;--------------------------------------
; Print the board.
.proc print_board
        ldy #0
loop:   tya             ; index divisible by 8 ?
        and #%00000111
        bne skip
        lda #13         ; print newline.
        jsr chrout
skip:   lda board,y
        jsr chrout
        iny
        cpy #64
        bne loop

        lda #13
        jmp chrout
.endproc

;--------------------------------------
; Gets the coordinate from the user.
;
; User input is not validated!
;
; In: -
; Out:
;       X/A: x and y coordinate if Carry=0
;       Carry: if 1 then user entered "end" or pressed RUN/STOP.
.proc get_coordinate
.rodata
end_txt:
        .byte "end", 13
        end_txt_length := * - end_txt
.bss
buffer_length:
        .res 1
buffer: .res 16

.code
        lda #0                  ; Read line into buffer.
        sta buffer_length
@L1:    jsr getchr
        beq @L1
        ldy buffer_length
        sta buffer,y
        inc buffer_length
        cmp #3                  ; RUN/STOP
        beq exit
        jsr chrout
        cmp #13                 ; RETURN
        bne @L1

        ldy #end_txt_length - 1 ; buffer == "end\n" ?
@L2:    lda buffer,y
        cmp end_txt,y
        bne continue
        dey
        bpl @L2
exit:
        sec
        rts
continue:
        lda buffer              ; convert coordinate
        sec
        sbc #'a'
        tax
        lda buffer+1
        clc
        sbc #'0'

        clc
        rts
.endproc

;--------------------------------------
; Checks a line of tiles which should be turned.
;
; In:
;       `check_line::x_`, `check_line::y_`: start coordinates
;       X/Y: delta x and y between -1 and 1 for the direction
;               in which the check is done.
; Out:
;       `check_line::turn_coordinates`: the indices into `board`
;               of the tiles to turn.
;       A: count of indices in `check_line::turn_coordinates`.
; Changes: `zp0`, `zp1`, `zp2`
.proc check_line
x_ := zp1
y_ := zp2
turn_length := zp0

.bss
turn_coordinates:
        .res 7

.code
        stx xd
        sty yd

        lda #0
        sta turn_length
loop:
        clc                     ; x += xd
        lda x_
xd := * + 1
        adc #0
        sta x_
        clc                     ; y += yd
        lda y_
yd := * + 1
        adc #0
        sta y_

        ;lda y_                  ; check if x and y between 0 and 7
        ora x_
        and #%11111000
        bne no_turns

        lda y_                  ; Y = y*8+x
        asl
        asl
        asl
        ora x_
        tay

        lda board,y             ; board(x,t) == other_player ?
        cmp other_player
        bne line_end

        tya                     ; store index into board in turn_coordinates.
        ldy turn_length
        sta turn_coordinates,y
        inc turn_length
        bne loop

line_end:
        cmp current_player      ; board(x,y) == current_player ?
        bne no_turns
        lda turn_length
        rts
no_turns:
        lda #0
        rts
.endproc

;--------------------------------------
; Make a move and then switch players.
;
; In:
;       X/A: coordinates of the move.
; Changes: `board`, `current_player`, `other_player`,
;       `zp0`, `zp1`, `zp2`, `zp3`, `zp4`
.proc move

xd := zp3
yd := zp4

.code
        stx x_
        sta y_

        asl                     ; Set new player tile.
        asl
        asl
        ora x_
        tay
        lda current_player
        sta board,y
        ;
        ; Two nested loops to check all 8 directions.
        ;
        lda #< -1               ; for xd = -1 to...
        sta xd
outer_loop:
        lda #< -1               ; for yd = -1 to...
        sta yd
inner_loop:
        ;lda yd                  ; if (xd | yd) = 0 then continue.
        ora xd
        beq inner_loop_continue
x_ := * + 1
        lda #0
        sta check_line::x_
y_ := * + 1
        lda #0
        sta check_line::y_
        ldx xd
        ldy yd
        jsr check_line

        cmp #0                  ; turns ?
        beq skip_turn_loop
        tay                     ; do turns.
        lda current_player
turn_loop:
        ldx check_line::turn_coordinates-1,y
        sta board,x
        dey
        bne turn_loop
skip_turn_loop:

inner_loop_continue:
        inc yd                  ; next yd
        lda yd
        cmp #2
        bne inner_loop

        inc xd                  ; next xd
        lda xd
        cmp #2
        bne outer_loop

        lda current_player      ; swap players.
        ldx other_player
        sta other_player
        stx current_player

        rts
.endproc