Learning Machine Code

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
Alessandro
Dynamite Dan
Posts: 1910
Joined: Wed Nov 15, 2017 11:10 am
Location: Messina, Italy
Contact:

Re: Learning Machine Code

Post by Alessandro »

lister_of_smeg wrote: Tue Jan 28, 2020 10:52 pm Yup, this. And as this results in you having no defined origin, I would hazard a guess that Pasmo defaults to 32768 in such a case.
It doesn't. If you do not specify any code beginning address with ORG in the source, Pasmo will produce, when assembling to a tape image file, a Bytes header stating address 0 as the first location, and all relative jumps in the code will take that into account.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Learning Machine Code

Post by Hedge1970 »

Ast A. Moore wrote: Tue Jan 28, 2020 10:20 pm I see. Well, I’m not familiar with either, but off the top of my head, I’d suggest putting a space in front of the ORG statement. Some assemblers assume that a letter at the first position of a line is a label, rather than a (pseudo-) instruction/directive.
uglifruit wrote: Wed Jan 29, 2020 9:41 am ....Just as a 'did you know' - finishing your assembly with END 32768 will compile the basic loader so it already does a Randomize 32768 when you launch it in your emulator.
Thank you Gents,

Not only did the space before ORG do the trick, but I got an awsome tip from Uglifruit as well.

Back to trying to understand whats going on :)
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Learning Machine Code

Post by Hedge1970 »

I remember years ago as a teenager people said you needed to be good at maths to be a programmer. Many years later I quite happily did web work, SQL, C#, DotNet etc and never found that maths played a significant part.

Now I get it, hex to dec to bin plus the relationships to the screen and attributes to hex and bin rra rrl wtf;). I’ve not yet got settled with understanding these relationships but I can see them and I can work my way through them but they don’t come naturally.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Learning Machine Code

Post by Ast A. Moore »

Hedge1970 wrote: Sat Feb 08, 2020 10:14 am Now I get it, hex to dec to bin plus the relationships to the screen and attributes to hex and bin rra rrl wtf;). I’ve not yet got settled with understanding these relationships but I can see them and I can work my way through them but they don’t come naturally.
That’s probably because there’s nothing inherently natural about hexadecimal or binary. Come to think of it, this is also true for the decimal system; we’re just familiar with it. (Well, we do have ten fingers and toes, but then there are cultures that don’t use base 10 counting systems.)

It’s totally fine to mix and match different counting systems throughout your code. I do it all the time. Binary is useful when working with bits (shifts, masks, etc.), hexadecimal is more suited for calculating addresses or working with 16–bit-wide registers, decimal is convenient for working out simple coordinates, score, and other “regular” values.

Advanced math is probably non-essential, but some basic math skills always come in handy.

Here’s a small real-life example of code that uses all three bases:

Code: Select all

	ld a,%01000111	;ATTR: no FLASH, BRIGHT, BLACK PAPER, WHITE INK
	ld hl,$5907	;ATTR address
	ld (hl),a
	inc l
	ld (hl),a

	ld b,6		;counter for a short pause
2$	halt
	djnz 2$
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Mastering MC Type In

Post by Hedge1970 »

Hi,

