6502Em - Samples

Add 2 numbers

After running the accumulator and the memory location $1000 will contain Hex 14, Decimal 20.

LDA#10
ADC#10
STA $1000
Top

Subtract 2 numbers

After running the accumulator and the memory location $1000 will contain Hex FE, Decimal 20, Binary 11111111, which is -1 in 2's complement

SEC
LDA#10
SBC#11
STA $1000
Top

Hello World

When run from location $1500, writes the text Hello World to the character based screen.

.ORG $1000
.BYTE "HELLO WORLD'
.ORG $1500
LDX#0
LOOP:
LDA $1000,X
STA $C000,X
LDA#1
STA $C8C1,X
INX
CPX#11
BNE LOOP:
KIL
Top

Simple Keyboard Interrupt Handler

This installs an interupt handler at $F100 which is the default location for interrupts. This handler simply checks for a keyboard interrupt and returns. The main part of the pogram is an infinite loop. When executed from location $1000, if you move to the VDU tab, click on the character screen and press keys so will see the time in the Interupt handler (Int) start to increase.

; Keyboard I/O Locations
.VAR KEYBOARD=$F000

.ORG $1000
CLI ; Clear Interrupt Disable so we can handle interrupts
OSLOOP:
JMP OSLOOP:

; ---------SERVICE ROUTINES-------------------------------------------------------------------
.ORG $F100
PHA
TXA
TSX
PHA
INX
INX
LDA $100,X ; get the status register from the stack
AND #$10   ; mask B flag
BNE BREAK:
BEQ IRQ:
BREAK:
KIL ; For Program interrupts default to stopping.
IRQ:
; Is this a keyboard interupt
LDA KEYBOARD
BEQ END:
; Handle keyboard interrupt
; We can do something here to handle a keystroke at the moment we simply return to the main routine
END:
PLA
TAX
PLA
CLI ; Start servicing interrupts
RTI ; Normal return from interrupt

; ---------- End Service Routines ----------------------------------
Top

Keyboard Processing

This installs an interupt handler at $F100 which is the default location for interrupts.This handler adds the keys to a buffer and the main loops processes them and adds them to the character based screen. If executed from location $1000 you can type on the character based screen as if it were a console window.

; Keyboard I/O Locations
.VAR KEYBOARD=$F000
.VAR SCANCODE=$F001
.VAR KEY=$F002
.VAR KEYBUFFERSTART=$F040
.VAR KEYBBUFFERWRITEPOS=$24
.VAR KEYBBUFFERREADPOS=$26
.VAR TIMER1=$50
.VAR KEYBSIZEOFBUFFER=10
; Video Location
.VAR CHARACTERSTARTLOCATION=$C000
.VAR COLORSTARTLOCATION=$C8C1
.VAR CURX=$CF02
.VAR CURY=$CF03
.VAR SCREENSTART=$20
.VAR SCREENMEMLO=$00
.VAR SCREENMEMHI=$C0
.VAR ATTRSTART=$22
.VAR ATTRMEMLO=$C1
.VAR ATTRMEMHI=$C8
.VAR COLUMNS=80
.VAR LINES=22

.ORG $1000
CLI ; Clear Interrupt Disable so we can handle interrupts
; My first operating system MIKIX :-)
; Set up the cursor
LDA#1
STA CURX
STA CURY
; SETUP VDU location indexer in zeropage
LDA#SCREENMEMLO
STA SCREENSTART
LDA#SCREENMEMHI
STA SCREENSTART+1
LDA#ATTRMEMLO
STA ATTRSTART
LDA#ATTRMEMHI
STA ATTRSTART+1
OSLOOP:
; Is there a keystroke in the buffer
LDX KEYBBUFFERREADPOS
CPX KEYBBUFFERWRITEPOS
BEQ OSLOOP: ; If the read and write values are the same then there is no keyboard input

LDY#0 ; Y register must be zero as we use it in the indexing
LDA KEYBUFFERSTART, X ; Load the key pressed into the acculumator
STA (SCREENSTART),Y ; Store the accumulator in the correct location in screen memory
; Increment screen memory pointer to point to the next location
CLC
LDA SCREENSTART
ADC#1
STA SCREENSTART
LDA#0
ADC SCREENSTART+1
STA SCREENSTART+1

LDA#31 ; Load the accumator with 31 (Foreground is white, background is blue)
STA (ATTRSTART),Y ; Store the accumulator in the correct location in screen memory

; Increment attribute memory pointer to point to the next location
CLC
LDA ATTRSTART
ADC#1
STA ATTRSTART
LDA#0
ADC ATTRSTART+1
STA ATTRSTART+1

JSR CURSORMOVE:

; Move the read pointer to the next position in the keyboard buffer
INC KEYBBUFFERREADPOS ; Increment the buffer position to point to the next slot
CPX #KEYBSIZEOFBUFFER ; See if we are at the end of the buffer
BNE OSLOOP:
LDA#0
STA KEYBBUFFERREADPOS ; Reset the write buffer to the start

JMP OSLOOP:


; ---------SUBROUTINES-----------------------------------------------------------------------
CURSORMOVE:
; Move the cursor wrapping if necessary
LDA CURX
CMP#COLUMNS
BEQ MOVENEXTLINE:
INC CURX
JMP CURSORMOVEEND:
MOVENEXTLINE:
LDA#1 ; First column
STA CURX

