Equb equw equd etc

Discuss all aspects of programming here. From 8-bit through to modern architectures.
Post Reply
User avatar
BadProgram
Posts: 33
Joined: Thu Mar 22, 2018 12:56 pm
Contact:

Equb equw equd etc

Post by BadProgram » Thu Nov 14, 2019 4:18 pm

Hello , i cant seam to get equb working in my programs.
It would seam the best way for loading data in (via a loop) however i just cant get the flow right.

[
.din
ldy#0:.loop
lda sdat,Y : sta&5000,Y : iny : cpy#7 : bne loop : rts

.sdat
equb 1,2,3,4,5,6,7,8
]
CALL din

i get the error "byte at " sometimes and other times nothing happens.

Can the equb be indexed with Y like this ?

Does the equb data need to be jumped over in an ASM program ? or will the assmbler walk past it with no issue ?

Does the data within equb need to be in hex ? or does the assembler take care of it ?.

Just how many bytes can i put into an equb ? should it be dimension-ed in memory with basic before hand ?

The current fantastic examples online make great use of equb but the program flow is harder to follow than a simple bbc assembler style one.

I would be grateful for any examples using EQUB. thanks

cmorley
Posts: 1113
Joined: Sat Jul 30, 2016 7:11 pm
Location: Oxford
Contact:

Re: Equb equw equd etc

Post by cmorley » Thu Nov 14, 2019 4:43 pm

Code: Select all

L.
   10FORpass%=0TO3STEP3
   20P%=&C00
   30[OPTpass%
   40.din
   50LDY#0:.loop
   60LDA sdat,Y:STA &7C00,Y:INY:CPY#7:BNE loop:RTS
   70.sdat
   80EQUB 65:EQUB 66:EQUB 67:EQUB 68
   90EQUB 69:EQUB 70:EQUB 71:EQUB 72
  100]
  110NEXT
  120CALL din
Your example shifted to mode 7 and to print ABCDEFG

User avatar
BadProgram
Posts: 33
Joined: Thu Mar 22, 2018 12:56 pm
Contact:

Re: Equb equw equd etc

Post by BadProgram » Thu Nov 14, 2019 5:21 pm

Thanks cmorley , your example shows EQUB being called for each byte is that the only way to use it ?

The examples ive seen online seam to stack bytes in with "," EQUB 1,2,3,4 etc. i suppose that is
on a modern assembler ?

Calling EQUB for each byte still reduces the instructions i would need enter by half (a load and store for each byte), so this demonstration is a solution i can be happy with. thankyou.

cmorley
Posts: 1113
Joined: Sat Jul 30, 2016 7:11 pm
Location: Oxford
Contact:

Re: Equb equw equd etc

Post by cmorley » Thu Nov 14, 2019 5:32 pm

BadProgram wrote:
Thu Nov 14, 2019 5:21 pm
Thanks cmorley , your example shows EQUB being called for each byte is that the only way to use it ?

The examples ive seen online seam to stack bytes in with "," EQUB 1,2,3,4 etc. i suppose that is
on a modern assembler ?

Calling EQUB for each byte still reduces the instructions i would need enter by half (a load and store for each byte), so this demonstration is a solution i can be happy with. thankyou.
I don't think BASIC 2 supports comma. If you try it then BASIC will only emit one byte. beebasm I think does support commas. I don't know about BASIC 3 and BASIC 4. JGH or someone else will know.

You can pack 4 bytes with EQUD instead... play around and look at what gets assembled in the output (on the OPT 3 pass).

julie_m
Posts: 103
Joined: Wed Jul 24, 2019 8:53 pm
Location: Derby, UK
Contact:

Re: Equb equw equd etc

Post by julie_m » Thu Nov 14, 2019 7:33 pm

EQUB inserts a single arbitrary byte value into your program. The value can be anything BASIC recognises as a single-byte number; a decimal or hex (prefixed with &) number, a variable (even an assembler label defined using the "dot" syntax; they are just the same as any other BASIC variable, except for the way of defining them *) or an expression.

EQUW inserts a two-byte word value into your program, units first.

EQUD inserts a four-byte double word value, again using the Goddess's preferred byte ordering ;)

EQUS inserts a string, without a terminating character (you will have to insert this separately yourself, or not, as you choose).

The following all produce the same result:

Code: Select all

.message
EQUB 83
EQUB ASC"I"
EQUB 66
EQUB 2+64
EQUB 76
EQUB 69

Code: Select all

.message
EQUD &42424957
EQUW &454C

