6502 crash course

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

6502 crash course

Post by pixelblip »

Hi there
I have a crazy idea that I want to get a tattoo for my 50th. It's at the end of August....with a program about me.
I wanted to do a program in 6502 ( my first ). It's a bit of a tall feet I know.
I wanted to draw some shapes and fill in the shapes. Maybe some colour cycling as well.

Any advice appreciated where to start. Even if I could draw lines and plot points that would be a good start.
There are many books I know.......anyway I'll put the feelers out there. Thanks!
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Doh I've just seen that 'Creative Assembler' in the post above. Let's try that and see how we go :wink:
gfoot
Posts: 987
Joined: Tue Apr 14, 2020 9:05 pm
Contact:

Re: 6502 crash course

Post by gfoot »

For the line and shape drawing, it may be easiest to practice in BASIC first and then convert it. All commands such as MODE, MOVE, DRAW, PLOT, GCOL are converted by BASIC into VDU code sequences which the OS interprets. You can write a 6502 program to emit the same sequences, to draw the same picture (and of course also draw text, etc).

You can even get the OS to capture the VDU codes for you, using *SPOOL, like this:

Code: Select all

 10 *SPOOL VDUCODES
 20 MODE 1
 30 GCOL 0,1
 40 MOVE 100,100
 50 MOVE 300,100
 60 PLOT 85,100,300
 70 PLOT 85,300,300
 80 *SPOOL
*DUMP VDUCODES will then show all the VDUs that happened, e.g. in this case:
  • MODE 1 is VDU 21,1
  • GCOL 0,1 is VDU 18,0,1
  • MOVE 100,100 is VDU 25,4,100,0,100,0
You could then convert the BASIC program to just output these VDU sequences, check that works, and then do the assembler version. In assembler all you need to do is load each number into the accumulator and JSR to &FFEE - so the MODE 1 command is just:

Code: Select all

    LDA #21 : JSR &FFEE
    LDA #1  : JSR &FFEE
And to keep the tattoo small, you probably want to do this in a loop rather than using a separate call for each!
EdwardianDuck
Posts: 330
Joined: Thu Aug 10, 2017 9:07 pm
Contact:

Re: 6502 crash course

Post by EdwardianDuck »

Sounds like a coding challenge in the making...

Most compact code vs most visually appealing code? Code style and choice of assembler syntax?

Personally I'd be worried about the tattoo artist making a transcription error resulting in the code having a bug. Although I guess given the quality of magazine type in listings back in the day, that might also be appropriate.

Jeremy

Disclaimer, I have no idea how tattoos work.
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Thanks that is great advice. I'm doing it for fun and just for what the heck. That vdu thing is a great idea.

I just had a thought. I could write it in basic...do compilers exist that will translate from basic into 6502? It would be the quickest way.
It's going to be something simple but very me....
It's .y mid life crisis phase now. Might as well embrace it
Last edited by pixelblip on Fri Jul 08, 2022 5:48 pm, edited 1 time in total.
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Ps I'm not sure about those other questions....clearly there is more to it than cobbling together a bit of code...compact would be better ...I could do it in owlett but by now at my time in life I need to do a bit of 6502 😊
SteveF
Posts: 1697
Joined: Fri Aug 28, 2015 9:34 pm
Contact:

Re: 6502 crash course

Post by SteveF »

pixelblip wrote: Fri Jul 08, 2022 12:23 pm I just had a thought. I could write it in basic...do compikers exist that will translate from basic into 6502? It would be the quickest way.
There are a few compilers around (e.g. Jeremy Ruston's, IIRC) but they are not going to generate super compact code, and in this situation I think even saving a byte has to be worth it!

It's quite a cool idea, although you're a lot braver than I am, or maybe I'm just further away from my next mid-life crisis :-) .

You could maybe just have the hex corresponding to the machine code tattooed as opposed to the actual assembler? This would be more compact, but might not be the look you're going for.

I won't sketch out example code as I understand you want to write it yourself, but learning enough 6502 yourself to loop over a string of bytes and send them to OSWRCH is probably not too hard and probably gives the most compact code (10-15-ish bytes plus the actual data). Depending on how "you" the code needs to be, you could write something yourself and get it working, then post it here for optimisation tips?

Edit: it's a pretty minor detail but if you're using a loop and you're going to have the assembler code tattooed instead of just the hex machine code, you'll need to decide what label syntax you want to immortalise. :-) The BBC BASIC assembler (and therefore beebasm) use a leading "." to indicate defining a label (".loop"), whereas many other assemblers use indentation - it's in the leftmost column, so it's a label (and the text is just "loop").
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Well I don't mind hex. I've a feeling it's going to be quite large. That will hurt! 😂 I could use mode7 for it which might simplify it just writing chars. Hmmm. I'll have to see what is involved plotting a character to the screen. Surely that can't be too difficult ☺️
User avatar
tricky
Posts: 7718
Joined: Tue Jun 21, 2011 9:25 am
Contact:

