########################################################################################### # # main assembler : A small assembler for the CP3F Olympia werke CPU # # BKoeglmeier Eching/Muc 17. Nov. 2021 # ########################################################################################### #////////////////////////////////////////////////////////////////////// #//// //// #//// Copyright (C) 2021 Bernhard Koeglmeier //// #//// //// #//// This source file may be used and distributed without //// #//// restriction provided that this copyright statement is not //// #//// removed from the file and that any derivative work contains //// #//// the original copyright notice and the associated disclaimer. //// #//// //// #//// This source file is free software; you can redistribute it //// #//// and/or modify it under the terms of the GNU Lesser General //// #//// Public License as published by the Free Software Foundation; //// #//// either version 2.1 of the License, or (at your option) any //// #//// later version. //// #//// //// #//// This source is distributed in the hope that it will be //// #//// useful, but WITHOUT ANY WARRANTY; without even the implied //// #//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// #//// PURPOSE. See the GNU Lesser General Public License for more //// #//// details. //// #//// //// #//// You should have received a copy of the GNU Lesser General //// #//// Public License along with this source; if not, download it //// #//// https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt //// #//// //// #////////////////////////////////////////////////////////////////////// # # Command line usage: python3 cp3f_assembler.py assemblersourcefilename without extension # # Supports labels and opcodes yet: # label: Start with Alphanum character (a..z/A..Z) and has to end with: # opcodes: e.g.: lak 3 ; load a nibble number to A (0000->7..4,number ->3..0 # out 0 ; output akku to port (7..0) # als ; arithmetic shift left in accu # eol 55 ; exor 8-bit constant with accu # lar 6 ; load register 6 to accu # jmp xyz ;jump to 11 Bit address # import sys if len(sys.argv) == 1: assemblerfilename = 'cp3f_test' else: assemblerfilename = str(sys.argv[1]) assemblersourcedatei = assemblerfilename + '.asm' assemblerlistdatei = assemblerfilename + '.lst' assemblerhexdatei = assemblerfilename + '.mem' # verilog ise ROM format print('Filename: ' + str(assemblersourcedatei)) programm_line_counter = 0 source_line_counter = 0 error_dict = { -2 : 'Konstant is more then 4 Bit', -3 : 'Out address is not in range 0..7', -4 : 'Jump target more then 11 Bit', -5 : 'Constant is not 8 Bit', -6 : 'Register Adress > 14 is not allowed' } # contains after the first assembly run all labels (strings ending with a : eg. jmptable: test: ) label_dict = { } hex_code_list = [] # contains all the hex values after assembly run for program ROM usage #----------------------------------------------------------------------------------------- # the address mode handler functions def constant8_fn(opcode_hex,liste): constant8 = int(liste[1],16) if constant8 < 256: opcode_list = [2,opcode_hex,constant8] else: print('Error Constant') opcode_list = [-5,opcode_hex] return (opcode_list) def reg4addr_fn(opcode_hex,liste): reg4addr = int(liste[1],16) if reg4addr < 15: opcode_list = [1,opcode_hex | reg4addr] else: print('Error Register Address') opcode_list = [-6,opcode_hex] return (opcode_list) def ioaddr3_fn(opcode_hex,liste): outaddr3 = int(liste[1],16) if outaddr3 < 8: opcode_list = [1,opcode_hex | outaddr3] else: print('Error in IO Address') opcode_list = [-3,opcode_hex] return (opcode_list) def jmpaddr11_fn(opcode_hex,liste): # TBD seqment grenzen prüfen jmptarget11 = liste[1] try: # label or a hex number? label has to start with alpha num (a..z,A..Z) jmptarget11_hex = int(jmptarget11,16) except: jmptarget11_hex = label_dict[jmptarget11] if jmptarget11_hex < 2048: opcode_list = [2,opcode_hex | (0x07 & (jmptarget11_hex>>8)),0xFF & jmptarget11_hex] else: print('Error Jump target too far away') opcode_list = [-4,opcode_hex] return (opcode_list) def konst4_fn(opcode_hex,liste): konstant4 = int(liste[1],16) if konstant4 < 16: opcode_list = [1,opcode_hex | konstant4] else: print('Error') opcode_list = [-2,opcode_hex] return (opcode_list) def implied_fn(opcode_hex,liste): opcode_list = [1,opcode_hex] return (opcode_list) #----------------------------------------------------------------------------------------- opcode_dict = { # opcodes ,no of args, arg type # opcodetablelle.ods 'RET' : [0b00000000,1,implied_fn], 'SAT' : [0b00000001,1,implied_fn], 'SIX' : [0b00000010,1,implied_fn], 'SST' : [0b00000011,1,implied_fn], 'LAL' : [0b00000100,1,constant8_fn], 'ANL' : [0b00000101,1,constant8_fn], 'LIX' : [0b00000110,1,implied_fn], 'LIY' : [0b00000111,1,implied_fn], 'LAV' : [0b00001000,1,implied_fn], ## -> akku 'LAW' : [0b00001001,1,implied_fn], 'LAX' : [0b00001010,1,implied_fn], 'LAY' : [0b00001011,1,implied_fn], 'EOL' : [0b00001100,1,constant8_fn], 'ORL' : [0b00001101,1,constant8_fn], 'ADL' : [0b00001110,1,constant8_fn], 'CML' : [0b00001111,1,constant8_fn], 'SZX' : [0b00010010,1,implied_fn], 'SZY' : [0b00010011,1,implied_fn], 'SQX' : [0b00010110,1,implied_fn], 'SQY' : [0b00010111,1,implied_fn], 'SAV' : [0b00011000,1,implied_fn], ## akku -> 'SAW' : [0b00011001,1,implied_fn], 'SAX' : [0b00011010,1,implied_fn], 'SAY' : [0b00011011,1,implied_fn], 'ALS' : [0b00011100,1,implied_fn], 'ARS' : [0b00011101,1,implied_fn], 'ALF' : [0b00011110,1,implied_fn], 'ARF' : [0b00011111,1,implied_fn], 'INP' : [0b00100000,1,ioaddr3_fn], ## IO 'LSS' : [0b00101000,1,ioaddr3_fn], 'OUT' : [0b00110000,1,ioaddr3_fn], 'LTS' : [0b00111000,1,ioaddr3_fn], 'JMP' : [0b01000000,1,jmpaddr11_fn], ## Jumps 'JAZ' : [0b01001000,1,jmpaddr11_fn], 'JAN' : [0b01010000,1,jmpaddr11_fn], 'JAP' : [0b01011000,1,jmpaddr11_fn], 'JSD' : [0b01100000,1,jmpaddr11_fn], 'JCN' : [0b01101000,1,jmpaddr11_fn], 'JCZ' : [0b01110000,1,jmpaddr11_fn], 'JSR' : [0b01111000,1,jmpaddr11_fn], 'LAR' : [0b10000000,1,reg4addr_fn], 'SAR' : [0b10010000,1,reg4addr_fn], 'ADR' : [0b10100000,1,reg4addr_fn], 'ANR' : [0b10110000,1,reg4addr_fn], 'EOR' : [0b11000000,1,reg4addr_fn], 'DER' : [0b11010000,1,reg4addr_fn], 'DAR' : [0b11100000,1,reg4addr_fn], 'LAK' : [0b11110000,1,konst4_fn] } #----------------------------------------------------------------------------------------- # other subroutines def listheader(fa,sourcefilename): fa.writelines("cpf3_assempler 0.0\n\n") fa.writelines("source File: " + assemblerlistdatei + "\n\n") fa.writelines("Line Loc Value Source\n") fa.writelines("------ ----- ------ -------\n") def write_target_line(fa,opcode_list,sourceline): fa.writelines(str("%05d" % source_line_counter) + ' ') fa.writelines(str("%03X" % programm_line_counter) + ' ') if len(opcode_list) > 0: opcode_len = opcode_list[0] opcode_hex = opcode_list[1] fa.writelines(hex(opcode_hex)) if opcode_len == 2: fa.writelines(hex(opcode_list[2])) else: fa.writelines(" ") if opcode_list[0] < 0: fa.writelines('\n ERROR ' + error_dict[opcode_list[0]]) else: fa.writelines(" ") fa.writelines(' ' + str(sourceline)) def mark_label(fa,labelname,current_programm_counter): label = labelname.partition(':') label_dict[label[0]] = current_programm_counter def handle_label(fa,labelname): return(labelname) def handle_address(opcode_hex,address_type,liste): return address_type(opcode_hex,liste) # calls the address-mode handler functions (*_fn) def opcode_handle(fa,liste,sourceline): #for wort in liste: # for debug only # print(wort+'|',end='') opcode = liste[0] opcode_dings = opcode_dict[opcode.upper()] opcode_hex = opcode_dings[0] argument_count = opcode_dings[1] if argument_count >= 0: address_type = opcode_dings[2] opcode_hexlist = handle_address(opcode_hex,address_type,liste) hex_code_list.append(opcode_hexlist[1]) if opcode_hexlist[0] == 2: hex_code_list.append(opcode_hexlist[2]) else: opcode_hexlist = [0,0] write_target_line(fa,opcode_hexlist,sourceline) #----------------------------------------------------------------------------------------- # main try: f = open(assemblersourcedatei,'r') fa = open(assemblerlistdatei,'w') #---------------------------------------------------------------------------- # first pass: a look loop for labels line = f.readline() while line !='': foundcomment = line.find(';') if foundcomment != 0: if foundcomment > 0: [line,comment,restline] = line.partition(';') liste = line.split() if len(liste) != 0: if liste[0].find(':') >= 0: mark_label(fa,liste[0],programm_line_counter) liste.pop(0) if len(liste) != 0: programm_line_counter = programm_line_counter + 1 line = f.readline() #--------------------------------------------------------------------------- # second pass: now do the assembler work programm_line_counter = 0 listheader(fa,assemblersourcedatei) f.seek(0) sourceline = f.readline() line = sourceline source_line_counter=0 while line !='': foundcomment = line.find(';') if foundcomment == 0: write_target_line(fa,[],sourceline) else: if foundcomment > 0: [line,comment,restline] = line.partition(';') liste = line.split() if len(liste) != 0: if liste[0].find(':') >= 0: labelname = '#' labelname = handle_label(fa,liste[0]) liste.pop(0) if len(liste) != 0: opcode_handle(fa,liste,sourceline) programm_line_counter = programm_line_counter + 1 sourceline = f.readline() line = sourceline source_line_counter = source_line_counter + 1; f.close() fa.close() # now write the hex values in a file ----------------------------------------------------------------- fw = open(assemblerhexdatei,'w') for hexval in hex_code_list: fw.writelines(str("%02X" % hexval)) # verilog hex format for ise verilog readmemh to ROM reader fw.write('\n') fw.close() print("Assembler done") except IOError: print("Cannot find file: " + assemblersourcedatei) #### the end --------------------------------------------------------------------------------------------