Code: Select all

.message
EQUS "WIBBLE"
The rest of the code to go with this might look like:

Code: Select all

.begin
LDX #0
.loop
LDA message,X
JSR &FFEE
INX
CPX #6
BNE loop
JMP &FFE7
* More on assembler labels: When using an OPT value with the twos bit clear, e.g. 0, 1, 4 or 5, instead of throwing "no such variable" when an attempt is made to read an undefined variable (e.g. when you have a forward jump like

Code: Select all

.prev_byte
LDA &70
BNE prev_byte1
DEC &71
.prev_byte1
DEC &70
this), BASIC will "just make something up" (actually, it will insert the current value of P%; or in the case of a Bxx instruction, an offset thereto) so the code length is right. But it won't actually store that in the missing variable; if you try to read it value in BASIC, you will get "No such variable". If it later encounters a definition for that variable, next time you rerun the assembly with OPT 2, 3, 6 or 7, it will insert the correct value; otherwise it will throw "No such variable" (or, confusingly, "Byte" if a one-byte number was expected). Odd OPT values display a listing of the lassembled code. Values with the fours bit set store the assembled code at O%, but define labels expecting it to be reloaded at P% (so you can assemble code to run from space taken up by the BASIC source code, or -- assuming the code doesn't self-modify [-X -- from sideways ROM).

User avatar
0xC0DE
Posts: 451
Joined: Tue Mar 19, 2019 7:52 pm
Location: The Netherlands
Contact:

Re: Equb equw equd etc

Post by 0xC0DE » Thu Nov 14, 2019 7:33 pm

BeebAsm definitely supports a list of comma-separated values after equb/equw/equd/equs. The values can be decimal, hexadecimal, binary, characters and strings. Edit: and variables, labels, macros, etc.
0xC0DE
:idea: Follow me on Twitter :idea: Visit my YouTube channel featuring my demos for Acorn Electron and BBC Micro

User avatar
Richard Russell
Posts: 1037
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Equb equw equd etc

Post by Richard Russell » Fri Nov 15, 2019 9:31 am

cmorley wrote:
Thu Nov 14, 2019 5:32 pm
I don't think BASIC 2 supports comma. If you try it then BASIC will only emit one byte. beebasm I think does support commas. I don't know about BASIC 3 and BASIC 4. JGH or someone else will know.
I don't know about those BASICs either, but none of mine (Z80, 8086, x86-32, x86-64, ARM) supports a comma-delimited list of parameters for EQUB (or DB, whichever is appropriate for the CPU). The only way to emit multiple values in one statement is to cheat by using a string:

Code: Select all

000000000DE11D00 01 02 03 04 05                  equb CHR$1 + CHR$2 + CHR$3 + CHR$4 + CHR$5
000000000DE11D05
(EQUB might need to be changed to EQUS in some BASICs).

User avatar
BadProgram
Posts: 33
Joined: Thu Mar 22, 2018 12:56 pm
Contact:

Re: Equb equw equd etc

Post by BadProgram » Fri Nov 15, 2019 8:35 pm

Julie_m thankyou for the lesson on OPT, while i do multi pass assemble in a FOR loop i have experienced "out of range", i will have to read the AUG again tonight, your information will be of aid thanks.

Richard Russell , that is interesting for you to say EQUB CHR$1+CHR$2 might be a cheat.

Learning the 6502 instruction set then attempting to put it to use on the BBC micro, i was struck by the integration of basic (variables and the likes),
but then i wonder, do other assembler allow for things like variables ? or is that a BBC assembler luxury i should avoid ?.

sta giant_variablename+another_giant_variablename

PS i was very happy using EQUB per byte and its nice to know that is common procedure in other assemblers.

julie_m
Posts: 103
Joined: Wed Jul 24, 2019 8:53 pm
Location: Derby, UK
Contact:

Re: Equb equw equd etc

Post by julie_m » Fri Nov 15, 2019 10:19 pm

