The Noob's Machine Code Thread

Anything QL Software or Programming Related.
User avatar
TMD2003
Trump Card
Posts: 168
Joined: Sat Oct 10, 2020 12:18 pm

The Noob's Machine Code Thread

Post by TMD2003 »

I may have been away a while, but I am not dead. And over the last three days, I've been hammering away on a QL again. (Well, QemuLator, but you get the idea). And I have written a fine SuperBASIC program - which works exactly the way I want it to, except for a few sound effects that I haven't experimented with yet - and I could release it as it is.

BUT - the original version of this program was a vehicle to show that I could use machine code on the ZX81, and because I had no access to an assembler, I had to assemble it by hand, and therefore keep it simple. And hence, the QL port of this program will be much the same - it will showcase my first determined attempt at 68008 machine code.

I have already found Andrew Pennell's book on the subject and it explains the instructions in a way I can understand, as well as introducing a few more that I might find useful, and already I'm translating the various MOVEs into their LD equivalents so I can understand it better. If it has a Z80 equivalent - and bear in mind I'm never going to go near the interrupt or stack pointer instructions until Death Valley freezes over - the chances are I won't have too many troubles with it.

I'm also looking at this Computer One assembler, or at least its manual, to see if it might serve my purpose, because hand-assembly looks very complicated.

But before I do anything, I just need to check that I've got the idea of RESPR right, and to check that I can do what I'm about to describe. If I start with mcs=RESPR(1024), I get a kilobyte of space somewhere in the QL's memory, for which the variable mcs stores the value of the first byte. Most people would start their programs at that first byte... but not me. I need the first six bytes to be available to POKE values four values in from BASIC, perform machine code manipulations on them, and dump the results in the other two bytes which can be PEEKed and used in the rest of the program. It's effectively a function to take two strings of length 2 and combine them into one by some Amazing Secret Formula (which is the machine code).

Is there a "correct" way to achieve this? My default method would be to use three NOPs (two bytes each) and it doesn't matter if that bit of code gets erased, it didn't do anything anyway, the important thing is that the space is there.

So if I've got this right, this should be the correct approach...

Code: Select all

100 mcs=RESPR(1024)
110 LBYTES ...(load in the code from outside)
...

1000 POKE mcs,CODE(a$(1))
1010 POKE mcs+1,CODE(a$(2))
1020 POKE mcs+2,CODE(b$(1))
1030 POKE mcs+3,CODE(b$(2))
1040 CALL mcs+6
1050 c$=CHR$(PEEK(mcs+4))&CHR$(PEEK(mcs+5))

I can already see one thing I might have trouble with - the not-entirely-obvious 68008 equivalents of PUSH and POP. All I can gather is that it'll involve register A7 in some way. Fortunatelty, the Z80 code I'm trying to convert doesn't use the stack at all, so this can wait for another day.


Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
mk79
QL Wafer Drive
Posts: 1349
Joined: Sun Feb 02, 2014 10:54 am
Location: Esslingen/Germany
Contact:

Re: The Noob's Machine Code Thread

Post by mk79 »

TMD2003 wrote:Is there a "correct" way to achieve this? My default method would be to use three NOPs (two bytes each) and it doesn't matter if that bit of code gets erased, it didn't do anything anyway, the important thing is that the space is there.
The "correct" way is to create a new SuperBasic keyword that takes 4 parameters and returns the result as a string, no PEEKing and POKEing. What you're planning is a botch, but should work in principle.
I can already see one thing I might have trouble with - the not-entirely-obvious 68008 equivalents of PUSH and POP. All I can gather is that it'll involve register A7 in some way. Fortunatelty, the Z80 code I'm trying to convert doesn't use the stack at all, so this can wait for another day.
Hm, maybe you should start reading Norman's Assembly eComic for the basics, linked somewhere else here in the forum.


User avatar
tofro
Font of All Knowledge
Posts: 2700
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: The Noob's Machine Code Thread

Post by tofro »

TMD2003 wrote:
But before I do anything, I just need to check that I've got the idea of RESPR right, and to check that I can do what I'm about to describe. If I start with mcs=RESPR(1024), I get a kilobyte of space somewhere in the QL's memory, for which the variable mcs stores the value of the first byte.
Yep, you got that right. That kByte of memory is yours to mess with now, guaranteed not to be touched by QDOS or any well-behaved piece of code. Note, on different systems equipped with different amounts of memory or different pieces of pre-loaded stuff, mcs will have a different value - all is flux in QDOS.
TMD2003 wrote: Most people would start their programs at that first byte... but not me. I need the first six bytes to be available to POKE values four values in from BASIC, perform machine code manipulations on them, and dump the results in the other two bytes which can be PEEKed and used in the rest of the program. It's effectively a function to take two strings of length 2 and combine them into one by some Amazing Secret Formula (which is the machine code).
Is there a "correct" way to achieve this?
Note that, different from older Sinclair computers, CALLing machine code in S*BASIC has actually a pretty flexible way to handover parameters from and to BASIC into machine code - While I agree that writing an S*BASIC extension might be a bit ambitious for a first-time machine coder, CALL allows you to pre-load registers D0 to A5 from BASIC and retrieve the value of D0 as a function return - So, there is actually no need to reserve a handover area.

