After going through a phase of recent nostalgia, coupled with some boredom, I decided that it would be a fun idea to write a Z80 assembler in Perl. There are other reasons for this, as my father is building a telescope, the controller of which runs via a Z80 microprocessor. Plus some day I might get around to resurrecting the small Z80-based computer I built when I was 15 and this would be useful.
Anyway, I digress: The actual instruction program isn't very big, but the hash containing the opcodes is a tad large at 694 lines, there being 694 instructions in the (documented) instruction set. There is a discussion of various issues at the bottom.
#!/usr/bin/perl -w
use strict;
# Set up big hash of hashes of hashes containing
# our instructions and opcodes.
my %opcodes = ('nop' => {'null' => {'null' => '00'}},
'rlca' => {'null' => {'null' => '07'}},
'rrca' => {'null' => {'null' => '0f'}},
'rla' => {'null' => {'null' => '17'}},
'rra' => {'null' => {'null' => '1f'}},
'daa' => {'null' => {'null' => '27'}},
'cpl' => {'null' => {'null' => '2f'}},
'scf' => {'null' => {'null' => '37'}},
'ccf' => {'null' => {'null' => '3f'}},
'halt' => {'null' => {'null' => '76'}},
'exx' => {'null' => {'null' => 'd9'}},
'di' => {'null' => {'null' => 'f3'}},
'ei' => {'null' => {'null' => 'fb'}},
'neg' => {'null' => {'null' => 'ed 44'}},
'retn' => {'null' => {'null' => 'ed 45'}},
'reti' => {'null' => {'null' => 'ed 4d'}},
'rrd' => {'null' => {'null' => 'ed 67'}},
'rld' => {'null' => {'null' => 'ed 6f'}},
'ldi' => {'null' => {'null' => 'ed a0'}},
'cpi' => {'null' => {'null' => 'ed a1'}},
'ini' => {'null' => {'null' => 'ed a2'}},
'outi' => {'null' => {'null' => 'ed a3'}},
'ldd' => {'null' => {'null' => 'ed a8'}},
'cpd' => {'null' => {'null' => 'ed a9'}},
'ind' => {'null' => {'null' => 'ed aa'}},
'outd' => {'null' => {'null' => 'ed ab'}},
'ldir' => {'null' => {'null' => 'ed bo'}},
'cpir' => {'null' => {'null' => 'ed b1'}},
'inir' => {'null' => {'null' => 'ed b2'}},
'otir' => {'null' => {'null' => 'ed b3'}},
'lddr' => {'null' => {'null' => 'ed b8'}},
'cpdr' => {'null' => {'null' => 'ed b9'}},
'indr' => {'null' => {'null' => 'ed ba'}},
'otdr' => {'null' => {'null' => 'ed bb'}},
'inc' => {'bc' => {'null' => '03'},
'b' => {'null' => '04'},
'c' => {'null' => '0c'},
'de' => {'null' => '13'},
'd' => {'null' => '14'},
'e' => {'null' => '1c'},
'hl' => {'null' => '23'},
'h' => {'null' => '24'},
'l' => {'null' => '2c'},
'sp' => {'null' => '33'},
'(hl)' => {'null' => '34'},
'a' => {'null' => '3c'},
'ix' => {'null' => 'dd 23'},
'iy' => {'null' => 'fd 23'},
'ixpo' => {'null' => 'dd 34'},
'iypo' => {'null' => 'fd 34'}},
'dec' => {'b' => {'null' => '05'},
'bc' => {'null' => '0b'},
'c' => {'null' => '0d'},
'd' => {'null' => '15'},
'de' => {'null' => '1b'},
'e' => {'null' => '1d'},
'h' => {'null' => '25'},
'hl' => {'null' => '2b'},
'l' => {'null' => '2d'},
'(hl)' => {'null' => '35'},
'sp' => {'null' => '3b'},
'a' => {'null' => '3d'},
'ix' => {'null' => 'dd 2b'},
'iy' => {'null' => 'fd 2b'},
'ixpo' => {'null' => 'dd 35'},
'iypo' => {'null' => 'fd 35'}},
'pop' => {'bc' => {'null' => 'c1'},
'de' => {'null' => 'd1'},
'hl' => {'null' => 'e1'},
'af' => {'null' => 'f1'},
'ix' => {'null' => 'dd e1'},
'iy' => {'null' => 'fd e1'}},
'push' => {'bc' => {'null' => 'c5'},
'de' => {'null' => 'd5'},
'hl' => {'null' => 'e5'},
'af' => {'null' => 'f5'},
'ix' => {'null' => 'dd e5'},
'iy' => {'null' => 'fd e5'}},
'jr' => {'nz' => {'d1b' => '20'},
'z' => {'d1b' => '28'},
'nc' => {'d1b' => '30'},
'c' => {'d1b' => '38'},
'd1b' => {'null' => '18'}},
'jp' => {'nz' => {'d2b' => 'c2'},
'z' => {'d2b' => 'ca'},
'nc' => {'d2b' => 'd2'},
'c' => {'d2b' => 'da'},
'po' => {'d2b' => 'e2'},
'pe' => {'d2b' => 'ea'},
'p' => {'d2b' => 'f2'},
'm' => {'d2b' => 'fa'},
'(hl)' => {'null' => 'e9'},
'(ix)' => {'null' => 'dd e9'},
'(iy)' => {'null' => 'fd e9'},
'd2b' => {'null' => 'c3'}},
'call' => {'nz' => {'d2b' => 'c4'},
'z' => {'d2b' => 'cc'},
'nc' => {'d2b' => 'd4'},
'c' => {'d2b' => 'dc'},
'po' => {'d2b' => 'e4'},
'pe' => {'d2b' => 'ec'},
'p' => {'d2b' => 'f4'},
'm' => {'d2b' => 'fc'},
'd2b' => {'null' => 'cd'}},
'rst' => {'00' => {'null' => 'c7'},
'08' => {'null' => 'cf'},
'10' => {'null' => 'd7'},
'18' => {'null' => 'df'},
'20' => {'null' => 'e7'},
'28' => {'null' => 'ef'},
'30' => {'null' => 'f7'},
'38' => {'null' => 'ff'}},
'ret' => {'nz' => {'null' => 'c0'},
'z' => {'null' => 'c8'},
'nc' => {'null' => 'd0'},
'c' => {'null' => 'd8'},
'po' => {'null' => 'e0'},
'pe' => {'null' => 'e8'},
'p' => {'null' => 'f0'},
'm' => {'null' => 'f8'},
'null' => {'null' => 'c9'}},
'bit' => {'0' => {'b' => 'cb 40',
'c' => 'cb 41',
'd' => 'cb 42',
'e' => 'cb 43',
'h' => 'cb 44',
'l' => 'cb 45',
'(hl)' => 'cb 46',
'a' => 'cb 47',
'ixpo' => 'dd cb 46',
'iypo' => 'fd cb 46'},
'1' => {'b' => 'cb 48',
'c' => 'cb 49',
'd' => 'cb 4a',
'e' => 'cb 4b',
'h' => 'cb 4c',
'l' => 'cb 4d',
'(hl)' => 'cb 4e',
'a' => 'cb 4f',
'ixpo' => 'dd cb 4e',
'iypo' => 'fd cb 4e'},
'2' => {'b' => 'cb 50',
'c' => 'cb 51',
'd' => 'cb 52',
'e' => 'cb 53',
'h' => 'cb 54',
'l' => 'cb 55',
'(hl)' => 'cb 56',
'a' => 'cb 57',
'ixpo' => 'dd cb 56',
'iypo' => 'fd cb 56'},
'3' => {'b' => 'cb 58',
'c' => 'cb 59',
'd' => 'cb 5a',
'e' => 'cb 5b',
'h' => 'cb 5c',
'l' => 'cb 5d',
'(hl)' => 'cb 5e',
'a' => 'cb 5f',
'ixpo' => 'dd cb 5e',
'iypo' => 'fd cb 5e'},
'4' => {'b' => 'cb 60',
'c' => 'cb 61',
'd' => 'cb 62',
'e' => 'cb 63',
'h' => 'cb 64',
'l' => 'cb 65',
'(hl)' => 'cb 66',
'a' => 'cb 67',
'ixpo' => 'dd cb 66',
'iypo' => 'fd cb 66'},
'5' => {'b' => 'cb 68',
'c' => 'cb 69',
'd' => 'cb 6a',
'e' => 'cb 6b',
'h' => 'cb 6c',
'l' => 'cb 6d',
'(hl)' => 'cb 6e',
'a' => 'cb 6f',
'ixpo' => 'dd cb 6e',
'iypo' => 'fd cb 6e'},
'6' => {'b' => 'cb 70',
'c' => 'cb 71',
'd' => 'cb 72',
'e' => 'cb 73',
'h' => 'cb 74',
'l' => 'cb 75',
'(hl)' => 'cb 76',
'a' => 'cb 77',
'ixpo' => 'dd cb 76',
'iypo' => 'fd cb 76'},
'7' => {'b' => 'cb 78',
'c' => 'cb 79',
'd' => 'cb 7a',
'e' => 'cb 7b',
'h' => 'cb 7c',
'l' => 'cb 7d',
'(hl)' => 'cb 7e',
'a' => 'cb 7f',
'ixpo' => 'dd cb 7e',
'iypo' => 'fd cb 7e'}},
'set' => { '0' => {'b' => 'cb c0',
'c' => 'cb c1',
'd' => 'cb c2',
'e' => 'cb c3',
'h' => 'cb c4',
'l' => 'cb c5',
'(hl)' => 'cb c6',
'a' => 'cb c7',
'ixpo' => 'dd cb c6',
'iypo' => 'fd cb c6'},
'1' => {'b' => 'cb c8',
'c' => 'cb c9',
'd' => 'cb ca',
'e' => 'cb cb',
'h' => 'cb cc',
'l' => 'cb cd',
'(hl)' => 'cb ce',
'a' => 'cb cf',
'ixpo' => 'dd cb ce',
'iypo' => 'fd cb ce'},
'2' => {'b' => 'cb d0',
'c' => 'cb d1',
'd' => 'cb d2',
'e' => 'cb d3',
'h' => 'cb d4',
'l' => 'cb d5',
'(hl)' => 'cb d6',
'a' => 'cb d7',
'ixpo' => 'dd cb d6',
'iypo' => 'fd cb d6'},
'3' => {'b' => 'cb d8',
'c' => 'cb d9',
'd' => 'cb da',
'e' => 'cb db',
'h' => 'cb dc',
'l' => 'cb dd',
'(hl)' => 'cb de',
'a' => 'cb df',
'ixpo' => 'dd cb de',
'iypo' => 'fd cb de'},
'4' => {'b' => 'cb e0',
'c' => 'cb e1',
'd' => 'cb e2',
'e' => 'cb e3',
'h' => 'cb e4',
'l' => 'cb e5',
'(hl)' => 'cb e6',
'a' => 'cb e7',
'ixpo' => 'dd cb e6',
'iypo' => 'fd cb e6'},
'5' => {'b' => 'cb e8',
'c' => 'cb e9',
'd' => 'cb ea',
'e' => 'cb eb',
'h' => 'cb ec',
'l' => 'cb ed',
'(hl)' => 'cb ee',
'a' => 'cb ef',
'ixpo' => 'dd cb ee',
'iypo' => 'fd cb ee'},
'6' => {'b' => 'cb f0',
'c' => 'cb f1',
'd' => 'cb f2',
'e' => 'cb f3',
'h' => 'cb f4',
'l' => 'cb f5',
'(hl)' => 'cb f6',
'a' => 'cb f7',
'ixpo' => 'dd cb f6',
'iypo' => 'fd cb f6'},
'7' => {'b' => 'cb f8',
'c' => 'cb f9',
'd' => 'cb fa',
'e' => 'cb fb',
'h' => 'cb fc',
'l' => 'cb fd',
'(hl)' => 'cb fe',
'a' => 'cb ff',
'ixpo' => 'dd cb fe',
'iypo' => 'fd cb fe'}},
'res' => {'0' => {'b' => 'cb 80',
'c' => 'cb 81',
'd' => 'cb 82',
'e' => 'cb 83',
'h' => 'cb 84',
'l' => 'cb 85',
'(hl)' => 'cb 86',
'a' => 'cb 87',
'ixpo' => 'dd cb 86',
'iypo' => 'fd cb 86'},
'1' => {'b' => 'cb 88',
'c' => 'cb 89',
'd' => 'cb 8a',
'e' => 'cb 8b',
'h' => 'cb 8c',
'l' => 'cb 8d',
'(hl)' => 'cb 8e',
'a' => 'cb 8f',
'ixpo' => 'dd cb 8e',
'iypo' => 'fd cb 8e'},
'2' => {'b' => 'cb 90',
'c' => 'cb 91',
'd' => 'cb 92',
'e' => 'cb 93',
'h' => 'cb 94',
'l' => 'cb 95',
'(hl)' => 'cb 96',
'a' => 'cb 97',
'ixpo' => 'dd cb 96',
'iypo' => 'fd cb 96'},
'3' => {'b' => 'cb 98',
'c' => 'cb 99',
'd' => 'cb 9a',
'e' => 'cb 9b',
'h' => 'cb 9c',
'l' => 'cb 9d',
'(hl)' => 'cb 9e',
'a' => 'cb 9f',
'ixpo' => 'dd cb 9e',
'iypo' => 'fd cb 9e'},
'4' => {'b' => 'cb a0',
'c' => 'cb a1',
'd' => 'cb a2',
'e' => 'cb a3',
'h' => 'cb a4',
'l' => 'cb a5',
'(hl)' => 'cb a6',
'a' => 'cb a7',
'ixpo' => 'dd cb a6',
'iypo' => 'fd cb a6'},
'5' => {'b' => 'cb a8',
'c' => 'cb a9',
'd' => 'cb aa',
'e' => 'cb ab',
'h' => 'cb ac',
'l' => 'cb ad',
'(hl)' => 'cb ae',
'a' => 'cb af',
'ixpo' => 'dd cb ae',
'iypo' => 'fd cb ae'},
'6' => {'b' => 'cb b0',
'c' => 'cb b1',
'd' => 'cb b2',
'e' => 'cb b3',
'h' => 'cb b4',
'l' => 'cb b5',
'(hl)' => 'cb b6',
'a' => 'cb b7',
'ixpo' => 'dd cb b6',
'iypo' => 'fd cb b6'},
'7' => {'b' => 'cb b8',
'c' => 'cb b9',
'd' => 'cb ba',
'e' => 'cb bb',
'h' => 'cb bc',
'l' => 'cb bd',
'(hl)' => 'cb be',
'a' => 'cb bf',
'ixpo' => 'dd cb be',
'iypo' => 'fd cb be'}},
'and' => {'b' => {'null' => 'a0'},
'c' => {'null' => 'a1'},
'd' => {'null' => 'a2'},
'e' => {'null' => 'a3'},
'h' => {'null' => 'a4'},
'l' => {'null' => 'a5'},
'(hl)' => {'null' => 'a6'},
'a' => {'null' => 'a7'},
'd1b' => {'null' => 'e6'},
'ixpo' => {'null' => 'dd a6'},
'iypo' => {'null' => 'fd a6'}},
'xor' => {'b' => {'null' => 'a8'},
'c' => {'null' => 'a9'},
'd' => {'null' => 'aa'},
'e' => {'null' => 'ab'},
'h' => {'null' => 'ac'},
'l' => {'null' => 'ad'},
'(hl)' => {'null' => 'ae'},
'a' => {'null' => 'af'},
'd1b' => {'null' => 'ee'},
'ixpo' => {'null' => 'dd ae'},
'iypo' => {'null' => 'fd ae'}},
'or' => {'b' => {'null' => 'b0'},
'c' => {'null' => 'b1'},
'd' => {'null' => 'b2'},
'e' => {'null' => 'b3'},
'h' => {'null' => 'b4'},
'l' => {'null' => 'b5'},
'(hl)' => {'null' => 'b6'},
'a' => {'null' => 'b7'},
'd1b' => {'null' => 'f6'},
'ixpo' => {'null' => 'dd b6'},
'iypo' => {'null' => 'fd b6'}},
'cp' => {'b' => {'null' => 'b8'},
'c' => {'null' => 'b9'},
'd' => {'null' => 'ba'},
'e' => {'null' => 'bb'},
'h' => {'null' => 'bc'},
'l' => {'null' => 'bd'},
'(hl)' => {'null' => 'be'},
'a' => {'null' => 'bf'},
'd1b' => {'null' => 'fe'},
'ixpo' => {'null' => 'dd be'},
'iypo' => {'null' => 'fd be'}},
'add' => {'a' => {'b' => '80',
'c' => '81',
'd' => '82',
'e' => '83',
'h' => '84',
'l' => '85',
'(hl)' => '86',
'a' => '87',
'd1b' => 'c6',
'ixpo' => 'dd 86',
'iypo' => 'fd 86'},
'ix' => {'bc' => 'dd 09',
'de' => 'dd 19',
'ix' => 'dd 29',
'sp' => 'dd 39'},
'iy' => {'bc' => 'fd 09',
'de' => 'fd 19',
'iy' => 'fd 29',
'sp' => 'fd 39'},
'hl' => {'bc' => '09',
'de' => '19',
'hl' => '29',
'sp' => '39'}},
'adc' => {'a' => {'b' => '88',
'c' => '89',
'd' => '8a',
'e' => '8b',
'h' => '8c',
'l' => '8d',
'(hl)' => '8e',
'a' => '8f',
'd1b' => 'ce',
'ixpo' => 'dd 8e',
'iypo' => 'fd 8e'},
'hl' => {'bc' => 'ed 4a',
'de' => 'ed 5a',
'hl' => 'ed 6a',
'sp' => 'ed 7a'}},
'sub' => {'b' => {'null' => '90'},
'c' => {'null' => '91'},
'd' => {'null' => '92'},
'e' => {'null' => '93'},
'h' => {'null' => '94'},
'l' => {'null' => '95'},
'(hl)'=> {'null' => '96'},
'a' => {'null' => '97'},
'd1b' => {'null' => 'd6'},
'ixpo'=> {'null' => 'dd 96'},
'iypo'=> {'null' => 'fd 96'}},
'sbc' => {'b' => {'null' => '98'},
'c' => {'null' => '99'},
'd' => {'null' => '9a'},
'e' => {'null' => '9b'},
'h' => {'null' => '9c'},
'l' => {'null' => '9d'},
'(hl)'=> {'null' => '9e'},
'a' => {'null' => '9f',
'd1b' => 'de'},
'd1b' => {'null' => 'de'},
'ixpo'=> {'null' => 'dd 9e'},
'iypo'=> {'null' => 'fd 9e'},
'hl' => {'bc' => 'ed 42',
'de' => 'ed 52',
'hl' => 'ed 62',
'sp' => 'ed 72'}},
'ex' => {'af' => {'af\'' => '08'},
'(sp)'=> {'hl' => 'e3',
'ix' => 'dd e3',
'iy' => 'fd e3'},
'de' => {'hl' => 'eb'}},
'im' => {'0' => {'null' => 'ed 46'},
'1' => {'null' => 'ed 56'},
'2' => {'null' => 'ed 5e'}},
'rlc' => {'b' => {'null' => 'cb 00'},
'c' => {'null' => 'cb 01'},
'd' => {'null' => 'cb 02'},
'e' => {'null' => 'cb 03'},
'h' => {'null' => 'cb 04'},
'l' => {'null' => 'cb 05'},
'(hl)'=> {'null' => 'cb 06'},
'a' => {'null' => 'cb 07'},
'ixpo'=> {'null' => 'dd cb 06'},
'iypo'=> {'null' => 'fd cb 06'}},
'rrc' => {'b' => {'null' => 'cb 08'},
'c' => {'null' => 'cb 09'},
'd' => {'null' => 'cb 0a'},
'e' => {'null' => 'cb 0b'},
'h' => {'null' => 'cb 0c'},
'l' => {'null' => 'cb 0d'},
'(hl)'=> {'null' => 'cb 0e'},
'a' => {'null' => 'cb 0f'},
'ixpo'=> {'null' => 'dd cb 0e'},
'iypo'=> {'null' => 'fd cb 0e'}},
'rl' => {'b' => {'null' => 'cb 10'},
'c' => {'null' => 'cb 11'},
'd' => {'null' => 'cb 12'},
'e' => {'null' => 'cb 13'},
'h' => {'null' => 'cb 14'},
'l' => {'null' => 'cb 15'},
'(hl)'=> {'null' => 'cb 16'},
'a' => {'null' => 'cb 17'},
'ixpo'=> {'null' => 'dd cb 16'},
'iypo'=> {'null' => 'fd cb 16'}},
'rr' => {'b' => {'null' => 'cb 18'},
'c' => {'null' => 'cb 19'},
'd' => {'null' => 'cb 1a'},
'e' => {'null' => 'cb 1b'},
'h' => {'null' => 'cb 1c'},
'l' => {'null' => 'cb 1d'},
'(hl)'=> {'null' => 'cb 1e'},
'a' => {'null' => 'cb 1f'},
'ixpo'=> {'null' => 'dd cb 1e'},
'iypo'=> {'null' => 'fd cb 1e'}},
'sla' => {'b' => {'null' => 'cb 20'},
'c' => {'null' => 'cb 21'},
'd' => {'null' => 'cb 22'},
'e' => {'null' => 'cb 23'},
'h' => {'null' => 'cb 24'},
'l' => {'null' => 'cb 25'},
'(hl)'=> {'null' => 'cb 26'},
'a' => {'null' => 'cb 27'},
'ixpo'=> {'null' => 'dd cb 26'},
'iypo'=> {'null' => 'fd cb 26'}},
'sra' => {'b' => {'null' => 'cb 28'},
'c' => {'null' => 'cb 29'},
'd' => {'null' => 'cb 2a'},
'e' => {'null' => 'cb 2b'},
'h' => {'null' => 'cb 2c'},
'l' => {'null' => 'cb 2d'},
'(hl)' => {'null' => 'cb 2e'},
'a' => {'null' => 'cb 2f'},
'ixpo' => {'null' => 'dd cb 2e'},
'iypo' => {'null' => 'fd cb 2e'}},
'srl' => {'b' => {'null' => 'cb 38'},
'c' => {'null' => 'cb 39'},
'd' => {'null' => 'cb 3a'},
'e' => {'null' => 'cb 3b'},
'h' => {'null' => 'cb 3c'},
'l' => {'null' => 'cb 3d'},
'(hl)' => {'null' => 'cb 3e'},
'a' => {'null' => 'cb 3f'},
'ixpo' => {'null' => 'dd cb 3e'},
'iypo' => {'null' => 'fd cb 3e'}},
'in' => {'b' => {'(c)' => 'ed 40'},
'c' => {'(c)' => 'ed 48'},
'd' => {'(c)' => 'ed 50'},
'e' => {'(c)' => 'ed 58'},
'h' => {'(c)' => 'ed 60'},
'l' => {'(c)' => 'ed 68'},
'a' => {'(c)' => 'ed 78',
'ia1b' => 'db'}},
'out' => {'(c)' => {'b' => 'ed 41',
'c' => 'ed 49',
'd' => 'ed 51',
'e' => 'ed 59',
'h' => 'ed 61',
'l' => 'ed 69',
'a' => 'ed 79'},
'ia1b' => {'a' => 'd3'}},
'ld' => {'(bc)' => {'a' => '02'},
'a' => {'(bc)' => '0a',
'(de)' => '1a',
'b' => '78',
'c' => '79',
'd' => '7a',
'e' => '7b',
'h' => '7c',
'l' => '7d',
'(hl)' => '7e',
'a' => '7f',
'i' => 'ed 57',
'r' => 'ed 5f',
'd1b' => '3e',
'ia2b' => '3a',
'ixpo' => 'dd 7e',
'iypo' => 'fd 7e'},
'(de)' => {'a' => '12'},
'b' => {'b' => '40',
'c' => '41',
'd' => '42',
'e' => '43',
'h' => '44',
'l' => '45',
'(hl)' => '46',
'a' => '47',
'd1b' => '06',
'ixpo' => 'dd 46',
'iypo' => 'fd 46'},
'c' => {'b' => '48',
'c' => '49',
'd' => '4a',
'e' => '4b',
'h' => '4c',
'l' => '4d',
'(hl)' => '4e',
'a' => '4f',
'd1b' => '0e',
'ixpo' => 'dd 4e',
'iypo' => 'fd 4e'},
'd' => {'b' => '50',
'c' => '51',
'd' => '52',
'e' => '53',
'h' => '54',
'l' => '55',
'(hl)' => '56',
'a' => '57',
'd1b' => '16',
'ixpo' => 'dd 56',
'iypo' => 'fd 56'},
'e' => {'b' => '58',
'c' => '59',
'd' => '5a',
'e' => '5b',
'h' => '5c',
'l' => '5d',
'(hl)' => '5e',
'a' => '5f',
'd1b' => '1e',
'ixpo' => 'dd 5e',
'iypo' => 'fd 5e'},
'h' => {'b' => '60',
'c' => '61',
'd' => '62',
'e' => '63',
'h' => '64',
'l' => '65',
'(hl)' => '66',
'a' => '67',
'd1b' => '26',
'ixpo' => 'dd 66',
'iypo' => 'fd 66'},
'l' => {'b' => '68',
'c' => '69',
'd' => '6a',
'e' => '6b',
'h' => '6c',
'l' => '6d',
'(hl)' => '6e',
'a' => '6f',
'd1b' => '2e',
'ixpo' => 'dd 6e',
'iypo' => 'fd 6e'},
'(hl)' => {'b' => '70',
'c' => '71',
'd' => '72',
'e' => '73',
'h' => '74',
'l' => '75',
'a' => '77',
'd1b' => '36'},
'sp' => {'hl' => 'f9',
'ix' => 'dd f9',
'iy' => 'fd f9',
'd2b' => '31',
'ia2b' => 'ed 7b'},
'r' => {'a' => 'ed 4f'},
'i' => {'a' => 'ed 47'},
'bc' => {'d2b' => '01',
'ia2b' => 'ed 4b'},
'de' => {'d2b' => '11',
'ia2b' => 'ed 5b'},
'hl' => {'d2b' => '21',
'ia2b' => '2a'},
'ix' => {'d2b' => 'dd 21',
'ia2b' => 'dd 2a'},
'iy' => {'d2b' => 'fd 21',
'ia2b' => 'fd 2a'},
'ia2b' => {'hl' => '22',
'a' => '32',
'ix' => 'dd 22',
'iy' => 'fd 22',
'bc' => 'ed 43',
'de' => 'ed 53',
'sp' => 'ed 77'},
'ixpo' => {'b' => 'dd 70',
'c' => 'dd 71',
'd' => 'dd 72',
'e' => 'dd 73',
'h' => 'dd 74',
'l' => 'dd 75',
'a' => 'dd 77'},
'iypo' => {'b' => 'fd 70',
'c' => 'fd 71',
'd' => 'fd 72',
'e' => 'fd 73',
'h' => 'fd 74',
'l' => 'fd 75',
'a' => 'fd 77'}},
'djnz' => {'d1b' => {'null' => '10'}});
# Main loop to open files and read/write them.
open (SOURCE, "< z80code.s") or die "Couldn't open source file.";
open (DEST, "> z80code.o") or die "Couldn't open destination file.";
while (<SOURCE>)
{
print DEST encode($_)."\n";
}
close DEST;
close SOURCE;
sub encode{
# Tokenise the line and return if empty.
my $line = $_[0];
chomp ($line);
if (!$line && defined($line))
{
return ("");
}
$line = lc ($line);
my $duplicate = $line;
$line =~ tr/,/ /;
my @tokens = split /\s/, $line;
@tokens = grep $_ ne '', @tokens;
@tokens = grep !($_ =~ m/ /), @tokens;
# Process 2nd and 3rd tokens, replacing with their datatype and
# storing the data if appropriate. Otherwise leave alone.
my $data;
foreach $_(@tokens[1..2])
{
unless (defined($_))
{
$_ = 'null';
next;
}
if ($_ =~ m/^[0-9a-f]{4}h$/)
{
$data = $_;
$data =~ tr/h//d;
$_ = 'd2b'; # Data or address, 2 bytes long
}
elsif ($_ =~ m/^[0-9a-f]{2}h$/)
{
$data = $_;
$data =~ tr/h//d;
$_ = 'd1b'; # Data or offset, 1 byte long
}
elsif ($_ =~ m/^\([0-9a-f]{4}h\)$/)
{
$data = $_;
$data =~ tr/\(\)h//d;
$_= 'ia2b'; # Indirect address, 2 bytes long
}
elsif ($_ =~ m/^\([0-9a-f]{2}h\)$/)
{
$data = $_;
$data =~ tr/h\(\)//d;
$_ = 'ia1b'; # Indirect address, 1 bytes long
}
elsif ($_ =~ m/^\(iy\+[0-9a-f]{2}h\)$/)
{
$data = $_;
$data =~ tr/\(\)iy\+h//d;
$_ = 'iypo'; # Index iy plus offset
}
elsif ($_ =~ m/^\(ix\+[0-9a-f]{2}h\)$/)
{
$data = $_;
$data =~ tr/\(\)ix\+h//d;
$_ = 'ixpo'; # Index ix plus offset
}
}
# Get the correct opcode from the array or return error msg.
my $answer = $opcodes{$tokens[0]}{$tokens[1]}{$tokens[2]};
unless ($answer)
{
return ("Instruction not found = \"$duplicate\"");
}
else
{
# Get data and return as a hex byte or pair of hex bytes.
$answer .= ' ';
if ($data && $data =~ m/^[0-9a-f]{4}$/)
{
$data = substr($data, 0, 2).' '.substr($data, 2, 2);
}
$answer .= $data ? $data : '';
return ($answer);
}
}
Notes - The assembler takes Z80 assembly language instructions and turns them into the relevant opcodes, expressed as hex bytes. The instructions should be written in the standard form - eg. ld a,(hl) - with the following caveats...
- All data or address hex digits in code - eg. ld a,33h - should be postfixed with the letter h. The program currently does not accept addresses or data in any other base. At some point, I'll get round to writing it so that it will accept it in decimal, binary and as a quoted ASCII character also.
- rst commands are the exception to the above - eg. rst 08, rst 10 etc...
- I haven't added the ability to use comments yet and there is no register or memory tracing functionality.
Comments - I've actually written two versions of this program: The first splits the hash into many smaller subsections and uses (even more!) if() {} constructions, for which reason it is far more messy but it runs about an order of magnitude faster (160 lines/s versus 16 lines/s.) The reason I prefer the version given above is that it is much 'cleaner' in terms of understanding (and hence functionality is more easily added) and I wrote and tested them both on a P120 laptop, which is hardly a high-end machine, so this disadvantage will be offset by a faster machine.
I hope that somebody finds it useful, or at least diverting. As I progress further with little bits which need doing, I'll update it.
Update - rejigged and tightened the code slightly to use an array-slice rather than simply repeating two very similar sections of code.
Update^2 - Thanks to Tachyon for goading me into altering the scope of %opcodes so that the code runs about two orders of magnitude faster. I had to user vars qw(%opcodes); because I'm forced to code in Perl 5.005.03
Update^3 - As Tachyon further points out, I don't actually need the use vars qw() because I've already declared %opcodes. I really need to go back and read up on lexical scoping ;-)
Elgon
"What this book tells me is that goose-stepping morons, such as yourself, should read books instead of burning them."
- Dr. Jones Snr, Indiana Jones and the Last Crusade