Re: 6502 crash course

Post by tricky »

Maybe look at the disassembled games and pinch a bit of code that looks appealing from one you like?
EdwardianDuck
Posts: 330
Joined: Thu Aug 10, 2017 9:07 pm
Contact:

Re: 6502 crash course

Post by EdwardianDuck »

If the code is large, you could upload it to, say GitHub and get a QR code of the URL as a tattoo. I think I'm having a strange day at work because that just popped into my mind and according to the Internet, QR code tattoos are a thing.

(Er, not a serious suggestion)
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Yeh I did consider that but they degrade over time and it seems every man and his dog is now getting one...it would have to point to a link and that could become broken...unless it was on GitHub. Or you embed the whole program into the code.
Another option couid be owlett and the compacted code. It looks quite cool and middle eastern. That might be a quicker route.
Although 6502 is really what it is about. That pure unadulterated code
Hex is going to be shorter but assembler looks cooler
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

I'll get it written I'm owlett tonight and then we'll see what I can do from there
User avatar
SKS1
Posts: 330
Joined: Sat Sep 19, 2020 12:04 am
Location: Highland Perthshire
Contact:

Re: 6502 crash course

Post by SKS1 »

Like GitHub will last... ;-)
Miserable old curmudgeon who still likes a bit of an ARM wrestle now and then. Pi 4, 3, ARMX6, SA Risc PC, A540, A440
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Well Github is like youtube isn't it - I mean it's a staple of the internet now........like reddit
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

I have to say gfoot your code here was very helpful! It is probably my best bet. Thanks a lot. ( and everyone else including you Tricky! ) Yeh maybe nicking a bit of code is allowed in a short time frame!
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

I've been searchin......https://www.youtube.com/watch?v=fWR9OPZTpLw

Sorry to go off topic. What is the best way to read a character from a Mode 7 screen and put it into an array or a data statement please?
I am going through BBC Basic but still not found the solution yet! Thanks :)
SteveF
Posts: 1697
Joined: Fri Aug 28, 2015 9:34 pm
Contact:

Re: 6502 crash course

Post by SteveF »

pixelblip wrote: Fri Jul 08, 2022 7:49 pm I've been searchin......https://www.youtube.com/watch?v=fWR9OPZTpLw

Sorry to go off topic. What is the best way to read a character from a Mode 7 screen and put it into an array or a data statement please?
You can use OSBYTE &87 to do it legally. Or if you're not in a shadow mode, not using a second processor and the screen hasn't scrolled, you can just use the ? operator in BASIC to peek &7C00+(Y*40)+X to get the character at (X,Y) out of the screen memory. (Edit: three characters have different codes when read/printed via the OS compared to the raw screen memory. To be really safe, if you plan to print the saved data back using OSWRCH later, read using OSBYTE &87, and if you plan to poke the saved data back directly into screen memory, read by peeking it out. But the chances of you running into this are fairly slim.)

Edit: there's an example of using OSBYTE &87 from BASIC in the user guide on page 432:

Code: Select all

DEF FNREADCH(X,Y)
LOCAL A%,LASTX,LASTY,C
LASTX=POS
LASTY=VPOS
VDU 31,X Y
A%=135
C=USR(&FFF4)
C=C AND &FFFF
C=C DIV &100
VDU 31,LASTX,LASTY
= CHR$(C)
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Thanks very much Steve!
User avatar
FourthStone
Posts: 1528
Joined: Thu Nov 17, 2016 2:29 am
Location: Brisbane, Australia
Contact:

Re: 6502 crash course

Post by FourthStone »

There's good 6502 websites where you can practice and learn to code...
https://skilldrick.github.io/easy6502/

Depending on how far you want to take this, The Termimator apparently ran on 6502... full body mod perhaps :wink: ?
Image
User avatar
pixelblip
Posts: 4051
Joined: Wed Feb 04, 2015 7:19 pm
Location: London
Contact:

Re: 6502 crash course

Post by pixelblip »

Thanks Fourthstone. I might need to be rebuilt like the Terminator now I am getting on :lol:
julie_m
Posts: 597
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

Re: 6502 crash course

Post by julie_m »