Its been quite a journey over the last few weeks and while there is no light on the horizon just yet, I can at least understand around 70% of the work I am copying. I am making my way through Mastering Machine Code (https://ia800604.us.archive.org/view_ar ... ectrum.pdf)

I am using Spin emulator and Pasmo compiler, whereas in the book the author Toni Baker provides her own machine code writing program and herein lies one of the challenges, that is trying to bridge the gap between what she says to do and what to do in my Pasmo/Spin environment.

Most of the examples and programs have been ok and with a lot of thought and a little tweak I manage to get them running, however this little game has me foxed. Its Chapter 15, Page 243 via the above link.

The code below is very similar to the book and while it may not be 100% the same, the issue I am asking about is perfectly working and copied below - the layout of the "spiral". It looks nothing like a spiral and - I hope I am correct here - the loop to work through the parts of the SPIRAL DEFW statements ( LD B,#0A) is only looping 10 times (0A) whereas there are 20 bytes of data so the program ignores the last 10 bytes? This code is three lines below the START label. For the DEFW statements Ive used both binary and hex and both behave the same way.

Code: Select all

			ORG 26621
			JP start
spiral		DEFW #FF,#E0
			DEFW #A0,#20
			DEFW #AF,#A0
			DEFW #A8,#A0
			DEFW #AA,#A0
			DEFW #AE,#A0
			DEFW #A0,#A0
			DEFW #BF,#A0
			DEFW #80,#20
			DEFW #FF,#E0
score_1		DEFM #0D
			DEFM "Your "
			DEFM "Score "
			DEFM "Now "
			DEFM "999000"
score_c		DEFM #16,#0B,#0F
			DEFM "000"
start		XOR A
			LD (#5C3C),A
			LD B,#0A
			LD HL,#6800
s_loop		LD E,(HL)
			INC HL
			LD D,(HL)
			INC HL
			EX DE,HL
r_loop		ADD HL,HL
			JR C,wall
			LD A,#3F
			LD (#5C8F),A		;used A to ld 3F
			JR prsq
wall		LD A,#00
			LD (#5C8F),A		;used A to ld 00
prsq		LD A,#2B
			RST #10
			LD A,H
			OR L
			JR NZ,r_loop
			LD A,#0D
			RST #10
			EX DE,HL
			DJNZ s_loop
			LD A,#38
			LD (#5821),A
			LD A,#30
			LD (#5C8F),A	;Used A to ld 30
			LD HL,#6828		;score_1
			LD B,#15
sc_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ sc_loop
			LD HL,#3939
			LD(#6841),HL	;score c+3
			LD(#6842),HL	;score c+4
			LD HL,#5821
			LD(#5C92),HL
			LD HL,#0000
			LD(#5C94),HL
loop		LD HL,#6843		;score_c + 5
deci		LD A,(HL)
			CP #0F
			JR NZ,positive
			LD B,#03
reset		INC HL
			LD (HL),#30
			DJNZ reset
			RET
positive	DEC A
			CP #2F
			JR NZ,ok
			LD (HL),#39	;REPLACE 
			DEC HL
			JR deci
ok			LD (HL),A
			LD HL,#683E
			LD B,#06
cs_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ cs_loop
			LD BC,#0800
delay		DEC BC
			LD A,B
			OR C
			JR NZ,delay
			CALL #028E
			LD A,E
			CP #09
			JR Z,left
			CP #10
			JR Z,down
			CP #11
			JR Z,right
			CP #12
			JR NZ,loop
up			LD DE,#FFE0
			JR move
left		LD DE,#FFFF
			JR move
down		LD DE,#0020
			JR move
right		LD DE,#0001
move		LD HL,(#5C94)
			LD A,H
			OR L
			JR Z,move_ok
			ADD HL,DE
			LD A,H
			OR L
			JR NZ,loop
move_ok		LD HL,(#5C92)
			LD A,(HL)
			XOR #07
			LD (HL),A
			ADD HL,DE
			LD A,(HL)
			XOR #07
			LD (HL),A
			LD (#5C92),HL
			LD HL,#0000
			CP #38
			JR Z,finish
			LD H,D
			LD L,E
finish		LD (#5C94),HL
			LD HL,(#5C92)
			LD DE,#5885
			SBC HL,DE
			RET Z
			JP loop
In my many many experiments with the code I was able to make this slightly modified version. However I thought I would make use of all 20 bytes and added a new loop called SET_C. But when I implement it I get two spirals but not joined side by side, rather separated. I have looked as hard as I can to understand why and realise that I have no understanding of how the first position of the spiral is .. positioned. If anyone can find it in themselves to help I would love to know, then I will/should be able to create a bigger spiral game screen.

Note in both above and below code I have replaced her #5536 for #5C8F to target the Speccy System Variable (ATTR T)

My Code

Code: Select all

			ORG 26621
			JP start
score_1		DEFM #0D,"Your "
			DEFM "score "
			DEFM "now " 
			DEFM "999000"				;22 letters and spaces
score_c		DEFM #16,#0B,#0F			;3
			DEFM "000"					;3
spiral		DEFW %11111111
			DEFW %00000001
			DEFW %11111101
			DEFW %11000101
			DEFW %10010101
			DEFW %10110101
			DEFW %10100101
			DEFW %10111101
			DEFW %10000001
			DEFW %11111111				;10 start 26644
start		XOR A
			LD (#5C3C),A
			LD B,#0A
			LD HL,#681C					;address of spiral data
set_c		LD C,#01
s_loop		LD E,(HL)
			INC HL
			LD D,(HL)
			INC HL
			EX DE,HL
r_loop		ADD HL,HL
			JR C,wall
			LD A,#3F
			LD (#5C8F),A				;ATTR_T 0011 1111 FL BR P2 P1 P0 I2 I1 I0
			JR prsq
wall		LD A,#00
			LD (#5C8F),A				;ATTR_T 0000 0000 FL BR P2 P1 P0 I2 I1 I0
prsq		LD A,#2B					;+ character
			RST #10
			LD A,H
			OR L
			JR NZ,r_loop
			EX DE,HL
			LD A,C
			DEC C
			CP #01
			JR NZ,s_loop
			LD A,#0D						;return new line
			RST #10
			DJNZ set_c
			LD A,#38
			LD (#5821),A
			LD A,#30
			LD (#5C8F),A					;Black on yellow
			LD HL,#6800						;score_1
			LD B,#15
sc_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ sc_loop
			LD HL,#3939
			LD (#6819),HL					;score_c +3
			LD (#681A),HL					;score_c +4
			LD HL,#5821
			LD(#5C92),HL					;Store current position
			LD HL,#0000
			LD(#5C94),HL 					;Store last move
loop		LD HL,#681B						;Score_c + 5
decemal		LD A,(HL)
			CP #0F
			JR NZ,positive
			LD B,#03
reset		INC HL
			LD (HL),#30 					;"0"
			DJNZ reset
			RET
positive	DEC A
			CP #2F
			JR NZ,ok
			LD (HL),#39						;"9" 
			DEC HL
			JR decemal
ok			LD (HL),A
			LD HL,#6816 					;score_c
			LD B,#06
cs_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ cs_loop
			LD BC,#0F00						;speed
delay		DEC BC
			LD A,B
			OR C
			JR NZ,delay
			CALL #028E						;Call KEYSCAN
			LD A,E
			CP #09
			JR Z,left
			CP #10
			JR Z,down
			CP #11
			JR Z,right
			CP #12
			JR NZ,loop
up			LD DE,#FFE0
			JR move
left		LD DE,#FFFF
			JR move
down		LD DE,#0020
			JR move
right		LD DE,#0001
move		LD HL,(#5C94)					;is player in wall?
			LD A,H
			OR L
			JR Z,move_ok
			ADD HL,DE
			LD A,H
			OR L
			JR NZ,loop
move_ok		LD HL,(#5C92)
			LD A,(HL)
			XOR #07
			LD (HL),A
			ADD HL,DE
			LD A,(HL)
			XOR #07
			LD (HL),A
			LD (#5C92),HL
			LD HL,#0000
			CP #38
			JR Z,finish
			LD H,D
			LD L,E
finish		LD (#5C94),HL				;Last move
			LD HL,(#5C92)
			LD DE,#58CB					;Square at center
			LD A,#A7
			LD (DE),A					;10100111 FL BR P2 P1 P0 I2 I1 I0
			SBC HL,DE
			RET Z
			JP loop
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Learning Machine Code

Post by Ast A. Moore »

DEFW is an assembler pseudoinstruciton that defines a word, that is a sixteen–bit-wide (i.e. two-byte) space. Different assemblers will treat its argument differently, if it’s not specified fully. In your case, DEFW #FF,#E0 actually means “write the following four bytes: $ff $00 $ef $00,” provided the assembler just fills out the unspecified byte with zeros.

What you probably want is to rewrite all those statements to read something like DEFW #FFE0, without the commas.

However, note that DEFW #FFE0 will probably store the data LSB-first, i.e. #E0 #FF, and not #FF #E0.

DEFB defines a byte, and DEFM is used by some assemblers to define a message (or string).

Most assemblers allow you use a comma to separate the arguments to save space:

DEFW #fd30,#e400,#21a0
DEFB 254,1,33,14
DEFM “string”, $20, “another string”

A lot of the times, the DEFB and DEFM statements are completely interchangeable.

There’s also DEFS, which defines (reserves) a space of a specific length and can (optionally) fill it with a specific value:

DEFS 30 $FE means, fill the next thirty bytes with the value $FE.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Learning Machine Code

Post by Hedge1970 »

Ast A. Moore wrote: Sun Feb 16, 2020 2:38 pm DEFW is an assembler pseudoinstruciton that defines a word, that is a sixteen–bit-wide (i.e. two-byte) space. Different assemblers will treat its argument differently, if it’s not specified fully. In your case, DEFW #FF,#E0 actually means “write the following four bytes: $ff $00 $ef $00,” provided the assembler just fills out the unspecified byte with zeros.
Oh my goodness I thought I had tried every combination... This works :) And it places the spiral at 0,0 which is where I was expecting it to be

Thank you so much.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Learning Machine Code

Post by Ast A. Moore »

Oh, a small optimization technique. Whenever possible, try avoiding the SBC HL,DE instruction. It takes 15 T states to execute and, unless you want it, requires that you clear the carry bit. What it does is it subtracts both DE and Carry from HL. If Carry is already zero, then it’s just a regular subtraction; if Carry is set, then an extra one will be subtracted from HL. So, if you don’t want that, you’ll have to precede it with an OR A—another 4 T states and an extra byte.

There’s no “subtract a 16-bit value without carry” instruction, unfortunately. However, there’s a workaround. If the value you want to subtract from HL is a constant, simply change its sign from “+” to “–” and use ADD HL,DE (only 11 T states).

So, instead of:

Code: Select all

	ld de,2343
	or a
	sbc hl,de
write this:

Code: Select all

	ld de,-2343
	add hl,de
That, of course, depends on whether the assembler can understand this notation. Still, even if it can’t you can convert your number yourself by subtracting it from $10000.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Learning Machine Code

Post by Hedge1970 »

I have seen certain discussions on optimisation and T-states, for now I am focused on getting a broad understanding of the key terms and how to structure a program. Its quite tricky when yoiur limited to the few registers to create looping structures. In higher level languages I would just make/add variables as and when needed, but here its a careful balance.

It may seem like a small thing to you but I am so grateful for the reply as I dont have many friends learning 8bit machine code :lol:

Here is the finished product albeit still with the now not needed SET_C loop I added.

Code: Select all

			ORG 26621
			JP start
score_1		DEFM #0D,"Your "
			DEFM "score "
			DEFM "now " 
			DEFM "999000"				;22 letters and spaces
score_c		DEFM #16,#0B,#0F			;3
			DEFM "000"					;3
spiral		DEFW #FFE0
			DEFW #A020
			DEFW #AFA0
			DEFW #A8A0
			DEFW #AAA0
			DEFW #AEA0
			DEFW #A0A0
			DEFW #BFA0
			DEFW #8020
			DEFW #FFE0;10
start		XOR A
			LD (#5C3C),A
			LD B,#0A
			LD HL,#681C					;address of spiral data
set_c		LD C,#01
s_loop		LD E,(HL)
			INC HL
			LD D,(HL)
			INC HL
			EX DE,HL
r_loop		ADD HL,HL
			JR C,wall
			LD A,#3F
			LD (#5C8F),A				;ATTR_T 0011 1111 FL BR P2 P1 P0 I2 I1 I0
			JR prsq
wall		LD A,#00
			LD (#5C8F),A				;ATTR_T 0000 0000 FL BR P2 P1 P0 I2 I1 I0
prsq		LD A,#2B					;+ character
			RST #10
			LD A,H
			OR L
			JR NZ,r_loop
			EX DE,HL
			LD A,C
			DEC C
			CP #01
			JR NZ,s_loop
			LD A,#0D						;return new line
			RST #10
			DJNZ set_c
			LD A,#38
			LD (#5821),A
			LD A,#30
			LD (#5C8F),A					;Black on yellow
			LD HL,#6800						;score_1
			LD B,#15
sc_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ sc_loop
			LD HL,#3939
			LD (#6819),HL					;score_c +3
			LD (#681A),HL					;score_c +4
			LD HL,#5821
			LD(#5C92),HL					;Store current position
			LD HL,#0000
			LD(#5C94),HL 					;Store last move
loop		LD HL,#681B						;Score_c + 5
decemal		LD A,(HL)
			CP #0F
			JR NZ,positive
			LD B,#03
reset		INC HL
			LD (HL),#30 					;"0"
			DJNZ reset
			RET
positive	DEC A
			CP #2F
			JR NZ,ok
			LD (HL),#39						;"9" 
			DEC HL
			JR decemal
ok			LD (HL),A
			LD HL,#6816 					;score_c
			LD B,#06
cs_loop		LD A,(HL)
			INC HL
			RST #10
			DJNZ cs_loop
			LD BC,#0F00						;speed
delay		DEC BC
			LD A,B
			OR C
			JR NZ,delay
			CALL #028E						;Call KEYSCAN
			LD A,E
			CP #09
			JR Z,left
			CP #10
			JR Z,down
			CP #11
			JR Z,right
			CP #12
			JR NZ,loop
up			LD DE,#FFE0
			JR move
left		LD DE,#FFFF
			JR move
down		LD DE,#0020
			JR move
right		LD DE,#0001
move		LD HL,(#5C94)					;is player in wall?
			LD A,H
			OR L
			JR Z,move_ok
			ADD HL,DE
			LD A,H
			OR L
			JR NZ,loop
move_ok		LD HL,(#5C92)
			LD A,(HL)
			XOR #07
			LD (HL),A
			ADD HL,DE
			LD A,(HL)
			XOR #07
			LD (HL),A
			LD (#5C92),HL
			LD HL,#0000
			CP #38
			JR Z,finish
			LD H,D
			LD L,E
finish		LD (#5C94),HL				;Last move
			LD HL,(#5C92)
			LD DE,#5885				;Square at center
			LD A,#A7
			LD (DE),A					;10100111 FL BR P2 P1 P0 I2 I1 I0
			SBC HL,DE
			RET Z
			JP loop
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Learning Machine Code

Post by Ast A. Moore »

You need to use labels properly, otherwise you’ll get confused by your own code in no time flat.

For example:

Code: Select all

LD HL,#681C		;address of spiral data
What if you want to move that data (and you will, if some other data comes before it)? Since you’ve already defined it (by assigning a label to it), just tell the assembler that:

Code: Select all

LD HL,spiral
The same goes for the rest of the data:

Code: Select all

LD (#6819),HL		;score_c +3
can be replaced with

Code: Select all

LD (score_c+3),HL
and so on.

That way, you can move your actual data somewhere else in your code, say, append it to the end, and avoid the unnecessary JP start instruction. In addition, your ORG statement doesn’t have to be meticulously calculated each time. Just stick an easy to type number there, like 40000.

I haven’t looked at your code with any diligence, but the penultimate instruction is RET Z, right before a JP. I don’t see any CALLs in your code (aside from a single ROM CALL). Where exactly is you code supposed to return to once it gets there and the condition is true?
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Learning Machine Code

Post by Hedge1970 »

It just bails to basic if your cross is at the center. Not my code, just a type in example - I could not write this yet.

I could tweak it with “game over” and “high score” statements.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Learning Machine Code

Post by Ast A. Moore »

Hedge1970 wrote: Sun Feb 16, 2020 4:11 pm It just bails to basic if your cross is at the center.
Ah, makes sense!
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Post Reply