Usually you would do the first pass with OPT 0 (or OPT 4 if you are planning to use a different reload address) to suppress errors and listing, and the second pass (not forgetting to reinitialise O% and P%) with OPT 3 (or OPT 7) to report errors and produce a listing (or OPT 2 or OPT 6 if you don't).

"Out of range" errors on the second pass are associated with conditional branches. On the 6502, these all use an 8-bit signed relative address. What that means in practical terms is the destination must be between 128 bytes before and 127 bytes after the address containing the offset (i.e., the second byte of the branch instruction. We need the program counter still to be pointing to this value, so we can read it from memory.) If the condition being tested is satisfied, then on the following clock cycle we add the second byte of the instruction to the low byte of the program counter. If this does not create a carry, then we have the correct address already and we can just carry on executing from that location; if it does create a carry, we have to take one more cycle to increase the high byte of the program counter before we can jump to the correct location. If the condition is not satisfied, then the program counter is increased by 1 as usual and we proceed with the next instruction. This takes a cycle less than if we branched, since we already knew the result of the test immediately (well ..... almost immediately ..... after the signals have passed through a few logic gates :) ) after reading the first byte; but as we don't actually need to do any processing with the second byte, we can just skip straight to the next location (which holds the first byte of the following instruction) after reading it. If you are doing something (such as disk I/O or display generation) where timing is absolutely critical, you may need to think carefully about where you are going to locate your program to avoid the extra cycle penalty incurred by a branch instruction crossing a page (256-byte) boundary, but you probably would never notice it otherwise.

Usually, conditional branches are either back to the top of a loop, or forward past a bit of code you do not wish to execute; in both cases a short distance, so the short form is useful for the memory (and time) it saves compared to having a three-byte instruction with a full absolute address. On the rare occasions when you ever do need to branch conditionally further than the relative addressing mode will allow, just use the opposite test to skip past a JMP instruction.

Another cause of "Out of Range" errors is forgetting to re-initialise P% (and maybe O%, if you are using OPT 4-7) for the second assembly pass. O% and P% are updated after each instruction as the assembly progresses; so if you initialised P% outside the loop, then on the second pass the program will be assembled after the botched copy generated during the first pass (with incorrect destinations on all the forward jumps, because the variables referred to had not yet been given values). If you were crazy enough to try running your code in this broken state, it most probably would hit one of these false addresses and get stuck in an infinite loop.

The tests are:
  • BEQ: Branch if EQual to zero (Z=1) This looks at the last value read, written or calculated, and feeds all the bits into a giant NOR gate. Z=1 only if all bits are zero.
  • BNE: Branch if Not Equal to zero (Z=0)
  • BCS: Branch if Carry Set (C=1)
  • BCC: Branch if Carry Clear (C=0)
  • BMI: Branch if MInus (N=1)This simply looks at bit 7 of the last value read, written or calculated.
  • BPL: Branch if Plus (N=0)
  • BVS: Branch if oVerflow Set (V=1) This means an addition between two large positive numbers or subtraction between two large negative numbers caused a false change of sign; the actual value is between 128 and 255 (although it looks negative) or -129 and -256 (despite looking positive).
  • BVC: Branch if oVerflow Clear (V=0)
There is no unconditional branch; but you can simulate it by forcing a flag, if you don't already know the state of one (perhaps the last value you touched was a constant), and then using a conditional branch. This may take up to 3 bytes if you need a forcing instruction, which is the same as a JMP and maybe a cycle slower; but it means that your code will always work, no matter where you place it in memory. There also isn't a relative version of JSR, that will store an address on the stack to return to later with an RTS.

Example code showing flag states:

Code: Select all

LDA #0 \ now Z=1
\ BEQ here would branch
SEC
\ BCS here would branch
SBC #1 \ now A=&FF,  C=0 (borrow occurred), Z=0 (not zero), N=1 (negative), V=0 (sign correct)
\ BPL here would NOT branch
LSR A \ now A=&7F, C=1, N=0
\ BCC here would NOT branch
STA &70
LDX #1
\ BNE here would branch
DEX \ now X=0
\BEQ here would branch

User avatar
BadProgram
Posts: 33
Joined: Thu Mar 22, 2018 12:56 pm
Contact:

Re: Equb equw equd etc

Post by BadProgram » Mon Nov 18, 2019 6:19 pm

Thanks again julie_m, my next questions would have been exactly as you have explained. While i understood how the branches worked i did not know the limits to branching i thought they would be quite large actually, but i was wrong.

And the idea of branching forward past instructions ! is one i had not discovered or learnt yet only having tiny programs, i understand what you have said and now branch limits are not a problem when a jmp can be made easily by jumping over itself when not needed. That is fantastic.

that answers all the mad theories i was dreaming up about branching. Multipass OPT i understand better now too, any problems i had would have been from foolishly trying to use a branch like a jsr/jmp.

The detail about the extra cycle is useful too thanks, my instruction chart shows the cycles however the assumptions i have made would not have caught the detail you pointed out.

Post Reply