-- -----------------------------------------------------------------------------
--
-- age.sql
-- Jamie Wood 2016
-- 
-- Uses the riscv.sql emulator to run a C program which calculates a person's
--  age from their DOB and the current date.
-- 
-- Example usage:
--   select age(25, 10, 1995);
-- 
-- -----------------------------------------------------------------------------
-- 
--                                   C Code:
-- 
-- void output(int x) {
--     volatile int *debug = (volatile int *) 0x87000000;
--     debug[0] = x;
-- }
-- 
-- void loadDate(int *day, int *month, int *year, unsigned int read_addr) {
--     volatile int *memaddr = (volatile int *) read_addr;
--     *day = memaddr[0];
--     *month = memaddr[1];
--     *year = memaddr[2];
-- }
-- 
-- int age(int d1, int m1, int y1, int d2, int m2, int y2) {
--     int tmp = 0;
--     if (y2 > y1) {
--         tmp = y2 - y1;
--         if (m2 < m1) {
--             tmp = tmp - 1;
--         } else if ((m2 == m1) && (d2 < d1)) {
--             tmp = tmp - 1;
--         }
--     }
--     return tmp;
-- }
-- 
-- int main(void) {
--     int birthday, birthmonth, birthyear;
--     int nowday, nowmonth, nowyear;
-- 
--     // load the dates from known memory addresses
--     loadDate(&birthday, &birthmonth, &birthyear, 0x10000);
--     loadDate(&nowday, &nowmonth, &nowyear, 0x12000);
--     
--     output(age(birthday, birthmonth, birthyear, nowday, nowmonth, nowyear));
--     
--     return 0;
-- }
-- 
-- -----------------------------------------------------------------------------

-- Clear any old program and load the machine code into the raw_prog table
truncate table raw_prog;
truncate table program;

insert into raw_prog (str_instr) values
('10001117'), ('00010113'), ('fe010113'), ('138000ef'), ('0000006f'),
('fd010113'), ('02812623'), ('03010413'), ('fca42e23'), ('870007b7'), 
('fef42623'), ('fec42783'), ('fdc42703'), ('00e7a023'), ('00000013'), 
('02c12403'), ('03010113'), ('00008067'), ('fd010113'), ('02812623'), 
('03010413'), ('fca42e23'), ('fcb42c23'), ('fcc42a23'), ('fcd42823'), 
('fd042783'), ('fef42623'), ('fec42783'), ('0007a703'), ('fdc42783'), 
('00e7a023'), ('fec42783'), ('0047a703'), ('fd842783'), ('00e7a023'), 
('fec42783'), ('0087a703'), ('fd442783'), ('00e7a023'), ('00000013'), 
('02c12403'), ('03010113'), ('00008067'), ('fc010113'), ('02812e23'), 
('04010413'), ('fca42e23'), ('fcb42c23'), ('fcc42a23'), ('fcd42823'), 
('fce42623'), ('fcf42423'), ('fe042623'), ('fc842703'), ('fd442783'), 
('04e7da63'), ('fc842703'), ('fd442783'), ('40f707b3'), ('fef42623'), 
('fcc42703'), ('fd842783'), ('00f75a63'), ('fec42783'), ('fff78793'), 
('fef42623'), ('0280006f'), ('fcc42703'), ('fd842783'), ('00f71e63'), 
('fd042703'), ('fdc42783'), ('00f75863'), ('fec42783'), ('fff78793'), 
('fef42623'), ('fec42783'), ('00078513'), ('03c12403'), ('04010113'), 
('00008067'), ('fd010113'), ('02112623'), ('02812423'), ('03010413'), 
('fe440613'), ('fe840713'), ('fec40793'), ('000106b7'), ('00070593'), 
('00078513'), ('eddff0ef'), ('fd840613'), ('fdc40713'), ('fe040793'), 
('000126b7'), ('00070593'), ('00078513'), ('ec1ff0ef'), ('fec42503'), 
('fe842583'), ('fe442603'), ('fe042683'), ('fdc42703'), ('fd842783'), 
('f09ff0ef'), ('00050793'), ('00078513'), ('e65ff0ef'), ('00000793'), 
('00078513'), ('02c12083'), ('02812403'), ('03010113'), ('00008067');

-- Disassemble the machine code into a form we can execute
call decode_program();

-- Delete the old function if it exists
drop function if exists age;

delimiter $$

-- ---------------------------------------------------------- --
-- Load the dates into memory and start the program executing --
-- ---------------------------------------------------------- --
create function age(birth_day int, birth_month int, birth_year int) returns int
begin
    
    -- Reset the registers and memory in case we've run something previously
    call reset_emulator();
    
    -- Store the birth date where the C code looks for it (0x10000)
    call mem_write_word(conv('10000', 16, 10), birth_day);
    call mem_write_word(conv('10004', 16, 10), birth_month);
    call mem_write_word(conv('10008', 16, 10), birth_year);
    
    -- Store the current date where the C code looks for it (0x12000)
    select now() into @now;
    call mem_write_word(conv('12000', 16, 10), day(@now));
    call mem_write_word(conv('12004', 16, 10), month(@now));
    call mem_write_word(conv('12008', 16, 10), year(@now));

    -- Execute the program
    set @_ = execute_program();
    
    -- Return the result, cast to an integer
    return (select cast(msg as unsigned int) from output limit 1);
end $$

delimiter ;