Tag Archives: 6502

BBC Micro 6502 Assembly: Simple Square Program

As promised, the “much anticipated” follow-up to ‘Hello World‘ in assembly. I’m trying to pick simple topics to show how this works so I make no apologies for the somewhat bland output, in this case, a square. I did make it a yellow square though.

Again, this is all written with the BBC Micro assembler so you will see hexadecimal notation indicated with an & instead of a $ like you may see in other assemblers.

Before we dive in, I need to describe how commands Plot, Draw and VDU work on the BBC. The BBC Micro’s DRAW command in BASIC is actually implemented using the same VDU command system as PLOT.

When you use DRAW x,y in BASIC, it internally:

  1. Converts to absolute screen coordinates
  2. Issues a PLOT 85, x, y command (or similar PLOT code)
  3. This PLOT command then generates a sequence of VDU commands

The similarity works like this:

DRAW → PLOT → VDU sequence

For example, PLOT 85, x, y (draw absolute line) becomes something like:

VDU 25, 85, x MOD 256, x DIV 256, y MOD 256, y DIV 256

So all three commands (DRAW, PLOT, VDU) are essentially layers of the same system:

  • VDU is the lowest level – sends bytes directly to the VDU driver
  • PLOT is a mid-level wrapper that formats graphics commands into VDU sequences (VDU 25 specifically)
  • DRAW is a high-level BASIC convenience command that translates to PLOT commands

This layered approach meant the 6502 assembly implementation could reuse the same VDU driver code for all graphics operations, with BASIC commands simply being syntactic sugar that is ultimately funneled down to VDU byte sequences.

Straight into the code. Don’t be scared off by the length – this makes it easier to understand.

10 REM BBC Micro 6502 Assembly Program to Draw a Square
   20 REM Uses OS graphics routines to draw a square on screen
   30 REM Type RUN to execute
   40 
   50 oswrch=&FFEE
   60 
   70 FOR pass=0 TO 2 STEP 2
   80 P%=&1000
   90 [
  100 OPT pass
  110 
  120 .start
  130  LDA #22      ; VDU 22 - set screen mode
  140  JSR oswrch
  150  LDA #1     ; Mode 1
  160  JSR oswrch
  170 
  180  LDA #16      ; VDU 16 - clear graphics area
  190  JSR oswrch
  200 
  210  LDA #29      ; VDU 29 - set graphics origin
  220  JSR oswrch
  230  LDA #0     ; X origin (low byte)
  240  JSR oswrch
  250  LDA #2; X origin (high byte)
  260  JSR oswrch
  270  LDA #0   ; Y origin (low byte)
  280  JSR oswrch
  290  LDA #2; Y origin (high byte)
  300  JSR oswrch
  310 
  320  LDA #18      ; VDU 18 - set graphics color
  330  JSR oswrch
  340  LDA #0; GCOL action (normal plotting)
  350  JSR oswrch
  360  LDA #2; Color 2 (Yellow in mode 1)
  370  JSR oswrch
  380 
  390  LDA #25      ; VDU 25 - PLOT command
  400  JSR oswrch
  410  LDA #4; PLOT 4 - move to absolute coordinates
  420  JSR oswrch
  430  LDA #&CE     ; X = -50 (low byte)
  440  JSR oswrch
  450  LDA #&FF     ; X = -50 (high byte)
  460  JSR oswrch
  470  LDA #200      ; Y = 50 (low byte)
  480  JSR oswrch
  490  LDA #0; Y = 50 (high byte)
  500  JSR oswrch
  510 
  520  LDA #25      ; VDU 25 - PLOT command
  530  JSR oswrch
  540  LDA #5; PLOT 5 - draw line to absolute coordinates
  550  JSR oswrch
  560  LDA #200      ; X = 50 (low byte)
  570  JSR oswrch
  580  LDA #0; X = 50 (high byte)
  590  JSR oswrch
  600  LDA #200      ; Y = 50 (low byte)
  610  JSR oswrch
  620  LDA #0; Y = 50 (high byte)
  630  JSR oswrch
  640 
  650  LDA #25      ; VDU 25 - PLOT command
  660  JSR oswrch
  670  LDA #5 ; PLOT 5 - draw line to absolute coordinates
  680  JSR oswrch
  690  LDA #200      ; X = 50 (low byte)
  700  JSR oswrch
  710  LDA #0; X = 50 (high byte)
  720  JSR oswrch
  730  LDA #&CE     ; Y = -50 (low byte)
  740  JSR oswrch
  750  LDA #&FF     ; Y = -50 (high byte)
  760  JSR oswrch
  770 
  780  LDA #25      ; VDU 25 - PLOT command
  790  JSR oswrch
  800  LDA #5; PLOT 5 - draw line to absolute coordinates
  810  JSR oswrch
  820  LDA #&CE     ; X = -50 (low byte)
  830  JSR oswrch
  840  LDA #&FF     ; X = -50 (high byte)
  850  JSR oswrch
  860  LDA #&CE     ; Y = -50 (low byte)
  870  JSR oswrch
  880  LDA #&FF     ; Y = -50 (high byte)
  890  JSR oswrch
  900 
  910  LDA #25      ; VDU 25 - PLOT command
  920  JSR oswrch
  930  LDA #5; PLOT 5 - draw line to absolute coordinates
  940  JSR oswrch
  950  LDA #&CE     ; X = -50 (low byte)
  960  JSR oswrch
  970  LDA #&FF     ; X = -50 (high byte)
  980  JSR oswrch
  990  LDA #200      ; Y = 50 (low byte)
 1000 JSR oswrch
 1010 LDA #0; Y = 50 (high byte)
 1020 JSR oswrch
 1030 RTS 
 1120 ]
 1130 NEXT pass

 1170 CALL &1000