And, for heaven's sake, start using an assembler! 68000 machine code is way too complex to fiddle with hand-assembly.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
TMD2003
Trump Card
Posts: 168
Joined: Sat Oct 10, 2020 12:18 pm

Re: The Noob's Machine Code Thread

Post by TMD2003 »

mk79 wrote:The "correct" way is to create a new SuperBasic keyword that takes 4 parameters and returns the result as a string, no PEEKing and POKEing. What you're planning is a botch, but should work in principle.
Well... that's what I've already done, seeing as that's what two years of fiddling around with SuperBASIC means I can do. However, it's rather too easy to figure out how it works... and I need to disguise it.

It's not as if the ZX81 couldn't have done what I made it do in BASIC, either - but the program was written in December 2020 and I'd still be here waiting for it to finish its first calculation.
tofro wrote:Yep, you got that right. That kByte of memory is yours to mess with now, guaranteed not to be touched by QDOS or any well-behaved piece of code. Note, on different systems equipped with different amounts of memory or different pieces of pre-loaded stuff, mcs will have a different value.
It was around 760,000 when I looked - I had QemuLator set to 640K
tofro wrote:And, for heaven's sake, start using an assembler! 68000 machine code is way too complex to fiddle with hand-assembly.
No worries about that. It's good to know that it's possible, it's also good to know that every instruction will be two bytes (unless there's some others I haven't discovered yet)... and it's good to know that D0 can be used the same way as BC (somehow). I'll investigate the Computer One assembler first - unless there's another one that's highly recommended.


Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Martin_Head
Aurora
Posts: 852
Joined: Tue Dec 17, 2013 1:17 pm

Re: The Noob's Machine Code Thread

Post by Martin_Head »

You might like to try the Talent Assembler Workbench. It's a Monitor as well as an Assembler, So you can trace through programs, and do memory dumps.
It's also got an online help for the commands.

You can get it here http://www.dilwyn.me.uk/asm/talentaw13.zip
And some updates here http://www.dilwyn.me.uk/asm/talentq.zip and here http://www.dilwyn.me.uk/asm/talentaw22.zip


User avatar
mk79
QL Wafer Drive
Posts: 1349
Joined: Sun Feb 02, 2014 10:54 am
Location: Esslingen/Germany
Contact:

Re: The Noob's Machine Code Thread

Post by mk79 »

TMD2003 wrote:No worries about that. It's good to know that it's possible, it's also good to know that every instruction will be two bytes (unless there's some others I haven't discovered yet)...
Well, try stuffing "move.l #$12345678,d0" into two bytes ;)
and it's good to know that D0 can be used the same way as BC (somehow). I'll investigate the Computer One assembler first - unless there's another one that's highly recommended.
Well, as a real assembler always QMAC. But apart from that one more vote for the Talent Workbench, it's really cool for playing around with assembler, I still use it today if I need to assemble and/or trace a few instructions from scratch.


User avatar
NormanDunbar
Forum Moderator
Posts: 2273
Joined: Tue Dec 14, 2010 9:04 am
Location: Leeds, West Yorkshire, UK
Contact:

Re: The Noob's Machine Code Thread

Post by NormanDunbar »

mk79 wrote:Hm, maybe you should start reading Norman's Assembly eComic for the basics, linked somewhere else here in the forum.
I would advise starting with the eBook version of the long running series in QL Today. That's available to download from https://github.com/NormanDunbar/QLAssem ... ses/latest.

Once that has been digested, perhaps start with the eComics Issues 2 onward are at https://github.com/NormanDunbar/QLAssem ... e/releases while Issue 1 is attached here.
Assembly_Language_001.pdf
(210.53 KiB) Downloaded 54 times


Cheers,
Norm.


Why do they put lightning conductors on churches?
Author of Arduino Software Internals
Author of Arduino Interrupts

No longer on Twitter, find me on https://mastodon.scot/@NormanDunbar.
User avatar
TMD2003
Trump Card
Posts: 168
Joined: Sat Oct 10, 2020 12:18 pm

Re: The Noob's Machine Code Thread