ADDRESSING MODES
The 6502 has various addressing modes, which specify an operand in different ways. An instruction may be 1, 2 or 3 bytes long, depending on its addressing mode. If you count long and short addresses, and indexing by X and Y, as distinct, then the 6502 could be said to support thirteen addressing modes.

Immediate -- indicated by #, as in LDA #&FF
The actual operand is supplied in the instruction itself.
Absolute -- as in STA &404
The instruction contains an address, usually two bytes, which is the location of the operand or the destination of a JMP or JSR instruction. Some instructions have an additional form which can take a short (1-byte) address in zero page. The assembler will take care of this for you, automatically choosing the short form if available and depending on the address supplied or the long form otherwise.
Indexed -- as in CMP wordlist,X
The instruction contains an address, to which the X or Y index register is added to get the location of the operand. The "big eight" instructions (LDA, STA, ADC, SBC, AND, ORA, EOR and CMP) can all take long and short ,X and long ,Y forms. Some others can take ,X with a long or (usually) short address. Note there is no LDY long,X or LDX long,Y!
Indirect -- indicated by (), as in JMP (rotv)
This addressing mode is only available with the JMP instruction. The instruction contains a long address. This address contains the low byte of the destination, and the following address -- or &xx00 if the given address was &xxFF -- the high byte.
Indexed Indirect -- indicated by (,X), as in LDA (&70,X)
The instruction contains a short address in zero page, to which the X index register is added -- ignoring any overflow -- to give an address in zero page which contains the low byte of the address where the actual operand is to be found. The high byte is in the following location, or &00 if the calculated address was &FF. This is a strange one. The only use I can think of for this one is choosing one of several lists or I/O ports.
Indirect Indexed -- indicated by (),Y, as in LDA (wlb),Y
The instruction contains a short address in zero page, which contains the low byte of a base address. The high byte of the base address is in the following location, or &00 if the supplied address was &FF. The Y index register is added to the base address to get the location of the operand. You will use this all the time for reading and writing databases of variable-length records; set the base address to point to the beginning of a long record and Y to 0 at the top of a loop, and increase the value in the Y register with INY to get successive bytes of the record.
Implied -- as in TXA
The instruction is complete in and of itself, and does not need to specify an additional operand.
Accumulator -- as in ROL A
This applies to the "Read-Modify-Write" instructions ROL, ASL, ROR and LSR. The instruction operates directly on the accumulator. Annoyingly, there is no INC A or DEC A on the version of the 6502 found in the standard Model B -- but they are among the extensions supported by the CMOS chip used in the Second Processor and the Master series, and which could be retrofitted to a Model B.
Relative -- as in BCC too_small
This addressing mode applies to all the conditional branch instructions (BEQ, BNE, BMI, BPL, BCS, BCC, BVS and BVC). The instruction contains an offset to the next instruction to be executed if the condition is satisfied. The offset is calculated from the current value of the program counter, which at the moment will still be pointing to the second byte of the instruction being executed; so the offset should be &01 to execute the next instruction regardless. &7F will give the maximum possible forward offset of 126 bytes; &80 will give the maximum possible backward offset of -129 bytes. If the condition is not satisfied, execution proceeds from the next instruction following the Bxx. The assembler will calculate the offset for you automatically; you should specify the address exactly as though it were absolute.
julie_m
Posts: 597
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

Re: 6502 crash course

Post by julie_m »

REGISTERS
The 6502 has few registers. Zero page (locations &0000 - &00FF) can be used almost as a set of extra registers.

ACCUMULATOR (A)
The accumulator is the main working register. It holds an 8-bit number.

X AND Y INDEX REGISTERS
The X and Y index registers can be used to give an offset from an address, or as general-purpose counters (they can be increased, decreased and compared). Each can hold an 8-bit number.

STACK POINTER (S)
The stack pointer is the 8-bit offset from &0100 of the "top" of the stack (which, just to confuse you, actually grows downwards).

PROGRAM COUNTER (PC)
The program counter gives the location of the instruction currently being executed (or some byte within it; the PC will advance while the 6502 is executing a multi-byte instruction). It is 16 bits wide.