; Check we are not at the end of the screen
LDA CURY
CMP#LINES
BEQ MOVETOTOP:
INC CURY
JMP CURSORMOVEEND:
MOVETOTOP:
LDA#1 ; First row
STA CURY

CURSORMOVEEND:
RTS

; ---------END SUBS---------------------------------------------------------------------------

; ---------SERVICE ROUTINES-------------------------------------------------------------------
.ORG $F100
PHA
TXA
TSX
PHA
INX
INX
LDA $100,X ; get the status register from the stack
AND #$10   ; mask B flag
BNE BREAK:
BEQ IRQ:
BREAK:
KIL ; For Program interrupts default to stopping.
IRQ:
; Is this a keyboard interupt
LDA KEYBOARD
BEQ END:
; Handle keyboard interrupt
LDX KEYBBUFFERWRITEPOS ; Load the X register with the buffer offset
LDA KEY ;load the key pressed into the accumulator
BEQ END: ;Ignore zero which is unmapped keys
STA KEYBUFFERSTART, X ; Store the key in the correct slot
INC KEYBBUFFERWRITEPOS ; Increment the buffer position to point to the next slot
CPX #KEYBSIZEOFBUFFER ; See if we are at the end of the buffer
BNE BUFFERNOTFULL:
LDA#0
STA KEYBBUFFERWRITEPOS ; Reset the write buffer to the start
BUFFERNOTFULL:
LDA #0 ;Handling of keyboard interrupt is complete so clear the flag
STA KEYBOARD 
END:
PLA
TAX
PLA
CLI ; Start servicing interrupts
RTI ; Normal return from interrupt


; ---------- End Service Routines ----------------------------------
Top

Simple Animation

This animates a space invader across the bitmap based screen using a timer to smooth this out. You should switch to the VDU page, select the bitmap screen and run from location $2000.

.VAR SPRITE1START=$0500
.VAR VRAM=$3000
.VAR TIMERLOCATION=$F003
.VAR TIMERDELAY=10
.VAR HardwareClearScreen = $08;

.VAR XRES=160
.VAR SPRITEWIDTH=5; Width is 10 and 1 byte holds 2 pixels

.VAR FROMLOC = 10
.VAR TOLOC = 12
.VAR SIZELOC=14
.VAR ROWSLOC=16
.ORG 16
.BYTE 10

.ORG $500
; SPRITE 1
.BYTE $00,$06,$66,$60,$00,$06,$66,$66,$66,$60,$66,$66,$66,$66,$66,$66,$66,$66,$66,$66,$66
.BYTE $06,$66,$60,$66,$66,$66,$66,$66,$66,$00,$66,$00,$66,$00,$06,$00,$00,$00,$60,$60
.BYTE $00,$00,$00,$06

.ORG $2000
;Show First Sprite moving a row of pixels at a time
; Move
LDA#SPRITEWIDTH ;Lo
STA SIZELOC
LDA#0  ;Hi
STA SIZELOC+1

.VAR VRAMOFFSET=0
.VAR VRAMOFFSETLOC=$60
.VAR MAXSPRITEMOVE=100

LDA#VRAMOFFSET
STA VRAMOFFSETLOC

INVADERLOOP:

; Start location of row x of the pixel
LDA#SPRITE1START<
STA FROMLOC 
LDA#SPRITE1START>
STA FROMLOC+1

; Start screenmem
; Add in the VRAMOFFSET
CLC
LDA#VRAM<
ADC VRAMOFFSETLOC
STA TOLOC
LDA#VRAM>
ADC #0
STA TOLOC+1

SPRITELOOP:

JSR MOVEMEM:

; Increase the data locaction for the sprite
CLC
LDA FROMLOC
ADC#SPRITEWIDTH
STA FROMLOC
LDA#0
ADC FROMLOC+1
STA FROMLOC+1

; Increase by the screen width to go to the next row
CLC
LDA TOLOC
ADC#XRES
STA TOLOC
LDA#0
ADC TOLOC+1
STA TOLOC+1

; Keep looping till all rows displayed
DEC  ROWSLOC
LDA ROWSLOC
BNE SPRITELOOP:

JSR WAIT:

;TODO Clear Screen
LDA#1
STA HardwareClearScreen
CHECKCLEAR:
LDA HardwareClearScreen
BNE CHECKCLEAR:

;Increment sprite location
LDA VRAMOFFSETLOC
ADC#1
STA VRAMOFFSETLOC

CMP#MAXSPRITEMOVE

; JUMP BACK TO THE START IF THERE IS MORE ANIMATIONS
BNE INVADERLOOP:

TEMP:
JMP TEMP:

WAIT:
LDA #TIMERDELAY
STA TIMERLOCATION

WAITLOOP:
LDA TIMERLOCATION
BNE WAITLOOP:

RTS


MOVEMEM:
LDY #0
LDX SIZELOC+1 ; High byte of size
BEQ MD2:
MD1:
LDA (FROMLOC),Y ; move a page at a time
STA (TOLOC),Y
INY
BNE MD1:
INC FROMLOC+1
INC TOLOC+1
DEX
BNE MD1:
MD2:
LDX SIZELOC ; Low byte of size
BEQ MD4:
MD3:
LDA (FROMLOC),Y ; move the remaining bytes
STA (TOLOC),Y
INY
DEX
BNE MD3:
MD4:
RTS
Top