Post by TMD2003 »

NormanDunbar wrote:I would advise starting with the eBook version of the long running series in QL Today. That's available to download from https://github.com/NormanDunbar/QLAssem ... ses/latest.
I'm looking at it now.

Before the weekend I compiled a list of Z80 instruction groups that I'd need to be able to translate to 68008 if I wanted my program to work:
- LD (the most obvious, it's the MOVE set)
- CALL (looks like it should be JSR)
- rotate/shift instructions (I have at least found the section on these - though I don't think I'll need the X flag for what I'm doing, just the C)
- BIT/SET/RES (in case the rotate/shift method doesn't work, though looking through the 68008 rotate/shift section, I don't think I'm going to need this)
- ADD/SUB (and not, at this stage, ADC or SBC; I've found the section on this as well)
- SCF/CCF (this eludes me so far - and I'd never use these on the Z80 as AND A clears the carry flag in one byte and use of SCF then CCF to do the same is frowned upon)
- CP (which I already know is CMP and is more powerful than the Z80's instruction)
- JR and possibly JP (which, from what I can gather, are BRA and JMP respectively, with a list of extra conditions)
- RET (that's RTS, and I'm still hoping there's an equivalent of RET C/NC/Z/NZ in case I ever need these - RET PE/PO/M/P are all far less important to me)

There's just one that I missed, which isn't in any list of Z80 opcodes because it isn't one, merely an assembler command. Is there a standardised equivalent of DEFB and DEFW? Those six initial bytes that I need to keep free all have labels. It looks like my plan to start with three NOPs might still be a viable workaround, seeing as three of those can be labelled (I'll call them LEFT, RIGHT and OUT), but I'd have to find a way to "MOVE.B LEFT+1,D0" as well as MOVE.B LEFT,D0. It looks like it might be possible, but might involve holding LEFT (and then RIGHT) in an address register and MOVE.Bing to D0 using that instead.

Incidentally, I've decided that I'm using D0 for the accumulator equivalent, A0 for instructions where HL is used as an address pointer, and if I need BC or DE, those can be D1 and D2 - or D1-D4 if I need single registers.

We will see. I'll start with the easy routines to convert character codes for 0-9 and A-F into values 0-15, and back again. Those look very straightforward.


Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
User avatar
NormanDunbar
Forum Moderator
Posts: 2273
Joined: Tue Dec 14, 2010 9:04 am
Location: Leeds, West Yorkshire, UK
Contact:

Re: The Noob's Machine Code Thread

Post by NormanDunbar »

The X flag is usually set as per the C flag. You will need it when you need ADC/SBC which will be the ADDX/SUBX instructions.

For CALL, you could use BSR.

SCF/CCF hmm. Using AND would clear the carry flag, but will also set the Z flag if the value was zero. Possibly not what would be desired.

You get RTS and that's your lot I'm afraid. Apart from RTI for interrupt handlers.

DEFB is DC.B and the address need not be even.

DEFW is DC.W and the address should be even for MC68008but need not be for MC68000 and above. Stick a DS.W 0 in front of your actual DS.W to force even address, if it is currently odd.

If you have:

Left dc.w $1234
...
Move.w left,d0

Then the contents of the word at "left" are loaded into D0. Or, you can:

Lea left,a0
Move.w (a0),d0

To do the same thing.

Cheers,
Norm.


Why do they put lightning conductors on churches?
Author of Arduino Software Internals
Author of Arduino Interrupts

No longer on Twitter, find me on https://mastodon.scot/@NormanDunbar.
User avatar
TMD2003
Trump Card
Posts: 168
Joined: Sat Oct 10, 2020 12:18 pm

Re: The Noob's Machine Code Thread

Post by TMD2003 »

Right, now I'm getting somewhere. I found DC.B and BSR within about five minutes of the last post.

Thinking that I might only have RTS to deal with, my original Z80 code had RET C to return early from the subroutine - I've changed it to JR C,{labelled_RETline} to jump to the final RET, it adds one byte per subroutine, which isn't exactly disastrous, and makes the translation easier. I can do it this way.

Assuming there's some value in any of the data registers, any value ANDed with itself returns the same value, so provided that register had a non-zero value it wouldn't set the Z flag. However, the manual says that if I were to AND.B #0,D* (using a register that has no other use), it should clear all five flags. And to avoid the potential for setting the Z flag after the operation, AND #224,D* should be even better (as only the lowest five bits need to be 0).

It is entirely possible that the main hurdle to overcome here is... the assembler itself! So far, I've only had any joy with the Computer One Assembler, but if I keep it simple, it appears to be doing what I need it to do. This is a good thing.


Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Post Reply