PROCESSOR STATUS WORD
This contains various flags indicating the state of the processor.
Bit 7: N -- Negative. This is 1 if the last number looked at (i.e. read, written or calculated) had bit 7 set. (You might think this is really only meaningful as the sign bit for the highest byte of a multi-byte number, but it gives us a very cheap form of AND #&80.)
Bit 6: V -- oVerflow. This is 1 is an ADC instruction caused bit 7 of the accumulator to change from 0 to 1, or an SBC instruction caused bit 7 of the accumulator to change from 1 to 0. Such a false change of sign can occur if a number exceeds the range -128 .. 127.
Bit 4: B -- Break. This is 1 after a BRK instruction has been executed. In a debugger running on real or emulated hardware, it will appear always to be 1, since a BRK was used to stop execution. A debugger built into an emulator will not have this limitation, since the emulated processor can be examined directly without having to execute a BRK.
Bit 3: D -- Decimal mode -- changes the behaviour of addition and subtraction so &10 comes after &09 instead of &0A, &29 comes before &30 instead of &2F, and so forth. Can be set and cleared with SED and CLD respectively. Slightly scary. The BBC micro's internal routines expect D=0 at all times.
Bit 2: I-- Interrupt Disable. Can be set with SEI and cleared with CLI. Scary, but necessary if you need to poke about with system registers and don't want them to be looked at while they are in a half-ready state. If you want to play nicely with the MOS then it's best to use PHP to save the processor status word including the current value of I, turn off interrupts with SEI, do whatever must not be interrupted and finally restore the interrupt state from beforehand using PLP. This way, if I was already set for some reason, you aren't going to clear it and maybe cause something else to be interrupted.
Bit 1: Z -- Zero. This is 1 if the last number looked at is zero, 0 otherwise. It can be thought of as an 8-input NOR gate fed from the bits of that number (one of which, from bit 7, is going to be used for the N flag).
Bit 0: C -- Carry. This is the result of the carry from the last addition, or the "no borrow" from the last subtraction. It is also used to catch the bit that falls out of the end when executing an ASL, LSR, ROL or ROR instruction, and to import a bit into the opposite end in a ROL or ROR instruction.

Some bits within the processor word, which depend on the results of operations, can be tested by means of conditional branch instructions:
BMI (Branch MInus) branches if N=1; BPL (Branch PLus) branches if N=0.
BVS (Branch V Set) branches if V=1; BVC (Branch V Clear) branches if V=0.
BCS (Branch C Set) branches if C=1; BCC (Branch C Clear) branches if C=0.
BEQ (Branch EQual) branches if Z=1; BNE (Branch Not Equal) branches if Z=0.

You need to clear the carry flag with CLC before (the low byte of) an addition, and you need to set the carry with SEC before (the low byte of) a subtraction. The reason for this is, the twos complement requires us to flip the bits and add one; we actually cheat a little by just flipping the bits, and relying on the carry to add the one. The carry flag will be 1 following a subtraction if there was no need to borrow a one, or 0 if a one was borrowed. In either case, the carry will automatically be correct for subsequent bytes.

It's very common to use the C flag to indicate a single-bit result when returning from a subroutine.

You can clear the overflow flag with CLV. There isn't a SEV instruction (though there is an input pin on the 6502 which sets V! This isn't connected to anything on the BBC micro; some special-purpose devices use it as a "data ready" indication which can be read much more quickly than an I/O port, using a waiting loop consisting of just a BVC statement branching to itself if V is clear); but if you ever need to set V programmatically for some reason (probably to return an extended result) you can point a BIT instruction to the address of a convenient JSR. Since this has opcode &60, bit 6 will be 1 and V will be set. (You probably will want to use V=0 for the more common case, just because it's so much quicker to clear V than to set it.)
User avatar
sweh
Posts: 3326
Joined: Sat Mar 10, 2012 12:05 pm
Location: 07410 New Jersey
Contact:

Re: 6502 crash course

Post by sweh »

Obviously the first program everyone writes in a language is "Hello World". So...

Code: Select all

LDX #0
.lp
LDA msg,X
BEQ end
JSR &FFEE
INX
BNE lp
.end
RTS
.msg
EQUS "Hello, World"
BRK
In hex (if assembled at &900) it would be something like

Code: Select all

A2 00 BD 0E 09 F0 06 20 EE FF E8 D0 F5 60 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 00
We can optimise for space by reversing the string (this was common BITD)

Code: Select all

LDX #(fin-msg)
.lp
LDA msg,X
JSR &FFEE
DEX
BPL lp
RTS
.msg
EQUS "dlroW ,olleH"
.fin
Which becomes

Code: Select all

A2 0C BD 0C 09 20 EE FF CA 10 F7 60 64 6C 72 6F  57 20 2C 6F 6C 6C 65 48 
If we pad it with an unecessary null it becomes a nice square

Code: Select all

A2 0C BD 0C 09 
20 EE FF CA 10 
F7 60 64 6C 72 
6F 57 20 2C 6F 
6C 6C 65 48 00
But you could make it print your name, your website URL, your twitter handle, your stardot handle... whatever, to make it personal to you. Even "I love <insert name here>" :-)
Rgds
Stephen
Post Reply

Return to “programming”