FURTHER EXPLANATION:

Hopefully the explanation on how it is working in a similar fashion to BASIC’s VDU command above helps describe how the line is drawn.

You will notice we call the OS Write Character routine after each change. This essentially tells the OS we are writing to the screen.

We have the concept of ‘low byte’ and ‘high byte’ because we are talking about an 8-bit system which can’t handle numbers greater than 255 (0-255). Since the mode 1 screen is 320 pixels this is obviously higher than 255. In this case, low byte refers to 1-127 when we consider it as binary (11111111 = 256 dec) and high byte is the next byte along starting at 5.

As with anything, best to test for yourself. I do most of my testing/playing using the excellent BeebEm. There are probably (almost certainly) better ways to accomplish this so feel free to post any comments with improvements. I don’t pretend to be an expert here by any stretch, merely trying to explain how BASIC maps to machine language for simple tasks.

Retro Computing – Writing ‘Hello World’ in BBC Micro 6502 Assembly Language

I anticipate writing occasional posts about BBC Micro machine code as I do harbour a fascination with this from all those years back as a 12 year old. Let’s start with something basic (or rather machine code, ho ho).

I think I probably speak for everyone when I say my first introduction to BASIC was writing something along the lines of:

10 PRINT "Simon Rules!"
20 GOTO 10

…to be greeted with my message endlessly scrolling down the screen. Happy days. It makes sense then to create the same in assembly language as an introduction then, surely? 🙂

In the traditions of this site, I will try keep it concise and to the point but as you can see below, in ASM you really have to tell the machine exactly what you need it to do – no simple instructions to simply write what you choose. Don’t worry though, explanations will follow. Here is the exact equivalent in ASM of what we just did above in BASIC:

20 P%=&2000
30 [
35.helloworld
36 EQUS "Hello World!"
37 EQUB &A
38 EQUB &D
40.print
50 LDX #0
60.loop
70 LDA helloworld,X
80 JSR &FFEE
90 INX
100 CMP #&D
110 BNE loop
115 LDX #0
116 JMP loop
120 RTS
130]
140CALL print

EXPLANATION

Line 20: P% represents the place in memory where the program will be run from and refers to the ‘stack pointer’. This is a built in variable and we will be running the program from &2000 in this case. Note: ‘&’ is used to denote hexadecimal notation will follow – many other machines at the time (and to this day for that matter) use the ‘$’ for this.

Line 30 and 130: All assembly code on the Beeb needs to be encased in square brackets.

Line 35: .helloworld marks the start of the function we use to define our output.

Line 36: On the BBC Micro, EQUS stands for Equate String and essentially reserves a piece of memory containing the string specified.

Line 37: On the BBC Micro EQUB stands for Equate Byte and essentially reserves a piece of memory containing the byte specified. So what’s &A ? This translates to decimal ’10’ and is the ASCII for the newline character. This will be processed after ‘Hello World!’

Line 38: As above but what’s &D ? This translates to decimal ’13’ and is the ASCII for the Carriage Return character. This will be processed after newline above and these two additions ensure anything printed subsequently is on a new line.

Line 40: .print marks the start of the routine we use to loop through and print our output.

Line 50: This loads the X register with zero. In practice, this marks the start of a loop, like a FOR loop at character 0 “H” in ‘Hello World!’

Line 60: .loop – marks the start of the loop we will use to cycle through the ‘Hello World!’ string we defines from line 36.

Line 70: Loads the accumulator by referencing the helloworld function at line 34. The X reference is keeping tack of where we are in the loop as we’re displaying the characters one by one (starting at 0 as mentioned in line 50).

Line 80: JSR stands for ‘Jump to SubRoutine’ and is used to jump to an in-built machine code routine to OSWRCH (Operating System Write Character). This is used, as you might imagine, to write characters to the screen and is found on the BBC Micro at &FFEE.

Line 90: INX is ‘Increment X register’ and increments this by one on each pass. This is part of our loop which is printing each of the characters in the string .

Line 100: CMP stands for ‘Compare’ and we are comparing the character we are on in the string with that here. ‘&D’ is decimal 13 and equates to the CR we mentioned in line 38. Since this is the last ASCII ‘character’ to be printed we know that if this is found, the full string has now been displayed.

Line 110: BNE stands for Branch if Not Equal and will continue the loop if &D hasn’t been found, above.

Line 115: Reset the X register back to 0. We need to do this as we will be re-running the whole code again in an endless loop and need to reset to the start of the string.

Line 116: Jump back into the loop. This is effectively ‘GOTO 10’ in the above BASIC version.

Line 120: Return From Subroutine (RTS) because &D has been found by the Compare above and we’re happy that the string we’ve printed is now complete. In practice, this is never reached as we are in a continuous loop but this is where it should go! If we were to remove lines 115 and 116 the program would complete here (and ‘Hello World!’ would be displayed once and finish).

Line 140: Finally we use the BASIC command CALL to execute the machine code routine to print the message.

That’s it…relatively straightforward although the BASIC equivalent is obviously somewhat easier to understand! I will hopefully do something a little more interesting next time, maybe draw a square on the screen or something. Let’s see.