# -*- mode: python; coding: utf-8; -*-

#  Codezero -- a microkernel for embedded systems.
#
#  Copyright © 2009  B Labs Ltd
#
#  This program is free software: you can redistribute it and/or modify it under the terms of the GNU
#  General Public License as published by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program 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 General Public
#  License for more details.
#
#  You should have received a copy of the GNU General Public License along with this program.  If not, see
#  <http://www.gnu.org/licenses/>.
#
#  Author: Russel Winder

import os.path
import subprocess

Import('environment', 'images')

def ksymToLds(target, source, env):
    symbols = ['break_virtual']
    with open(target[0].path, 'w') as asmFile:
        asmFile.write('''
/*
 * %s autogenerated from %s.
 *
 * This file is included by the loader sources so that any
 * kernel symbol address can be known in advance and stopped
 * at by debuggers before virtual memory is enabled.
 */
''' % (target[0].name, source[0].name))
        for symbol in symbols:
            process = subprocess.Popen('arm-none-eabi-objdump -d ' + source[0].path + ' | grep "<' + symbol + '>"', shell=True, stdout=subprocess.PIPE)
            assert process.wait() == 0
            address, name = process.stdout.read().split()
            assert '<' + symbol + '>:' == name
            asmFile.write( '''
.section .text
.align 4
.global %s
.type %s, function
.equ %s, %s
''' % (symbol, symbol, symbol, (hex(int(address, 16) - 0xf0000000))))

def createKernelSFile(target, source, env):
    with open(target[0].path, 'w') as asmFile:
        asmFile.write('''
/*
 * This file is autogenerated.
 *
 * This file defines kernel symbols extracted from the
 * kernel image in their physical address.
 */

.include "%s"

.section .kernel

.incbin "%s"

''' % (source[0].path , source[1].path))
        for image in source[2:]:
            asmFile.write('''
.align 4
.section .%s
.incbin "%s"
''' % (os.path.splitext(image.name)[0], image.path))

def createMainC(target, source, env):
    with open(source[0].path) as inFile:
        with open(target[0].path, 'w') as outFile:
            externs = ''
            declarations = ''
            loads = ''
            for item in source[1:]:
                name = os.path.splitext(item.name)[0]
                if name == 'start' : name = 'kernel'
                externs += '''
extern char _start_%s[];
extern char _end_%s[];
''' % (name, name)
                declarations += '        void *%s_entry = NULL;\n' % (name,)
                loads += '''
        printf("Loading the %s...\\n");
        load_image(&%s_entry, _start_%s, _end_%s);
''' %(name, name, name, name)
            text = inFile.read()
            text = text.replace('__EXTERNAL_SYMBOLS_EDIT_MARKER__', externs)
            text = text.replace('__DECLARATIONS_EDIT_MARKER__', declarations)
            text = text.replace('__LOAD_STATEMENTS_EDIT_MARKER__', loads)
            outFile.write(text)

def createLinkerScript(target, source, env):
    with open(source[0].path) as inFile:
        with open(target[0].path, 'w') as outFile:
            linkerItems = ''
            for item in source[1:]:
                name = os.path.splitext(item.name)[0]
                if name == 'start' : name = 'kernel'
                linkerItems += '''
		_start_%s = .;
		*(.%s)
		_end_%s = .;
''' % (name, name, name)
            outFile.write(inFile.read().replace('__LINKER_ITEMS_EDIT_MARKER__', linkerItems))

startAxfS = Command('start.axf.S', images[0], ksymToLds)

#  In the following, it is crucially important that the order of the sources is as it is -- assumptions are
#  made in the functions doing the processing.

kernelS = Command('kernel.S', [startAxfS] + images, createKernelSFile)
mainC = Command('main.c', ['main.c.in'] + images, createMainC)
linkerScript = Command('linker.lds', ['linker.lds.in'] + images, createLinkerScript)

##  TODO:  deal with the situation where there are other .c and .S files in the directory.

objects = environment.Object(['arch.c' , kernelS, startAxfS, mainC])
Depends(objects, environment['configFiles'])
Depends(objects, images)
program = environment.Program('final', objects + [environment['baremetal_crt0']])
Depends(program, linkerScript)

Return('program')
