This is a technically more sophisticated step. We'll add the possibility of a borderless level, and also a second charset.
Especially the latter requires a thorough planning of where to put things in memory. Remember, the VIC (the C64s graphic chip) can only see a 16K range at once. This means, all visible characters, sprite and screen memory must reside inside one 16K bank.
The new memory layout looks like this:
;screen back color $BC00 to $BFFF
;charset2 $C000 to $C800
;screen back char $C800 to $CBFF
;screen $CC00 to $CFFF
;sprites $D000 to $F7FF
;charset $F800 to $FFFF
Once that's down (and fits) the code changes are rather simple. A new byte is added per screen to allow for 8 flags. Bit 0 means no border, bit 1 means second charset.
Implementing borderless is easy, check the bit, and it set, skip the level border drawing:
lda LEVEL_CONFIG
and #$01
bne .SkipBorder
;draw level border
lda # sta ZEROPAGE_POINTER_1
lda #>LEVEL_BORDER_DATA
sta ZEROPAGE_POINTER_1 + 1
jsr .BuildLevel
.SkipBorder
sta VIC_MEMORY_CONTROL
Actually, the second char set is quite similar to implement:
;.. snip
lda LEVEL_CONFIG
and #$02
beq .SetCharSet1
;set charset 2
lda #$30
-
sta VIC_MEMORY_CONTROL
jsr DisplayLevelNumber
rts
.SetCharSet1
lda #$3e
jmp -
Setting the second char set is as simple as adding another copy routine:
;take source address from CHARSET 2
LDA # STA ZEROPAGE_POINTER_1
LDA #>CHARSET_2
STA ZEROPAGE_POINTER_1 + 1
;now copy
jsr CopyCharSet2
;------------------------------------------------------------
;copies charset from ZEROPAGE_POINTER_1 to ZEROPAGE_POINTER_2
;------------------------------------------------------------
!zone CopyCharSet2
CopyCharSet2
;set target address ($F000)
lda #$00
sta ZEROPAGE_POINTER_2
lda #$C0
sta ZEROPAGE_POINTER_2 + 1
ldx #$00
ldy #$00
lda #0
sta PARAM2
.NextLine
lda (ZEROPAGE_POINTER_1),Y
sta (ZEROPAGE_POINTER_2),Y
inx
iny
cpx #$08
bne .NextLine
cpy #$00
bne .PageBoundaryNotReached
;we have reached the next 256 bytes, inc high byte
inc ZEROPAGE_POINTER_1 + 1
inc ZEROPAGE_POINTER_2 + 1
.PageBoundaryNotReached
;only copy 254 chars to keep irq vectors intact
inc PARAM2
lda PARAM2
cmp #254
beq .CopyCharsetDone
ldx #$00
jmp .NextLine
.CopyCharsetDone
rts
The last one is a subtle change, due to the border not always used the ClearPlayScreen routine is adjusted to clear one more line.
Have fun!