Kroc / v80

A minimal Z80 assembler, running on Z80. Useful for bootstrapping bigger projects.
MIT License
25 stars 3 forks source link

SLL should assemble identically to SLA, not SL1 #14

Open gvvaughan opened 22 hours ago

gvvaughan commented 22 hours ago

from https://wiki.specnext.dev/Extended_Z80_instruction_set:

SLL (Shift Left Logical) This mnemonic has no associated opcode. There is no difference between a logical and arithmetic shift left, so both can use SLA, but some assemblers will allow SLL as an equivalent. Unfortunately, some will also assemble it as SL1. So it's probably worth just avoiding.

But looking at is2_z80.v80 I see:

.b      4 *16 | 1  "la.a"       $27 #OP_CB      ;    CB 27    | sla.a
.b      1 *16 | 4     "b"       $20 #OP_CB      ;    CB 20    | sla.b
.b      1 *16 | 4     "c"       $21 #OP_CB      ;    CB 21    | sla.c
.b      1 *16 | 4     "d"       $22 #OP_CB      ;    CB 22    | sla.d
.b      1 *16 | 4     "e"       $23 #OP_CB      ;    CB 23    | sla.e
.b      1 *16 | 4     "h"       $24 #OP_CB      ;    CB 24    | sla.h
.b      1 *16 | 4     "l"       $25 #OP_CB      ;    CB 25    | sla.l
.b      3 *16 | 3    "*hl"      $26 #OP_CB      ;    CB 26    | sla*hl
.b      2 *16 | 4     "ix"      $26 #OP_CBXO    ; DD CB nn 26 | sla*ix    $nn
.b      1 *16 | 5      "y"      $26 #OP_CBYO    ; FD CB nn 26 | sla*iy    $nn
;===============================================================================
.b      3 *16 | 2   "l.a"       $37 #OP_CB      ;    CB 37    | sll.a
.b      1 *16 | 4     "b"       $30 #OP_CB      ;    CB 30    | sll.b
.b      1 *16 | 4     "c"       $31 #OP_CB      ;    CB 31    | sll.c
.b      1 *16 | 4     "d"       $32 #OP_CB      ;    CB 32    | sll.d
.b      1 *16 | 4     "e"       $33 #OP_CB      ;    CB 33    | sll.e
.b      1 *16 | 4     "h"       $34 #OP_CB      ;    CB 34    | sll.h
.b      1 *16 | 4     "l"       $35 #OP_CB      ;    CB 35    | sll.l
.b      3 *16 | 3    "*hl"      $36 #OP_CB      ;    CB 36    | sll*hl
.b      2 *16 | 4     "ix"      $36 #OP_CBXO    ; DD CB nn 36 | sll*ix    $nn
.b      1 *16 | 5      "y"      $36 #OP_CBYO    ; FD CB nn 36 | sll*iy    $nn
;-------------------------------------------------------------------------------
.b      3 *16 | 2   "1.a"       $37 #OP_CB      ;    CB 37    | sl1.a
.b      1 *16 | 4     "b"       $30 #OP_CB      ;    CB 30    | sl1.b
.b      1 *16 | 4     "c"       $31 #OP_CB      ;    CB 31    | sl1.c
.b      1 *16 | 4     "d"       $32 #OP_CB      ;    CB 32    | sl1.d
.b      1 *16 | 4     "e"       $33 #OP_CB      ;    CB 33    | sl1.e
.b      1 *16 | 4     "h"       $34 #OP_CB      ;    CB 34    | sl1.h
.b      1 *16 | 4     "l"       $35 #OP_CB      ;    CB 35    | sl1.l
.b      3 *16 | 3    "*hl"      $36 #OP_CB      ;    CB 36    | sl1*hl
.b      2 *16 | 4     "ix"      $36 #OP_CBXO    ; DD CB nn 36 | sl1*ix    $nn
.b      1 *16 | 5      "y"      $36 #OP_CBYO    ; FD CB nn 36 | sl1*iy    $nn

Where sll instructions are treated like sl1 instead of sla.

Kroc commented 22 hours ago

That's the Spectrum Next Z80N, which is not a normal Z80. SLL is often an alias to SL1, the broken shift-left instruction; at least as WLA-DX supports it; every opcode output by WLA-DX is compared against V80 to verify correctness. See "z80.wla" in "tests" folder.

gvvaughan commented 22 hours ago

Equivalence to WLA-DX seems to be a misfeature in this case. If SLL is not an official Z80 mnemonic, then an assembler can chose to use the opcodes for SL1 (presumably incase of a 1 vs l typo?), or treat shift-left-logical as identical to shift-left-arithmetic and use the opcodes for SLA instead.

If you know all this and prefer WLA-DX equivalence over mathematical pedantry, feel free to close this without changing anything! :-)

Kroc commented 21 hours ago

Even though there is no official opcode, other assemblers and existing code already use this form, so I just went with matching convention. We could remove "SLL" entirely and require "SL1"; but spec.next are incorrect here with their assumption. The use of "1" is intentional, the instruction is faulty and inserts a 1 into the left-most bit instead of a zero.